@dxos/react-ui-canvas 0.8.4-main.b97322e → 0.8.4-main.bc2380dfbc

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 (98) hide show
  1. package/LICENSE +102 -5
  2. package/dist/lib/browser/index.mjs +1128 -372
  3. package/dist/lib/browser/index.mjs.map +4 -4
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/node-esm/index.mjs +1128 -372
  6. package/dist/lib/node-esm/index.mjs.map +4 -4
  7. package/dist/lib/node-esm/meta.json +1 -1
  8. package/dist/types/src/components/Canvas/Canvas.d.ts +2 -2
  9. package/dist/types/src/components/Canvas/Canvas.d.ts.map +1 -1
  10. package/dist/types/src/components/Canvas/Canvas.stories.d.ts +12 -4
  11. package/dist/types/src/components/Canvas/Canvas.stories.d.ts.map +1 -1
  12. package/dist/types/src/components/CellGrid/CellGrid.d.ts +21 -0
  13. package/dist/types/src/components/CellGrid/CellGrid.d.ts.map +1 -0
  14. package/dist/types/src/components/CellGrid/CellGrid.stories.d.ts +21 -0
  15. package/dist/types/src/components/CellGrid/CellGrid.stories.d.ts.map +1 -0
  16. package/dist/types/src/components/CellGrid/headers/Ruler.d.ts +15 -0
  17. package/dist/types/src/components/CellGrid/headers/Ruler.d.ts.map +1 -0
  18. package/dist/types/src/components/CellGrid/headers/TrackHeader.d.ts +19 -0
  19. package/dist/types/src/components/CellGrid/headers/TrackHeader.d.ts.map +1 -0
  20. package/dist/types/src/components/CellGrid/headers/index.d.ts +3 -0
  21. package/dist/types/src/components/CellGrid/headers/index.d.ts.map +1 -0
  22. package/dist/types/src/components/CellGrid/index.d.ts +6 -0
  23. package/dist/types/src/components/CellGrid/index.d.ts.map +1 -0
  24. package/dist/types/src/components/CellGrid/input/index.d.ts +3 -0
  25. package/dist/types/src/components/CellGrid/input/index.d.ts.map +1 -0
  26. package/dist/types/src/components/CellGrid/input/pointer.d.ts +29 -0
  27. package/dist/types/src/components/CellGrid/input/pointer.d.ts.map +1 -0
  28. package/dist/types/src/components/CellGrid/input/wheel.d.ts +14 -0
  29. package/dist/types/src/components/CellGrid/input/wheel.d.ts.map +1 -0
  30. package/dist/types/src/components/CellGrid/render/index.d.ts +3 -0
  31. package/dist/types/src/components/CellGrid/render/index.d.ts.map +1 -0
  32. package/dist/types/src/components/CellGrid/render/overlay-layer.d.ts +21 -0
  33. package/dist/types/src/components/CellGrid/render/overlay-layer.d.ts.map +1 -0
  34. package/dist/types/src/components/CellGrid/render/static-layer.d.ts +36 -0
  35. package/dist/types/src/components/CellGrid/render/static-layer.d.ts.map +1 -0
  36. package/dist/types/src/components/CellGrid/state/atoms.d.ts +23 -0
  37. package/dist/types/src/components/CellGrid/state/atoms.d.ts.map +1 -0
  38. package/dist/types/src/components/CellGrid/state/index.d.ts +4 -0
  39. package/dist/types/src/components/CellGrid/state/index.d.ts.map +1 -0
  40. package/dist/types/src/components/CellGrid/state/types.d.ts +39 -0
  41. package/dist/types/src/components/CellGrid/state/types.d.ts.map +1 -0
  42. package/dist/types/src/components/CellGrid/state/viewport.d.ts +52 -0
  43. package/dist/types/src/components/CellGrid/state/viewport.d.ts.map +1 -0
  44. package/dist/types/src/components/CellGrid/state/viewport.test.d.ts +2 -0
  45. package/dist/types/src/components/CellGrid/state/viewport.test.d.ts.map +1 -0
  46. package/dist/types/src/components/FPS.d.ts.map +1 -1
  47. package/dist/types/src/components/Grid/Grid.d.ts +2 -2
  48. package/dist/types/src/components/Grid/Grid.d.ts.map +1 -1
  49. package/dist/types/src/components/Grid/Grid.stories.d.ts +19 -4
  50. package/dist/types/src/components/Grid/Grid.stories.d.ts.map +1 -1
  51. package/dist/types/src/components/index.d.ts +1 -0
  52. package/dist/types/src/components/index.d.ts.map +1 -1
  53. package/dist/types/src/hooks/index.d.ts +1 -0
  54. package/dist/types/src/hooks/index.d.ts.map +1 -1
  55. package/dist/types/src/hooks/projection.d.ts +1 -1
  56. package/dist/types/src/hooks/projection.d.ts.map +1 -1
  57. package/dist/types/src/hooks/useDrag.d.ts +6 -0
  58. package/dist/types/src/hooks/useDrag.d.ts.map +1 -0
  59. package/dist/types/src/hooks/useWheel.d.ts.map +1 -1
  60. package/dist/types/src/types.d.ts +1 -1
  61. package/dist/types/src/types.d.ts.map +1 -1
  62. package/dist/types/src/util/svg.d.ts +1 -1
  63. package/dist/types/src/util/svg.d.ts.map +1 -1
  64. package/dist/types/src/util/svg.stories.d.ts +12 -4
  65. package/dist/types/src/util/svg.stories.d.ts.map +1 -1
  66. package/dist/types/src/util/util.d.ts.map +1 -1
  67. package/dist/types/tsconfig.tsbuildinfo +1 -1
  68. package/package.json +27 -26
  69. package/src/components/Canvas/Canvas.stories.tsx +13 -11
  70. package/src/components/Canvas/Canvas.tsx +5 -5
  71. package/src/components/CellGrid/CellGrid.stories.tsx +238 -0
  72. package/src/components/CellGrid/CellGrid.tsx +266 -0
  73. package/src/components/CellGrid/headers/Ruler.tsx +71 -0
  74. package/src/components/CellGrid/headers/TrackHeader.tsx +58 -0
  75. package/src/components/CellGrid/headers/index.ts +6 -0
  76. package/src/components/CellGrid/index.ts +9 -0
  77. package/src/components/CellGrid/input/index.ts +6 -0
  78. package/src/components/CellGrid/input/pointer.ts +208 -0
  79. package/src/components/CellGrid/input/wheel.ts +68 -0
  80. package/src/components/CellGrid/render/index.ts +6 -0
  81. package/src/components/CellGrid/render/overlay-layer.ts +66 -0
  82. package/src/components/CellGrid/render/static-layer.ts +112 -0
  83. package/src/components/CellGrid/state/atoms.ts +43 -0
  84. package/src/components/CellGrid/state/index.ts +7 -0
  85. package/src/components/CellGrid/state/types.ts +40 -0
  86. package/src/components/CellGrid/state/viewport.test.ts +50 -0
  87. package/src/components/CellGrid/state/viewport.ts +94 -0
  88. package/src/components/FPS.tsx +3 -3
  89. package/src/components/Grid/Grid.stories.tsx +10 -9
  90. package/src/components/Grid/Grid.tsx +15 -17
  91. package/src/components/index.ts +1 -0
  92. package/src/hooks/index.ts +1 -0
  93. package/src/hooks/projection.tsx +2 -2
  94. package/src/hooks/useDrag.tsx +96 -0
  95. package/src/hooks/useWheel.tsx +1 -28
  96. package/src/types.ts +1 -1
  97. package/src/util/svg.stories.tsx +9 -9
  98. package/src/util/svg.tsx +1 -1
