Skip to content

Commit 03534d2

Browse files
add folder item card & new folder action
1 parent 0f3aff0 commit 03534d2

13 files changed

Lines changed: 359 additions & 126 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import React from "react";
2+
import { SquareIcon } from "@radix-ui/react-icons";
3+
import {
4+
DashboardItemCard,
5+
DashboardItemCardProps,
6+
} from "./dashboard-item-card";
7+
import { DashboardFolderItem } from "../core";
8+
9+
function FolderCardPreview() {
10+
return (
11+
<div
12+
style={{
13+
display: "flex",
14+
justifyContent: "center",
15+
alignItems: "center",
16+
height: 160,
17+
width: 300,
18+
boxSizing: "border-box",
19+
}}
20+
>
21+
<img
22+
style={{
23+
height: "100%",
24+
width: 120,
25+
margin: "auto",
26+
objectFit: "contain",
27+
objectPosition: "center",
28+
}}
29+
src="/assets/mac-folder-icon.png"
30+
/>
31+
</div>
32+
);
33+
34+
//
35+
}
36+
37+
export type FolderCardProps = Omit<
38+
DashboardItemCardProps,
39+
"icon" | "preview" | "label"
40+
> &
41+
Omit<DashboardFolderItem, "$type">;
42+
43+
export const FolderCard = React.forwardRef(function (
44+
{ name, ...props }: FolderCardProps,
45+
ref: React.Ref<HTMLDivElement>
46+
) {
47+
return (
48+
<DashboardItemCard
49+
ref={ref}
50+
{...props}
51+
label={name}
52+
icon={<SquareIcon color="white" />}
53+
preview={<FolderCardPreview />}
54+
/>
55+
);
56+
});
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import React, { useRef } from "react";
2+
import type { ReflectSceneNode } from "@design-sdk/figma-node";
3+
import { FigmaNodeBitmapView } from "@code-editor/canvas-renderer-bitmap";
4+
import { SceneNodeIcon } from "@code-editor/node-icons";
5+
import { useInViewport } from "react-in-viewport";
6+
import {
7+
DashboardItemCard,
8+
DashboardItemCardProps,
9+
} from "./dashboard-item-card";
10+
11+
function SceneCardPreview({
12+
maxWidth,
13+
scene,
14+
}: {
15+
maxWidth: number;
16+
scene: ReflectSceneNode;
17+
}) {
18+
const visibilityRef = useRef();
19+
20+
const { enterCount } = useInViewport(visibilityRef);
21+
22+
// max allowed zoom = 1
23+
const scale = Math.min(maxWidth / scene.width, 1);
24+
const { height, type } = scene;
25+
26+
return (
27+
<div
28+
ref={visibilityRef}
29+
style={{
30+
height: height * scale,
31+
width: maxWidth,
32+
}}
33+
>
34+
{/* transformer */}
35+
<div
36+
id="view"
37+
style={{
38+
transform: `scale(${scale})`,
39+
}}
40+
>
41+
<FigmaNodeBitmapView
42+
background={"white"}
43+
key={scene.id}
44+
target={scene}
45+
isPanning={false}
46+
isZooming={false}
47+
zoom={null}
48+
inViewport={enterCount > 0}
49+
focused={false}
50+
/>
51+
</div>
52+
</div>
53+
);
54+
55+
//
56+
}
57+
58+
export interface SceneCardProps
59+
extends Omit<DashboardItemCardProps, "icon" | "preview" | "label"> {
60+
scene: ReflectSceneNode;
61+
}
62+
63+
export const SceneCard = React.forwardRef(function (
64+
{ scene, ...props }: SceneCardProps,
65+
ref: React.Ref<HTMLDivElement>
66+
) {
67+
return (
68+
<DashboardItemCard
69+
ref={ref}
70+
{...props}
71+
label={scene.name}
72+
icon={<SceneNodeIcon type={scene.type} color="white" />}
73+
preview={<SceneCardPreview scene={scene} maxWidth={300} />}
74+
/>
75+
);
76+
});

editor-packages/editor-dashboard/components/scene-card.tsx renamed to editor-packages/editor-dashboard/components/dashboard-item-card.tsx

Lines changed: 21 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
1-
import React, { useRef } from "react";
1+
import React from "react";
22
import styled from "@emotion/styled";
3-
import type { ReflectSceneNode } from "@design-sdk/figma-node";
4-
import { FigmaNodeBitmapView } from "@code-editor/canvas-renderer-bitmap";
5-
import { SceneNodeIcon } from "@code-editor/node-icons";
63
import Highlighter from "react-highlight-words";
7-
import { useInViewport } from "react-in-viewport";
8-
import { mergeRefs } from "react-merge-refs";
94

