ai-design-system 0.1.0 → 0.1.1

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.
Files changed (48) hide show
  1. package/README.md +0 -269
  2. package/components/ai-elements/canvas.tsx +2 -1
  3. package/components/ai-elements/edge.tsx +28 -20
  4. package/components/ai-elements/node.tsx +23 -4
  5. package/components/blocks/WorkflowCanvas/WorkflowCanvas.stories.tsx +279 -0
  6. package/components/blocks/WorkflowCanvas/WorkflowCanvas.tsx +180 -0
  7. package/components/blocks/WorkflowCanvas/index.ts +2 -0
  8. package/components/blocks/index.ts +15 -0
  9. package/components/composites/StateNode/StateNode.stories.tsx +281 -0
  10. package/components/composites/StateNode/StateNode.tsx +100 -0
  11. package/components/composites/StateNode/index.ts +2 -0
  12. package/components/composites/TransitionNode/TransitionNode.stories.tsx +260 -0
  13. package/components/composites/TransitionNode/TransitionNode.tsx +80 -0
  14. package/components/composites/TransitionNode/index.ts +2 -0
  15. package/components/composites/WorkflowToolbar/WorkflowToolbar.stories.tsx +352 -0
  16. package/components/composites/WorkflowToolbar/WorkflowToolbar.tsx +195 -0
  17. package/components/composites/WorkflowToolbar/index.ts +7 -0
  18. package/components/composites/index.ts +13 -1
  19. package/components/features/AIDocEditor/README.md +127 -0
  20. package/components/features/PageLayout/README.md +122 -0
  21. package/components/features/PageLayout/usePageLayout.d.ts +2 -0
  22. package/components/features/RefinementPanel/RefinementPanel.stories.tsx +2 -2
  23. package/components/features/RefinementPanel/index.ts +1 -1
  24. package/components/features/RefinementPanel/useRefinementPanel.mock.ts +1 -1
  25. package/components/features/SpecNavigator/README.md +116 -0
  26. package/components/features/SpecNavigator/SpecNavigator.stories.tsx +2 -2
  27. package/components/features/SpecNavigator/useSpecNavigator.mock.ts +1 -1
  28. package/components/features/WorkflowBuilder/README.md +159 -0
  29. package/components/features/WorkflowBuilder/WorkflowBuilder.behaviors.stories.tsx +136 -0
  30. package/components/features/WorkflowBuilder/WorkflowBuilder.mocks.ts +36 -0
  31. package/components/features/WorkflowBuilder/WorkflowBuilder.stories.tsx +135 -0
  32. package/components/features/WorkflowBuilder/WorkflowBuilder.tsx +97 -0
  33. package/components/features/WorkflowBuilder/index.ts +2 -0
  34. package/components/features/WorkflowBuilder/useWorkflowBuilder.d.ts +44 -0
  35. package/components/features/WorkflowBuilder/useWorkflowBuilder.mock.ts +116 -0
  36. package/components/features/index.ts +8 -0
  37. package/components/index.ts +1 -1
  38. package/components/primitives/ButtonGroup/ButtonGroup.stories.tsx +35 -0
  39. package/components/primitives/ButtonGroup/ButtonGroup.tsx +39 -0
  40. package/components/primitives/ButtonGroup/index.ts +3 -0
  41. package/components/primitives/index.ts +1 -0
  42. package/components/ui/animated-border.tsx +79 -0
  43. package/dist/index.cjs +897 -167
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.d.ts +99 -0
  46. package/dist/index.js +886 -169
  47. package/dist/index.js.map +1 -1
  48. package/package.json +14 -12