package/package.json CHANGED
@@ -1,59 +1,60 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-canvas",
3
- "version": "0.8.4-main.b97322e",
3
+ "version": "0.8.4-main.bc2380dfbc",
4
4
  "description": "A canvas component.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
- "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
11
+ "license": "FSL-1.1-Apache-2.0",
8
12
  "author": "DXOS.org",
9
13
  "type": "module",
10
14
  "exports": {
11
15
  ".": {
16
+ "source": "./src/index.ts",
12
17
  "types": "./dist/types/src/index.d.ts",
13
18
  "browser": "./dist/lib/browser/index.mjs",
14
19
  "node": "./dist/lib/node-esm/index.mjs"
15
20
  }
16
21
  },
17
22
  "types": "dist/types/src/index.d.ts",
18
- "typesVersions": {
19
- "*": {}
20
- },
21
23
  "files": [
22
24
  "dist",
23
25
  "src"
24
26
  ],
25
27
  "dependencies": {
26
- "@preact-signals/safe-react": "^0.9.0",
27
- "@preact/signals-core": "^1.9.0",
28
+ "@effect-atom/atom-react": "^0.5.0",
28
29
  "@radix-ui/react-context": "1.1.1",
29
30
  "bind-event-listener": "^3.0.0",
30
31
  "d3": "^7.9.0",
31
32
  "react-resize-detector": "^11.0.1",
32
33
  "transformation-matrix": "^2.16.1",
33
- "@dxos/debug": "0.8.4-main.b97322e",
34
- "@dxos/invariant": "0.8.4-main.b97322e",
35
- "@dxos/log": "0.8.4-main.b97322e",
36
- "@dxos/util": "0.8.4-main.b97322e"
34
+ "@dxos/debug": "0.8.4-main.bc2380dfbc",
35
+ "@dxos/util": "0.8.4-main.bc2380dfbc",
36
+ "@dxos/log": "0.8.4-main.bc2380dfbc",
37
+ "@dxos/invariant": "0.8.4-main.bc2380dfbc"
37
38
  },
