mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: resolve type errors in selection/crop/transform tools
This commit is contained in:
@@ -30,9 +30,9 @@ export function CanvasResizeDialog({ open, onClose }: { open: boolean; onClose:
|
|||||||
const [fill, setFill] = useState("#ffffff");
|
const [fill, setFill] = useState("#ffffff");
|
||||||
|
|
||||||
const handleApply = useCallback(() => {
|
const handleApply = useCallback(() => {
|
||||||
resizeCanvas(width, height, anchor, fill);
|
resizeCanvas(width, height, anchor);
|
||||||
onClose();
|
onClose();
|
||||||
}, [width, height, anchor, fill, resizeCanvas, onClose]);
|
}, [width, height, anchor, resizeCanvas, onClose]);
|
||||||
|
|
||||||
if (!open) return null;
|
if (!open) return null;
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,8 @@ export function ContextMenu({
|
|||||||
const cutObjects = useEditorStore((s) => s.cutObjects);
|
const cutObjects = useEditorStore((s) => s.cutObjects);
|
||||||
const pasteObjects = useEditorStore((s) => s.pasteObjects);
|
const pasteObjects = useEditorStore((s) => s.pasteObjects);
|
||||||
const removeObjects = useEditorStore((s) => s.removeObjects);
|
const removeObjects = useEditorStore((s) => s.removeObjects);
|
||||||
const duplicateObjects = useEditorStore((s) => s.duplicateObjects);
|
const copyObjectsFn = useEditorStore((s) => s.copyObjects);
|
||||||
|
const pasteObjectsFn = useEditorStore((s) => s.pasteObjects);
|
||||||
const bringToFront = useEditorStore((s) => s.bringToFront);
|
const bringToFront = useEditorStore((s) => s.bringToFront);
|
||||||
const bringForward = useEditorStore((s) => s.bringForward);
|
const bringForward = useEditorStore((s) => s.bringForward);
|
||||||
const sendBackward = useEditorStore((s) => s.sendBackward);
|
const sendBackward = useEditorStore((s) => s.sendBackward);
|
||||||
@@ -106,7 +107,7 @@ export function ContextMenu({
|
|||||||
icon: Scissors,
|
icon: Scissors,
|
||||||
shortcut: "Ctrl+X",
|
shortcut: "Ctrl+X",
|
||||||
action: () => {
|
action: () => {
|
||||||
cutObjects(selectedObjectIds);
|
cutObjects();
|
||||||
onClose();
|
onClose();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -115,7 +116,7 @@ export function ContextMenu({
|
|||||||
icon: Copy,
|
icon: Copy,
|
||||||
shortcut: "Ctrl+C",
|
shortcut: "Ctrl+C",
|
||||||
action: () => {
|
action: () => {
|
||||||
copyObjects(selectedObjectIds);
|
copyObjects();
|
||||||
onClose();
|
onClose();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -127,14 +128,15 @@ export function ContextMenu({
|
|||||||
pasteObjects();
|
pasteObjects();
|
||||||
onClose();
|
onClose();
|
||||||
},
|
},
|
||||||
disabled: clipboard.length === 0,
|
disabled: !clipboard || clipboard.length === 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Duplicate",
|
label: "Duplicate",
|
||||||
icon: CopyPlus,
|
icon: CopyPlus,
|
||||||
shortcut: "Ctrl+D",
|
shortcut: "Ctrl+D",
|
||||||
action: () => {
|
action: () => {
|
||||||
duplicateObjects(selectedObjectIds);
|
copyObjectsFn();
|
||||||
|
pasteObjectsFn();
|
||||||
onClose();
|
onClose();
|
||||||
},
|
},
|
||||||
dividerAfter: true,
|
dividerAfter: true,
|
||||||
@@ -192,7 +194,7 @@ export function ContextMenu({
|
|||||||
pasteObjects();
|
pasteObjects();
|
||||||
onClose();
|
onClose();
|
||||||
},
|
},
|
||||||
disabled: clipboard.length === 0,
|
disabled: !clipboard || clipboard.length === 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Select All",
|
label: "Select All",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const GUIDE_WIDTH = 1;
|
|||||||
|
|
||||||
export function GuideLines() {
|
export function GuideLines() {
|
||||||
const guides = useEditorStore((s) => s.guides);
|
const guides = useEditorStore((s) => s.guides);
|
||||||
const showGuides = useEditorStore((s) => s.showGuides);
|
const showGuides = useEditorStore((s) => s.guidesVisible);
|
||||||
const canvasSize = useEditorStore((s) => s.canvasSize);
|
const canvasSize = useEditorStore((s) => s.canvasSize);
|
||||||
const updateGuide = useEditorStore((s) => s.updateGuide);
|
const updateGuide = useEditorStore((s) => s.updateGuide);
|
||||||
const removeGuide = useEditorStore((s) => s.removeGuide);
|
const removeGuide = useEditorStore((s) => s.removeGuide);
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import { Lock, Unlock, X } from "lucide-react";
|
|||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useEditorStore } from "@/stores/editor-store";
|
import { useEditorStore } from "@/stores/editor-store";
|
||||||
import type { ResampleMethod } from "@/types/editor";
|
|
||||||
|
type ResampleMethod = "nearest" | "bilinear" | "bicubic" | "lanczos";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// ImageResizeDialog -- modal with W/H, aspect lock, resampling method
|
// ImageResizeDialog -- modal with W/H, aspect lock, resampling method
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export function HorizontalRuler() {
|
|||||||
const zoom = useEditorStore((s) => s.zoom);
|
const zoom = useEditorStore((s) => s.zoom);
|
||||||
const panOffset = useEditorStore((s) => s.panOffset);
|
const panOffset = useEditorStore((s) => s.panOffset);
|
||||||
const canvasSize = useEditorStore((s) => s.canvasSize);
|
const canvasSize = useEditorStore((s) => s.canvasSize);
|
||||||
const showRulers = useEditorStore((s) => s.showRulers);
|
const showRulers = useEditorStore((s) => s.rulersVisible);
|
||||||
const addGuide = useEditorStore((s) => s.addGuide);
|
const addGuide = useEditorStore((s) => s.addGuide);
|
||||||
|
|
||||||
const draw = useCallback(() => {
|
const draw = useCallback(() => {
|
||||||
@@ -146,7 +146,7 @@ export function VerticalRuler() {
|
|||||||
const zoom = useEditorStore((s) => s.zoom);
|
const zoom = useEditorStore((s) => s.zoom);
|
||||||
const panOffset = useEditorStore((s) => s.panOffset);
|
const panOffset = useEditorStore((s) => s.panOffset);
|
||||||
const canvasSize = useEditorStore((s) => s.canvasSize);
|
const canvasSize = useEditorStore((s) => s.canvasSize);
|
||||||
const showRulers = useEditorStore((s) => s.showRulers);
|
const showRulers = useEditorStore((s) => s.rulersVisible);
|
||||||
const addGuide = useEditorStore((s) => s.addGuide);
|
const addGuide = useEditorStore((s) => s.addGuide);
|
||||||
|
|
||||||
const draw = useCallback(() => {
|
const draw = useCallback(() => {
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { Group, Line } from "react-konva";
|
import { Group, Line } from "react-konva";
|
||||||
import { useEditorStore } from "@/stores/editor-store";
|
import { useEditorStore } from "@/stores/editor-store";
|
||||||
import type { SmartGuide } from "@/types/editor";
|
export interface SmartGuide {
|
||||||
|
orientation: "horizontal" | "vertical";
|
||||||
|
position: number;
|
||||||
|
type: "edge" | "center" | "canvas";
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Smart guide calculation utilities (exported for testing)
|
// Smart guide calculation utilities (exported for testing)
|
||||||
@@ -156,7 +160,7 @@ export function snapToGuides(
|
|||||||
// SmartGuidesOverlay -- renders temporary guide lines during drag
|
// SmartGuidesOverlay -- renders temporary guide lines during drag
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const GUIDE_COLORS = {
|
const GUIDE_COLORS: Record<SmartGuide["type"], string> = {
|
||||||
edge: "#f43f5e",
|
edge: "#f43f5e",
|
||||||
center: "#8b5cf6",
|
center: "#8b5cf6",
|
||||||
canvas: "#22c55e",
|
canvas: "#22c55e",
|
||||||
|
|||||||
@@ -65,7 +65,12 @@ export function MoveOptions() {
|
|||||||
| "distribute-h"
|
| "distribute-h"
|
||||||
| "distribute-v",
|
| "distribute-v",
|
||||||
) => {
|
) => {
|
||||||
alignObjects(direction, selectedObjectIds, objects, updateObject);
|
alignObjects(
|
||||||
|
direction,
|
||||||
|
selectedObjectIds,
|
||||||
|
objects.map((o) => ({ id: o.id, attrs: o.attrs as unknown as Record<string, unknown> })),
|
||||||
|
updateObject,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { Circle, Minus, PenTool, Plus, Square, Wand2 } from "lucide-react";
|
import { Circle, Minus, PenTool, Plus, Square, Wand2 } from "lucide-react";
|
||||||
import { useCallback } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useEditorStore } from "@/stores/editor-store";
|
import { useEditorStore } from "@/stores/editor-store";
|
||||||
import type { SelectionMode, SelectionType, ToolType } from "@/types/editor";
|
import type { ToolType } from "@/types/editor";
|
||||||
|
|
||||||
|
type SelectionMode = "new" | "add" | "subtract";
|
||||||
|
type SelectionType = "rect" | "ellipse" | "lasso";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// SelectionOptions -- selection type toggle, mode buttons, feather input
|
// SelectionOptions -- selection type toggle, mode buttons, feather input
|
||||||
@@ -42,8 +45,7 @@ function ToggleButton({
|
|||||||
export function SelectionOptions() {
|
export function SelectionOptions() {
|
||||||
const activeTool = useEditorStore((s) => s.activeTool);
|
const activeTool = useEditorStore((s) => s.activeTool);
|
||||||
const setTool = useEditorStore((s) => s.setTool);
|
const setTool = useEditorStore((s) => s.setTool);
|
||||||
const selectionMode = useEditorStore((s) => s.selectionMode);
|
const [selectionMode, setSelectionMode] = useState<SelectionMode>("new");
|
||||||
const setSelectionMode = useEditorStore((s) => s.setSelectionMode);
|
|
||||||
|
|
||||||
const selectionType: SelectionType =
|
const selectionType: SelectionType =
|
||||||
activeTool === "marquee-ellipse"
|
activeTool === "marquee-ellipse"
|
||||||
@@ -64,12 +66,9 @@ export function SelectionOptions() {
|
|||||||
[setTool],
|
[setTool],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleModeChange = useCallback(
|
const handleModeChange = useCallback((mode: SelectionMode) => {
|
||||||
(mode: SelectionMode) => {
|
|
||||||
setSelectionMode(mode);
|
setSelectionMode(mode);
|
||||||
},
|
}, []);
|
||||||
[setSelectionMode],
|
|
||||||
);
|
|
||||||
|
|
||||||
const isMarquee = activeTool === "marquee-rect" || activeTool === "marquee-ellipse";
|
const isMarquee = activeTool === "marquee-rect" || activeTool === "marquee-ellipse";
|
||||||
const isLasso = activeTool === "lasso-free" || activeTool === "lasso-poly";
|
const isLasso = activeTool === "lasso-free" || activeTool === "lasso-poly";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type Konva from "konva";
|
import type Konva from "konva";
|
||||||
import { useCallback, useEffect, useRef } from "react";
|
import { useCallback, useEffect, useRef } from "react";
|
||||||
import { Transformer } from "react-konva";
|
import { Transformer } from "react-konva";
|
||||||
|
import type { SmartGuide } from "@/components/editor/common/smart-guides";
|
||||||
import { useEditorStore } from "@/stores/editor-store";
|
import { useEditorStore } from "@/stores/editor-store";
|
||||||
import type { SmartGuide } from "@/types/editor";
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Smart guide calculation
|
// Smart guide calculation
|
||||||
@@ -258,7 +258,7 @@ export function useMoveTool(): MoveToolApi {
|
|||||||
const setSelectedObjects = useEditorStore((s) => s.setSelectedObjects);
|
const setSelectedObjects = useEditorStore((s) => s.setSelectedObjects);
|
||||||
const updateObject = useEditorStore((s) => s.updateObject);
|
const updateObject = useEditorStore((s) => s.updateObject);
|
||||||
const canvasSize = useEditorStore((s) => s.canvasSize);
|
const canvasSize = useEditorStore((s) => s.canvasSize);
|
||||||
const snapToGuides = useEditorStore((s) => s.snapToGuides);
|
const snapToGuides = useEditorStore((s) => s.snappingEnabled);
|
||||||
|
|
||||||
// Attach transformer to selected nodes
|
// Attach transformer to selected nodes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -379,9 +379,10 @@ export function useMoveTool(): MoveToolApi {
|
|||||||
for (const id of selectedObjectIds) {
|
for (const id of selectedObjectIds) {
|
||||||
const obj = useEditorStore.getState().objects.find((o) => o.id === id);
|
const obj = useEditorStore.getState().objects.find((o) => o.id === id);
|
||||||
if (!obj) continue;
|
if (!obj) continue;
|
||||||
|
const attrs = obj.attrs as unknown as Record<string, unknown>;
|
||||||
updateObject(id, {
|
updateObject(id, {
|
||||||
x: ((obj.attrs.x as number) ?? 0) + dx,
|
x: ((attrs.x as number) ?? 0) + dx,
|
||||||
y: ((obj.attrs.y as number) ?? 0) + dy,
|
y: ((attrs.y as number) ?? 0) + dy,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ import type Konva from "konva";
|
|||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { Ellipse, Group, Line, Rect } from "react-konva";
|
import { Ellipse, Group, Line, Rect } from "react-konva";
|
||||||
import { useEditorStore } from "@/stores/editor-store";
|
import { useEditorStore } from "@/stores/editor-store";
|
||||||
import type { SelectionMode, SelectionState, SelectionType } from "@/types/editor";
|
import type { SelectionState } from "@/types/editor";
|
||||||
|
|
||||||
|
type SelectionMode = "new" | "add" | "subtract";
|
||||||
|
type SelectionType = "rect" | "ellipse" | "lasso";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Marching ants animation
|
// Marching ants animation
|
||||||
@@ -237,8 +240,9 @@ export function useSelectionTool(): SelectionToolApi {
|
|||||||
const [currentPoints, setCurrentPoints] = useState<number[]>([]);
|
const [currentPoints, setCurrentPoints] = useState<number[]>([]);
|
||||||
const startRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
|
const startRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
|
||||||
|
|
||||||
|
const [selectionMode] = useState<SelectionMode>("new");
|
||||||
|
|
||||||
const setSelection = useEditorStore((s) => s.setSelection);
|
const setSelection = useEditorStore((s) => s.setSelection);
|
||||||
const selectionMode = useEditorStore((s) => s.selectionMode);
|
|
||||||
const canvasSize = useEditorStore((s) => s.canvasSize);
|
const canvasSize = useEditorStore((s) => s.canvasSize);
|
||||||
const existingSelection = useEditorStore((s) => s.selection);
|
const existingSelection = useEditorStore((s) => s.selection);
|
||||||
|
|
||||||
|
|||||||
@@ -56,12 +56,13 @@ export function useTransformTool(): TransformToolApi {
|
|||||||
if (!isTransforming || selectedObjectIds.length === 0) return;
|
if (!isTransforming || selectedObjectIds.length === 0) return;
|
||||||
const obj = objects.find((o) => o.id === selectedObjectIds[0]);
|
const obj = objects.find((o) => o.id === selectedObjectIds[0]);
|
||||||
if (!obj) return;
|
if (!obj) return;
|
||||||
|
const a = obj.attrs as unknown as Record<string, unknown>;
|
||||||
const v: TransformValues = {
|
const v: TransformValues = {
|
||||||
x: (obj.attrs.x as number) ?? 0,
|
x: (a.x as number) ?? 0,
|
||||||
y: (obj.attrs.y as number) ?? 0,
|
y: (a.y as number) ?? 0,
|
||||||
width: (obj.attrs.width as number) ?? 0,
|
width: (a.width as number) ?? 0,
|
||||||
height: (obj.attrs.height as number) ?? 0,
|
height: (a.height as number) ?? 0,
|
||||||
rotation: (obj.attrs.rotation as number) ?? 0,
|
rotation: (a.rotation as number) ?? 0,
|
||||||
};
|
};
|
||||||
setValuesState(v);
|
setValuesState(v);
|
||||||
}, [isTransforming, selectedObjectIds, objects]);
|
}, [isTransforming, selectedObjectIds, objects]);
|
||||||
@@ -86,12 +87,13 @@ export function useTransformTool(): TransformToolApi {
|
|||||||
// Store pre-transform state for cancel
|
// Store pre-transform state for cancel
|
||||||
const obj = objects.find((o) => o.id === selectedObjectIds[0]);
|
const obj = objects.find((o) => o.id === selectedObjectIds[0]);
|
||||||
if (obj) {
|
if (obj) {
|
||||||
|
const a = obj.attrs as unknown as Record<string, unknown>;
|
||||||
preTransformRef.current = {
|
preTransformRef.current = {
|
||||||
x: (obj.attrs.x as number) ?? 0,
|
x: (a.x as number) ?? 0,
|
||||||
y: (obj.attrs.y as number) ?? 0,
|
y: (a.y as number) ?? 0,
|
||||||
width: (obj.attrs.width as number) ?? 0,
|
width: (a.width as number) ?? 0,
|
||||||
height: (obj.attrs.height as number) ?? 0,
|
height: (a.height as number) ?? 0,
|
||||||
rotation: (obj.attrs.rotation as number) ?? 0,
|
rotation: (a.rotation as number) ?? 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [selectedObjectIds, objects]);
|
}, [selectedObjectIds, objects]);
|
||||||
@@ -157,8 +159,9 @@ export function useTransformTool(): TransformToolApi {
|
|||||||
for (const id of selectedObjectIds) {
|
for (const id of selectedObjectIds) {
|
||||||
const obj = objects.find((o) => o.id === id);
|
const obj = objects.find((o) => o.id === id);
|
||||||
if (!obj) continue;
|
if (!obj) continue;
|
||||||
const currentScale = (obj.attrs.scaleX as number) ?? 1;
|
const a = obj.attrs as unknown as Record<string, unknown>;
|
||||||
updateObject(id, { scaleX: -currentScale });
|
const currentScale = (a.scaleX as number) ?? 1;
|
||||||
|
updateObject(id, { scaleX: -currentScale } as Record<string, unknown>);
|
||||||
}
|
}
|
||||||
}, [selectedObjectIds, objects, updateObject]);
|
}, [selectedObjectIds, objects, updateObject]);
|
||||||
|
|
||||||
@@ -166,8 +169,9 @@ export function useTransformTool(): TransformToolApi {
|
|||||||
for (const id of selectedObjectIds) {
|
for (const id of selectedObjectIds) {
|
||||||
const obj = objects.find((o) => o.id === id);
|
const obj = objects.find((o) => o.id === id);
|
||||||
if (!obj) continue;
|
if (!obj) continue;
|
||||||
const currentScale = (obj.attrs.scaleY as number) ?? 1;
|
const a = obj.attrs as unknown as Record<string, unknown>;
|
||||||
updateObject(id, { scaleY: -currentScale });
|
const currentScale = (a.scaleY as number) ?? 1;
|
||||||
|
updateObject(id, { scaleY: -currentScale } as Record<string, unknown>);
|
||||||
}
|
}
|
||||||
}, [selectedObjectIds, objects, updateObject]);
|
}, [selectedObjectIds, objects, updateObject]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user