package/README.md CHANGED
@@ -36,272 +36,3 @@ pnpm storybook
36
36
  ```
37
37
 
38
38
  Open [http://localhost:6006](http://localhost:6006) to view the component library.
39
-
40
- ## Design System Governance
41
-
42
- ### Pre-Commit Hooks
43
-
44
- The design system enforces strict quality standards through automated pre-commit hooks:
45
-
46
- **Master Validation Script**
47
- - Runs all design system validations in a single command
48
- - Run manually: `bash scripts/run-all-validations.sh` or `pnpm run prebuild`
49
-
50
- **Layer Import Architecture**
51
- - Validates that components follow the layered architecture pattern
52
- - Ensures primitives don't import from patterns or features
53
- - Ensures patterns don't import from features
54
- - Run manually: `python3 scripts/validate-layer-imports.py`
55
-
56
- **Storybook Coverage**
57
- - Ensures every component has a corresponding `.stories.tsx` file
58
- - Validates story file naming conventions
59
- - Run manually: `node scripts/validate-storybook-coverage.js`
60
-
61
- **Adding New Validations**
62
- To add a new validation to the master script, edit `scripts/run-all-validations.sh`:
63
- 1. Add a new section with clear header and description
64
- 2. Run your validation script and capture exit code
65
- 3. Add the exit code to the final results check
66
- 4. The script provides clear guidance with comments for adding new validations
67
-
68
- **Bypassing Hooks (Not Recommended)**
69
- ```bash
70
- git commit --no-verify
71
- ```
72
-
73
- Only bypass hooks if you have a valid reason and plan to fix violations immediately.
74
-
75
- ## Project Structure
76
-
77
- ```
78
- packages/ui-lib/
79
- ├── components/
80
- │ ├── primitives/ # Base components (buttons, inputs, etc.)
81
- │ ├── patterns/ # Composite components (cards, forms, etc.)
82
- │ └── features/ # Feature-specific components
83
- ├── scripts/
84
- │ ├── hooks/ # Git hooks (tracked in version control)
85
- │ │ └── pre-commit # Pre-commit validation hook
86
- │ ├── setup-hooks.sh # Hook installation script
87
- │ ├── run-all-validations.sh # Master validation script
88
- │ ├── validate-layer-imports.py
89
- │ └── validate-storybook-coverage.js
90
- ├── styles/ # Global styles and themes
91
- └── tokens/ # Design tokens
92
- ```
93
-
94
- ## Layer Architecture
95
-
96
- The design system follows a strict three-layer architecture:
97
-
98
- 1. **Primitives** - Base components with no dependencies on other layers
99
- - Examples: Button, Input, Textarea, Badge
100
- - Can only import from external libraries and utilities
101
-
102
- 2. **Patterns** - Composite components built from primitives
103
- - Examples: Card, Form, Dialog
104
- - Can import from primitives and external libraries
105
-
106
- 3. **Features** - Feature-specific components
107
- - Examples: AIConversation, RefinementPanel
108
- - Can import from primitives, patterns, and external libraries
109
-
110
- **Import Rules:**
111
- - Primitives → No internal imports
112
- - Patterns → Can import Primitives
113
- - Features → Can import Primitives + Patterns
114
-
115
- ## Available Scripts
116
-
117
- ```bash
118
- pnpm dev # Start development server
119
- pnpm build # Build for production
120
- pnpm start # Start production server
121
- pnpm lint # Run ESLint
122
- pnpm tokens:build # Build design tokens
123
- pnpm tokens:watch # Watch design tokens
124
- pnpm storybook # Start Storybook dev server
125
- pnpm build-storybook # Build Storybook for production
126
- ```
127
-
128
- ## Creating New Components
129
-
130
- ### 1. Choose the Correct Layer
131
-
132
- Determine which layer your component belongs to:
133
- - **Primitive**: Standalone, reusable UI element
134
- - **Pattern**: Combination of primitives
135
- - **Feature**: Domain-specific component
136
-
137
- ### 2. Create Component File
138
-
139
- Create your component following the established structure:
140
-
141
- ```tsx
142
- // components/primitives/YourComponent/YourComponent.tsx
143
- import { cva, type VariantProps } from "class-variance-authority";
144
- import { cn } from "@/lib/utils";
145
-
146
- const yourComponentVariants = cva(
147
- "base-classes",
148
- {
149
- variants: {
150
- variant: {
151
- default: "variant-classes",
152
- secondary: "variant-classes",
153
- },
154
- size: {
155
- default: "size-classes",
156
- sm: "size-classes",
157
- lg: "size-classes",
158
- },
159
- },
160
- defaultVariants: {
161
- variant: "default",
162
- size: "default",
163
- },
164
- }
165
- );
166
-
167
- export interface YourComponentProps
168
- extends React.HTMLAttributes<HTMLElement>,
169
- VariantProps<typeof yourComponentVariants> {
170
- // Additional props
171
- }
172
-
173
- export function YourComponent({
174
- className,
175
- variant,
176
- size,
177
- ...props
178
- }: YourComponentProps) {
179
- return (
180
- <div
181
- className={cn(yourComponentVariants({ variant, size, className }))}
182
- {...props}
183
- />
184
- );
185
- }
186
- ```
187
-
188
- ### 3. Create Index File
189
-
190
- ```tsx
191
- // components/primitives/YourComponent/index.ts
192
- export { YourComponent } from "./YourComponent";
193
- export type { YourComponentProps } from "./YourComponent";
194
- ```
195
-
196
- ### 4. Create Storybook Story (REQUIRED)
197
-
198
- Every component must have a `.stories.tsx` file:
199
-
200
- ```tsx
201
- // components/primitives/YourComponent/YourComponent.stories.tsx
202
- import type { Meta, StoryObj } from "@storybook/react";
203
- import { YourComponent } from "./YourComponent";
204
-
205
- const meta = {
206
- title: "Primitives/YourComponent",
207
- component: YourComponent,
208
- tags: ["autodocs"],
209
- parameters: {
210
- layout: "centered",
211
- },
212
- } satisfies Meta<typeof YourComponent>;
213
-
214
- export default meta;
215
- type Story = StoryObj<typeof meta>;
216
-
217
- export const Default: Story = {
218
- args: {
219
- // default props
220
- },
221
- };
222
-
223
- export const Variant: Story = {
224
- args: {
225
- variant: "secondary",
226
- },
227
- };
228
- ```
229
-
230
- ### 5. Validate
231
-
232
- Run validation scripts to ensure compliance:
233
-
234
- ```bash
235
- # Run all validations (recommended)
236
- bash scripts/run-all-validations.sh
237
- # or
238
- pnpm run prebuild
239
-
240
- # Run individual validations
241
- python3 scripts/validate-layer-imports.py # Layer architecture
242
- node scripts/validate-storybook-coverage.js # Storybook coverage
243
- ```
244
-
245
- ## Design Tokens
246
-
247
- The design system uses Style Dictionary for design tokens management:
248
-
249
- ```bash
250
- # Build tokens once
251
- pnpm tokens:build
252
-
253
- # Watch for changes
254
- pnpm tokens:watch
255
- ```
256
-
257
- Tokens are defined in the `tokens/` directory and compiled to CSS custom properties.
258
-
259
- ## Technology Stack
260
-
261
- - **Framework**: Next.js 16 with App Router
262
- - **UI**: React 19
263
- - **Styling**: Tailwind CSS 4
264
- - **Component Variants**: Class Variance Authority (CVA)
265
- - **Component Foundation**: Radix UI primitives
266
- - **Documentation**: Storybook 10
267
- - **Type Safety**: TypeScript 5 (strict mode)
268
- - **Icons**: Lucide React
269
- - **Animation**: Motion (Framer Motion)
270
-
271
- ## Accessibility
272
-
273
- All components must meet WCAG 2.1 Level AA standards:
274
- - Keyboard navigation support
275
- - Screen reader compatibility
276
- - Proper ARIA attributes
277
- - Focus management
278
- - Color contrast requirements
279
-
280
- ## Theming
281
-
282
- The design system supports:
283
- - Light and dark modes
284
- - Design token-based theming
285
- - CSS custom properties
286
- - Responsive design across all breakpoints
287
-
288
- ## Contributing
289
-
290
- 1. Set up git hooks: `./scripts/setup-hooks.sh`
291
- 2. Create components following the layer architecture
292
- 3. Include Storybook stories for all components
293
- 4. Ensure all validations pass before committing
294
- 5. Document accessibility features and keyboard shortcuts
295
-
296
- ## Learn More
297
-
298
- - [Next.js Documentation](https://nextjs.org/docs)
299
- - [Tailwind CSS](https://tailwindcss.com)
300
- - [Radix UI](https://www.radix-ui.com)
301
- - [shadcn/ui](https://ui.shadcn.com)
302
- - [Storybook](https://storybook.js.org)
303
- - [CVA](https://cva.style)
304
-
305
- ## Support
306
-
307
- For questions or issues with the design system, contact the Design System Guardian or open an issue in the repository.
@@ -9,13 +9,14 @@ type CanvasProps = ReactFlowProps & {
9
9
 
10
10
  export const Canvas = ({ children, ...props }: CanvasProps) => (
11
11
  <ReactFlow
12
+ {...props}
12
13
  deleteKeyCode={["Backspace", "Delete"]}
13
14
  fitView
14
15
  panOnDrag={false}
15
16
  panOnScroll
17
+ proOptions={{ hideAttribution: true }}
16
18
  selectionOnDrag={true}
17
19
  zoomOnDoubleClick={false}
18
- {...props}
19
20
  >
20
21
  <Background bgColor="var(--sidebar)" />
21
22
  <Controls />
@@ -41,11 +41,9 @@ const Temporary = ({
41
41
 
42
42
  const getHandleCoordsByPosition = (
43
43
  node: InternalNode<Node>,
44
- handlePosition: Position
44
+ handlePosition: Position,
45
+ handleType: "source" | "target"
45
46
  ) => {
46
- // Choose the handle type based on position - Left is for target, Right is for source
47
- const handleType = handlePosition === Position.Left ? "target" : "source";
48
-
49
47
  const handle = node.internals.handleBounds?.[handleType]?.find(
50
48
  (h) => h.position === handlePosition
51
49
  );
@@ -57,9 +55,6 @@ const getHandleCoordsByPosition = (
57
55
  let offsetX = handle.width / 2;
58
56
  let offsetY = handle.height / 2;
59
57
 
60
- // this is a tiny detail to make the markerEnd of an edge visible.
61
- // The handle position that gets calculated has the origin top-left, so depending which side we are using, we add a little offset
62
- // when the handlePosition is Position.Right for example, we need to add an offset as big as the handle itself in order to get the correct position
63
58
  switch (handlePosition) {
64
59
  case Position.Left:
65
60
  offsetX = 0;
@@ -87,19 +82,32 @@ const getEdgeParams = (
87
82
  source: InternalNode<Node>,
88
83
  target: InternalNode<Node>
89
84
  ) => {
90
- const sourcePos = Position.Right;
91
- const [sx, sy] = getHandleCoordsByPosition(source, sourcePos);
92
- const targetPos = Position.Left;
93
- const [tx, ty] = getHandleCoordsByPosition(target, targetPos);
94
-
95
- return {
96
- sx,
97
- sy,
98
- tx,
99
- ty,
100
- sourcePos,
101
- targetPos,
102
- };
85
+ const sx = source.internals.positionAbsolute.x + (source.measured?.width ?? 0) / 2;
86
+ const sy = source.internals.positionAbsolute.y + (source.measured?.height ?? 0) / 2;
87
+ const tx = target.internals.positionAbsolute.x + (target.measured?.width ?? 0) / 2;
88
+ const ty = target.internals.positionAbsolute.y + (target.measured?.height ?? 0) / 2;
89
+
90
+ const dx = tx - sx;
91
+ const dy = ty - sy;
92
+
93
+ // Pick source/target positions based on dominant direction
94
+ let sourcePos: Position;
95
+ let targetPos: Position;
96
+
97
+ if (Math.abs(dx) > Math.abs(dy)) {
98
+ // Horizontal dominant
99
+ sourcePos = dx > 0 ? Position.Right : Position.Left;
100
+ targetPos = dx > 0 ? Position.Left : Position.Right;
101
+ } else {
102
+ // Vertical dominant
103
+ sourcePos = dy > 0 ? Position.Bottom : Position.Top;
104
+ targetPos = dy > 0 ? Position.Top : Position.Bottom;
105
+ }
106
+
107
+ const [srcX, srcY] = getHandleCoordsByPosition(source, sourcePos, "source");
108
+ const [tgtX, tgtY] = getHandleCoordsByPosition(target, targetPos, "target");
109
+
110
+ return { sx: srcX, sy: srcY, tx: tgtX, ty: tgtY, sourcePos, targetPos };
103
111
  };
104
112
 
105
113
  const Animated = ({ id, source, target, markerEnd, style }: EdgeProps) => {
@@ -7,6 +7,7 @@ import {
7
7
  CardHeader,
8
8
  CardTitle,
9
9
  } from "@/components/ui/card";
10
+ import { AnimatedBorder } from "@/components/ui/animated-border";
10
11
  import { cn } from "@/lib/utils";
11
12
  import { Handle, Position } from "@xyflow/react";
12
13
  import type { ComponentProps } from "react";
@@ -16,18 +17,36 @@ export type NodeProps = ComponentProps<typeof Card> & {
16
17
  target: boolean;
17
18
  source: boolean;
18
19
  };
20
+ status?: "idle" | "running" | "success" | "error";
19
21
  };
20
22
 
21
- export const Node = ({ handles, className, ...props }: NodeProps) => (
23
+ export const Node = ({ handles, className, status, ...props }: NodeProps) => (
22
24
  <Card
23
25
  className={cn(
24
- "node-container relative size-full h-auto w-sm gap-0 rounded-md p-0",
26
+ "node-container relative size-full h-auto w-sm gap-0 rounded-md bg-card p-0 transition-all duration-200",
27
+ status === "success" && "border-green-500 border-2",
28
+ status === "error" && "border-red-500 border-2",
25
29
  className
26
30
  )}
27
31
  {...props}
28
32
  >
29
- {handles.target && <Handle position={Position.Left} type="target" />}
30
- {handles.source && <Handle position={Position.Right} type="source" />}
33
+ {status === "running" && <AnimatedBorder />}
34
+ {handles.target && (
35
+ <>
36
+ <Handle id="target-top" position={Position.Top} type="target" />
37
+ <Handle id="target-right" position={Position.Right} type="target" />
38
+ <Handle id="target-bottom" position={Position.Bottom} type="target" />
39
+ <Handle id="target-left" position={Position.Left} type="target" />
40
+ </>
41
+ )}
42
+ {handles.source && (
43
+ <>
44
+ <Handle id="source-top" position={Position.Top} type="source" />
45
+ <Handle id="source-right" position={Position.Right} type="source" />
46
+ <Handle id="source-bottom" position={Position.Bottom} type="source" />
47
+ <Handle id="source-left" position={Position.Left} type="source" />
48
+ </>
49
+ )}
31
50
  {props.children}
32
51
  </Card>
33
52
  );
@@ -0,0 +1,279 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { useState } from "react";
3
+ import type { NodeChange, EdgeChange, Connection } from "@xyflow/react";
4
+ import { applyNodeChanges, applyEdgeChanges } from "@xyflow/react";
5
+ import { WorkflowCanvas, type WorkflowNode, type WorkflowEdge } from "./WorkflowCanvas";
6
+ import "@xyflow/react/dist/style.css";
7
+
8
+ /**
9
+ * WorkflowCanvas Stories
10
+ *
11
+ * WorkflowCanvas is a complete workflow canvas composite with ReactFlow integration.
12
+ * It provides a visual canvas for building workflow diagrams with state and transition nodes.
13
+ *
14
+ * ## Features
15
+ * - ReactFlow canvas with grid background
16
+ * - State nodes and Transition nodes
17
+ * - Bezier curve edges with animation
18
+ * - Pan and zoom controls
19
+ * - Minimap toggle
20
+ * - Node drag and drop
21
+ * - Edge creation by dragging from handles
22
+ * - Duplicate connection prevention
23
+ * - Delete nodes/edges with Backspace/Delete
24
+ * - Keyboard shortcuts (Cmd+/ for fit view)
25
+ *
26
+ * ## Usage Guidelines
27
+ *
28
+ * ### Do's
29
+ * - Provide nodes and edges data
30
+ * - Handle onNodesChange and onEdgesChange for state updates
31
+ * - Use onConnect to handle new connections
32
+ * - Set showMinimap for large workflows
33
+ *
34
+ * ### Don'ts
35
+ * - Don't use without proper height container
36
+ * - Don't mix node types incorrectly
37
+ */
38
+ const meta = {
39
+ title: "Blocks/WorkflowCanvas",
40
+ component: WorkflowCanvas,
41
+ tags: ["autodocs"],
42
+ parameters: {
43
+ layout: "fullscreen",
44
+ docs: {
45
+ description: {
46
+ component:
47
+ "Complete workflow canvas composite with ReactFlow integration for building visual workflow diagrams.",
48
+ },
49
+ },
50
+ },
51
+ } satisfies Meta<typeof WorkflowCanvas>;
52
+
53
+ export default meta;
54
+ type Story = StoryObj<typeof meta>;
55
+
56
+ const initialNodes: WorkflowNode[] = [
57
+ {
58
+ id: "1",
59
+ type: "transition",
60
+ position: { x: 300, y: 50 },
61
+ data: { label: "Start", type: "transition", status: "idle" },
62
+ },
63
+ {
64
+ id: "2",
65
+ type: "state",
66
+ position: { x: 300, y: 130 },
67
+ data: { label: "Approve Order", description: "Approve the order", type: "state", status: "idle" },
68
+ },
69
+ {
70
+ id: "3",
71
+ type: "transition",
72
+ position: { x: 300, y: 210 },
73
+ data: { label: "Order Approved", type: "transition", status: "idle" },
74
+ },
75
+ {
76
+ id: "4",
77
+ type: "state",
78
+ position: { x: 300, y: 290 },
79
+ data: { label: "Process Payment", description: "Process payment", type: "state", status: "idle" },
80
+ },
81
+ ];
82
+
83
+ const initialEdges: WorkflowEdge[] = [
84
+ { id: "e1-2", source: "1", target: "2", type: "animated" },
85
+ { id: "e2-3", source: "2", target: "3", type: "animated" },
86
+ { id: "e3-4", source: "3", target: "4", type: "animated" },
87
+ ];
88
+
89
+ /**
90
+ * Default workflow canvas with basic nodes and edges
91
+ */
92
+ export const Default: Story = {
93
+ render: () => {
94
+ const [nodes, setNodes] = useState<WorkflowNode[]>(initialNodes);
95
+ const [edges, setEdges] = useState<WorkflowEdge[]>(initialEdges);
96
+
97
+ const onNodesChange = (changes: NodeChange[]) => {
98
+ setNodes((nds) => applyNodeChanges(changes, nds) as WorkflowNode[]);
99
+ };
100
+
101
+ const onEdgesChange = (changes: EdgeChange[]) => {
102
+ setEdges((eds) => applyEdgeChanges(changes, eds) as WorkflowEdge[]);
103
+ };
104
+
105
+ const onConnect = (connection: Connection) => {
106
+ setEdges((eds) => [
107
+ ...eds,
108
+ {
109
+ id: `e${connection.source}-${connection.target}`,
110
+ source: connection.source!,
111
+ target: connection.target!,
112
+ type: "animated",
113
+ },
114
+ ]);
115
+ };
116
+
117
+ return (
118
+ <div style={{ width: "100vw", height: "100vh" }}>
119
+ <WorkflowCanvas
120
+ nodes={nodes}
121
+ edges={edges}
122
+ onNodesChange={onNodesChange}
123
+ onEdgesChange={onEdgesChange}
124
+ onConnect={onConnect}
125
+ />
126
+ </div>
127
+ );
128
+ },
129
+ };
130
+
131
+ /**
132
+ * Workflow canvas with minimap enabled
133
+ */
134
+ export const WithMinimap: Story = {
135
+ render: () => {
136
+ const [nodes, setNodes] = useState<WorkflowNode[]>(initialNodes);
137
+ const [edges, setEdges] = useState<WorkflowEdge[]>(initialEdges);
138
+
139
+ const onNodesChange = (changes: NodeChange[]) => {
140
+ setNodes((nds) => applyNodeChanges(changes, nds) as WorkflowNode[]);
141
+ };
142
+
143
+ const onEdgesChange = (changes: EdgeChange[]) => {
144
+ setEdges((eds) => applyEdgeChanges(changes, eds) as WorkflowEdge[]);
145
+ };
146
+
147
+ const onConnect = (connection: Connection) => {
148
+ setEdges((eds) => [
149
+ ...eds,
150
+ {
151
+ id: `e${connection.source}-${connection.target}`,
152
+ source: connection.source!,
153
+ target: connection.target!,
154
+ type: "animated",
155
+ },
156
+ ]);
157
+ };
158
+
159
+ return (
160
+ <div style={{ width: "100vw", height: "100vh" }}>
161
+ <WorkflowCanvas
162
+ nodes={nodes}
163
+ edges={edges}
164
+ onNodesChange={onNodesChange}
165
+ onEdgesChange={onEdgesChange}
166
+ onConnect={onConnect}
167
+ showMinimap={true}
168
+ />
169
+ </div>
170
+ );
171
+ },
172
+ };
173
+
174
+ /**
175
+ * Complex workflow with multiple branches
176
+ */
177
+ export const ComplexWorkflow: Story = {
178
+ render: () => {
179
+ const complexNodes: WorkflowNode[] = [
180
+ { id: "start", type: "transition", position: { x: 300, y: 0 }, data: { label: "Start", type: "transition", status: "idle" } },
181
+ { id: "req", type: "transition", position: { x: 300, y: 100 }, data: { label: "Order Requested", type: "transition", status: "idle" } },
182
+ { id: "approve", type: "state", position: { x: 100, y: 200 }, data: { label: "Approve Order", type: "state", status: "idle" } },
183
+ { id: "reject", type: "state", position: { x: 500, y: 200 }, data: { label: "Reject Order", type: "state", status: "idle" } },
184
+ { id: "cancel", type: "state", position: { x: 0, y: 320 }, data: { label: "Cancel Order", type: "state", status: "idle" } },
185
+ { id: "approved", type: "transition", position: { x: 200, y: 320 }, data: { label: "Order Approved", type: "transition", status: "idle" } },
186
+ { id: "rejected", type: "transition", position: { x: 500, y: 320 }, data: { label: "Order Rejected", type: "transition", status: "idle" } },
187
+ { id: "start_proc", type: "state", position: { x: 300, y: 420 }, data: { label: "Start Order Processing", type: "state", status: "idle" } },
188
+ { id: "cancelled", type: "transition", position: { x: 0, y: 440 }, data: { label: "Order Cancelled", type: "transition", status: "idle" } },
189
+ { id: "complete", type: "state", position: { x: 150, y: 540 }, data: { label: "Complete Order Processing",type: "state", status: "idle" } },
190
+ { id: "processing", type: "transition", position: { x: 400, y: 540 }, data: { label: "Order Processing", type: "transition", status: "idle" } },
191
+ { id: "hold", type: "state", position: { x: 600, y: 540 }, data: { label: "Place Order On Hold", type: "state", status: "idle" } },
192
+ { id: "resume", type: "state", position: { x: 750, y: 420 }, data: { label: "Resume Order Processing", type: "state", status: "idle" } },
193
+ { id: "ready", type: "transition", position: { x: 150, y: 660 }, data: { label: "Order Ready for Delivery",type: "transition", status: "idle" } },
194
+ { id: "on_hold", type: "transition", position: { x: 600, y: 660 }, data: { label: "Order On Hold", type: "transition", status: "idle" } },
195
+ ];
196
+
197
+ const complexEdges: WorkflowEdge[] = [
198
+ { id: "e1", source: "start", target: "req", type: "animated" },
199
+ { id: "e2", source: "req", target: "approve", type: "animated" },
200
+ { id: "e3", source: "req", target: "reject", type: "animated" },
201
+ { id: "e4", source: "approve", target: "cancel", type: "animated" },
202
+ { id: "e5", source: "approve", target: "approved", type: "animated" },
203
+ { id: "e6", source: "reject", target: "rejected", type: "animated" },
204
+ { id: "e7", source: "approved", target: "start_proc", type: "animated" },
205
+ { id: "e8", source: "cancel", target: "cancelled", type: "animated" },
206
+ { id: "e9", source: "start_proc",target: "complete", type: "animated" },
207
+ { id: "e10", source: "start_proc",target: "processing", type: "animated" },
208
+ { id: "e11", source: "processing",target: "hold", type: "animated" },
209
+ { id: "e12", source: "hold", target: "resume", type: "animated" },
210
+ { id: "e13", source: "complete", target: "ready", type: "animated" },
211
+ { id: "e14", source: "hold", target: "on_hold", type: "animated" },
212
+ ];
213
+
214
+ const [nodes, setNodes] = useState<WorkflowNode[]>(complexNodes);
215
+ const [edges, setEdges] = useState<WorkflowEdge[]>(complexEdges);
216
+
217
+ const onNodesChange = (changes: NodeChange[]) => {
218
+ setNodes((nds) => applyNodeChanges(changes, nds) as WorkflowNode[]);
219
+ };
220
+
221
+ const onEdgesChange = (changes: EdgeChange[]) => {
222
+ setEdges((eds) => applyEdgeChanges(changes, eds) as WorkflowEdge[]);
223
+ };
224
+
225
+ const onConnect = (connection: Connection) => {
226
+ setEdges((eds) => [
227
+ ...eds,
228
+ {
229
+ id: `e${connection.source}-${connection.target}`,
230
+ source: connection.source!,
231
+ target: connection.target!,
232
+ type: "animated",
233
+ },
234
+ ]);
235
+ };
236
+
237
+ return (
238
+ <div style={{ width: "100vw", height: "100vh" }}>
239
+ <WorkflowCanvas
240
+ nodes={nodes}
241
+ edges={edges}
242
+ onNodesChange={onNodesChange}
243
+ onEdgesChange={onEdgesChange}
244
+ onConnect={onConnect}
245
+ showMinimap={true}
246
+ />
247
+ </div>
248
+ );
249
+ },
250
+ };
251
+
252
+ /**
253
+ * Empty workflow canvas
254
+ */
255
+ export const Empty: Story = {
256
+ render: () => {
257
+ const [nodes, setNodes] = useState<WorkflowNode[]>([]);
258
+ const [edges, setEdges] = useState<WorkflowEdge[]>([]);
259
+
260
+ const onNodesChange = (changes: NodeChange[]) => {
261
+ setNodes((nds) => applyNodeChanges(changes, nds) as WorkflowNode[]);
262
+ };
263
+
264
+ const onEdgesChange = (changes: EdgeChange[]) => {
265
+ setEdges((eds) => applyEdgeChanges(changes, eds) as WorkflowEdge[]);
266
+ };
267
+
268
+ return (
269
+ <div style={{ width: "100vw", height: "100vh" }}>
270
+ <WorkflowCanvas
271
+ nodes={nodes}
272
+ edges={edges}
273
+ onNodesChange={onNodesChange}
274
+ onEdgesChange={onEdgesChange}
275
+ />
276
+ </div>
277
+ );
278
+ },
279
+ };