refactor: reorganize src structure

This commit is contained in:
2026-05-31 12:46:21 +00:00
parent f349617427
commit 25bf171f6b
21 changed files with 26 additions and 26 deletions
+110
View File
@@ -0,0 +1,110 @@
import { type DrawingMeta } from "../../core/shared";
type DrawingSidebarProps = {
open: boolean;
drawings: DrawingMeta[];
activeId: string | null;
activeTitle: string;
onClose: () => void;
onCreate: () => void;
onSelect: (drawingId: string) => void;
onDelete: (drawingId: string) => void;
onTitleChange: (title: string) => void;
onTitleSubmit: () => void;
};
function DrawerIcon() {
return (
<svg viewBox="0 0 24 24" aria-hidden="true">
<rect x="3.5" y="5" width="17" height="14" rx="2.5" fill="none" stroke="currentColor" strokeWidth="1.8" />
<path d="M9 5v14" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
<path d="M5.75 9h1.5M5.75 12h1.5M5.75 15h1.5" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
</svg>
);
}
export function DrawingsToggle({ onClick }: { onClick: () => void }) {
return (
<button
type="button"
className="secondary-button app-actions-toggle"
onClick={onClick}
aria-label="Open drawings"
>
<DrawerIcon />
<span className="sr-only">Open drawings</span>
</button>
);
}
export function DrawingSidebar({
open,
drawings,
activeId,
activeTitle,
onClose,
onCreate,
onSelect,
onDelete,
onTitleChange,
onTitleSubmit,
}: DrawingSidebarProps) {
return (
<>
<div
className={open ? "sidebar-backdrop sidebar-backdrop-open" : "sidebar-backdrop"}
onClick={onClose}
aria-hidden={!open}
/>
<aside className={open ? "sidebar sidebar-open" : "sidebar"} aria-hidden={!open}>
<div className="sidebar-header">
<h1>excali-box</h1>
<div className="sidebar-actions">
<button type="button" className="primary-button" onClick={onCreate}>
New
</button>
<button type="button" className="icon-button" onClick={onClose} aria-label="Close drawings">
×
</button>
</div>
</div>
<div className="drawing-list">
{drawings.map((drawing) => (
<div
key={drawing.id}
className={drawing.id === activeId ? "drawing-item drawing-item-active" : "drawing-item"}
>
{drawing.id === activeId ? (
<div className="drawing-link">
<input
className="title-input"
value={activeTitle}
onChange={(event) => onTitleChange(event.target.value)}
onBlur={onTitleSubmit}
onKeyDown={(event) => {
if (event.key === "Enter") {
event.currentTarget.blur();
}
}}
/>
</div>
) : (
<button type="button" className="drawing-link" onClick={() => onSelect(drawing.id)}>
<span className="drawing-title">{drawing.title}</span>
</button>
)}
<button
type="button"
className="icon-button"
onClick={() => onDelete(drawing.id)}
aria-label={`Delete ${drawing.title}`}
>
×
</button>
</div>
))}
</div>
</aside>
</>
);
}
+61
View File
@@ -0,0 +1,61 @@
import "../../../node_modules/@excalidraw/excalidraw/dist/prod/index.css";
import { Excalidraw } from "@excalidraw/excalidraw";
import type {
AppState,
BinaryFiles,
ExcalidrawInitialDataState,
} from "@excalidraw/excalidraw/types";
import { type ScenePayload } from "../../core/shared";
type EditorCanvasProps = {
activeId: string | null;
scene: ScenePayload | null;
loading: boolean;
error: string | null;
editorReloadNonce: number;
onSceneChange: (scene: ScenePayload) => void;
onEditorActivity: () => void;
};
function sceneToInitialData(scene: ScenePayload): ExcalidrawInitialDataState {
return scene as ExcalidrawInitialDataState;
}
function sceneFromEditor(
elements: readonly unknown[],
appState: AppState,
files: BinaryFiles,
): ScenePayload {
return {
elements: [...elements],
appState: appState as unknown as Record<string, unknown>,
files: files as unknown as Record<string, unknown>,
};
}
export function EditorCanvas({
activeId,
scene,
loading,
error,
editorReloadNonce,
onSceneChange,
onEditorActivity,
}: EditorCanvasProps) {
if (loading || !scene) {
return <div className="editor-loading">{error ?? "Loading..."}</div>;
}
return (
<div className="editor-frame">
<Excalidraw
key={`${activeId}:${editorReloadNonce}`}
initialData={sceneToInitialData(scene)}
onChange={(elements, appState, files) => {
onEditorActivity();
onSceneChange(sceneFromEditor(elements, appState, files));
}}
/>
</div>
);
}