|
1 | | -import React, { useCallback, useMemo, useState } from "react"; |
| 1 | +import React, { useCallback, useEffect, useMemo, useState } from "react"; |
2 | 2 | import { TreeView } from "@editor-ui/editor"; |
3 | 3 | import { |
4 | 4 | LayerRow, |
5 | 5 | IconContainer, |
6 | 6 | LayerIcon, |
7 | 7 | } from "./editor-layer-hierarchy-item"; |
8 | | -import { useEditorState, useWorkspace } from "core/states"; |
| 8 | +import { |
| 9 | + FigmaReflectRepository, |
| 10 | + useEditorState, |
| 11 | + useWorkspace, |
| 12 | +} from "core/states"; |
9 | 13 | import { useDispatch } from "core/dispatch"; |
10 | 14 | import { |
11 | 15 | flattenNodeTree, |
12 | 16 | FlattenedDisplayItemNode, |
13 | 17 | } from "./editor-layer-heriarchy-controller"; |
| 18 | +import type { ReflectSceneNode } from "@design-sdk/figma-node"; |
14 | 19 |
|
15 | 20 | // TODO: |
16 | 21 | // - add navigate context menu |
17 | 22 | // - add go to main component |
18 | 23 | // - add reveal and focus to selected layers |
19 | 24 |
|
20 | | -export function DesignLayerHierarchy() { |
| 25 | +/** |
| 26 | + * |
| 27 | + * @param props.rootNodeIDs - root node ids to display @default: null |
| 28 | + * @param props.expandAll - expand all nodes by default @default: false |
| 29 | + * @returns |
| 30 | + */ |
| 31 | +export function DesignLayerHierarchy({ |
| 32 | + rootNodeIDs = null, |
| 33 | + expandAll = false, |
| 34 | +}: { |
| 35 | + rootNodeIDs?: string[]; |
| 36 | + expandAll?: boolean; |
| 37 | +}) { |
21 | 38 | const [state] = useEditorState(); |
| 39 | + const { selectedNodes, selectedPage, design } = state; |
22 | 40 | const { highlightLayer, highlightedLayer } = useWorkspace(); |
23 | 41 | const dispatch = useDispatch(); |
24 | 42 |
|
25 | | - const { selectedNodes, selectedPage, design } = state; |
26 | | - |
27 | 43 | const [expands, setExpands] = useState<string[]>(state?.selectedNodes ?? []); |
28 | 44 |
|
29 | | - const root = selectedPage |
30 | | - ? design.pages.find((p) => p.id == selectedPage).children |
31 | | - : [design?.input?.entry]; |
| 45 | + // get the root nodes (if the rootNodeIDs is not specified, use the selected page's children) |
| 46 | + let roots: ReflectSceneNode[] = []; |
| 47 | + if (rootNodeIDs?.length > 0) { |
| 48 | + roots = rootNodeIDs.reduce((acc, item) => { |
| 49 | + acc.push(findUnder(item, design)); |
| 50 | + return acc; |
| 51 | + }, []); |
| 52 | + } else { |
| 53 | + roots = selectedPage |
| 54 | + ? design.pages.find((p) => p.id == selectedPage).children |
| 55 | + : [design?.input?.entry]; |
| 56 | + } |
32 | 57 |
|
33 | 58 | const layers: FlattenedDisplayItemNode[][] = useMemo(() => { |
34 | | - return root |
35 | | - ? root |
| 59 | + return roots |
| 60 | + ? roots |
36 | 61 | .filter(Boolean) |
37 | 62 | .map((layer) => flattenNodeTree(layer, selectedNodes, expands)) |
38 | 63 | : []; |
39 | | - }, [root, state?.selectedNodes, expands]); |
| 64 | + }, [roots, state?.selectedNodes, expands]); |
| 65 | + |
| 66 | + useEffect(() => { |
| 67 | + if (expandAll) { |
| 68 | + const ids = layers.reduce((acc, item) => { |
| 69 | + acc.push(...item.map((i) => i.id)); |
| 70 | + return acc; |
| 71 | + }, [] as string[]); |
| 72 | + setExpands(ids); |
| 73 | + } |
| 74 | + }, [layers, expandAll]); |
40 | 75 |
|
41 | 76 | const renderItem = useCallback( |
42 | 77 | ({ |
@@ -96,3 +131,20 @@ export function DesignLayerHierarchy() { |
96 | 131 | ); |
97 | 132 | // |
98 | 133 | } |
| 134 | + |
| 135 | +/** |
| 136 | + * This only supports root frame at the moment. |
| 137 | + * partof: nodeQ |
| 138 | + * @param node |
| 139 | + * @param design |
| 140 | + * @returns |
| 141 | + */ |
| 142 | +function findUnder(node: string, design: FigmaReflectRepository) { |
| 143 | + for (const page of design.pages) { |
| 144 | + for (const frame of page.children) { |
| 145 | + if (frame.id === node) { |
| 146 | + return frame; |
| 147 | + } |
| 148 | + } |
| 149 | + } |
| 150 | +} |
0 commit comments