Add some sidebars and license stuff
This commit is contained in:
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
pnpm pre-commit
|
@ -4,4 +4,6 @@
|
|||||||
**/node_modules
|
**/node_modules
|
||||||
/public
|
/public
|
||||||
/dist
|
/dist
|
||||||
|
.husky
|
||||||
|
src/assets/
|
||||||
|
|
||||||
|
44
license-emit.js
Normal file
44
license-emit.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { spawn } from 'child_process';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { outputJsonSync } from 'fs-extra/esm';
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
const licenseReportFile = [`${__dirname}/src/assets/licenses.json`];
|
||||||
|
|
||||||
|
const licenseReportExec = {
|
||||||
|
// exec: `${__dirname}/node_modules/.bin/license-report`,
|
||||||
|
exec: `license-report`,
|
||||||
|
args: [`--config=${__dirname}/license-report.config.json`],
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
name: z.string(),
|
||||||
|
licenseType: z.string(),
|
||||||
|
author: z.string(),
|
||||||
|
link: z.string().url().optional(),
|
||||||
|
installedFrom: z.string().url().optional(),
|
||||||
|
}[]
|
||||||
|
*/
|
||||||
|
const externalLicenses = [];
|
||||||
|
|
||||||
|
const emitReport = (file, toExec, externalLicenses) => {
|
||||||
|
const exec = spawn(toExec.exec, toExec.args);
|
||||||
|
|
||||||
|
let licensesString = '';
|
||||||
|
exec.stdout.on('data', (data) => {
|
||||||
|
licensesString += data.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
exec.on('close', (code) => {
|
||||||
|
console.log(`child process exited with code ${code}`);
|
||||||
|
const licenses = JSON.parse(licensesString);
|
||||||
|
const report = licenses.concat(externalLicenses);
|
||||||
|
outputJsonSync(file, report, { spaces: 2 });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const file of licenseReportFile) {
|
||||||
|
emitReport(file, licenseReportExec, externalLicenses);
|
||||||
|
}
|
4
license-report.config.json
Normal file
4
license-report.config.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"output": "json",
|
||||||
|
"fields": ["name", "licenseType", "author", "link", "installedFrom"]
|
||||||
|
}
|
35
package.json
35
package.json
@ -3,23 +3,40 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"lint-staged": {
|
||||||
|
"*": [
|
||||||
|
"node license-emit.js"
|
||||||
|
],
|
||||||
|
"*.{ts,tsx,js,cjs,json,html,css}": [
|
||||||
|
"prettier --write"
|
||||||
|
],
|
||||||
|
"src/**/*.{ts,tsx}": [
|
||||||
|
"eslint --fix --report-unused-disable-directives --max-warnings 0"
|
||||||
|
]
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"pre-commit": "lint-staged",
|
||||||
|
"pre-build:license-report": "node license-emit.js",
|
||||||
|
"build": "run-s pre-build build:*",
|
||||||
|
"build:vite": "tsc && vite build",
|
||||||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"fmt": "prettier --write \"**/*.{ts,tsx,js,cjs,json,html,css}\"",
|
"fmt": "prettier --write \"**/*.{ts,tsx,js,cjs,json,html,css}\"",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview",
|
||||||
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.11.1",
|
|
||||||
"@emotion/styled": "^11.11.0",
|
|
||||||
"@excalidraw/excalidraw": "^0.15.2",
|
"@excalidraw/excalidraw": "^0.15.2",
|
||||||
"@fontsource/roboto": "^5.0.5",
|
"@fontsource/roboto": "^5.0.5",
|
||||||
"@mui/icons-material": "^5.14.1",
|
"@mui/icons-material": "^5.14.1",
|
||||||
"@mui/material": "^5.14.1",
|
"@mui/material": "^5.14.1",
|
||||||
|
"@mui/styled-engine-sc": "^5.12.0",
|
||||||
"jotai": "^2.2.2",
|
"jotai": "^2.2.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0",
|
||||||
|
"styled-components": "^6.0.4",
|
||||||
|
"ts-pattern": "^5.0.4",
|
||||||
|
"zod": "^3.21.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.2.14",
|
"@types/react": "^18.2.14",
|
||||||
@ -30,8 +47,14 @@
|
|||||||
"eslint": "^8.44.0",
|
"eslint": "^8.44.0",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.1",
|
"eslint-plugin-react-refresh": "^0.4.1",
|
||||||
|
"fs-extra": "^11.1.1",
|
||||||
|
"husky": "^8.0.0",
|
||||||
|
"license-report": "^6.4.0",
|
||||||
|
"lint-staged": "^13.2.3",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.0",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.0.2",
|
||||||
"vite": "^4.4.0"
|
"vite": "^4.4.0",
|
||||||
|
"vite-tsconfig-paths": "^4.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3139
pnpm-lock.yaml
generated
3139
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
124
src/App.tsx
124
src/App.tsx
@ -1,119 +1,21 @@
|
|||||||
import { Excalidraw, Sidebar } from '@excalidraw/excalidraw';
|
import styled from 'styled-components';
|
||||||
import { Box, Button, Container } from '@mui/material';
|
import ExcalidrawMain from '@/Components/Excalidraw';
|
||||||
import type {
|
|
||||||
ExcalidrawProps,
|
|
||||||
ExcalidrawImperativeAPI,
|
|
||||||
} from '@excalidraw/excalidraw/types/types';
|
|
||||||
import { FC, useState } from 'react';
|
|
||||||
|
|
||||||
const ExcalidrawContainer: FC<
|
export const Main = styled.div`
|
||||||
ExcalidrawProps & { refCallback: (api: ExcalidrawImperativeAPI) => void }
|
overflow: hidden;
|
||||||
> = (props) => {
|
position: absolute;
|
||||||
return (
|
top: 50%;
|
||||||
<Container>
|
left: 50%;
|
||||||
<Box
|
margin-right: -50%;
|
||||||
sx={{
|
transform: translate(-50%, -50%);
|
||||||
border: '1.5px solid',
|
`;
|
||||||
width: '95vw',
|
|
||||||
height: '95vh',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Excalidraw {...props} ref={props.refCallback} />
|
|
||||||
</Box>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
// TODO: jotai premitives
|
|
||||||
const isCollaborating = false;
|
|
||||||
|
|
||||||
const [excalidrawAPI, setExcalidrawAPI] = useState<ExcalidrawImperativeAPI>();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Main>
|
||||||
sx={{
|
<ExcalidrawMain />
|
||||||
display: 'grid',
|
</Main>
|
||||||
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;
|
export default App;
|
||||||
|
87
src/Components/Excalidraw.tsx
Normal file
87
src/Components/Excalidraw.tsx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import { ExcalidrawImperativeAPI } from '@excalidraw/excalidraw/types/types';
|
||||||
|
import ExcalidrawContainer from './Excalidraw/Container';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import Sidebar, { SidebarVariant } from './Excalidraw/Sidebar';
|
||||||
|
import RightTopUI from './Excalidraw/RightTopUI';
|
||||||
|
|
||||||
|
export const ExcalidrawMain = () => {
|
||||||
|
const [excalidrawAPI, setExcalidrawAPI] = useState<ExcalidrawImperativeAPI>();
|
||||||
|
|
||||||
|
const [sidebarVariant, setSidebarVariant] =
|
||||||
|
useState<SidebarVariant>('general');
|
||||||
|
|
||||||
|
const [toggleState, setToggleState] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const onClose = useCallback(
|
||||||
|
() => setToggleState(!toggleState),
|
||||||
|
[toggleState]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ExcalidrawContainer
|
||||||
|
refCallback={(api) => setExcalidrawAPI(api)}
|
||||||
|
autoFocus={true}
|
||||||
|
name="Excalidraw with WebRTC"
|
||||||
|
renderTopRightUI={() => (
|
||||||
|
<RightTopUI
|
||||||
|
toggleState={toggleState}
|
||||||
|
setToggleState={setToggleState}
|
||||||
|
setSidebarVariant={setSidebarVariant}
|
||||||
|
excalidrawAPI={excalidrawAPI!}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
renderSidebar={() => {
|
||||||
|
return <Sidebar variant={sidebarVariant} variantProps={{ onClose }} />;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
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 ExcalidrawMain;
|
68
src/Components/Excalidraw/Container.tsx
Normal file
68
src/Components/Excalidraw/Container.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { Excalidraw } from '@excalidraw/excalidraw';
|
||||||
|
import type {
|
||||||
|
ExcalidrawProps,
|
||||||
|
ExcalidrawImperativeAPI,
|
||||||
|
} from '@excalidraw/excalidraw/types/types';
|
||||||
|
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export type WindowRect = {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ExcalidrawBoxProps = {
|
||||||
|
$windowRect: WindowRect;
|
||||||
|
$scale?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ExcalidrawBox = styled.div<ExcalidrawBoxProps>`
|
||||||
|
${(props) => {
|
||||||
|
const { $windowRect: windowRect, $scale: $scale } = props;
|
||||||
|
const scale = $scale! ? $scale : 1;
|
||||||
|
const { width, height } = windowRect;
|
||||||
|
|
||||||
|
const scaledWidth = width * scale;
|
||||||
|
const scaledHeight = height * scale;
|
||||||
|
return `
|
||||||
|
width: ${scaledWidth}px;
|
||||||
|
height: ${scaledHeight}px;
|
||||||
|
`;
|
||||||
|
}}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export type ExcalidrawContainerProps = ExcalidrawProps & {
|
||||||
|
refCallback: (api: ExcalidrawImperativeAPI) => void;
|
||||||
|
} & Omit<ExcalidrawBoxProps, '$windowRect'>;
|
||||||
|
|
||||||
|
export const ExcalidrawContainer: FC<ExcalidrawContainerProps> = (props) => {
|
||||||
|
const scale = useMemo(
|
||||||
|
() => (props.$scale ? props.$scale : 1),
|
||||||
|
[props.$scale]
|
||||||
|
);
|
||||||
|
|
||||||
|
const [windowRect, setWindowRect] = useState<WindowRect>({
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onResize = useCallback(() => {
|
||||||
|
setWindowRect({
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener('resize', onResize);
|
||||||
|
return () => window.removeEventListener('resize', onResize);
|
||||||
|
}, [onResize]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ExcalidrawBox $scale={scale} $windowRect={windowRect}>
|
||||||
|
<Excalidraw {...props} ref={props.refCallback} />
|
||||||
|
</ExcalidrawBox>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ExcalidrawContainer;
|
43
src/Components/Excalidraw/RightTopUI.tsx
Normal file
43
src/Components/Excalidraw/RightTopUI.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { ExcalidrawImperativeAPI } from '@excalidraw/excalidraw/types/types';
|
||||||
|
import { FC, useCallback } from 'react';
|
||||||
|
import { ToggleButton, ToggleButtonGroup } from '@/Components/Utilities';
|
||||||
|
import { SidebarVariant, SidebarVariantSchema } from './Sidebar';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const RightTopUIPropsScheme = z.object({
|
||||||
|
setSidebarVariant: z.function().args(SidebarVariantSchema).returns(z.void()),
|
||||||
|
setToggleState: z.function().args(z.boolean()).returns(z.void()),
|
||||||
|
toggleState: z.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type RightTopUIProps = z.infer<typeof RightTopUIPropsScheme> & {
|
||||||
|
excalidrawAPI: ExcalidrawImperativeAPI;
|
||||||
|
};
|
||||||
|
|
||||||
|
const RightTopUI: FC<RightTopUIProps> = (props) => {
|
||||||
|
const excalidrawAPI = props.excalidrawAPI;
|
||||||
|
|
||||||
|
const sidebarToggle = useCallback(
|
||||||
|
(variant: SidebarVariant) => {
|
||||||
|
if (excalidrawAPI && !props.toggleState) {
|
||||||
|
props.setToggleState(props.toggleState);
|
||||||
|
props.setSidebarVariant(variant);
|
||||||
|
excalidrawAPI.toggleMenu('customSidebar');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[excalidrawAPI, props]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToggleButtonGroup
|
||||||
|
exclusive
|
||||||
|
onChange={(_, value: SidebarVariant) => sidebarToggle(value)}
|
||||||
|
disabled={props.toggleState}
|
||||||
|
>
|
||||||
|
<ToggleButton value="general">General</ToggleButton>
|
||||||
|
<ToggleButton value="collaboration">Collaboration</ToggleButton>
|
||||||
|
</ToggleButtonGroup>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RightTopUI;
|
40
src/Components/Excalidraw/Sidebar.tsx
Normal file
40
src/Components/Excalidraw/Sidebar.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { match } from 'ts-pattern';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import CollaborationSidebar, {
|
||||||
|
CollaborationSidebarPropsSchema,
|
||||||
|
} from './Sidebar/Collaboration';
|
||||||
|
import GeneralSidebar, { GeneralSidebarPropsSchema } from './Sidebar/General';
|
||||||
|
|
||||||
|
export const SidebarVariantSchema = z.enum(['collaboration', 'general']);
|
||||||
|
|
||||||
|
export type SidebarVariant = z.infer<typeof SidebarVariantSchema>;
|
||||||
|
|
||||||
|
export const SidebarVariantPropsSchema = z.union([
|
||||||
|
CollaborationSidebarPropsSchema,
|
||||||
|
GeneralSidebarPropsSchema,
|
||||||
|
]);
|
||||||
|
|
||||||
|
export type SidebarVariantProps = z.infer<typeof SidebarVariantPropsSchema>;
|
||||||
|
|
||||||
|
export const SidebarProrps = z.object({
|
||||||
|
variant: SidebarVariantSchema,
|
||||||
|
variantProps: SidebarVariantPropsSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type SidebarProps = z.infer<typeof SidebarProrps>;
|
||||||
|
|
||||||
|
export const Sidebar: FC<SidebarProps> = (props) => {
|
||||||
|
return match(props.variant)
|
||||||
|
.with('collaboration', () => {
|
||||||
|
const _props = CollaborationSidebarPropsSchema.parse(props.variantProps);
|
||||||
|
return <CollaborationSidebar {..._props} />;
|
||||||
|
})
|
||||||
|
.with('general', () => {
|
||||||
|
const _props = GeneralSidebarPropsSchema.parse(props.variantProps);
|
||||||
|
return <GeneralSidebar {..._props} />;
|
||||||
|
})
|
||||||
|
.exhaustive();
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Sidebar;
|
22
src/Components/Excalidraw/Sidebar/Base.tsx
Normal file
22
src/Components/Excalidraw/Sidebar/Base.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { SidebarProvider } from '@/Components/Utilities';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
export const SidebarBasePropsSchema = z.object({
|
||||||
|
onClose: z.function().returns(z.void()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type SidebarBaseProps = z.infer<typeof SidebarBasePropsSchema> & {
|
||||||
|
children?: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SidebarProviderBase: FC<SidebarBaseProps> = (props) => {
|
||||||
|
return (
|
||||||
|
<SidebarProvider dockable={true} onClose={props.onClose}>
|
||||||
|
{props.children}
|
||||||
|
</SidebarProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SidebarProviderBase;
|
23
src/Components/Excalidraw/Sidebar/Collaboration.tsx
Normal file
23
src/Components/Excalidraw/Sidebar/Collaboration.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { Button, SidebarHeader } from '@/Components/Utilities';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import SidebarProviderBase, { SidebarBasePropsSchema } from './Base';
|
||||||
|
|
||||||
|
export type CollaborationSidebarProps = z.infer<
|
||||||
|
typeof CollaborationSidebarPropsSchema
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const CollaborationSidebarPropsSchema = z
|
||||||
|
.object({})
|
||||||
|
.merge(SidebarBasePropsSchema);
|
||||||
|
|
||||||
|
const CollaborationSidebar: FC<CollaborationSidebarProps> = (props) => {
|
||||||
|
return (
|
||||||
|
<SidebarProviderBase onClose={props.onClose}>
|
||||||
|
<SidebarHeader>Collaboration Settings</SidebarHeader>
|
||||||
|
<Button>Button</Button>
|
||||||
|
</SidebarProviderBase>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CollaborationSidebar;
|
21
src/Components/Excalidraw/Sidebar/General.tsx
Normal file
21
src/Components/Excalidraw/Sidebar/General.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { Button, SidebarHeader } from '@/Components/Utilities';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import SidebarProviderBase, { SidebarBasePropsSchema } from './Base';
|
||||||
|
|
||||||
|
export type GeneralSidebarProps = z.infer<typeof GeneralSidebarPropsSchema>;
|
||||||
|
|
||||||
|
export const GeneralSidebarPropsSchema = z
|
||||||
|
.object({})
|
||||||
|
.merge(SidebarBasePropsSchema);
|
||||||
|
|
||||||
|
const GeneralSidebar: FC<GeneralSidebarProps> = (props) => {
|
||||||
|
return (
|
||||||
|
<SidebarProviderBase onClose={props.onClose}>
|
||||||
|
<SidebarHeader>General Settings</SidebarHeader>
|
||||||
|
<Button>Button</Button>
|
||||||
|
</SidebarProviderBase>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GeneralSidebar;
|
16
src/Components/Utilities.tsx
Normal file
16
src/Components/Utilities.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
import {
|
||||||
|
Button as ButtonBase,
|
||||||
|
ToggleButtonGroup as ToggleButtonGroupBase,
|
||||||
|
ToggleButton as ToggleButtonBase,
|
||||||
|
} from '@mui/material';
|
||||||
|
import { Sidebar } from '@excalidraw/excalidraw';
|
||||||
|
|
||||||
|
export const SidebarProvider = styled(Sidebar)``;
|
||||||
|
export const SidebarHeader = styled(Sidebar.Header)``;
|
||||||
|
|
||||||
|
export const Button = styled(ButtonBase)``;
|
||||||
|
|
||||||
|
export const ToggleButtonGroup = styled(ToggleButtonGroupBase)``;
|
||||||
|
|
||||||
|
export const ToggleButton = styled(ToggleButtonBase)``;
|
198
src/assets/licenses.json
Normal file
198
src/assets/licenses.json
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "@excalidraw/excalidraw",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "n/a",
|
||||||
|
"link": "https://github.com/excalidraw/excalidraw",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/@excalidraw/excalidraw/-/excalidraw-0.15.2.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@fontsource/roboto",
|
||||||
|
"licenseType": "Apache-2.0",
|
||||||
|
"author": "Google Inc.",
|
||||||
|
"link": "git+https://github.com/fontsource/font-files.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.0.5.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@mui/icons-material",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "MUI Team",
|
||||||
|
"link": "git+https://github.com/mui/material-ui.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.14.1.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@mui/material",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "MUI Team",
|
||||||
|
"link": "git+https://github.com/mui/material-ui.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/@mui/material/-/material-5.14.1.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@mui/styled-engine-sc",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "MUI Team",
|
||||||
|
"link": "git+https://github.com/mui/material-ui.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/@mui/styled-engine-sc/-/styled-engine-sc-5.12.0.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "jotai",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "Daishi Kato",
|
||||||
|
"link": "git+https://github.com/pmndrs/jotai.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/jotai/-/jotai-2.2.2.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "react",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "n/a",
|
||||||
|
"link": "git+https://github.com/facebook/react.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "react-dom",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "n/a",
|
||||||
|
"link": "git+https://github.com/facebook/react.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "styled-components",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "Glen Maddern",
|
||||||
|
"link": "git+https://github.com/styled-components/styled-components.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/styled-components/-/styled-components-6.0.4.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ts-pattern",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "Gabriel Vergnaud",
|
||||||
|
"link": "git+ssh://git@github.com/gvergnaud/ts-pattern.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-5.0.4.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "zod",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "Colin McDonnell <colin@colinhacks.com>",
|
||||||
|
"link": "git+https://github.com/colinhacks/zod.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@types/react",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "n/a",
|
||||||
|
"link": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/@types/react/-/react-18.2.14.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@types/react-dom",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "n/a",
|
||||||
|
"link": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.6.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@typescript-eslint/eslint-plugin",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "n/a",
|
||||||
|
"link": "git+https://github.com/typescript-eslint/typescript-eslint.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.61.0.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@typescript-eslint/parser",
|
||||||
|
"licenseType": "BSD-2-Clause",
|
||||||
|
"author": "n/a",
|
||||||
|
"link": "git+https://github.com/typescript-eslint/typescript-eslint.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.61.0.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@vitejs/plugin-react-swc",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "Arnaud Barré (https://github.com/ArnaudBarre)",
|
||||||
|
"link": "git+https://github.com/vitejs/vite-plugin-react-swc.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.3.2.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eslint",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
||||||
|
"link": "git+https://github.com/eslint/eslint.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eslint-plugin-react-hooks",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "n/a",
|
||||||
|
"link": "git+https://github.com/facebook/react.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eslint-plugin-react-refresh",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "Arnaud Barré (https://github.com/ArnaudBarre)",
|
||||||
|
"link": "git+https://github.com/ArnaudBarre/eslint-plugin-react-refresh.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.1.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fs-extra",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "JP Richardson <jprichardson@gmail.com>",
|
||||||
|
"link": "git+https://github.com/jprichardson/node-fs-extra.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "husky",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "Typicode <typicode@gmail.com>",
|
||||||
|
"link": "git+https://github.com/typicode/husky.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/husky/-/husky-8.0.0.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "license-report",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "Yaniv Kessler",
|
||||||
|
"link": "git+https://github.com/ironSource/license-report.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/license-report/-/license-report-6.4.0.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "lint-staged",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "Andrey Okonetchnikov <andrey@okonet.ru>",
|
||||||
|
"link": "git+https://github.com/okonet/lint-staged.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.3.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "npm-run-all",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "Toru Nagashima",
|
||||||
|
"link": "git+https://github.com/mysticatea/npm-run-all.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "prettier",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "James Long",
|
||||||
|
"link": "git+https://github.com/prettier/prettier.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "typescript",
|
||||||
|
"licenseType": "Apache-2.0",
|
||||||
|
"author": "Microsoft Corp.",
|
||||||
|
"link": "git+https://github.com/Microsoft/TypeScript.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vite",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "Evan You",
|
||||||
|
"link": "git+https://github.com/vitejs/vite.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/vite/-/vite-4.4.0.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vite-tsconfig-paths",
|
||||||
|
"licenseType": "MIT",
|
||||||
|
"author": "aleclarson",
|
||||||
|
"link": "git+https://github.com/aleclarson/vite-tsconfig-paths.git",
|
||||||
|
"installedFrom": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.2.0.tgz"
|
||||||
|
}
|
||||||
|
]
|
5
src/index.css
Normal file
5
src/index.css
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
place-items: center;
|
||||||
|
}
|
@ -1,11 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import App from './App.tsx';
|
import App from './App.tsx';
|
||||||
|
import './index.css';
|
||||||
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(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
|
13
src/types/license.ts
Normal file
13
src/types/license.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const License = z.array(
|
||||||
|
z.object({
|
||||||
|
name: z.string(),
|
||||||
|
licenseType: z.string(),
|
||||||
|
author: z.string(),
|
||||||
|
link: z.string().url().optional(),
|
||||||
|
installedFrom: z.string().url().optional(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
export type License = z.infer<typeof License>;
|
@ -5,7 +5,6 @@
|
|||||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|
||||||
/* Bundler mode */
|
/* Bundler mode */
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
@ -13,13 +12,22 @@
|
|||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
/* Linting */
|
/* Linting */
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
/* Aliases */
|
||||||
|
"baseUrl": "./",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"],
|
||||||
|
"@mui/styled-engine": ["./node_modules/@mui/styled-engine-sc"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,22 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import react from '@vitejs/plugin-react-swc';
|
import react from '@vitejs/plugin-react-swc';
|
||||||
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@mui/styled-engine': '@mui/styled-engine-sc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
optimizeDeps: {
|
||||||
|
include: [
|
||||||
|
'react',
|
||||||
|
'react-dom',
|
||||||
|
'@mui/material',
|
||||||
|
'@excalidraw/excalidraw',
|
||||||
|
'styled-components',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [react(), tsconfigPaths()],
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user