diff --git a/editor-packages/editor-canvas/README.md b/editor-packages/editor-canvas/README.md index aa198bad..095f93ff 100644 --- a/editor-packages/editor-canvas/README.md +++ b/editor-packages/editor-canvas/README.md @@ -1 +1,29 @@ -# A Interactive canvas for runtime frames. +# A Html5 backend Interactive canvas for runtime frames. + +## The system + +- Canvas +- Hud +- Event +- Math +- Iframe +- Node +- Host + +## General architecture + +- `ScaffoldCanvas` - A single component canvas that holds both renderer and eventsystem +- `RenderOnlyCanvas + EventSystem` - A Customizable system for complex and heavy rendering. (use saperate render host with iframe) + +## Events + +gesture events + +- move (pan) +- zoom (pinch) + +- create node +- remove node +- move node +- resize node +- rename node diff --git a/editor-packages/editor-canvas/canvas-event-target/canvas-event-target.tsx b/editor-packages/editor-canvas/canvas-event-target/canvas-event-target.tsx index ed277943..97c21e56 100644 --- a/editor-packages/editor-canvas/canvas-event-target/canvas-event-target.tsx +++ b/editor-packages/editor-canvas/canvas-event-target/canvas-event-target.tsx @@ -96,6 +96,9 @@ export function CanvasEventTarget({ const [first_wheel_event, set_first_wheel_event] = useState>(); + // this is a hack to prevent from onDragStart being called even when no movement is detected. + const [drag_start_emitted, set_drag_start_emitted] = useState(false); + useGesture( { onPinch: onZooming, @@ -150,7 +153,10 @@ export function CanvasEventTarget({ return; } - onDragStart(s); + if (s.delta[0] || s.delta[1]) { + onDragStart(s); + set_drag_start_emitted(true); + } }, onDrag: (s) => { if (isSpacebarPressed) { @@ -161,6 +167,10 @@ export function CanvasEventTarget({ return; } + if ((s.delta[0] || s.delta[1]) && !drag_start_emitted) { + set_drag_start_emitted(true); + onDragStart(s); + } onDrag(s); }, onDragEnd: (s) => { @@ -169,6 +179,7 @@ export function CanvasEventTarget({ return; } + set_drag_start_emitted(false); onDragEnd(s); }, onMouseDown: onPointerDown, diff --git a/editor-packages/editor-canvas/canvas/canvas.tsx b/editor-packages/editor-canvas/canvas/canvas.tsx index 595f8cad..671d6e14 100644 --- a/editor-packages/editor-canvas/canvas/canvas.tsx +++ b/editor-packages/editor-canvas/canvas/canvas.tsx @@ -9,10 +9,15 @@ import { OnPointerDownHandler, OnDragHandler, } from "../canvas-event-target"; -import { get_hovering_target, centerOf } from "../math"; +import { + target_of_point, + centerOf, + edge_scrolling, + target_of_area, +} from "../math"; import { utils } from "@design-sdk/core"; import { LazyFrame } from "@code-editor/canvas/lazy-frame"; -import { HudCustomRenderers, HudSurface } from "./hud-surface"; +import { HudCustomRenderers, HudSurface } from "../hud"; import type { Box, XY, CanvasTransform, XYWH } from "../types"; import type { FrameOptimizationFactors } from "../frame"; const designq = utils.query; @@ -89,7 +94,7 @@ export function Canvas({ ...props }: { viewbound: Box; - onSelectNode?: (node?: ReflectSceneNode) => void; + onSelectNode?: (...node: ReflectSceneNode[]) => void; onClearSelection?: () => void; } & CanvasCustomRenderers & CanvasState & { @@ -131,6 +136,7 @@ export function Canvas({ ? [offset[0] / zoom, offset[1] / zoom] : [0, 0]; const [isPanning, setIsPanning] = useState(false); + const [isDraggomg, setIsDragging] = useState(false); const [marquee, setMarquee] = useState(null); const cvtransform: CanvasTransform = { @@ -151,17 +157,46 @@ export function Canvas({ setHoveringLayer(wshighlight); }, [highlightedLayer]); + // area selection hook + useEffect(() => { + if (marquee) { + const area: XYWH = [ + marquee[0] / zoom, + marquee[1] / zoom, + marquee[2] / zoom, + marquee[3] / zoom, + ]; + + const selections = target_of_area({ + area, + tree: nodes, + contain: false, + }); + + // https://stackoverflow.com/a/19746771 + const same = + selectedNodes.length === selections?.length && + selectedNodes.every((value, index) => value === selections[index].id); + + if (!same) { + onSelectNode(...selections); + } + } + // + }, [marquee]); + const onPointerMove: OnPointerMoveHandler = (state) => { - if (isPanning || isZooming) { + if (isPanning || isZooming || isDraggomg) { // don't perform hover calculation while transforming. return; } - const hovering = get_hovering_target({ + const hovering = target_of_point({ point: state.xy, tree: nodes, zoom: zoom, offset: nonscaled_offset, margin: LAYER_HOVER_HIT_MARGIN, + reverse: true, }); if (!hovering) { @@ -223,27 +258,48 @@ export function Canvas({ setOffset([newx, newy]); }; + const onDragStart: OnDragHandler = (s) => { + onClearSelection(); + setIsDragging(true); + setHoveringLayer(null); + + // set the marquee start point + const [x, y] = s.initial; + const [ox, oy] = offset; + const [x1, y1] = [x - ox, y - oy]; + setMarquee([x1, y1, 0, 0]); + }; + const onDrag: OnDragHandler = (s) => { - const [x1, y1] = s.initial; - const [x2, y2] = [ + const [ox, oy] = offset; + const [x, y] = [ // @ts-ignore s.event.clientX, // @ts-ignore s.event.clientY, ]; - const [ox, oy] = offset; - const [x, y, w, h] = [ - x1 - ox, - y1 - oy, - x2 - x1, // w - y2 - y1, // h - ]; - setMarquee([x, y, w, h]); + const [x1, y1] = [x - ox, y - oy]; + + if (marquee) { + const [w, h] = [ + x1 - marquee[0], // w + y1 - marquee[1], // h + ]; + setMarquee([marquee[0], marquee[1], w, h]); + } + + // edge scrolling + const [cx, cy] = [x, y]; + const [dx, dy] = edge_scrolling(cx, cy, viewbound); + if (dx || dy) { + setOffset([ox + dx, oy + dy]); + } }; const onDragEnd: OnDragHandler = (s) => { setMarquee(null); + setIsDragging(false); }; const is_canvas_transforming = isPanning || isZooming; @@ -299,8 +355,8 @@ export function Canvas({ onPointerMoveStart={() => {}} onPointerMoveEnd={() => {}} onPointerDown={onPointerDown} + onDragStart={onDragStart} onDrag={onDrag} - onDragStart={() => {}} // TODO: onDragEnd={onDragEnd} > diff --git a/editor-packages/editor-canvas/docs/commands.md b/editor-packages/editor-canvas/docs/commands.md new file mode 100644 index 00000000..cd6b6dca --- /dev/null +++ b/editor-packages/editor-canvas/docs/commands.md @@ -0,0 +1,7 @@ +- move back +- move front + +- copy +- paste +- move under parent +- lock / unlock diff --git a/editor-packages/editor-canvas/docs/feature-area-selection.md b/editor-packages/editor-canvas/docs/feature-area-selection.md new file mode 100644 index 00000000..1c87074e --- /dev/null +++ b/editor-packages/editor-canvas/docs/feature-area-selection.md @@ -0,0 +1,25 @@ +# Area selection (Marquee selection) + +> This feature yet does not consider vector networks. (boolean operation) + +Keymode + +- default +- cmd + +Parameters + +- raycast mode: + - hit - if the target has an intersection with the area. + - contain - if the target is contained in the area. +- type + - frame + - group + - frame + - element (others) +- is root - this only effects to the frame node. + +Final output for painting + +- selections (each selection will get a highlight) +- bounding box (abstract group) diff --git a/editor-packages/editor-canvas/docs/feature-drag-edge-scrolling.md b/editor-packages/editor-canvas/docs/feature-drag-edge-scrolling.md new file mode 100644 index 00000000..a60e46a2 --- /dev/null +++ b/editor-packages/editor-canvas/docs/feature-drag-edge-scrolling.md @@ -0,0 +1,3 @@ +# Canvas Drag Scroll (Scroll / Pan while dragging) + +scroll (translate) a canvas while dragging (marquee, move, resize element). if hit the edge of the canvas, the canvas will scroll. diff --git a/editor-packages/editor-canvas/docs/hud.md b/editor-packages/editor-canvas/docs/hud.md new file mode 100644 index 00000000..ba46ef22 --- /dev/null +++ b/editor-packages/editor-canvas/docs/hud.md @@ -0,0 +1,42 @@ +# Hud system + +node + +- hover outline +- resize knob +- rotate knob +- border radius knob +- frame title +- draft editor (text editing) +- multiplayer cursor +- resizing size indicator +- rotating rotation indicator + +ruler & guide + +- ruler +- guide (user defined guide line) +- highlight guide (hovered, selected guide line) +- snap line (snapping guide line) +- layout grids (grid-template-columns) +- spacing guide (size between 2 nodes) + +layout + +- layout placement guide (preview the place of an moving item after placement inside a certain lyout - row / col) + +- interaction knob +- interaction line + +popover + +- popovers + +feedback (comments) + +- pin +- create pin + +vector + +- TODO: diff --git a/editor-packages/editor-canvas/docs/index.md b/editor-packages/editor-canvas/docs/index.md new file mode 100644 index 00000000..f8f0694c --- /dev/null +++ b/editor-packages/editor-canvas/docs/index.md @@ -0,0 +1 @@ +# Html5 Backend Canvas docs diff --git a/editor-packages/editor-canvas/canvas/hud-surface.tsx b/editor-packages/editor-canvas/hud/hud-surface.tsx similarity index 100% rename from editor-packages/editor-canvas/canvas/hud-surface.tsx rename to editor-packages/editor-canvas/hud/hud-surface.tsx diff --git a/editor-packages/editor-canvas/hud/index.ts b/editor-packages/editor-canvas/hud/index.ts new file mode 100644 index 00000000..a69ded74 --- /dev/null +++ b/editor-packages/editor-canvas/hud/index.ts @@ -0,0 +1,2 @@ +export { HudSurface } from "./hud-surface"; +export type { HudCustomRenderers, DisplayNodeMeta } from "./hud-surface"; diff --git a/editor-packages/editor-canvas/lazy-frame/lazy-frame.tsx b/editor-packages/editor-canvas/lazy-frame/lazy-frame.tsx index 0ee65869..1a8a946c 100644 --- a/editor-packages/editor-canvas/lazy-frame/lazy-frame.tsx +++ b/editor-packages/editor-canvas/lazy-frame/lazy-frame.tsx @@ -24,7 +24,7 @@ export function LazyFrame({ pointerEvents: "none", transition: "opacity 50ms ease-out 0s", transformOrigin: "left top", - transform: `translateX(${x}px) translateY(${y}px)`, + transform: `translate3d(${x}px, ${y}px, 0)`, willChange: "transform", display: "block", position: "fixed", @@ -40,7 +40,7 @@ export function LazyFrame({ style={{ width: "100%", height: "100%", - display: inViewport ? "block" : "none", + contentVisibility: inViewport ? "visible" : "hidden", }} > {children} diff --git a/editor-packages/editor-canvas/marquee/marquee.tsx b/editor-packages/editor-canvas/marquee/marquee.tsx index a80d5637..9b407315 100644 --- a/editor-packages/editor-canvas/marquee/marquee.tsx +++ b/editor-packages/editor-canvas/marquee/marquee.tsx @@ -31,7 +31,7 @@ export function Marquee({ rect }: { rect: XYWH }) { height: Math.abs(h), willChange: "transform, opacity", transformOrigin: "0px 0px", - transform: `translateX(${x}px) translateY(${y}px) rotate(${r}deg)`, + transform: `translate3d(${x}px, ${y}px, 0) rotate(${r}deg)`, border: `${canvasSelectionRectBorderColor} 1px solid`, }} > diff --git a/editor-packages/editor-canvas/math/hovering-target.ts b/editor-packages/editor-canvas/math/hovering-target.ts deleted file mode 100644 index 301192a2..00000000 --- a/editor-packages/editor-canvas/math/hovering-target.ts +++ /dev/null @@ -1,87 +0,0 @@ -// TODO: -/** - * if the array's order represents the reversed index (depth) set this to true. - */ - -/** - * get the hovering target node from nested children tree. - * - ignore invisible nodes. - * - target is layer in higher level. (e.g. child of a parent is hovering target if both matches the point) - */ -export function get_hovering_target({ - point, - zoom, - tree, - offset = [0, 0], - ignore, - margin = 0, -}: { - /** - * relative mouse point from canvas 0, 0 - */ - point: [number, number]; - zoom: number; - tree: T[]; - /** - * offset of the canvas (canvas xy transform) - */ - offset: [number, number]; - ignore?: (item: T) => boolean; - margin?: number; -}): T | undefined { - const [ox, oy] = offset; - for (const item of tree) { - if ( - is_point_in_xywh(point, [ - (item.absoluteX + ox) * zoom, - (item.absoluteY + oy) * zoom, - item.width * zoom + margin, - item.height * zoom + margin, - ]) - ) { - if (ignore && ignore(item)) { - // TODO: invalid logic gate - continue; - } - if (item.children) { - const hovering_child = get_hovering_target({ - point, - zoom, - tree: item.children as T[], - ignore, - margin, - offset, - }); - if (hovering_child) { - return hovering_child; - } - } - return item; - } - } -} - -function is_point_in_xywh( - point: [number, number], - xywh: [number, number, number, number] -): boolean { - const [x, y] = point; - const [x0, y0, w, h] = xywh; - const inbound = x >= x0 && x <= x0 + w && y >= y0 && y <= y0 + h; - return inbound; -} - -interface Tree { - id: string; - /** - * absolute x point. - */ - absoluteX: number; - /** - * absolute y point. - */ - absoluteY: number; - width: number; - height: number; - children?: Tree[] | undefined; -} diff --git a/editor-packages/editor-canvas/math/index.ts b/editor-packages/editor-canvas/math/index.ts index f640ed54..70d1cd75 100644 --- a/editor-packages/editor-canvas/math/index.ts +++ b/editor-packages/editor-canvas/math/index.ts @@ -1,2 +1,4 @@ -export * from "./hovering-target"; +export * from "./target-of-area"; +export * from "./target-of-point"; export * from "./center-of"; +export * from "./viewbound-edge-scrolling"; diff --git a/editor-packages/editor-canvas/math/target-of-area.ts b/editor-packages/editor-canvas/math/target-of-area.ts new file mode 100644 index 00000000..e1b6a6b2 --- /dev/null +++ b/editor-packages/editor-canvas/math/target-of-area.ts @@ -0,0 +1,152 @@ +import type { Tree, XY, XYWH } from "../types"; + +/** + * target of area + * calculates the target node from nested children tree recursively. + * + * if the current looping node matches the condition, don't go any deeper. + * if the current looping node doesn't match the condition, go deeper, loop trough its children if present. + * + * for example: + * - if the area is (0, 0, 100, 100), and the children are [ [0, 0, 50, 50], [50, 50, 50, 50] ], the target is both two items. + * - when contain is true: if the area is (0, 0, 50, 50), and the children are [ [0, 0, 50, 50], [50, 50, 50, 50] ], the target is both two items. + * - when contain is false: if the area is (0, 0, 50, 50), and the children are [ [0, 0, 50, 50], [50, 50, 50, 50] ], the target is only first item. + * + * + * @param area - [x, y, w, h] the marquee data + * @param tree - the tree to search + * @param ignore - provide a function that returns boolean. if true, the current item will be ignored. this is usefull when you want to ignore grouping nodes, e.g. when command key is pressed while marquee, it should only select the deep down nodes. + * @param margin - the margin of raycasting + * @param contain - if true, the target node should be contained in the area. if false, the target node should be intersected with the area. + * @param contain - reverse the order of the tree (after copy with `Array.from()`). + * + */ +export function target_of_area( + { + area, + tree, + ignore, + margin = 0, + contain, + reverse = true, + }: { + area: XYWH; + tree: T[]; + margin?: number; + contain: boolean; + ignore?: (item: T) => boolean; + reverse?: boolean; + }, + depth: number = 0 +): T[] { + const items = reverse ? Array.from(tree).reverse() : tree; + + const result: T[] = []; + + for (const item of items) { + if (ignore?.(item)) { + continue; + } else { + if ( + is_rect_in_rect_raycast( + [item.absoluteX, item.absoluteY, item.width, item.height], + area, + contain + ) + ) { + // console.log(item, ) + result.push(item); + } else { + if (item.children) { + const targets = target_of_area( + { + area, + tree: item.children as T[], + ignore, + margin, + contain, + reverse, + }, + depth + 1 + ); + if (targets?.length) { + result.push(...targets); + } + } + } + } + } + return result; +} + +function is_rect_in_rect_raycast(a: XYWH, b: XYWH, contain: boolean): boolean { + if (contain) { + return is_rect_in_rect(a, b); + } else { + return is_rect_intersect_rect(a, b); + } +} + +/** + * check if the rect a is contained in the rect b + * @param a - [x, y, w, h] the first rect, where "rect a" in "rect b" + * @param b - [x, y, w, h] the second rect, where "rect a" in "rect b" + * @returns + */ +function is_rect_in_rect(a: XYWH, b: XYWH): boolean { + const [x1, y1, w1, h1] = a; + const [x2, y2, w2, h2] = b; + + throw new Error("not implemented"); + // return x1 >= x2 && y1 >= y2 && x1 + w1 <= x2 + w2 && y1 + h1 <= y2 + h2; +} + +/** + * check if two area has an intersection + * + * for example: + * - a: [0, 0, 100, 100], b: [0, 0, 50, 50] => true + * - a: [0, 0, 100, 100], b: [100, 100, 50, 50] => false + * - a: [0, 0, 100, 100], b: [50, 50, 50, 50] => true + * - a: [0, 0, 100, 100], b: [0, 0, 100, 100] => true + * - a: [0, 0, -100, -100], b: [0, 0, 100, 100] => false + * - a: [0, 0, -100, -100], b: [-10, -10, 100, 100] => true + * - a: [0, 0, 100, 100], b: [0, 0, -100, -100] => false + * - a: [-10, 0, 11, 1], b: [0, 0, 20, 20] => true + * + * @param a + * @param b + * @returns + */ +function is_rect_intersect_rect(a: XYWH, b: XYWH): boolean { + const [ax, ay, aw, ah] = a; + const [bx, by, bw, bh] = b; + + const [ax1, ay1, ax2, ay2] = [ax, ay, ax + aw, ay + ah]; + const [bx1, by1, bx2, by2] = [bx, by, bx + bw, by + bh]; + + return !( + Math.max(ax1, ax2) < Math.min(bx1, bx2) || + Math.min(ax1, ax2) > Math.max(bx1, bx2) || + Math.min(ay1, ay2) > Math.max(by1, by2) || + Math.max(ay1, ay2) < Math.min(by1, by2) + ); + + // preserve below for readability (above is optimized) + + // const max_ax = Math.max(ax1, ax2); + // const min_ax = Math.min(ax1, ax2); + // const max_ay = Math.max(ay1, ay2); + // const min_ay = Math.min(ay1, ay2); + // const max_bx = Math.max(bx1, bx2); + // const min_bx = Math.min(bx1, bx2); + // const max_by = Math.max(by1, by2); + // const min_by = Math.min(by1, by2); + + // return !( + // max_ax < min_bx || + // min_ax > max_bx || + // min_ay > max_by || + // max_ay < min_by + // ); +} diff --git a/editor-packages/editor-canvas/math/target-of-point.ts b/editor-packages/editor-canvas/math/target-of-point.ts new file mode 100644 index 00000000..e26a814c --- /dev/null +++ b/editor-packages/editor-canvas/math/target-of-point.ts @@ -0,0 +1,84 @@ +// TODO: +/** + * if the array's order represents the reversed index (depth) set this to true. + */ + +import type { Tree, XY, XYWH } from "../types"; + +/** + * get the hovering target node from nested children tree. + * - ignore invisible nodes. + * - target is layer in higher level. (e.g. child of a parent is hovering target if both matches the point) + */ +export function target_of_point( + { + point, + zoom, + tree, + offset = [0, 0], + ignore, + margin = 0, + reverse = true, + }: { + /** + * relative mouse point from canvas 0, 0 + */ + point: XY; + zoom: number; + tree: T[]; + /** + * offset of the canvas (canvas xy transform) + */ + offset: XY; + ignore?: (item: T) => boolean; + margin?: number; + reverse?: boolean; + }, + depth = 0 +): T | undefined { + const [ox, oy] = offset; + + const items = reverse ? Array.from(tree).reverse() : tree; + + for (const item of items) { + if ( + is_point_in_xywh(point, [ + (item.absoluteX + ox) * zoom, + (item.absoluteY + oy) * zoom, + item.width * zoom + margin, + item.height * zoom + margin, + ]) + ) { + if (ignore?.(item)) { + // TODO: invalid logic gate + continue; + } + if (item.children) { + const hovering_child = target_of_point( + { + point, + zoom, + tree: item.children as T[], + ignore, + margin, + offset, + reverse, + }, + depth + 1 + ); + if (hovering_child) { + return hovering_child; + } + } + + return item; + } + } +} + +function is_point_in_xywh(point: XY, xywh: XYWH): boolean { + const [x, y] = point; + const [x0, y0, w, h] = xywh; + const inbound = x >= x0 && x <= x0 + w && y >= y0 && y <= y0 + h; + return inbound; +} diff --git a/editor-packages/editor-canvas/math/viewbound-edge-scrolling.ts b/editor-packages/editor-canvas/math/viewbound-edge-scrolling.ts new file mode 100644 index 00000000..4146b59c --- /dev/null +++ b/editor-packages/editor-canvas/math/viewbound-edge-scrolling.ts @@ -0,0 +1,48 @@ +/** + * edge scrolling + * scroll (translate) the canvas if the cursor is near the edge (viewbound) with marginal value. + * get the distance from each edge. + * + * for example, + * - if the cursor is at [19, 19], and the margin is 20, the canvas will be translated to [-1, -1] + * - if the cursor is at [19, 19], and the margin is 10, the canvas will be translated to [0, 0] + * - if the cursor is at [0, 0], and the margin is 10, the canvas will be translated to [-10, -10] + * - if the cursor is at [0, 0], and the margin is 20, the canvas will be translated to [-20, -20] + * - if the cursor is at [1920, 1080] on a [0, 0, 1920, 1090] viewbound, and the margin is 20, the canvas will be translated to [20, 20] + * - if the cursor is at [1920, 0] on a [0, 0, 1920, 1090] viewbound, and the margin is 20, the canvas will be translated to [20, -20] + * - if the cursor is at [1920, 500] on a [0, 0, 1920, 1090] viewbound, and the margin is 20, the canvas will be translated to [20, 0] + * + * + * + * @param cx x coordinate of the cursor + * @param cy y coordinate of the cursor + * @param viewbound the viewbound of the canvas (l, t, b, r) + * @param margin the margin value (default 40px) + * @param factor the returned value will be multiplied by this factor (default 1/4) + * + * @returns [number, number] the translation of the canvas + */ +export function edge_scrolling( + cx: number, + cy: number, + viewbound: [number, number, number, number], + margin = 40, + factor = 1 / 4 +): [number, number] { + const [l, t, b, r] = viewbound; + let [dx, dy] = [0, 0]; + + if (cx < l + margin) { + dx = l - cx + margin; + } else if (cx > r - margin) { + dx = r - cx - margin; + } + + if (cy < t + margin) { + dy = t - cy + margin; + } else if (cy > b - margin) { + dy = b - cy - margin; + } + + return [dx * factor, dy * factor]; +} diff --git a/editor-packages/editor-canvas/nodes/README.md b/editor-packages/editor-canvas/nodes/README.md new file mode 100644 index 00000000..20d5e19e --- /dev/null +++ b/editor-packages/editor-canvas/nodes/README.md @@ -0,0 +1,8 @@ +# Nodes + +- TextNode +- FrameNode +- RectangleNode +- EllipseNode +- VectorNode +- PolygonNode diff --git a/editor-packages/editor-canvas/nodes/index.ts b/editor-packages/editor-canvas/nodes/index.ts new file mode 100644 index 00000000..e20cd3f8 --- /dev/null +++ b/editor-packages/editor-canvas/nodes/index.ts @@ -0,0 +1 @@ +export * from "./text"; diff --git a/editor-packages/editor-canvas/types/index.ts b/editor-packages/editor-canvas/types/index.ts index a7c26966..907c5e37 100644 --- a/editor-packages/editor-canvas/types/index.ts +++ b/editor-packages/editor-canvas/types/index.ts @@ -5,3 +5,17 @@ export type CanvasTransform = { xy: XY; }; export type Box = [number, number, number, number]; +export interface Tree { + id: string; + /** + * absolute x point. + */ + absoluteX: number; + /** + * absolute y point. + */ + absoluteY: number; + width: number; + height: number; + children?: Tree[] | undefined; +} diff --git a/editor/components/canvas/isolated-canvas.tsx b/editor/components/canvas/isolated-canvas.tsx index 3cab82ed..752d87c9 100644 --- a/editor/components/canvas/isolated-canvas.tsx +++ b/editor/components/canvas/isolated-canvas.tsx @@ -219,7 +219,7 @@ const TransformContainer = ({
; +export default function CanvasServerPage() { + const router = useRouter(); + + const { key: __fk } = router.query; + const filkey = __fk as string; + + const [canvasSizingRef, canvasBounds] = useMeasure(); + const [selectedPage, setSelectedPage] = useState(null); + + // useEffect(() => { + // const handler = (e) => { + // // + // }; + + // window.addEventListener("message", handler); + // return () => { + // window.removeEventListener("message", handler); + // }; + // }, []); + + // const thisPageNodes = selectedPage + // ? design.pages.find((p) => p.id == selectedPage).children.filter(Boolean) + // : []; + + const thisPageNodes = []; + + return ( +
+ { + // dispatch({ type: "select-node", node: node?.id }); + }} + onClearSelection={() => { + // dispatch({ type: "select-node", node: null }); + }} + nodes={thisPageNodes} + // initialTransform={ } // TODO: if the initial selection is provided from first load, from the query param, we have to focus to fit that node. + renderItem={(p) => { + return ( + // + + ); + }} + config={{ + can_highlight_selected_layer: true, + marquee: { + disabled: true, + }, + }} + renderFrameTitle={(p) => ( + { + // startIsolatedViewMode(); + }} + /> + )} + /> +
+ ); } diff --git a/editor/scaffolds/canvas/canvas.tsx b/editor/scaffolds/canvas/canvas.tsx index e79d81f1..2c1739be 100644 --- a/editor/scaffolds/canvas/canvas.tsx +++ b/editor/scaffolds/canvas/canvas.tsx @@ -2,7 +2,10 @@ import React, { useCallback } from "react"; import styled from "@emotion/styled"; import { Canvas } from "@code-editor/canvas"; import { useEditorState, useWorkspace } from "core/states"; -import { Preview } from "scaffolds/preview"; +import { + D2CVanillaPreview, + WebWorkerD2CVanillaPreview, +} from "scaffolds/preview"; import useMeasure from "react-use-measure"; import { useDispatch } from "core/dispatch"; import { FrameTitleRenderer } from "./render/frame-title"; @@ -110,10 +113,10 @@ export function VisualContentArea() { ]} filekey={state.design.key} pageid={selectedPage} - selectedNodes={selectedNodes.filter(Boolean)} + selectedNodes={selectedNodes} highlightedLayer={highlightedLayer} - onSelectNode={(node) => { - dispatch({ type: "select-node", node: node?.id }); + onSelectNode={(...nodes) => { + dispatch({ type: "select-node", node: nodes.map((n) => n.id) }); }} onClearSelection={() => { dispatch({ type: "select-node", node: null }); @@ -121,18 +124,26 @@ export function VisualContentArea() { nodes={thisPageNodes} // initialTransform={ } // TODO: if the initial selection is provided from first load, from the query param, we have to focus to fit that node. renderItem={(p) => { - return ; + return ( + // + + ); }} config={{ can_highlight_selected_layer: true, marquee: { - disabled: true, + disabled: false, }, }} renderFrameTitle={(p) => ( )} diff --git a/editor/scaffolds/canvas/render/frame-title.tsx b/editor/scaffolds/canvas/render/frame-title.tsx index 61434cef..8681cd2d 100644 --- a/editor/scaffolds/canvas/render/frame-title.tsx +++ b/editor/scaffolds/canvas/render/frame-title.tsx @@ -17,7 +17,9 @@ export function FrameTitleRenderer({ onHoverChange, onSelect, onRunClick, + runnable = false, }: FrameTitleProps & { + runnable?: boolean; onRunClick: () => void; }) { const [x, y] = xy; @@ -47,7 +49,9 @@ export function FrameTitleRenderer({ xy={[x, height_considered_y_transform]} {...hoverProps} > - {selected && } + {selected && runnable && ( + + )} { + // + }, []); + + return ( +