Skip to content

Commit 58de8d8

Browse files
committed
feat: Context Panel for Flex Nodes
1 parent 8623940 commit 58de8d8

File tree

2 files changed

+178
-1
lines changed

2 files changed

+178
-1
lines changed

src/components/shared/ReactFlow/FlowCanvas/FlexNode/FlexNode.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,39 @@
11
import { type Node, type NodeProps } from "@xyflow/react";
2+
import { useEffect } from "react";
23

34
import { BlockStack } from "@/components/ui/layout";
45
import { Paragraph } from "@/components/ui/typography";
56
import { cn } from "@/lib/utils";
7+
import { useContextPanel } from "@/providers/ContextPanelProvider";
68

9+
import { FlexNodeEditor } from "./FlexNodeEditor";
710
import type { FlexNodeData } from "./types";
811

912
type FlexNodeProps = NodeProps<Node<FlexNodeData>>;
1013

1114
const FlexNode = ({ data, id, selected }: FlexNodeProps) => {
12-
const { properties } = data;
15+
const { properties, readOnly } = data;
1316
const { color, zIndex } = properties;
1417

18+
const {
19+
setContent,
20+
clearContent,
21+
setOpen: setContextPanelOpen,
22+
} = useContextPanel();
23+
24+
useEffect(() => {
25+
if (selected) {
26+
setContent(<FlexNodeEditor flexNode={data} readOnly={readOnly} />);
27+
setContextPanelOpen(true);
28+
}
29+
30+
return () => {
31+
if (selected) {
32+
clearContent();
33+
}
34+
};
35+
}, [data, readOnly, selected]);
36+
1537
return (
1638
<div
1739
key={id}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import { ContentBlock } from "@/components/shared/ContextPanel/Blocks/ContentBlock";
2+
import { KeyValueList } from "@/components/shared/ContextPanel/Blocks/KeyValueList";
3+
import { CopyText } from "@/components/shared/CopyText/CopyText";
4+
import { Input } from "@/components/ui/input";
5+
import { Label } from "@/components/ui/label";
6+
import { BlockStack, InlineStack } from "@/components/ui/layout";
7+
import { Textarea } from "@/components/ui/textarea";
8+
import { Paragraph, Text } from "@/components/ui/typography";
9+
10+
import type { FlexNodeData } from "./types";
11+
12+
interface FlexNodeEditorProps {
13+
flexNode: FlexNodeData;
14+
readOnly?: boolean;
15+
}
16+
17+
export const FlexNodeEditor = ({
18+
flexNode,
19+
readOnly = false,
20+
}: FlexNodeEditorProps) => {
21+
const { properties, size, position } = flexNode;
22+
23+
return (
24+
<BlockStack gap="4" className="h-full px-2">
25+
<Text size="lg" weight="semibold" className="wrap-anywhere">
26+
Sticky Note
27+
</Text>
28+
29+
<ContentEditor properties={properties} readOnly={readOnly} />
30+
31+
<ColorEditor properties={properties} readOnly={readOnly} />
32+
33+
<KeyValueList
34+
title="Layout"
35+
items={[
36+
{
37+
label: "size",
38+
value: `${size.width} x ${size.height}`,
39+
copyable: true,
40+
},
41+
{
42+
label: "position",
43+
value: `${position.x}, ${position.y}`,
44+
copyable: true,
45+
},
46+
{
47+
label: "z-index",
48+
value: `${properties.zIndex}`,
49+
copyable: true,
50+
},
51+
]}
52+
/>
53+
</BlockStack>
54+
);
55+
};
56+
57+
const ContentEditor = ({
58+
properties,
59+
readOnly,
60+
}: {
61+
properties: FlexNodeData["properties"];
62+
readOnly: boolean;
63+
}) => {
64+
if (readOnly) {
65+
return (
66+
<KeyValueList
67+
title="Content"
68+
items={[
69+
{
70+
label: "Title",
71+
value: properties.title,
72+
copyable: true,
73+
},
74+
{
75+
value: properties.content,
76+
copyable: true,
77+
},
78+
]}
79+
/>
80+
);
81+
}
82+
83+
return (
84+
<ContentBlock title="Content">
85+
<BlockStack gap="2">
86+
<BlockStack>
87+
<Label
88+
htmlFor="flex-node-title"
89+
className="text-muted-foreground text-xs"
90+
>
91+
Title
92+
</Label>
93+
<Input
94+
id="flex-node-title"
95+
value={properties.title}
96+
className="text-sm"
97+
readOnly
98+
/>
99+
</BlockStack>
100+
<BlockStack>
101+
<Label
102+
htmlFor="flex-node-content"
103+
className="text-muted-foreground text-xs"
104+
>
105+
Note
106+
</Label>
107+
<Textarea
108+
id="flex-node-content"
109+
value={properties.content}
110+
className="text-xs"
111+
readOnly
112+
/>
113+
</BlockStack>
114+
</BlockStack>
115+
</ContentBlock>
116+
);
117+
};
118+
119+
const ColorEditor = ({
120+
properties,
121+
readOnly,
122+
}: {
123+
properties: FlexNodeData["properties"];
124+
readOnly: boolean;
125+
}) => {
126+
if (readOnly) {
127+
return (
128+
<KeyValueList
129+
title="Color"
130+
items={[
131+
{
132+
label: "Backgroud",
133+
value: properties.color,
134+
copyable: true,
135+
},
136+
]}
137+
/>
138+
);
139+
}
140+
141+
return (
142+
<ContentBlock title="Color">
143+
<BlockStack gap="1">
144+
<InlineStack gap="4" blockAlign="center">
145+
<Paragraph size="xs">Background</Paragraph>
146+
<div
147+
className="aspect-square h-4 rounded-full border border-muted-foreground"
148+
style={{ backgroundColor: properties.color }}
149+
/>
150+
<CopyText className="text-xs font-mono">{properties.color}</CopyText>
151+
</InlineStack>
152+
</BlockStack>
153+
</ContentBlock>
154+
);
155+
};

0 commit comments

Comments
 (0)