Initial commit

This commit is contained in:
2023-07-20 17:00:34 +09:00
commit 3397d7096d
13 changed files with 2383 additions and 0 deletions

27
.eslintrc.cjs Normal file
View File

@ -0,0 +1,27 @@
/* eslint-env node */
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:react-hooks/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: true,
tsconfigRootDir: __dirname,
},
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'@typescript-eslint/no-non-null-assertion': 'off',
},
};

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

7
.prettierignore Normal file
View File

@ -0,0 +1,7 @@
**/.git
**/.svn
**/.hg
**/node_modules
/public
/dist

9
.prettierrc.js Normal file
View File

@ -0,0 +1,9 @@
/** @type {import("prettier").Options} */
const config = {
trailingComma: 'es5',
tabWidth: 2,
semi: true,
singleQuote: true,
};
export default config;

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Excalidraw with WebRTC</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

37
package.json Normal file
View File

@ -0,0 +1,37 @@
{
"name": "excalidraw-p2p",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"fmt": "prettier --write \"**/*.{ts,tsx,js,cjs,json,html,css}\"",
"preview": "vite preview"
},
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@excalidraw/excalidraw": "^0.15.2",
"@fontsource/roboto": "^5.0.5",
"@mui/icons-material": "^5.14.1",
"@mui/material": "^5.14.1",
"jotai": "^2.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"@typescript-eslint/eslint-plugin": "^5.61.0",
"@typescript-eslint/parser": "^5.61.0",
"@vitejs/plugin-react-swc": "^3.3.2",
"eslint": "^8.44.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.1",
"prettier": "^3.0.0",
"typescript": "^5.0.2",
"vite": "^4.4.0"
}
}

2090
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

119
src/App.tsx Normal file
View File

@ -0,0 +1,119 @@
import { Excalidraw, Sidebar } from '@excalidraw/excalidraw';
import { Box, Button, Container } from '@mui/material';
import type {
ExcalidrawProps,
ExcalidrawImperativeAPI,
} from '@excalidraw/excalidraw/types/types';
import { FC, useState } from 'react';
const ExcalidrawContainer: FC<
ExcalidrawProps & { refCallback: (api: ExcalidrawImperativeAPI) => void }
> = (props) => {
return (
<Container>
<Box
sx={{
border: '1.5px solid',
width: '95vw',
height: '95vh',
}}
>
<Excalidraw {...props} ref={props.refCallback} />
</Box>
</Container>
);
};
function App() {
// TODO: jotai premitives
const isCollaborating = false;
const [excalidrawAPI, setExcalidrawAPI] = useState<ExcalidrawImperativeAPI>();
return (
<Container
sx={{
display: 'grid',
height: '100%',
width: '100%',
placeContent: 'center',
overflow: 'hidden',
}}
>
<ExcalidrawContainer
refCallback={(api) => setExcalidrawAPI(api)}
isCollaborating={isCollaborating}
autoFocus={true}
name="Excalidraw with WebRTC"
renderTopRightUI={() => {
return (
<Button
onClick={() => {
excalidrawAPI?.toggleMenu('customSidebar');
}}
>
Button
</Button>
);
}}
renderSidebar={() => {
return (
<Sidebar dockable={true}>
<Sidebar.Header> Header </Sidebar.Header>
<Button>Button</Button>
</Sidebar>
);
}}
/>
</Container>
);
}
/*
export interface ExcalidrawProps {
// Render UI elements
renderTopRightUI?: (isMobile: boolean, appState: AppState) => JSX.Element | null;
renderSidebar?: () => JSX.Element | null;
onChange?: (elements: readonly ExcalidrawElement[], appState: AppState, files: BinaryFiles) => void;
initialData?: ExcalidrawInitialDataState | null | Promise<ExcalidrawInitialDataState | null>;
excalidrawRef?: ForwardRef<ExcalidrawAPIRefValue>;
isCollaborating?: boolean;
onPointerUpdate?: (payload: {
pointer: {
x: number;
y: number;
};
button: "down" | "up";
pointersMap: Gesture["pointers"];
}) => void;
onPaste?: (data: ClipboardData, event: ClipboardEvent | null) => Promise<boolean> | boolean;
langCode?: Language["code"];
onPointerDown?: (activeTool: AppState["activeTool"], pointerDownState: PointerDownState) => void;
generateIdForFile?: (file: File) => string | Promise<string>;
UIOptions?: Partial<UIOptions>;
renderCustomStats?: (elements: readonly NonDeletedExcalidrawElement[], appState: AppState) => JSX.Element;
viewModeEnabled?: boolean;
zenModeEnabled?: boolean;
gridModeEnabled?: boolean;
libraryReturnUrl?: string;
name?: string;
theme?: Theme;
detectScroll?: boolean;
handleKeyboardGlobally?: boolean;
onLibraryChange?: (libraryItems: LibraryItems) => void | Promise<any>;
autoFocus?: boolean;
onLinkOpen?: (element: NonDeletedExcalidrawElement, event: CustomEvent<{
nativeEvent: MouseEvent | React.PointerEvent<HTMLCanvasElement>;
}>) => void;
onScrollChange?: (scrollX: number, scrollY: number) => void;
children?: React.ReactNode;
}
*/
export default App;

14
src/main.tsx Normal file
View File

@ -0,0 +1,14 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);

1
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

25
tsconfig.json Normal file
View File

@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

10
tsconfig.node.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

7
vite.config.ts Normal file
View File

@ -0,0 +1,7 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
});