diff --git a/editor/app/(tools)/(playground)/playground/image/_page.tsx b/editor/app/(tools)/(playground)/playground/image/_page.tsx index 9f3a34b1b..de35ec8f5 100644 --- a/editor/app/(tools)/(playground)/playground/image/_page.tsx +++ b/editor/app/(tools)/(playground)/playground/image/_page.tsx @@ -43,7 +43,7 @@ import { ViewportRoot, EditorSurface, AutoInitialFitTransformer, - useDocument, + useCurrentEditor, } from "@/grida-canvas-react"; import { FontFamilyListProvider } from "@/scaffolds/sidecontrol/controls/font-family"; import { useEditorHotKeys } from "@/grida-canvas-react/viewport/hotkeys"; @@ -124,15 +124,13 @@ export default function ImagePlayground() { function CanvasConsumer() { const { withAuth, session } = useContinueWithAuth(); const credits = useCredits(); - const editor = useDocument(); + const editor = useCurrentEditor(); const [prompt, setPrompt] = useState(""); const model = useImageModelConfig("black-forest-labs/flux-schnell"); const { generate, key, loading, image, start, end } = useGenerateImage(); const onCommit = (value: { text: string }) => { - const id = editor.createNodeId(); - editor.insertNode({ - _$id: id, + const id = editor.insertNode({ type: "image", name: value.text, width: model.width, diff --git a/editor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/design/template-duo-001-viewer.tsx b/editor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/design/template-duo-001-viewer.tsx index c87fb23eb..d2c1cf5cc 100644 --- a/editor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/design/template-duo-001-viewer.tsx +++ b/editor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/design/template-duo-001-viewer.tsx @@ -12,7 +12,7 @@ import { AutoInitialFitTransformer, StandaloneSceneBackground, UserCustomTemplatesProvider, - useDocument, + useCurrentEditor, } from "@/grida-canvas-react"; import { Zoom } from "@/scaffolds/sidecontrol/sidecontrol-node-selection"; import { WorkbenchUI } from "@/components/workbench"; @@ -21,7 +21,6 @@ import { PreviewProvider } from "@/grida-canvas-react-starter-kit/starterkit-pre import { Platform } from "@/lib/platform"; import { TemplateData } from "@/theme/templates/west-referral/templates"; import { ReadonlyPropsEditorInstance } from "@/scaffolds/props-editor"; -import { useTransform } from "@/grida-canvas-react/provider"; import MessageAppFrame from "@/components/frames/message-app-frame"; import { editor } from "@/grida-canvas"; import { useEditor } from "@/grida-canvas-react"; @@ -276,14 +275,13 @@ export function CampaignTemplateDuo001Viewer({ // will be removed after useEditor is ready function EditorUXServer({ focus }: { focus: { node?: string } }) { - const { select } = useDocument(); - const { fit } = useTransform(); + const editor = useCurrentEditor(); useEffect( () => { if (focus.node) { - select([focus.node]); - fit([focus.node], { margin: 64, animate: true }); + editor.select([focus.node]); + editor.fit([focus.node], { margin: 64, animate: true }); } }, // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/editor/app/(workbench)/[org]/[proj]/[id]/form/start/page.tsx b/editor/app/(workbench)/[org]/[proj]/[id]/form/start/page.tsx index b396b7eee..30a172f11 100644 --- a/editor/app/(workbench)/[org]/[proj]/[id]/form/start/page.tsx +++ b/editor/app/(workbench)/[org]/[proj]/[id]/form/start/page.tsx @@ -53,7 +53,6 @@ import { StandaloneDocumentEditor, ViewportRoot, EditorSurface, - useDocument, useRootTemplateInstanceNode, } from "@/grida-canvas-react"; import { composeEditorDocumentAction } from "@/scaffolds/editor/action"; diff --git a/editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsx b/editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsx index 60e70e3ba..d890f774c 100644 --- a/editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsx +++ b/editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsx @@ -6,7 +6,7 @@ import { CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; -import { useDocument } from "@/grida-canvas-react"; +import { useCurrentEditor } from "@/grida-canvas-react"; type ArtboardData = { name: string; @@ -15,10 +15,10 @@ type ArtboardData = { }; const ArtboardList = () => { - const { insertNode } = useDocument(); + const editor = useCurrentEditor(); const onClickItem = (item: ArtboardData) => { - insertNode({ + editor.insertNode({ type: "container", position: "absolute", name: item.name, diff --git a/editor/grida-canvas-react-starter-kit/starterkit-hierarchy/index.tsx b/editor/grida-canvas-react-starter-kit/starterkit-hierarchy/index.tsx index 177d43323..a2f8578cc 100644 --- a/editor/grida-canvas-react-starter-kit/starterkit-hierarchy/index.tsx +++ b/editor/grida-canvas-react-starter-kit/starterkit-hierarchy/index.tsx @@ -7,7 +7,7 @@ import React, { useCallback, useRef, } from "react"; -import { useDocument } from "@/grida-canvas-react"; +import { useCurrentEditor, useEditorState } from "@/grida-canvas-react"; import { Tree, TreeDragLine, @@ -35,11 +35,7 @@ import { EyeClosedIcon, LockOpen1Icon, } from "@radix-ui/react-icons"; -import { - useCurrentScene, - useNodeAction, - useTransform, -} from "@/grida-canvas-react/provider"; +import { useCurrentSceneState } from "@/grida-canvas-react/provider"; import { NodeTypeIcon } from "@/grida-canvas-react-starter-kit/starterkit-icons/node-type-icon"; import { cn } from "@/components/lib/utils"; import grida from "@grida/schema"; @@ -52,7 +48,7 @@ function SceneItemContextMenuWrapper({ scene_id: string; onStartRenaming?: () => void; }>) { - const { deleteScene, duplicateScene } = useDocument(); + const editor = useCurrentEditor(); return ( @@ -71,7 +67,7 @@ function SceneItemContextMenuWrapper({ { - duplicateScene(scene_id); + editor.duplicateScene(scene_id); }} className="text-xs" > @@ -80,7 +76,7 @@ function SceneItemContextMenuWrapper({ { - deleteScene(scene_id); + editor.deleteScene(scene_id); }} className="text-xs" > @@ -92,7 +88,9 @@ function SceneItemContextMenuWrapper({ } export function ScenesList() { - const { scenes: scenesmap, scene_id, loadScene, renameScene } = useDocument(); + const editor = useCurrentEditor(); + const scenesmap = useEditorState(editor, (state) => state.document.scenes); + const scene_id = useEditorState(editor, (state) => state.scene_id); const scenes = useMemo(() => { return Object.values(scenesmap).sort( @@ -110,7 +108,7 @@ export function ScenesList() { selectedItems: scene_id ? [scene_id] : [], }, setSelectedItems: (items) => { - loadScene((items as string[])[0]); + editor.loadScene((items as string[])[0]); }, getItemName: (item) => { if (item.getId() === "") return ""; @@ -174,7 +172,7 @@ export function ScenesList() { isRenaming={isRenaming} initialValue={scene.name} onValueCommit={(name) => { - renameScene(scene.id, name); + editor.renameScene(scene.id, name); tree.abortRenaming(); }} className="font-normal h-8 text-xs! px-2! py-1.5!" @@ -196,9 +194,7 @@ function NodeHierarchyItemContextMenuWrapper({ node_id: string; onStartRenaming?: () => void; }>) { - const { copy, deleteNode, order } = useDocument(); - const { fit } = useTransform(); - const change = useNodeAction(node_id)!; + const editor = useCurrentEditor(); return ( @@ -206,7 +202,7 @@ function NodeHierarchyItemContextMenuWrapper({ { - copy(node_id); + editor.copy(node_id); }} className="text-xs" > @@ -225,14 +221,14 @@ function NodeHierarchyItemContextMenuWrapper({ {/* {}}>Copy */} {/* Paste here */} order(node_id, "front")} + onSelect={() => editor.order(node_id, "front")} className="text-xs" > Bring to front {"]"} order(node_id, "back")} + onSelect={() => editor.order(node_id, "back")} className="text-xs" > Send to back @@ -240,27 +236,33 @@ function NodeHierarchyItemContextMenuWrapper({ {/* Add Container */} - + editor.toggleNodeActive(node_id)} + className="text-xs" + > Set Active/Inactive {"⌘⇧H"} { - fit([node_id], { margin: 64, animate: true }); + editor.fit([node_id], { margin: 64, animate: true }); }} className="text-xs" > Zoom to fit {"⇧1"} - + editor.toggleNodeLocked(node_id)} + className="text-xs" + > Lock/Unlock {"⌘⇧L"} { - deleteNode(node_id); + editor.deleteNode(node_id); }} className="text-xs" > @@ -273,37 +275,24 @@ function NodeHierarchyItemContextMenuWrapper({ } export function NodeHierarchyList() { - const { - document, - mv, - select, - hoverNode, - toggleNodeLocked, - toggleNodeActive, - changeNodeName, - } = useDocument(); - - const { id, name, children, selection, hovered_node_id } = useCurrentScene(); + const editor = useCurrentEditor(); + const document_ctx = useEditorState(editor, (state) => state.document_ctx); - const expandedItems = useMemo(() => { - return children.filter( - (id) => (document.nodes[id] as grida.program.nodes.UnknwonNode).expanded - ); - }, [id, children]); + const { id, name, children, selection, hovered_node_id } = + useCurrentSceneState(); // root item id must be "" const tree = useTree({ rootItemId: "", canReorder: true, initialState: { - expandedItems: expandedItems, selectedItems: selection, }, state: { selectedItems: selection, }, setSelectedItems: (items) => { - select(items as string[]); + editor.select(items as string[]); }, getItemName: (item) => { if (item.getId() === "") { @@ -320,21 +309,18 @@ export function NodeHierarchyList() { const target_id = target.item.getId(); const index = "insertionIndex" in target ? target.insertionIndex : undefined; - mv(ids, target_id, index); + editor.mv(ids, target_id, index); }, indent: 6, dataLoader: { getItem(itemId) { - return document.nodes[itemId]; + return editor.state.document.nodes[itemId]; }, getChildren: (itemId) => { if (itemId === "") { return children; } - const node = document.nodes[itemId]; - return ( - (node as grida.program.nodes.i.IChildrenReference)?.children || [] - ); + return editor.state.document_ctx.__ctx_nid_to_children_ids[itemId]; }, }, features: [ @@ -347,7 +333,7 @@ export function NodeHierarchyList() { useEffect(() => { tree.rebuildTree(); - }, [document]); + }, [document_ctx]); return ( @@ -373,10 +359,10 @@ export function NodeHierarchyList() { item={item} className="w-full h-7 max-h-7 py-0.5" onPointerEnter={() => { - hoverNode(node.id, "enter"); + editor.hoverNode(node.id, "enter"); }} onPointerLeave={() => { - hoverNode(node.id, "leave"); + editor.hoverNode(node.id, "leave"); }} > { - changeNodeName(node.id, name); + editor.changeNodeName(node.id, name); tree.abortRenaming(); }} className="px-1 py-0.5 font-normal text-[11px]" @@ -411,7 +397,7 @@ export function NodeHierarchyList() { @@ -300,7 +306,8 @@ const defaultColors = { }; function ClipboardColor() { - const { clipboardColor, setClipboardColor } = useDocument(); + const editor = useCurrentEditor(); + const clipboardColor = useEditorState(editor, (s) => s.user_clipboard_color); const color = clipboardColor ?? { r: 0, g: 0, b: 0, a: 1 }; @@ -333,7 +340,7 @@ function ClipboardColor() { > diff --git a/editor/scaffolds/sidebar/sidebar-node-hierarchy-list.tsx b/editor/scaffolds/sidebar/sidebar-node-hierarchy-list.tsx index d6517e378..fd293dc3f 100644 --- a/editor/scaffolds/sidebar/sidebar-node-hierarchy-list.tsx +++ b/editor/scaffolds/sidebar/sidebar-node-hierarchy-list.tsx @@ -1,7 +1,7 @@ "use client"; import React from "react"; -import { useDocument } from "@/grida-canvas-react"; +import { useCurrentEditor } from "@/grida-canvas-react"; import { SidebarGroup, SidebarGroupAction, @@ -15,7 +15,7 @@ import { } from "@/grida-canvas-react-starter-kit/starterkit-hierarchy"; export function ScenesGroup() { - const { createScene } = useDocument(); + const editor = useCurrentEditor(); return ( Scenes - createScene()}> + editor.createScene()}> New Scene diff --git a/editor/scaffolds/sidecontrol/controls/ext-zoom.tsx b/editor/scaffolds/sidecontrol/controls/ext-zoom.tsx index d27f49d27..47dfd0526 100644 --- a/editor/scaffolds/sidecontrol/controls/ext-zoom.tsx +++ b/editor/scaffolds/sidecontrol/controls/ext-zoom.tsx @@ -10,17 +10,19 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { WorkbenchUI } from "@/components/workbench"; -import cmath from "@grida/cmath"; import { Input } from "@/components/ui/input"; -import { useEventTarget, useTransform } from "@/grida-canvas-react/provider"; +import { useTransformState } from "@/grida-canvas-react/provider"; import { cn } from "@/components/lib/utils"; +import { + useCurrentEditor, + useEditorState, +} from "@/grida-canvas-react/use-editor"; export function ZoomControl({ className }: { className?: string }) { - const { transform, scale, fit, zoomIn, zoomOut } = useTransform(); - const { ruler, setRulerState, pixelgrid, setPixelGridState } = - useEventTarget(); - - const [scaleX, scaleY] = cmath.transform.getScale(transform); + const editor = useCurrentEditor(); + const ruler = useEditorState(editor, (state) => state.ruler); + const pixelgrid = useEditorState(editor, (state) => state.pixelgrid); + const { scaleX } = useTransformState(); const pct = Math.round(scaleX * 100); @@ -39,14 +41,14 @@ export function ZoomControl({ className }: { className?: string }) { max={256} onChange={(e) => { const v = parseInt(e.target.value) / 100; - if (v) scale(v, "center"); + if (v) editor.scale(v, "center"); }} className={WorkbenchUI.inputVariants({ size: "sm" })} /> Zoom in @@ -54,7 +56,7 @@ export function ZoomControl({ className }: { className?: string }) { Zoom out @@ -62,7 +64,7 @@ export function ZoomControl({ className }: { className?: string }) { fit("*")} + onSelect={() => editor.fit("*")} className="text-xs" > Zoom to fit @@ -70,7 +72,7 @@ export function ZoomControl({ className }: { className?: string }) { fit("selection")} + onSelect={() => editor.fit("selection")} className="text-xs" > Zoom to selection @@ -78,14 +80,14 @@ export function ZoomControl({ className }: { className?: string }) { scale(0.5, "center")} + onSelect={() => editor.scale(0.5, "center")} className="text-xs" > Zoom to 50% scale(1, "center")} + onSelect={() => editor.scale(1, "center")} className="text-xs" > Zoom to 100% @@ -93,7 +95,7 @@ export function ZoomControl({ className }: { className?: string }) { scale(2, "center")} + onSelect={() => editor.scale(2, "center")} className="text-xs" > Zoom to 200% @@ -102,8 +104,7 @@ export function ZoomControl({ className }: { className?: string }) { { - if (pixelgrid === "on") setPixelGridState("off"); - if (pixelgrid === "off") setPixelGridState("on"); + editor.togglePixelGrid(); }} className="text-xs" > @@ -113,8 +114,7 @@ export function ZoomControl({ className }: { className?: string }) { { - if (ruler === "on") setRulerState("off"); - if (ruler === "off") setRulerState("on"); + editor.toggleRuler(); }} className="text-xs" > diff --git a/editor/scaffolds/sidecontrol/controls/font-size.tsx b/editor/scaffolds/sidecontrol/controls/font-size.tsx index 0dc3e529c..1d8d411a8 100644 --- a/editor/scaffolds/sidecontrol/controls/font-size.tsx +++ b/editor/scaffolds/sidecontrol/controls/font-size.tsx @@ -1,12 +1,11 @@ -import { Input } from "@/components/ui/input"; -import { WorkbenchUI } from "@/components/workbench"; import { Select } from "@radix-ui/react-select"; import { SelectContent, SelectItem } from "@/components/ui/select"; import * as SelectPrimitive from "@radix-ui/react-select"; import { Button } from "@/components/ui-editor/button"; import { CaretDownIcon } from "@radix-ui/react-icons"; import { cn } from "@/components/lib/utils"; -import { TChange, TMixed } from "./utils/types"; +import type { editor } from "@/grida-canvas"; +import type { TMixed } from "./utils/types"; import { PropertyNumber } from "../ui"; export function FontSizeControl({ @@ -14,7 +13,7 @@ export function FontSizeControl({ onValueChange, }: { value?: TMixed; - onValueChange?: (change: TChange) => void; + onValueChange?: (change: editor.api.NumberChange) => void; }) { return (
diff --git a/editor/scaffolds/sidecontrol/controls/letter-spacing.tsx b/editor/scaffolds/sidecontrol/controls/letter-spacing.tsx index 54eda7855..01b4ea18f 100644 --- a/editor/scaffolds/sidecontrol/controls/letter-spacing.tsx +++ b/editor/scaffolds/sidecontrol/controls/letter-spacing.tsx @@ -1,4 +1,5 @@ -import { TChange, TMixed } from "./utils/types"; +import type { TMixed } from "./utils/types"; +import type { editor } from "@/grida-canvas"; import { PropertyNumber } from "../ui"; export function LetterSpacingControl({ @@ -6,7 +7,7 @@ export function LetterSpacingControl({ onValueChange, }: { value?: TMixed; - onValueChange?: (change: TChange) => void; + onValueChange?: (change: editor.api.NumberChange) => void; }) { return ( ; - onValueChange?: (change: TChange) => void; + onValueChange?: (change: editor.api.NumberChange) => void; }) { return ( ; - onValueChange?: (change: TChange) => void; + onValueChange?: (change: editor.api.NumberChange) => void; }) { return (
; - onValueChange?: (change: TChange) => void; + onValueChange?: (change: editor.api.NumberChange) => void; }) { return ( ; - onValueChange?: (change: TChange) => void; + onValueChange?: (change: editor.api.NumberChange) => void; }) { return ( = - | { - type: "set"; - value: T; - } - | { - type: "delta"; - value: T; - }; - export type TMixed = typeof grida.mixed | T; diff --git a/editor/scaffolds/sidecontrol/sidecontrol-doctype-form.tsx b/editor/scaffolds/sidecontrol/sidecontrol-doctype-form.tsx index 5f83f02ff..c7074fa6f 100644 --- a/editor/scaffolds/sidecontrol/sidecontrol-doctype-form.tsx +++ b/editor/scaffolds/sidecontrol/sidecontrol-doctype-form.tsx @@ -22,7 +22,7 @@ import { PropertyLine, PropertyLineLabel } from "./ui"; import { EditBinaryExpression } from "../panels/extensions/v-edit"; import { PopoverClose } from "@radix-ui/react-popover"; import { Align, Selection } from "./sidecontrol-node-selection"; -import { useDocument } from "@/grida-canvas-react/provider"; +import { useSelectionState } from "@/grida-canvas-react/provider"; export function SideControlDoctypeForm() { const [state] = useEditorState(); @@ -53,7 +53,7 @@ function SelectedPageForm() { } function SelectedPageFormStart() { - const { selection } = useDocument(); + const { selection } = useSelectionState(); if (selection.length === 0) { return ; } else { diff --git a/editor/scaffolds/sidecontrol/sidecontrol-doctype-site.tsx b/editor/scaffolds/sidecontrol/sidecontrol-doctype-site.tsx index cfa37c8ae..e9f5ead7e 100644 --- a/editor/scaffolds/sidecontrol/sidecontrol-doctype-site.tsx +++ b/editor/scaffolds/sidecontrol/sidecontrol-doctype-site.tsx @@ -2,7 +2,6 @@ import React from "react"; import { Align, Selection, Zoom } from "./sidecontrol-node-selection"; -import { SidebarSection } from "@/components/sidebar"; import { DocumentProperties } from "./sidecontrol-document-properties"; import { Button } from "@/components/ui/button"; import { @@ -12,7 +11,7 @@ import { import { Rnd } from "react-rnd"; import { Portal } from "@radix-ui/react-portal"; import { Cross2Icon } from "@radix-ui/react-icons"; -import { useDocument } from "@/grida-canvas-react"; +import { useCurrentEditor } from "@/grida-canvas-react"; import parsecolor from "color-parse"; import { PreviewButton } from "@/grida-canvas-react-starter-kit/starterkit-preview"; import { cn } from "@/components/lib/utils"; @@ -66,7 +65,7 @@ function ThemeEditorPortal({ // persistanceKey?: string; onOpenChange?: (open: boolean) => void; }) { - const { schemaPutProperty } = useDocument(); + const editor = useCurrentEditor(); return ( @@ -137,7 +136,7 @@ function ThemeEditorPortal({ } `; - schemaPutProperty("user-custom-css", { + editor.schemaPutProperty("user-custom-css", { type: "string", default: propertiescss, }); diff --git a/editor/scaffolds/sidecontrol/sidecontrol-document-properties.tsx b/editor/scaffolds/sidecontrol/sidecontrol-document-properties.tsx index 9e7694b55..c5acd4570 100644 --- a/editor/scaffolds/sidecontrol/sidecontrol-document-properties.tsx +++ b/editor/scaffolds/sidecontrol/sidecontrol-document-properties.tsx @@ -22,7 +22,7 @@ import { CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; -import { useDocument, useNode } from "@/grida-canvas-react"; +import { useCurrentEditor, useDocumentState } from "@/grida-canvas-react"; import grida from "@grida/schema"; import type cg from "@grida/cg"; import { RGBAColorControl } from "./controls/color"; @@ -31,17 +31,18 @@ import { DropdownMenuContent, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { useCurrentScene } from "@/grida-canvas-react/provider"; +import { useCurrentSceneState } from "@/grida-canvas-react/provider"; function SceneBackgroundPropertyLine() { - const { backgroundColor, setBackgroundColor } = useCurrentScene(); + const editor = useCurrentEditor(); + const { id: scene_id, backgroundColor } = useCurrentSceneState(); return ( { - setBackgroundColor(color); + editor.changeSceneBackground(scene_id, color); }} /> @@ -49,18 +50,13 @@ function SceneBackgroundPropertyLine() { } export function DocumentProperties({ className }: { className?: string }) { - const { - document, - schemaDefineProperty, - schemaRenameProperty, - schemaDeleteProperty, - schemaUpdateProperty, - } = useDocument(); + const editor = useCurrentEditor(); + const { document } = useDocumentState(); const keys = Object.keys(document.properties ?? {}); const addProperty = () => { - schemaDefineProperty(); + editor.schemaDefineProperty(); }; return ( @@ -97,13 +93,13 @@ export function DocumentProperties({ className }: { className?: string }) { definition={property} name={key} onNameChange={(newName) => { - schemaRenameProperty(key, newName); + editor.schemaRenameProperty(key, newName); }} onDefinitionChange={(value) => { - schemaUpdateProperty(key, value); + editor.schemaUpdateProperty(key, value); }} onRemove={() => { - schemaDeleteProperty(key); + editor.schemaDeleteProperty(key); }} /> ); diff --git a/editor/scaffolds/sidecontrol/sidecontrol-global.tsx b/editor/scaffolds/sidecontrol/sidecontrol-global.tsx index 4ff608976..a359037e1 100644 --- a/editor/scaffolds/sidecontrol/sidecontrol-global.tsx +++ b/editor/scaffolds/sidecontrol/sidecontrol-global.tsx @@ -72,7 +72,7 @@ import { useDialogState } from "@/components/hooks/use-dialog-state"; import { FormStartPage } from "@/theme/templates/formstart"; import { PropsControl } from "./controls/props"; import { - useDocument, + useDocumentState, useNode, useTemplateDefinition, } from "@/grida-canvas-react/provider"; @@ -86,6 +86,7 @@ import { AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; +import { useCurrentEditor } from "@/grida-canvas-react"; const { default: all, ...variants } = _variants; @@ -154,7 +155,7 @@ export function SideControlGlobal() { } function FormStartPageControl() { - const { changeNodeProps } = useDocument(); + const editor = useCurrentEditor(); // FIXME: 250303 UNKNOWN const { props, template_id, properties } = useNode("page"); @@ -184,7 +185,7 @@ function FormStartPageControl() { properties={properties!} props={shallowProps} onValueChange={(k, v) => { - changeNodeProps("page", k, v); + editor.changeNodeProps("page", k, v); }} /> diff --git a/editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx b/editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx index 83bad4e9f..67cdcb96c 100644 --- a/editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx +++ b/editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx @@ -45,7 +45,12 @@ import { LengthPercentageControl } from "./controls/length-percentage"; import { LayoutControl } from "./controls/layout"; import { AxisControl } from "./controls/axis"; import { MaxlengthControl } from "./controls/maxlength"; -import { useComputedNode, useDocument, useNode } from "@/grida-canvas-react"; +import { + useComputedNode, + useCurrentEditor, + useDocumentState, + useEditorState, +} from "@/grida-canvas-react"; import { Crosshair2Icon, LockClosedIcon, @@ -58,12 +63,13 @@ import { StrokeCapControl } from "./controls/stroke-cap"; import grida from "@grida/schema"; import assert from "assert"; import { - useCurrentScene, - useEditorFlags, - useNodeAction, - useSelection, + useCurrentSceneState, + useEditorFlagsState, + useNodeActions, + useNodeState, + useCurrentSelection, useSelectionPaints, - useTopNode, + useSelectionState, } from "@/grida-canvas-react/provider"; import { Checkbox } from "@/components/ui/checkbox"; import { Toggle } from "@/components/ui/toggle"; @@ -84,9 +90,11 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { PropertyAccessExpressionControl } from "./controls/props-property-access-expression"; +import { editor } from "@/grida-canvas"; export function Align() { - const { selection, align, distributeEvenly } = useDocument(); + const editor = useCurrentEditor(); + const { selection } = useSelectionState(); const has_selection = selection.length >= 1; return ( @@ -94,10 +102,10 @@ export function Align() { <_AlignControl disabled={!has_selection} onAlign={(alignment) => { - align("selection", alignment); + editor.align("selection", alignment); }} onDistributeEvenly={(axis) => { - distributeEvenly("selection", axis); + editor.distributeEvenly("selection", axis); }} /> @@ -113,7 +121,8 @@ export function Selection({ empty?: React.ReactNode; config?: ControlsConfig; }) { - const { selection } = useDocument(); + const instance = useCurrentEditor(); + const selection = useEditorState(instance, (state) => state.selection); const selection_length = selection.length; @@ -158,9 +167,14 @@ function SelectionMixedProperties({ }: { config?: ControlsConfig; }) { - const scene = useCurrentScene(); + const scene = useCurrentSceneState(); - const { selection: ids, nodes, properties, actions: change } = useSelection(); + const { + selection: ids, + nodes, + properties, + actions: change, + } = useCurrentSelection(); const { id, name, @@ -214,7 +228,7 @@ function SelectionMixedProperties({ } = properties; const sid = ids.join(","); - const is_root = ids.length === 0 && scene.children.includes(ids[0]); // assuming when root is selected, only root is selected + // const is_root = ids.length === 0 && scene.children.includes(ids[0]); // assuming when root is selected, only root is selected const types = new Set(nodes.map((n) => n.type)); const _types = Array.from(types); @@ -658,16 +672,67 @@ function SelectedNodeProperties({ }: { config?: ControlsConfig; }) { - const { selection, document } = useDocument(); - const { debug } = useEditorFlags(); - const scene = useCurrentScene(); + const instance = useCurrentEditor(); + const selection = useEditorState(instance, (state) => state.selection); + const documentProperties = useEditorState( + instance, + (state) => state.document.properties + ); + const { debug } = useEditorFlagsState(); assert(selection.length === 1); const node_id = selection[0]; - const actions = useNodeAction(node_id)!; + const actions = useNodeActions(node_id)!; + + // const node = useNode(node_id); + const node = useNodeState(node_id, (node) => ({ + id: node.id, + name: node.name, + active: node.active, + locked: node.locked, + component_id: node.component_id, + properties: node.properties, + src: node.src, + text: node.text, + type: node.type, + opacity: node.opacity, + cornerRadius: node.cornerRadius, + fill: node.fill, + stroke: node.stroke, + strokeWidth: node.strokeWidth, + strokeCap: node.strokeCap, + fit: node.fit, + fontFamily: node.fontFamily, + fontWeight: node.fontWeight, + fontSize: node.fontSize, + lineHeight: node.lineHeight, + letterSpacing: node.letterSpacing, + textAlign: node.textAlign, + textAlignVertical: node.textAlignVertical, + maxLength: node.maxLength, + + // + border: node.border, + // + padding: node.padding, + boxShadow: node.boxShadow, - const node = useNode(node_id); - const root = useTopNode(node_id); + // + layout: node.layout, + direction: node.direction, + mainAxisAlignment: node.mainAxisAlignment, + crossAxisAlignment: node.crossAxisAlignment, + mainAxisGap: node.mainAxisGap, + crossAxisGap: node.crossAxisGap, + + // + href: node.href, + target: node.target, + cursor: node.cursor, + + // x + userdata: node.userdata, + })); const computed = useComputedNode(node_id); const { id, @@ -675,22 +740,13 @@ function SelectedNodeProperties({ active, locked, component_id, - style, type, opacity, cornerRadius, - rotation, fill, stroke, strokeWidth, strokeCap, - position, - width, - height, - left, - top, - right, - bottom, fit, fontFamily, fontWeight, @@ -724,26 +780,19 @@ function SelectedNodeProperties({ userdata, } = node; - const document_properties = document.properties; - const properties = node.properties; - const root_properties = root.properties; - // const istemplate = type?.startsWith("templates/"); const is_instance = type === "instance"; const is_templateinstance = type === "template_instance"; const is_text = type === "text"; const is_image = type === "image"; const is_container = type === "container"; - const is_root = node_id === root.id; - const is_single_mode_root = - scene.constraints.children === "single" && is_root; const is_flex_container = is_container && layout === "flex"; const is_stylable = type !== "template_instance"; return (
@@ -788,67 +837,9 @@ function SelectedNodeProperties({ )} - - + {config.position !== "off" && } + {config.size !== "off" && } +