ai-design-system 0.1.72 → 0.1.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/ai-elements/file-tree.tsx +12 -32
- package/components/ai-elements/node.tsx +32 -0
- package/components/ai-elements/prompt-input.tsx +8 -4
- package/components/ai-elements/tool.tsx +17 -3
- package/components/blocks/WorkflowCanvas/WorkflowCanvas.tsx +2 -0
- package/components/blocks/WorkflowCanvas/interfaces.ts +6 -1
- package/components/composites/ApprovalCard/ApprovalCard.tsx +8 -3
- package/components/composites/DefaultSwitcher/DefaultSwitcher.tsx +5 -1
- package/components/composites/PromptInput/PromptInput.tsx +16 -5
- package/components/composites/SpatialContainerNode/SpatialContainerNode.stories.tsx +169 -0
- package/components/composites/SpatialContainerNode/SpatialContainerNode.tsx +316 -0
- package/components/composites/SpatialContainerNode/index.ts +6 -0
- package/components/composites/StateNode/StateNode.tsx +3 -1
- package/components/composites/StreamingMarkdown.tsx +42 -3
- package/components/composites/TransitionNode/TransitionNode.tsx +3 -1
- package/components/composites/TriggerNode/TriggerNode.tsx +3 -1
- package/components/composites/index.ts +4 -0
- package/components/features/{RefinementPanel/RefinementPanel.behaviors.stories.tsx → ChatPanel/ChatPanel.behaviors.stories.tsx} +6 -6
- package/components/features/{RefinementPanel/RefinementPanel.mocks.ts → ChatPanel/ChatPanel.mocks.ts} +1 -1
- package/components/features/ChatPanel/ChatPanel.stories.tsx +214 -0
- package/components/features/{RefinementPanel/RefinementPanel.tsx → ChatPanel/ChatPanel.tsx} +53 -44
- package/components/features/ChatPanel/index.ts +28 -0
- package/components/features/{RefinementPanel/useRefinementPanel.d.ts → ChatPanel/useChatPanel.d.ts} +12 -50
- package/components/features/{RefinementPanel/useRefinementPanel.mock.ts → ChatPanel/useChatPanel.mock.ts} +6 -6
- package/components/features/NodeEditor/NodeEditor.mocks.ts +268 -0
- package/components/features/NodeEditor/NodeEditor.stories.tsx +22 -1
- package/components/features/NodeEditor/NodeEditor.tsx +8 -5
- package/components/features/PageLayout/PageLayout.mocks.ts +1 -1
- package/components/features/TextEditor/TextEditor.tsx +44 -12
- package/components/features/index.ts +3 -3
- package/dist/index.cjs +572 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +782 -0
- package/dist/index.d.ts +240 -192
- package/dist/index.js +573 -104
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/utils/markdown-formatter.ts +78 -0
- package/components/features/RefinementPanel/RefinementPanel.stories.tsx +0 -287
- package/components/features/RefinementPanel/index.ts +0 -28
- /package/components/features/{RefinementPanel → ChatPanel}/README.md +0 -0
|
@@ -142,19 +142,13 @@ export const FileTreeFolder = ({
|
|
|
142
142
|
children,
|
|
143
143
|
...props
|
|
144
144
|
}: FileTreeFolderProps) => {
|
|
145
|
-
const { expandedPaths, togglePath
|
|
146
|
-
useContext(FileTreeContext);
|
|
145
|
+
const { expandedPaths, togglePath } = useContext(FileTreeContext);
|
|
147
146
|
const isExpanded = expandedPaths.has(path);
|
|
148
|
-
const isSelected = selectedPath === path;
|
|
149
147
|
|
|
150
148
|
const handleOpenChange = useCallback(() => {
|
|
151
149
|
togglePath(path);
|
|
152
150
|
}, [togglePath, path]);
|
|
153
151
|
|
|
154
|
-
const handleSelect = useCallback(() => {
|
|
155
|
-
onSelect?.(path);
|
|
156
|
-
}, [onSelect, path]);
|
|
157
|
-
|
|
158
152
|
const folderContextValue = useMemo(
|
|
159
153
|
() => ({ isExpanded, name, path }),
|
|
160
154
|
[isExpanded, name, path]
|
|
@@ -169,30 +163,16 @@ export const FileTreeFolder = ({
|
|
|
169
163
|
tabIndex={0}
|
|
170
164
|
{...props}
|
|
171
165
|
>
|
|
172
|
-
<
|
|
173
|
-
|
|
174
|
-
"flex w-full items-center gap-1 rounded px-2 py-1 text-left transition-colors hover:bg-muted/50"
|
|
175
|
-
isSelected && "bg-muted"
|
|
176
|
-
)}
|
|
177
|
-
>
|
|
178
|
-
<CollapsibleTrigger asChild>
|
|
179
|
-
<button
|
|
180
|
-
className="flex shrink-0 cursor-pointer items-center border-none bg-transparent p-0"
|
|
181
|
-
type="button"
|
|
182
|
-
>
|
|
183
|
-
<ChevronRightIcon
|
|
184
|
-
className={cn(
|
|
185
|
-
"size-4 shrink-0 text-muted-foreground transition-transform",
|
|
186
|
-
isExpanded && "rotate-90"
|
|
187
|
-
)}
|
|
188
|
-
/>
|
|
189
|
-
</button>
|
|
190
|
-
</CollapsibleTrigger>
|
|
191
|
-
<button
|
|
192
|
-
className="flex min-w-0 flex-1 cursor-pointer items-center gap-1 border-none bg-transparent p-0 text-left"
|
|
193
|
-
onClick={handleSelect}
|
|
194
|
-
type="button"
|
|
166
|
+
<CollapsibleTrigger asChild>
|
|
167
|
+
<div
|
|
168
|
+
className="flex w-full cursor-pointer items-center gap-1 rounded px-2 py-1 text-left transition-colors hover:bg-muted/50 select-none"
|
|
195
169
|
>
|
|
170
|
+
<ChevronRightIcon
|
|
171
|
+
className={cn(
|
|
172
|
+
"size-4 shrink-0 text-muted-foreground transition-transform",
|
|
173
|
+
isExpanded && "rotate-90"
|
|
174
|
+
)}
|
|
175
|
+
/>
|
|
196
176
|
<FileTreeIcon>
|
|
197
177
|
{isExpanded ? (
|
|
198
178
|
<FolderOpenIcon className="size-4 text-blue-500" />
|
|
@@ -201,8 +181,8 @@ export const FileTreeFolder = ({
|
|
|
201
181
|
)}
|
|
202
182
|
</FileTreeIcon>
|
|
203
183
|
<FileTreeName>{name}</FileTreeName>
|
|
204
|
-
</
|
|
205
|
-
</
|
|
184
|
+
</div>
|
|
185
|
+
</CollapsibleTrigger>
|
|
206
186
|
<CollapsibleContent>
|
|
207
187
|
<div className="ml-4 border-l pl-2">{children}</div>
|
|
208
188
|
</CollapsibleContent>
|
|
@@ -88,3 +88,35 @@ export const NodeFooter = ({ className, ...props }: NodeFooterProps) => (
|
|
|
88
88
|
{...props}
|
|
89
89
|
/>
|
|
90
90
|
);
|
|
91
|
+
|
|
92
|
+
export function getHighlightIconColor(
|
|
93
|
+
status?: string,
|
|
94
|
+
highlightStatus?: string,
|
|
95
|
+
defaultColorClass: string = "text-primary"
|
|
96
|
+
): string {
|
|
97
|
+
const effectiveHighlight =
|
|
98
|
+
highlightStatus ??
|
|
99
|
+
(status === "success" || status === "completed" || status === "done"
|
|
100
|
+
? "done"
|
|
101
|
+
: status === "pending_hitl" || status === "pending"
|
|
102
|
+
? "pending"
|
|
103
|
+
: status === "running" || status === "active"
|
|
104
|
+
? "active"
|
|
105
|
+
: status === "error" || status === "failed"
|
|
106
|
+
? "error"
|
|
107
|
+
: undefined);
|
|
108
|
+
|
|
109
|
+
switch (effectiveHighlight) {
|
|
110
|
+
case "done":
|
|
111
|
+
return "text-green-500";
|
|
112
|
+
case "pending":
|
|
113
|
+
return "text-amber-500";
|
|
114
|
+
case "active":
|
|
115
|
+
return "text-indigo-400";
|
|
116
|
+
case "error":
|
|
117
|
+
return "text-red-500";
|
|
118
|
+
default:
|
|
119
|
+
return defaultColorClass;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
@@ -963,22 +963,26 @@ export const PromptInputSubmit = ({
|
|
|
963
963
|
...props
|
|
964
964
|
}: PromptInputSubmitProps) => {
|
|
965
965
|
let Icon = <SendIcon className="size-4" />;
|
|
966
|
+
let buttonVariant = variant;
|
|
967
|
+
let customClass = className;
|
|
966
968
|
|
|
967
969
|
if (status === "submitted") {
|
|
968
970
|
Icon = <Loader2Icon className="size-4 animate-spin" />;
|
|
969
971
|
} else if (status === "streaming") {
|
|
970
|
-
Icon = <SquareIcon className="size-
|
|
972
|
+
Icon = <SquareIcon className="size-3.5 fill-current" />;
|
|
973
|
+
buttonVariant = "default";
|
|
974
|
+
customClass = cn("rounded-full", className);
|
|
971
975
|
} else if (status === "error") {
|
|
972
976
|
Icon = <XIcon className="size-4" />;
|
|
973
977
|
}
|
|
974
978
|
|
|
975
979
|
return (
|
|
976
980
|
<InputGroupButton
|
|
977
|
-
aria-label="Submit"
|
|
978
|
-
className={
|
|
981
|
+
aria-label={status === "streaming" ? "Stop" : "Submit"}
|
|
982
|
+
className={customClass}
|
|
979
983
|
size={size}
|
|
980
984
|
type="submit"
|
|
981
|
-
variant={
|
|
985
|
+
variant={buttonVariant}
|
|
982
986
|
{...props}
|
|
983
987
|
>
|
|
984
988
|
{children ?? Icon}
|
|
@@ -36,6 +36,8 @@ import type { ComponentProps, ReactNode } from "react";
|
|
|
36
36
|
import { isValidElement } from "react";
|
|
37
37
|
import { CodeBlock } from "./code-block";
|
|
38
38
|
|
|
39
|
+
import { Shimmer } from "@/components/ai-elements/shimmer";
|
|
40
|
+
|
|
39
41
|
export type ToolProps = ComponentProps<typeof Collapsible>;
|
|
40
42
|
|
|
41
43
|
export const Tool = ({ className, ...props }: ToolProps) => (
|
|
@@ -65,7 +67,7 @@ const getStatusBadge = (status: ToolUIPart["state"]) => {
|
|
|
65
67
|
|
|
66
68
|
const icons: Record<ToolUIPart["state"], ReactNode> = {
|
|
67
69
|
"input-streaming": <CircleIcon className="size-4" />,
|
|
68
|
-
"input-available": <ClockIcon className="size-4 animate-
|
|
70
|
+
"input-available": <ClockIcon className="size-4 animate-spin text-primary" />,
|
|
69
71
|
"approval-requested": <ClockIcon className="size-4 text-yellow-600" />,
|
|
70
72
|
"approval-responded": <CheckCircleIcon className="size-4 text-blue-600" />,
|
|
71
73
|
"output-available": <CheckCircleIcon className="size-4 text-green-600" />,
|
|
@@ -73,10 +75,22 @@ const getStatusBadge = (status: ToolUIPart["state"]) => {
|
|
|
73
75
|
"output-denied": <XCircleIcon className="size-4 text-orange-600" />,
|
|
74
76
|
};
|
|
75
77
|
|
|
78
|
+
const isRunning = status === "input-available";
|
|
79
|
+
|
|
76
80
|
return (
|
|
77
|
-
<Badge
|
|
81
|
+
<Badge
|
|
82
|
+
className={cn(
|
|
83
|
+
"gap-1.5 rounded-full text-xs font-medium transition-all relative overflow-hidden",
|
|
84
|
+
isRunning && "border border-primary/30 bg-primary/10 text-primary shadow-xs"
|
|
85
|
+
)}
|
|
86
|
+
variant={isRunning ? "outline" : "secondary"}
|
|
87
|
+
>
|
|
78
88
|
{icons[status]}
|
|
79
|
-
{
|
|
89
|
+
{isRunning ? (
|
|
90
|
+
<Shimmer className="text-xs font-medium text-foreground inline-block">Running</Shimmer>
|
|
91
|
+
) : (
|
|
92
|
+
labels[status]
|
|
93
|
+
)}
|
|
80
94
|
</Badge>
|
|
81
95
|
);
|
|
82
96
|
};
|
|
@@ -16,6 +16,7 @@ import { Panel } from "@/components/ai-elements/panel";
|
|
|
16
16
|
import { StateNode } from "@/components/composites/StateNode";
|
|
17
17
|
import { TransitionNode } from "@/components/composites/TransitionNode";
|
|
18
18
|
import { TriggerNode } from "@/components/composites/TriggerNode";
|
|
19
|
+
import { SpatialContainerNode } from "@/components/composites/SpatialContainerNode";
|
|
19
20
|
import type { WorkflowCanvasProps, WorkflowEdge } from "./interfaces";
|
|
20
21
|
import "@xyflow/react/dist/style.css";
|
|
21
22
|
|
|
@@ -29,6 +30,7 @@ const nodeTypes = {
|
|
|
29
30
|
state: StateNode,
|
|
30
31
|
transition: TransitionNode,
|
|
31
32
|
trigger: TriggerNode,
|
|
33
|
+
spatialContainer: SpatialContainerNode,
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
function WorkflowCanvasInner({
|
|
@@ -3,8 +3,13 @@ import type React from "react";
|
|
|
3
3
|
import type { StateNodeData } from "@/components/composites/StateNode";
|
|
4
4
|
import type { TransitionNodeData } from "@/components/composites/TransitionNode";
|
|
5
5
|
import type { TriggerNodeData } from "@/components/composites/TriggerNode";
|
|
6
|
+
import type { SpatialContainerNodeData } from "@/components/composites/SpatialContainerNode";
|
|
6
7
|
|
|
7
|
-
export type WorkflowNodeData =
|
|
8
|
+
export type WorkflowNodeData =
|
|
9
|
+
| StateNodeData
|
|
10
|
+
| TransitionNodeData
|
|
11
|
+
| TriggerNodeData
|
|
12
|
+
| SpatialContainerNodeData;
|
|
8
13
|
export type WorkflowNode = Node<WorkflowNodeData>;
|
|
9
14
|
|
|
10
15
|
export interface WorkflowEdge {
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
ConfirmationActions,
|
|
14
14
|
ConfirmationAction,
|
|
15
15
|
} from "@/components/ai-elements/confirmation";
|
|
16
|
+
import { StreamingMarkdown } from "@/components/composites/StreamingMarkdown";
|
|
16
17
|
|
|
17
18
|
export interface ActionRequest {
|
|
18
19
|
name: string;
|
|
@@ -209,10 +210,14 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
|
|
|
209
210
|
className={cn("w-full", className)}
|
|
210
211
|
>
|
|
211
212
|
<ConfirmationTitle>
|
|
212
|
-
<div className="flex items-start justify-between gap-4
|
|
213
|
-
<div className="flex items-start gap-3">
|
|
213
|
+
<div className="flex items-start justify-between gap-4">
|
|
214
|
+
<div className="flex items-start gap-3 min-w-0 flex-1">
|
|
214
215
|
<QuestionIcon className="h-5 w-5 text-muted-foreground mt-0.5 flex-shrink-0" />
|
|
215
|
-
<
|
|
216
|
+
<div className="flex-1 min-w-0 max-h-60 overflow-y-auto pr-1 text-sm leading-relaxed text-foreground [&>*:first-child]:mt-0 [&>*:last-child]:mb-0">
|
|
217
|
+
<StreamingMarkdown controls={false}>
|
|
218
|
+
{currentQuestion.question}
|
|
219
|
+
</StreamingMarkdown>
|
|
220
|
+
</div>
|
|
216
221
|
</div>
|
|
217
222
|
{questions.length > 1 && (
|
|
218
223
|
<div className="flex items-center gap-1.5 text-xs text-muted-foreground flex-shrink-0 font-normal">
|
|
@@ -66,7 +66,11 @@ export const DefaultSwitcher = React.memo<DefaultSwitcherProps>(
|
|
|
66
66
|
</SelectTrigger>
|
|
67
67
|
<SelectContent>
|
|
68
68
|
{themes.map((theme) => (
|
|
69
|
-
<SelectItem
|
|
69
|
+
<SelectItem
|
|
70
|
+
key={theme.value}
|
|
71
|
+
value={theme.value}
|
|
72
|
+
onClick={() => onValueChange?.(theme.value)}
|
|
73
|
+
>
|
|
70
74
|
{theme.label}
|
|
71
75
|
</SelectItem>
|
|
72
76
|
))}
|
|
@@ -32,6 +32,7 @@ export interface PromptInputBlockProps
|
|
|
32
32
|
) => void | Promise<void>;
|
|
33
33
|
dialog?: ReactNode;
|
|
34
34
|
loading?: boolean;
|
|
35
|
+
onStop?: () => void;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
export const PromptInput = React.memo<PromptInputBlockProps>(
|
|
@@ -43,17 +44,21 @@ export const PromptInput = React.memo<PromptInputBlockProps>(
|
|
|
43
44
|
onSubmit,
|
|
44
45
|
dialog,
|
|
45
46
|
loading = false,
|
|
47
|
+
onStop,
|
|
46
48
|
...props
|
|
47
49
|
}) => {
|
|
48
50
|
const handleSubmit = React.useCallback(
|
|
49
51
|
(message: PromptInputMessage, event: FormEvent<HTMLFormElement>) => {
|
|
50
|
-
if (disabled) {
|
|
52
|
+
if (disabled || (loading && onStop)) {
|
|
51
53
|
event.preventDefault();
|
|
54
|
+
if (loading && onStop) {
|
|
55
|
+
onStop();
|
|
56
|
+
}
|
|
52
57
|
return;
|
|
53
58
|
}
|
|
54
59
|
onSubmit(message, event);
|
|
55
60
|
},
|
|
56
|
-
[disabled, onSubmit]
|
|
61
|
+
[disabled, loading, onStop, onSubmit]
|
|
57
62
|
);
|
|
58
63
|
|
|
59
64
|
const isControlled = value !== undefined && onChange !== undefined;
|
|
@@ -71,22 +76,28 @@ export const PromptInput = React.memo<PromptInputBlockProps>(
|
|
|
71
76
|
return <>{dialog}</>;
|
|
72
77
|
}
|
|
73
78
|
|
|
79
|
+
const isStopping = loading && Boolean(onStop);
|
|
80
|
+
|
|
74
81
|
const promptInputContent = (
|
|
75
82
|
<AIPromptInput onSubmit={handleSubmit} {...props}>
|
|
76
83
|
<PromptInputBody>
|
|
77
84
|
<PromptInputTextarea
|
|
78
85
|
placeholder={placeholder}
|
|
79
|
-
disabled={
|
|
86
|
+
disabled={disabled}
|
|
80
87
|
onChange={isControlled ? handleControlledChange : undefined}
|
|
81
88
|
/>
|
|
82
89
|
</PromptInputBody>
|
|
83
90
|
<PromptInputFooter>
|
|
84
91
|
<PromptInputTools>
|
|
85
|
-
<Button variant="ghost" size="icon" className="h-8 w-8 rounded-full" type="button" disabled={disabled}>
|
|
92
|
+
<Button variant="ghost" size="icon" className="h-8 w-8 rounded-full" type="button" disabled={disabled || loading}>
|
|
86
93
|
<Icon name="plus" size="sm" />
|
|
87
94
|
</Button>
|
|
88
95
|
</PromptInputTools>
|
|
89
|
-
<PromptInputSubmit
|
|
96
|
+
<PromptInputSubmit
|
|
97
|
+
disabled={disabled || (loading && !onStop)}
|
|
98
|
+
status={loading ? (onStop ? "streaming" : "submitted") : undefined}
|
|
99
|
+
onClick={isStopping ? (e: React.MouseEvent) => { e.preventDefault(); onStop?.(); } : undefined}
|
|
100
|
+
/>
|
|
90
101
|
</PromptInputFooter>
|
|
91
102
|
</AIPromptInput>
|
|
92
103
|
);
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
|
|
2
|
+
import { SpatialContainerNode } from "./SpatialContainerNode";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SpatialContainerNode Composite Stories
|
|
6
|
+
*
|
|
7
|
+
* SpatialContainerNode represents a 2.5D visual container or spatial grid region
|
|
8
|
+
* in node editors and workflow canvas components. It acts as a parent node for grouping
|
|
9
|
+
* sub-nodes, cards, and sticky notes inside defined spatial domains.
|
|
10
|
+
*
|
|
11
|
+
* ## Features
|
|
12
|
+
* - 2.5D Extruded 3D visual styling, flat border regions, or glassmorphism aesthetics
|
|
13
|
+
* - Built-in Resizer (`NodeResizer`) for interactive sizing
|
|
14
|
+
* - Themed color variants (Blue, Indigo, Emerald, Amber, Purple, Rose, Cyan, Slate)
|
|
15
|
+
* - Category badges and node action toolbars
|
|
16
|
+
* - Built-in connection handles for inter-container relationship routing
|
|
17
|
+
*/
|
|
18
|
+
const meta = {
|
|
19
|
+
title: "Composites/SpatialContainerNode",
|
|
20
|
+
component: SpatialContainerNode,
|
|
21
|
+
tags: ["autodocs"],
|
|
22
|
+
parameters: {
|
|
23
|
+
layout: "centered",
|
|
24
|
+
docs: {
|
|
25
|
+
description: {
|
|
26
|
+
component:
|
|
27
|
+
"Visual container node for grouping sub-nodes in spatial layouts, Business Model Canvas grids, and architectural region diagrams.",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
} satisfies Meta<typeof SpatialContainerNode>;
|
|
32
|
+
|
|
33
|
+
export default meta;
|
|
34
|
+
type Story = StoryObj<typeof meta>;
|
|
35
|
+
|
|
36
|
+
export const Default: Story = {
|
|
37
|
+
args: {
|
|
38
|
+
id: "container-1",
|
|
39
|
+
selected: false,
|
|
40
|
+
width: 340,
|
|
41
|
+
height: 220,
|
|
42
|
+
data: {
|
|
43
|
+
label: "Value Propositions",
|
|
44
|
+
description: "Core products, services, and bundle offerings for target customers",
|
|
45
|
+
type: "spatialContainer",
|
|
46
|
+
themeColor: "indigo",
|
|
47
|
+
variant: "extruded3d",
|
|
48
|
+
icon: "box",
|
|
49
|
+
badgeText: "3 Services",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const Extruded3D: Story = {
|
|
55
|
+
args: {
|
|
56
|
+
id: "container-2",
|
|
57
|
+
selected: true,
|
|
58
|
+
width: 340,
|
|
59
|
+
height: 220,
|
|
60
|
+
data: {
|
|
61
|
+
label: "Key Activities",
|
|
62
|
+
description: "Production, problem-solving, and platform maintenance tasks",
|
|
63
|
+
type: "spatialContainer",
|
|
64
|
+
themeColor: "emerald",
|
|
65
|
+
variant: "extruded3d",
|
|
66
|
+
icon: "activity",
|
|
67
|
+
badgeText: "Active",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const Glassmorphism: Story = {
|
|
73
|
+
args: {
|
|
74
|
+
id: "container-3",
|
|
75
|
+
selected: false,
|
|
76
|
+
width: 340,
|
|
77
|
+
height: 220,
|
|
78
|
+
data: {
|
|
79
|
+
label: "Customer Relationships",
|
|
80
|
+
description: "Automated onboarding, dedicated personal assistance, and community support",
|
|
81
|
+
type: "spatialContainer",
|
|
82
|
+
themeColor: "purple",
|
|
83
|
+
variant: "glassmorphism",
|
|
84
|
+
icon: "heart",
|
|
85
|
+
badgeText: "High Touch",
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const FlatRegion: Story = {
|
|
91
|
+
args: {
|
|
92
|
+
id: "container-4",
|
|
93
|
+
selected: false,
|
|
94
|
+
width: 340,
|
|
95
|
+
height: 220,
|
|
96
|
+
data: {
|
|
97
|
+
label: "Cost Structure",
|
|
98
|
+
description: "Fixed cloud infrastructure, developer salaries, and R&D licenses",
|
|
99
|
+
type: "spatialContainer",
|
|
100
|
+
themeColor: "rose",
|
|
101
|
+
variant: "flat",
|
|
102
|
+
icon: "wallet",
|
|
103
|
+
badgeText: "$120k/mo",
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const ColorVariants: Story = {
|
|
109
|
+
render: () => (
|
|
110
|
+
<div className="grid grid-cols-2 gap-6 p-4">
|
|
111
|
+
<SpatialContainerNode
|
|
112
|
+
id="c-blue"
|
|
113
|
+
selected={false}
|
|
114
|
+
width={300}
|
|
115
|
+
height={180}
|
|
116
|
+
data={{
|
|
117
|
+
label: "Key Partners",
|
|
118
|
+
type: "spatialContainer",
|
|
119
|
+
themeColor: "blue",
|
|
120
|
+
variant: "extruded3d",
|
|
121
|
+
icon: "handshake",
|
|
122
|
+
badgeText: "Suppliers",
|
|
123
|
+
}}
|
|
124
|
+
/>
|
|
125
|
+
<SpatialContainerNode
|
|
126
|
+
id="c-emerald"
|
|
127
|
+
selected={false}
|
|
128
|
+
width={300}
|
|
129
|
+
height={180}
|
|
130
|
+
data={{
|
|
131
|
+
label: "Key Resources",
|
|
132
|
+
type: "spatialContainer",
|
|
133
|
+
themeColor: "emerald",
|
|
134
|
+
variant: "extruded3d",
|
|
135
|
+
icon: "database",
|
|
136
|
+
badgeText: "Assets",
|
|
137
|
+
}}
|
|
138
|
+
/>
|
|
139
|
+
<SpatialContainerNode
|
|
140
|
+
id="c-amber"
|
|
141
|
+
selected={false}
|
|
142
|
+
width={300}
|
|
143
|
+
height={180}
|
|
144
|
+
data={{
|
|
145
|
+
label: "Channels",
|
|
146
|
+
type: "spatialContainer",
|
|
147
|
+
themeColor: "amber",
|
|
148
|
+
variant: "extruded3d",
|
|
149
|
+
icon: "truck",
|
|
150
|
+
badgeText: "Distribution",
|
|
151
|
+
}}
|
|
152
|
+
/>
|
|
153
|
+
<SpatialContainerNode
|
|
154
|
+
id="c-purple"
|
|
155
|
+
selected={false}
|
|
156
|
+
width={300}
|
|
157
|
+
height={180}
|
|
158
|
+
data={{
|
|
159
|
+
label: "Customer Segments",
|
|
160
|
+
type: "spatialContainer",
|
|
161
|
+
themeColor: "purple",
|
|
162
|
+
variant: "extruded3d",
|
|
163
|
+
icon: "users",
|
|
164
|
+
badgeText: "B2B & B2C",
|
|
165
|
+
}}
|
|
166
|
+
/>
|
|
167
|
+
</div>
|
|
168
|
+
),
|
|
169
|
+
};
|