38
39
  "devDependencies": {
39
40
  "@types/d3": "^7.4.3",
40
- "@types/react": "~18.2.0",
41
- "@types/react-dom": "~18.2.0",
42
- "effect": "3.17.0",
43
- "react": "~18.2.0",
44
- "react-dom": "~18.2.0",
45
- "vite": "5.4.7",
46
- "@dxos/random": "0.8.4-main.b97322e",
47
- "@dxos/react-ui": "0.8.4-main.b97322e",
48
- "@dxos/storybook-utils": "0.8.4-main.b97322e",
49
- "@dxos/react-ui-theme": "0.8.4-main.b97322e"
41
+ "@types/react": "~19.2.7",
42
+ "@types/react-dom": "~19.2.3",
43
+ "effect": "3.20.0",
44
+ "react": "~19.2.3",
45
+ "react-dom": "~19.2.3",
46
+ "vite": "^8.0.13",
47
+ "@dxos/random": "0.8.4-main.bc2380dfbc",
48
+ "@dxos/react-ui": "0.8.4-main.bc2380dfbc",
49
+ "@dxos/ui-theme": "0.8.4-main.bc2380dfbc",
50
+ "@dxos/storybook-utils": "0.8.4-main.bc2380dfbc"
50
51
  },
51
52
  "peerDependencies": {
52
- "effect": "3.13.3",
53
- "react": "~18.2.0",
54
- "react-dom": "~18.2.0",
55
- "@dxos/react-ui": "0.8.4-main.b97322e",
56
- "@dxos/react-ui-theme": "0.8.4-main.b97322e"
53
+ "effect": "3.20.0",
54
+ "react": "~19.2.3",
55
+ "react-dom": "~19.2.3",
56
+ "@dxos/react-ui": "0.8.4-main.bc2380dfbc",
57
+ "@dxos/ui-theme": "0.8.4-main.bc2380dfbc"
57
58
  },