10-
export interface SceneCardProps {
11-
scene: ReflectSceneNode;
5+
export interface DashboardItemCardProps {
126
selected?: boolean;
137
onClick?: (e) => void;
148
onDoubleClick?: () => void;
@@ -18,31 +12,28 @@ export interface SceneCardProps {
1812
* an explicit field to set the view as accepting drag state view.
1913
*/
2014
isOver?: boolean;
15+
preview: React.ReactNode;
16+
label: string;
17+
icon?: React.ReactNode;
2118
}
2219

23-
export const SceneCard = React.forwardRef(function (
20+
export const DashboardItemCard = React.forwardRef(function (
2421
{
25-
style = {},
26-
scene,
27-
selected,
2822
onClick,
2923
onDoubleClick,
30-
q,
24+
selected,
3125
isOver,
32-
}: SceneCardProps,
33-
ref
26+
style = {},
27+
label,
28+
q,
29+
icon,
30+
preview,
31+
}: DashboardItemCardProps,
32+
ref: React.Ref<HTMLDivElement>
3433
) {
35-
const visibilityRef = useRef();
36-
const { enterCount } = useInViewport(visibilityRef);
37-
38-
const maxwidth = 300;
39-
40-
// max allowed zoom = 1
41-
const scale = Math.min(maxwidth / scene.width, 1);
42-
const { height, type } = scene;
4334
return (
4435
<Card
45-
ref={mergeRefs([visibilityRef, ref])}
36+
ref={ref}
4637
onClick={onClick}
4738
onDoubleClick={onDoubleClick}
4839
data-selected={selected}
@@ -51,43 +42,18 @@ export const SceneCard = React.forwardRef(function (
5142
...style,
5243
}}
5344
>
54-
{/* sizer */}
55-
<Preview
56-
data-selected={selected}
57-
data-over={isOver}
58-
style={{
59-
height: height * scale,
60-
width: maxwidth,
61-
}}
62-
>
45+
<PreviewContainer data-selected={selected} data-over={isOver}>
6346
<span id="overlay" />
64-
{/* transformer */}
65-
<div
66-
id="view"
67-
style={{
68-
transform: `scale(${scale})`,
69-
}}
70-
>
71-
<FigmaNodeBitmapView
72-
background={"white"}
73-
key={scene.id}
74-
target={scene}
75-
isPanning={false}
76-
isZooming={false}
77-
zoom={null}
78-
inViewport={enterCount > 0}
79-
focused={false}
80-
/>
81-
</div>
82-
</Preview>
47+
{preview}
48+
</PreviewContainer>
8349
<footer>
8450
<label>
85-
<SceneNodeIcon type={type} color="white" />
51+
{!!icon && icon}
8652
<Highlighter
8753
className="name"
8854
highlightClassName="name"
8955
searchWords={q ? [q] : []}
90-
textToHighlight={scene.name}
56+
textToHighlight={label}
9157
autoEscape // required to escape regex special characters, like, `+`, `(`, `)`, etc.
9258
/>
9359
</label>
@@ -131,7 +97,7 @@ const Card = styled.div`
13197
}
13298
`;
13399

134-
const Preview = styled.div`
100+
const PreviewContainer = styled.div`
135101
outline: 1px solid rgba(0, 0, 0, 0.1);
136102
border-radius: 2px;
137103
overflow: hidden;
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
export * from "./scene-card";
1+
export * from "./dashboard-item-card-folder";
2+
export * from "./dashboard-item-card-scene";
3+
export * from "./dashboard-item-card";
24
export * from "./icon-button";
35
export * from "./hierarchy-item";
46
export * from "./section-header";

editor-packages/editor-dashboard/core/action.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export type Action =
22
| NewSectionAction
3-
| NewFolderAction
3+
| MakeDirAction
44
| FilterAction
55
| FoldUnfoldAction
66
| FoldUnfoldAllAction;
@@ -17,9 +17,13 @@ export type FilterAction = {
1717
query: string;
1818
};
1919

20-
export type NewFolderAction = {
21-
type: "hierarchy/new-directory";
22-
path: string;
20+
export type MakeDirAction = {
21+
type: "hierarchy/mkdir";
22+
cwd: string;
23+
/**
24+
* name of the directory. if non provided, it will automatically assign a name like "Untitled 1"
25+
*/
26+
name?: string;
2327
};
2428

2529
export type FoldUnfoldAllAction = FoldAllAction | UnfoldAllAction;
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
export { DashboardStateProvider } from "./provider";
1+
export { DashboardStateProvider, useDashboard } from "./provider";
2+
3+
//
4+
export * from "./action";
5+
export * from "./state";

editor-packages/editor-dashboard/core/initial.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { FigmaReflectRepository } from "editor/core/states";
2+
import type { ReflectSceneNode } from "@design-sdk/figma-node";
23
import type {
34
ComponentItem,
45
DashboardFolderItem,
@@ -26,7 +27,7 @@ export function initialHierarchy(
2627
): DashboardHierarchy {
2728
//
2829

29-
const grouped = group(design, { filter: null });
30+
const grouped = group<ReflectSceneNode>(design, { filter: null });
3031

3132
const sections: Array<DashboardFolderItem> = Array.from(grouped.keys()).map(
3233
(k): DashboardFolderItem => {
@@ -39,6 +40,7 @@ export function initialHierarchy(
3940
contents: items.map((i) => ({
4041
$type: "frame-scene",
4142
id: i.id,
43+
scene: i,
4244
name: i.name,
4345
path: k + "/" + i.name,
4446
type: "FRAME",

editor-packages/editor-dashboard/core/provider.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,17 @@ export function useDashboard() {
136136
[dispatch]
137137
);
138138

139+
const mkdir = useCallback(
140+
(cwd: string, name?: string) => {
141+
dispatch({
142+
type: "hierarchy/mkdir",
143+
cwd,
144+
name,
145+
});
146+
},
147+
[dispatch]
148+
);
149+
139150
return {
140151
...state,
141152
selection: editorState.selectedNodes,
@@ -147,5 +158,6 @@ export function useDashboard() {
147158
unfoldAll,
148159
fold,
149160
unfold,
161+
mkdir,
150162
};
151163
}

0 commit comments

Comments
 (0)