Skip to content

Commit

Permalink
Refactor tools
Browse files Browse the repository at this point in the history
  • Loading branch information
mateuszmigas committed Mar 6, 2024
1 parent 1e4f9f9 commit b3bfe4c
Show file tree
Hide file tree
Showing 33 changed files with 862 additions and 192 deletions.
4 changes: 3 additions & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-menubar": "^1.0.4",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@tauri-apps/api": "^1",
"class-variance-authority": "^0.7.0",
Expand All @@ -33,6 +35,7 @@
"@types/node": "^20.11.24",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@uiw/react-color-sketch": "^2.1.1",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.18",
"postcss": "^8.4.35",
Expand All @@ -42,4 +45,3 @@
"vitest": "^1.3.1"
}
}

3 changes: 2 additions & 1 deletion apps/web/src/components/appContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { LayersPanel } from "./panels/layersPanel";
import { HistoryPanel } from "./panels/historyPanel";
import { MetadataPanel } from "./panels/metadataPanel";
import { CanvasViewport } from "./canvasViewport";
import { ToolSettingsBar } from "./toolSettingsBar";

const LeftContent = () => {
return (
Expand Down Expand Up @@ -51,7 +52,7 @@ export const AppContent = () => {
<ResizableHandle />
<ResizablePanel>
<>
<div className="h-panel-header border-b px-2">Tool settings todo</div>
<ToolSettingsBar />
<CanvasViewport />
</>
</ResizablePanel>
Expand Down
37 changes: 20 additions & 17 deletions apps/web/src/components/canvasViewport.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import { Viewport, defaultViewport } from "@/utils/manipulation";
import { useRef } from "react";
import { DrawingToolConfig } from "@/drawing-tools";
import { useCanvasRenderer } from "@/hooks/useCanvasRenderer";
import { useViewportManipulator } from "@/hooks/useViewportManipulator";
import { useFitCanvasToViewport as useFitToViewportOnInit } from "@/hooks/useFitToViewportOnInit";
import { useDrawTool } from "@/hooks/useDrawTool";

const toolConfig: DrawingToolConfig = {
type: "pen",
settings: {
color: "blue",
size: 4,
},
};
import { useToolStore } from "@/store/toolState";
import { Position } from "@/utils/common";

const size = {
width: 1000,
Expand All @@ -21,21 +14,31 @@ const size = {

const layerId = "1";

const screenPositionToCanvasPosition = (
position: Position,
viewport: Viewport
) => {
return {
x: (position.x - viewport.position.x) / viewport.zoom,
y: (position.y - viewport.position.y) / viewport.zoom,
};
};

export const CanvasViewport = () => {
const hostElementRef = useRef<HTMLDivElement>(null);
const viewportRef = useRef<Viewport>(defaultViewport);
const renderer = useCanvasRenderer(hostElementRef, size);
const drawToolId = useToolStore((state) => state.selectedToolId);
const drawToolSettings = useToolStore(
(state) => state.toolSettings[drawToolId]
);

useDrawTool(
hostElementRef,
toolConfig,
(position) => {
const viewport = viewportRef.current!;
return {
x: (position.x - viewport.position.x) / viewport.zoom,
y: (position.y - viewport.position.y) / viewport.zoom,
};
},
drawToolId,
drawToolSettings,
(position) =>
screenPositionToCanvasPosition(position, viewportRef.current!),
() => renderer.getDrawContext(layerId)
);

Expand Down
9 changes: 6 additions & 3 deletions apps/web/src/components/icon.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* This is an icon aggregator where all icons from various libraries are imported. */
import { Pen, Pencil, Moon, Sun, Plus } from "lucide-react";
import { assertNever } from "@/utils/typeGuards";
import { Pen, Pencil, Moon, Sun, Plus, Brush } from "lucide-react";

export type IconType = "pen" | "pencil" | "moon" | "sun" | "plus";
export type IconType = "pen" | "pencil" | "moon" | "sun" | "plus" | "brush";
export type IconSize = "small" | "medium";

const renderLucideIcon = (
Expand All @@ -21,8 +22,10 @@ const renderLucideIcon = (
return <Sun className={className} size={fontSize} />;
case "plus":
return <Plus className={className} size={fontSize} />;
case "brush":
return <Brush className={className} size={fontSize} />;
default:
return null;
return assertNever(icon);
}
};

Expand Down
15 changes: 8 additions & 7 deletions apps/web/src/components/panels/selectToolPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { PanelHeader } from "./panelHeader";
import { translations } from "@/translations";
import { IconButton } from "../iconButton";
import { useDrawingToolStore } from "@/store";
import { DrawingToolType } from "@/drawing-tools";
import { memo } from "react";
import { ToolId, defaultToolsSettings } from "@/tools";

const tools: DrawingToolType[] = ["pen", "pencil"];
const tools: ToolId[] = Object.keys(defaultToolsSettings) as ToolId[];

export const SelectToolPanel = () => {
const { selectedTool, setSelectedTool } = useDrawingToolStore(
export const SelectToolPanel = memo(() => {
const { selectedToolId, setSelectedToolId } = useDrawingToolStore(
(state) => state
);
return (
Expand All @@ -16,15 +17,15 @@ export const SelectToolPanel = () => {
<div className="flex flex-wrap flex-row gap-small p-small">
{tools.map((tool) => (
<IconButton
className={selectedTool === tool ? "bg-accent" : ""}
className={selectedToolId === tool ? "bg-accent" : ""}
key={tool}
type={tool}
size="medium"
onClick={() => setSelectedTool(tool)}
onClick={() => setSelectedToolId(tool)}
/>
))}
</div>
</div>
);
};
});

19 changes: 19 additions & 0 deletions apps/web/src/components/tool-settings/colorSetting.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Color } from "@/utils/common";
import { cn } from "@/utils/css";

export const ColorSetting = (props: {
value: Color;
onChange: (newValue: Color) => void;
className?: string;
}) => {
const { value, onChange, className } = props;
return (
<input
className={cn("w-[40px] rounded-md h-input-thin", className)}
type="color"
value={value}
onChange={(e) => onChange(e.target.value)}
></input>
);
};

39 changes: 39 additions & 0 deletions apps/web/src/components/tool-settings/optionSetting.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { cn } from "@/utils/css";
import {
Select,
SelectTrigger,
SelectValue,
SelectContent,
SelectItem,
} from "../ui/select";

export const OptionSetting = <T extends number | string>(props: {
value: T;
onChange: (newValue: T) => void;
options: {
value: T;
label: string;
}[];
placeholder?: string;
className?: string;
}) => {
const { value, onChange, options, placeholder, className } = props;
return (
<Select
value={value as string}
onValueChange={(value) => onChange(value as T)}
>
<SelectTrigger className={cn("text-xs h-input-thin", className)}>
<SelectValue placeholder={placeholder} />
</SelectTrigger>
<SelectContent>
{options.map((option) => (
<SelectItem key={option.value} value={option.value as string}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
);
};

31 changes: 31 additions & 0 deletions apps/web/src/components/tool-settings/toolSetting.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { DrawToolSettingType } from "@/tools/draw-tools/drawTool";
import { ColorSetting } from "./colorSetting";
import { ToolId, toolsMetadata } from "@/tools";
import { OptionSetting } from "./optionSetting";

export const ToolSetting = (props: {
toolId: ToolId;
settingKey: string;
type: DrawToolSettingType;
value: unknown;
onChange: (newValue: unknown) => void;
}) => {
const { type, value, onChange, toolId, settingKey } = props;

if (type === "color") {
return <ColorSetting value={value as string} onChange={onChange} />;
}
if (type === "size") {
const options = toolsMetadata[toolId].settings[settingKey].options;
return (
<OptionSetting
value={value as number}
onChange={onChange}
options={options as { value: number; label: string }[]}
/>
);
}

return null;
};

43 changes: 43 additions & 0 deletions apps/web/src/components/toolSettingsBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useToolStore } from "@/store/toolState";
import { toolsMetadata } from "@/tools";
import { ToolSetting } from "./tool-settings/toolSetting";
import { Separator } from "@radix-ui/react-separator";

export const ToolSettingsBar = () => {
const selectedToolId = useToolStore((state) => state.selectedToolId);
const settings = useToolStore((state) => state.toolSettings[selectedToolId]);
const updateToolSettings = useToolStore((state) => state.updateToolSettings);

return (
<div className="h-10 border-b px-2 flex flex-row gap-big items-center">
{Object.entries(settings).map(([id, value]) => {
const label = toolsMetadata[selectedToolId].settings[id].name;
return (
<div
key={id}
className="flex flex-row items-center gap-small text-xs"
>
{label}
<ToolSetting
toolId={selectedToolId}
settingKey={id}
type={toolsMetadata[selectedToolId].settings[id].type}
value={value}
onChange={(newValue) => {
updateToolSettings(selectedToolId, {
...settings,
[id]: newValue,
});
}}
></ToolSetting>
<Separator
orientation="vertical"
className="h-6 w-px bg-border mx-1"
/>
</div>
);
})}
</div>
);
};

Loading

0 comments on commit b3bfe4c

Please sign in to comment.