58
59
  "publishConfig": {
59
60
  "access": "public"
@@ -2,18 +2,16 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import type { Meta, StoryObj } from '@storybook/react-vite';
8
6
  import React from 'react';
9
7
 
10
- import { withLayout, withTheme } from '@dxos/storybook-utils';
8
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
9
 
12
- import { Canvas } from './Canvas';
13
- import { useCanvasContext, useWheel } from '../../hooks';
10
+ import { useCanvasContext, useDrag, useWheel } from '../../hooks';
14
11
  import { type Point } from '../../types';
15
12
  import { testId } from '../../util';
16
13
  import { Grid, type GridProps } from '../Grid';
14
+ import { Canvas } from './Canvas';
17
15
 
18
16
  const size = 128;
19
17
 
@@ -33,7 +31,7 @@ const DefaultStory = (props: GridProps) => {
33
31
 
34
32
  const TwoCanvases = (props: GridProps) => {
35
33
  return (
36
- <div className='grid grid-cols-2 gap-2 w-full h-full'>
34
+ <div className='grid grid-cols-2 gap-2 h-full w-full'>
37
35
  <div className='h-full relative'>
38
36
  <Canvas>
39
37
  <Grid {...props} />
@@ -52,6 +50,7 @@ const TwoCanvases = (props: GridProps) => {
52
50
 
53
51
  const Content = () => {
54
52
  useWheel();
53
+ useDrag();
55
54
  return (
56
55
  <div>
57
56
  {points.map(({ x, y }, i) => (
@@ -88,22 +87,25 @@ const Item = (p: Point) => {
88
87
  );
89
88
  };
90
89
 
91
- const meta: Meta<GridProps> = {
90
+ const meta = {
92
91
  title: 'ui/react-ui-canvas/Canvas',
93
92
  component: Grid,
94
93
  render: DefaultStory,
95
- decorators: [withTheme, withLayout({ fullscreen: true })],
96
- };
94
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
95
+ parameters: {
96
+ layout: 'fullscreen',
97
+ },
98
+ } satisfies Meta<typeof Grid>;
97
99
 
98
100
  export default meta;
99
101
 
100
- type Story = StoryObj<GridProps>;
102
+ type Story = StoryObj<typeof meta>;
101
103
 
102
104
  export const Default: Story = {
103
105
  args: { size: 16 },
104
106
  };
105
107
 
106
108
  export const SideBySide: Story = {
107
- args: { size: 16 },
108
109
  render: TwoCanvases,
110
+ args: { size: 16 },
109
111
  };
@@ -15,9 +15,9 @@ import React, {
15
15
  import { useResizeDetector } from 'react-resize-detector';
16
16
 
17
17
  import { type ThemedClassName } from '@dxos/react-ui';
18
- import { mx } from '@dxos/react-ui-theme';
18
+ import { mx } from '@dxos/ui-theme';
19
19
 
20
- import { defaultOrigin, CanvasContext, ProjectionMapper, type ProjectionState } from '../../hooks';
20
+ import { CanvasContext, ProjectionMapper, type ProjectionState, defaultOrigin } from '../../hooks';
21
21
 
22
22
  export interface CanvasController {
23
23
  setProjection(projection: ProjectionState): Promise<void>;
@@ -30,7 +30,7 @@ export type CanvasProps = ThemedClassName<PropsWithChildren<Partial<ProjectionSt
30
30
  * Manages CSS projection.
31
31
  */
32
32
  export const Canvas = forwardRef<CanvasController, CanvasProps>(
33
- ({ children, classNames, scale: _scale = 1, offset: _offset = defaultOrigin, ...props }, forwardedRef) => {
33
+ ({ children, classNames, scale: scaleProp = 1, offset: offsetProp = defaultOrigin, ...props }, forwardedRef) => {
34
34
  // Size.
35
35
  const { ref, width = 0, height = 0 } = useResizeDetector();
36
36
 
@@ -38,7 +38,7 @@ export const Canvas = forwardRef<CanvasController, CanvasProps>(
38
38
  const [ready, setReady] = useState(false);
39
39
 
40
40
  // Projection.
41
- const [{ scale, offset }, setProjection] = useState<ProjectionState>({ scale: _scale, offset: _offset });
41
+ const [{ scale, offset }, setProjection] = useState<ProjectionState>({ scale: scaleProp, offset: offsetProp });
42
42
  useEffect(() => {
43
43
  if (width && height && offset === defaultOrigin) {
44
44
  setProjection({ scale, offset: { x: width / 2, y: height / 2 } });
@@ -76,7 +76,7 @@ export const Canvas = forwardRef<CanvasController, CanvasProps>(
76
76
  <CanvasContext.Provider
77
77
  value={{ root: ref.current, ready, width, height, scale, offset, styles, projection, setProjection }}
78
78
  >
79
- <div role='none' {...props} className={mx('absolute inset-0 overflow-hidden', classNames)} ref={ref}>
79
+ <div {...props} className={mx('absolute inset-0 overflow-hidden', classNames)} ref={ref}>
80
80
  {ready ? children : null}
81
81
  </div>
82
82
  </CanvasContext.Provider>
@@ -0,0 +1,238 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { RegistryContext, Registry } from '@effect-atom/atom-react';
6
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
7
+ import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
8
+
9
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
10
+
11
+ import { CellGrid, type CellGridProps } from './CellGrid';
12
+ import { toggleCell } from './input';
13
+ import { type RenderCell } from './render';
14
+ import { createCellGridAtoms } from './state/atoms';
15
+ import type { Cell, CellCoord, Row, Tool } from './state/types';
16
+
17
+ type SequencerData = { velocity: number };
18
+
19
+ const trackColors = [
20
+ '#ef4444', // red
21
+ '#f97316', // orange
22
+ '#eab308', // yellow
23
+ '#22c55e', // green
24
+ '#06b6d4', // cyan
25
+ '#3b82f6', // blue
26
+ '#a855f7', // purple
27
+ '#ec4899', // pink
28
+ ];
29
+
30
+ const renderSequencerCell: RenderCell<SequencerData> = ({ ctx, x, y, w, h, cell }) => {
31
+ const inset = 1;
32
+ const radius = 4;
33
+ const color = trackColors[cell.row % trackColors.length];
34
+ const velocity = cell.data?.velocity ?? 0.8;
35
+ ctx.fillStyle = color;
36
+ ctx.globalAlpha = 0.3 + velocity * 0.7;
37
+ roundedRect(ctx, x + inset, y + inset, w - inset * 2, h - inset * 2, radius);
38
+ ctx.fill();
39
+ ctx.globalAlpha = 1;
40
+ };
41
+
42
+ const renderDataVizCell: RenderCell<{ magnitude: number }> = ({ ctx, x, y, w, h, cell }) => {
43
+ const magnitude = cell.data?.magnitude ?? 0.5;
44
+ const cx = x + w / 2;
45
+ const cy = y + h / 2;
46
+ const r = Math.max(2, (Math.min(w, h) / 2 - 2) * magnitude);
47
+ ctx.fillStyle = `hsl(${(cell.row * 30) % 360}, 70%, 55%)`;
48
+ ctx.globalAlpha = 0.85;
49
+ ctx.beginPath();
50
+ ctx.arc(cx, cy, r, 0, Math.PI * 2);
51
+ ctx.fill();
52
+ ctx.globalAlpha = 1;
53
+ };
54
+
55
+ const roundedRect = (ctx: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number): void => {
56
+ const radius = Math.min(r, w / 2, h / 2);
57
+ ctx.beginPath();
58
+ ctx.moveTo(x + radius, y);
59
+ ctx.lineTo(x + w - radius, y);
60
+ ctx.quadraticCurveTo(x + w, y, x + w, y + radius);
61
+ ctx.lineTo(x + w, y + h - radius);
62
+ ctx.quadraticCurveTo(x + w, y + h, x + w - radius, y + h);
63
+ ctx.lineTo(x + radius, y + h);
64
+ ctx.quadraticCurveTo(x, y + h, x, y + h - radius);
65
+ ctx.lineTo(x, y + radius);
66
+ ctx.quadraticCurveTo(x, y, x + radius, y);
67
+ ctx.closePath();
68
+ };
69
+
70
+ // Deterministic 0..1 random — Linear Congruential Generator. Used in story
71
+ // seeding so the same args produce the same grid every render.
72
+ const makeLcg = (seed: number) => {
73
+ let state = seed >>> 0;
74
+ return () => {
75
+ state = (state * 1664525 + 1013904223) >>> 0;
76
+ return state / 0xffffffff;
77
+ };
78
+ };
79
+
80
+ type Variant = 'sequencer' | 'data-viz';
81
+
82
+ type StoryProps = Pick<CellGridProps, 'headers'> & {
83
+ variant: Variant;
84
+ tool: Tool;
85
+ numCols: number;
86
+ numRows: number;
87
+ cellWidth: number;
88
+ cellHeight: number;
89
+ playback: boolean;
90
+ };
91
+
92
+ const DefaultStory = ({ variant, tool, numCols, numRows, cellWidth, cellHeight, playback, headers }: StoryProps) => {
93
+ const registry = useContext(RegistryContext);
94
+
95
+ const atoms = useMemo(
96
+ () => createCellGridAtoms<SequencerData | { magnitude: number }>({ cellWidth, cellHeight }),
97
+ [cellWidth, cellHeight],
98
+ );
99
+ const rows: Row[] = useMemo(
100
+ () =>
101
+ Array.from({ length: numRows }, (_, index) => ({
102
+ id: `r${index}`,
103
+ label: variant === 'sequencer' ? `Track ${index + 1}` : `Series ${index + 1}`,
104
+ })),
105
+ [numRows, variant],
106
+ );
107
+
108
+ // Set tool whenever it changes.
109
+ useEffect(() => {
110
+ registry.set(atoms.tool, tool);
111
+ }, [registry, atoms.tool, tool]);
112
+
113
+ // Seed with sample data. Use a deterministic LCG instead of Math.random so
114
+ // story renders are stable across runs (helpful for visual review / snapshots).
115
+ useEffect(() => {
116
+ const next = new Map<string, Cell<SequencerData | { magnitude: number }>>();
117
+ const lcg = makeLcg(0xc0ffee);
118
+ if (variant === 'sequencer') {
119
+ for (let row = 0; row < numRows; row++) {
120
+ for (let col = 0; col < numCols; col += row + 2) {
121
+ if (col % (row + 1) === 0) {
122
+ next.set(`${col},${row}`, { col, row, length: 1, data: { velocity: 0.5 + lcg() * 0.5 } });
123
+ }
124
+ }
125
+ }
126
+ } else {
127
+ for (let row = 0; row < numRows; row++) {
128
+ for (let col = 0; col < numCols; col++) {
129
+ if (lcg() < 0.35) {
130
+ next.set(`${col},${row}`, { col, row, length: 1, data: { magnitude: lcg() } });
131
+ }
132
+ }
133
+ }
134
+ }
135
+ registry.set(atoms.cells, next);
136
+ }, [registry, atoms.cells, variant, numCols, numRows]);
137
+
138
+ // Animated playhead.
139
+ const playRef = useRef<number | null>(null);
140
+ useEffect(() => {
141
+ if (!playback) {
142
+ registry.set(atoms.playhead, null);
143
+ return;
144
+ }
145
+ const start = performance.now();
146
+ const period = 4_000; // ms to traverse numCols.
147
+ const tick = (now: number) => {
148
+ const t = ((now - start) % period) / period;
149
+ registry.set(atoms.playhead, t * numCols);
150
+ playRef.current = requestAnimationFrame(tick);
151
+ };
152
+ playRef.current = requestAnimationFrame(tick);
153
+ return () => {
154
+ if (playRef.current !== null) {
155
+ cancelAnimationFrame(playRef.current);
156
+ }
157
+ registry.set(atoms.playhead, null);
158
+ };
159
+ }, [registry, atoms.playhead, playback, numCols]);
160
+
161
+ const renderCell = variant === 'sequencer' ? (renderSequencerCell as RenderCell) : (renderDataVizCell as RenderCell);
162
+
163
+ const handleToggle = (coord: CellCoord) => {
164
+ toggleCell(registry, atoms, coord, ({ col, row }) => ({
165
+ col,
166
+ row,
167
+ length: 1,
168
+ data: variant === 'sequencer' ? { velocity: 0.8 } : { magnitude: 0.7 },
169
+ }));
170
+ };
171
+
172
+ return (
173
+ <div className='absolute inset-0'>
174
+ <CellGrid
175
+ atoms={atoms as any}
176
+ rows={rows}
177
+ renderCell={renderCell}
178
+ headers={headers}
179
+ onCellToggle={handleToggle}
180
+ />
181
+ </div>
182
+ );
183
+ };
184
+
185
+ const RegistryWrapper = ({ children }: { children: React.ReactNode }) => {
186
+ const [registry] = useState(() => Registry.make());
187
+ return <RegistryContext.Provider value={registry}>{children}</RegistryContext.Provider>;
188
+ };
189
+
190
+ const meta: Meta<typeof DefaultStory> = {
191
+ title: 'ui/react-ui-canvas/CellGrid',
192
+ component: DefaultStory,
193
+ render: (args) => (
194
+ <RegistryWrapper>
195
+ <DefaultStory {...args} />
196
+ </RegistryWrapper>
197
+ ),
198
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
199
+ parameters: { layout: 'fullscreen' },
200
+ argTypes: {
201
+ variant: { control: { type: 'inline-radio' }, options: ['sequencer', 'data-viz'] },
202
+ // 'resize' is deferred (PR description "Deferred / out of v1") — hide from controls.
203
+ tool: { control: { type: 'inline-radio' }, options: ['toggle', 'select'] },
204
+ numCols: { control: { type: 'number', min: 16, max: 1024, step: 16 } },
205
+ numRows: { control: { type: 'number', min: 1, max: 64, step: 1 } },
206
+ cellWidth: { control: { type: 'number', min: 8, max: 64, step: 1 } },
207
+ cellHeight: { control: { type: 'number', min: 12, max: 64, step: 1 } },
208
+ playback: { control: 'boolean' },
209
+ },
210
+ };
211
+
212
+ export default meta;
213
+
214
+ type Story = StoryObj<typeof meta>;
215
+
216
+ export const Sequencer: Story = {
217
+ args: {
218
+ variant: 'sequencer',
219
+ tool: 'toggle',
220
+ numCols: 64,
221
+ numRows: 8,
222
+ cellWidth: 24,
223
+ cellHeight: 24,
224
+ playback: true,
225
+ },
226
+ };
227
+
228
+ export const DataViz: Story = {
229
+ args: {
230
+ variant: 'data-viz',
231
+ tool: 'select',
232
+ numCols: 256,
233
+ numRows: 12,
234
+ cellWidth: 20,
235
+ cellHeight: 20,
236
+ playback: false,
237
+ },
238
+ };