@dxos/react-ui-canvas 0.10.0 → 0.11.0

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 (34) hide show
  1. package/dist/lib/index.mjs +1312 -0
  2. package/dist/lib/index.mjs.map +1 -0
  3. package/dist/types/src/components/CellGrid/input/pointer.d.ts +1 -1
  4. package/dist/types/src/components/CellGrid/input/pointer.d.ts.map +1 -1
  5. package/dist/types/src/components/CellGrid/input/wheel.d.ts +1 -1
  6. package/dist/types/src/components/CellGrid/input/wheel.d.ts.map +1 -1
  7. package/dist/types/src/components/CellGrid/state/atoms.d.ts +1 -1
  8. package/dist/types/src/components/CellGrid/state/atoms.d.ts.map +1 -1
  9. package/dist/types/src/hooks/useCanvasContext.d.ts.map +1 -1
  10. package/dist/types/src/util/index.d.ts +1 -0
  11. package/dist/types/src/util/index.d.ts.map +1 -1
  12. package/dist/types/src/util/svg-path.d.ts +4 -0
  13. package/dist/types/src/util/svg-path.d.ts.map +1 -0
  14. package/dist/types/src/util/svg.d.ts +0 -1
  15. package/dist/types/src/util/svg.d.ts.map +1 -1
  16. package/dist/types/src/util/svg.stories.d.ts.map +1 -1
  17. package/dist/types/tsconfig.tsbuildinfo +1 -1
  18. package/package.json +14 -14
  19. package/src/components/CellGrid/CellGrid.tsx +2 -2
  20. package/src/components/CellGrid/headers/Ruler.tsx +1 -1
  21. package/src/components/CellGrid/input/pointer.ts +1 -1
  22. package/src/components/CellGrid/input/wheel.ts +1 -1
  23. package/src/components/CellGrid/state/atoms.ts +1 -1
  24. package/src/hooks/{useCanvasContext.tsx → useCanvasContext.ts} +3 -0
  25. package/src/util/index.ts +1 -0
  26. package/src/util/svg-path.ts +13 -0
  27. package/src/util/svg.stories.tsx +2 -1
  28. package/src/util/svg.tsx +1 -4
  29. package/dist/lib/browser/index.mjs +0 -1451
  30. package/dist/lib/browser/index.mjs.map +0 -7
  31. package/dist/lib/browser/meta.json +0 -1
  32. package/dist/lib/node-esm/index.mjs +0 -1453
  33. package/dist/lib/node-esm/index.mjs.map +0 -7
  34. package/dist/lib/node-esm/meta.json +0 -1
@@ -1,1451 +0,0 @@
1
- // src/components/Canvas/Canvas.tsx
2
- import React2, { forwardRef, useEffect as useEffect3, useImperativeHandle, useMemo, useState } from "react";
3
- import { useResizeDetector } from "react-resize-detector";
4
- import { mx as mx2 } from "@dxos/ui-theme";
5
-
6
- // src/hooks/projection.tsx
7
- import { easeSinOut, interpolate, interpolateObject, transition } from "d3";
8
- import { applyToPoints, compose, identity, inverse, scale as scaleMatrix, translate as translateMatrix } from "transformation-matrix";
9
- var defaultOrigin = {
10
- x: 0,
11
- y: 0
12
- };
13
- var ProjectionMapper = class {
14
- _bounds = {
15
- width: 0,
16
- height: 0
17
- };
18
- _scale = 1;
19
- _offset = defaultOrigin;
20
- _toScreen = identity();
21
- _toModel = identity();
22
- constructor(bounds, scale, offset) {
23
- if (bounds && scale && offset) {
24
- this.update(bounds, scale, offset);
25
- }
26
- }
27
- update(bounds, scale, offset) {
28
- this._bounds = bounds;
29
- this._scale = scale;
30
- this._offset = offset;
31
- this._toScreen = compose(
32
- // NOTE: Order is important.
33
- translateMatrix(this._offset.x, this._offset.y),
34
- scaleMatrix(this._scale)
35
- );
36
- this._toModel = inverse(this._toScreen);
37
- return this;
38
- }
39
- get bounds() {
40
- return this._bounds;
41
- }
42
- get scale() {
43
- return this._scale;
44
- }
45
- get offset() {
46
- return this._offset;
47
- }
48
- toScreen(points) {
49
- return applyToPoints(this._toScreen, points);
50
- }
51
- toModel(points) {
52
- return applyToPoints(this._toModel, points);
53
- }
54
- };
55
- var getZoomTransform = ({ scale, offset, pos, newScale }) => {
56
- return {
57
- scale: newScale,
58
- offset: {
59
- x: pos.x - (pos.x - offset.x) * (newScale / scale),
60
- y: pos.y - (pos.y - offset.y) * (newScale / scale)
61
- }
62
- };
63
- };
64
- var zoomInPlace = (setTransform, pos, offset, current, next, delay = 200) => {
65
- const is = interpolate(current, next);
66
- transition().ease(easeSinOut).duration(delay).tween("zoom", () => (t) => {
67
- const newScale = is(t);
68
- setTransform(getZoomTransform({
69
- scale: current,
70
- newScale,
71
- offset,
72
- pos
73
- }));
74
- });
75
- };
76
- var noop = () => {
77
- };
78
- var zoomTo = (setTransform, current, next, delay = 200, cb = noop) => {
79
- const is = interpolateObject({
80
- scale: current.scale,
81
- ...current.offset
82
- }, {
83
- scale: next.scale,
84
- ...next.offset
85
- });
86
- transition().ease(easeSinOut).duration(delay).tween("zoom", () => (t) => {
87
- const { scale, x, y } = is(t);
88
- setTransform({
89
- scale,
90
- offset: {
91
- x,
92
- y
93
- }
94
- });
95
- }).on("end", cb);
96
- };
97
-
98
- // src/hooks/useCanvasContext.tsx
99
- import { createContext, useContext } from "react";
100
- import { raise } from "@dxos/debug";
101
- var CanvasContext = /* @__PURE__ */ createContext(null);
102
- var useCanvasContext = () => {
103
- return useContext(CanvasContext) ?? raise(new Error("Missing CanvasContext"));
104
- };
105
-
106
- // src/hooks/useDrag.tsx
107
- import { bind } from "bind-event-listener";
108
- import { useEffect, useRef } from "react";
109
- var useDrag = (_options = {}) => {
110
- const { root, setProjection } = useCanvasContext();
111
- const state = useRef({
112
- panning: false,
113
- x: 0,
114
- y: 0
115
- });
116
- useEffect(() => {
117
- if (!root) {
118
- return;
119
- }
120
- return bind(root, {
121
- type: "pointerdown",
122
- listener: (ev) => {
123
- if (ev.button !== 0) {
124
- return;
125
- }
126
- if (ev.defaultPrevented) {
127
- return;
128
- }
129
- if (ev.target !== root || ev.shiftKey) {
130
- return;
131
- }
132
- ev.preventDefault();
133
- root.setPointerCapture(ev.pointerId);
134
- state.current = {
135
- panning: true,
136
- x: ev.clientX,
137
- y: ev.clientY
138
- };
139
- const moveUnbind = bind(root, {
140
- type: "pointermove",
141
- listener: (ev2) => {
142
- if (!state.current.panning) {
143
- return;
144
- }
145
- const dx = ev2.clientX - state.current.x;
146
- const dy = ev2.clientY - state.current.y;
147
- state.current.x = ev2.clientX;
148
- state.current.y = ev2.clientY;
149
- setProjection((prev) => ({
150
- ...prev,
151
- offset: {
152
- x: prev.offset.x + dx,
153
- y: prev.offset.y + dy
154
- }
155
- }));
156
- }
157
- });
158
- const upUnbind = bind(root, {
159
- type: "pointerup",
160
- listener: (ev2) => {
161
- state.current.panning = false;
162
- root.releasePointerCapture(ev2.pointerId);
163
- moveUnbind();
164
- upUnbind();
165
- }
166
- });
167
- }
168
- });
169
- }, [
170
- root
171
- ]);
172
- };
173
-
174
- // src/hooks/useWheel.tsx
175
- import { bindAll } from "bind-event-listener";
176
- import { useEffect as useEffect2 } from "react";
177
-
178
- // src/util/svg.tsx
179
- import React from "react";
180
- import { mx } from "@dxos/ui-theme";
181
- var createPath = (points, join = false) => {
182
- return [
183
- "M",
184
- points.map(({ x, y }) => `${x},${y}`).join(" L "),
185
- join ? "Z" : ""
186
- ].join(" ");
187
- };
188
- var Markers = ({ id = "dx-marker", classNames }) => {
189
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Arrow, {
190
- id: `${id}-arrow-start`,
191
- dir: "start",
192
- classNames
193
- }), /* @__PURE__ */ React.createElement(Arrow, {
194
- id: `${id}-arrow-end`,
195
- dir: "end",
196
- classNames
197
- }), /* @__PURE__ */ React.createElement(Arrow, {
198
- id: `${id}-triangle-start`,
199
- dir: "start",
200
- closed: true,
201
- classNames
202
- }), /* @__PURE__ */ React.createElement(Arrow, {
203
- id: `${id}-triangle-end`,
204
- dir: "end",
205
- closed: true,
206
- classNames
207
- }), /* @__PURE__ */ React.createElement(Marker, {
208
- id: `${id}-circle`,
209
- pos: {
210
- x: 8,
211
- y: 8
212
- },
213
- size: {
214
- width: 16,
215
- height: 16
216
- }
217
- }, /* @__PURE__ */ React.createElement("circle", {
218
- cx: 8,
219
- cy: 8,
220
- r: 5,
221
- stroke: "context-stroke",
222
- className: mx(classNames)
223
- })));
224
- };
225
- var Marker = ({ id, className, children, pos: { x: refX, y: refY }, size: { width: markerWidth, height: markerHeight }, fill, ...rest }) => /* @__PURE__ */ React.createElement("marker", {
226
- id,
227
- className,
228
- refX,
229
- refY,
230
- markerWidth,
231
- markerHeight,
232
- markerUnits: "strokeWidth",
233
- orient: "auto",
234
- ...rest
235
- }, children);
236
- var Arrow = ({ classNames, id, size = 16, dir = "end", closed = false }) => /* @__PURE__ */ React.createElement(Marker, {
237
- id,
238
- size: {
239
- width: size,
240
- height: size
241
- },
242
- pos: dir === "end" ? {
243
- x: size,
244
- y: size / 2
245
- } : {
246
- x: 0,
247
- y: size / 2
248
- }
249
- }, /* @__PURE__ */ React.createElement("path", {
250
- fill: closed ? void 0 : "none",
251
- stroke: "context-stroke",
252
- className: mx(classNames),
253
- d: createPath(dir === "end" ? [
254
- {
255
- x: 1,
256
- y: 1
257
- },
258
- {
259
- x: size,
260
- y: size / 2
261
- },
262
- {
263
- x: 1,
264
- y: size - 1
265
- }
266
- ] : [
267
- {
268
- x: size - 1,
269
- y: 1
270
- },
271
- {
272
- x: 0,
273
- y: size / 2
274
- },
275
- {
276
- x: size - 1,
277
- y: size - 1
278
- }
279
- ], closed)
280
- }));
281
- var GridPattern = ({ classNames, id, size, offset }) => /* @__PURE__ */ React.createElement("pattern", {
282
- id,
283
- x: (size / 2 + offset.x) % size,
284
- y: (size / 2 + offset.y) % size,
285
- width: size,
286
- height: size,
287
- patternUnits: "userSpaceOnUse"
288
- }, /* @__PURE__ */ React.createElement("g", {
289
- className: mx(classNames)
290
- }, /* @__PURE__ */ React.createElement("line", {
291
- x1: 0,
292
- y1: size / 2,
293
- x2: size,
294
- y2: size / 2
295
- }), /* @__PURE__ */ React.createElement("line", {
296
- x1: size / 2,
297
- y1: 0,
298
- x2: size / 2,
299
- y2: size
300
- })));
301
-
302
- // src/util/util.ts
303
- var logged = false;
304
- var getRelativePoint = (el, ev) => {
305
- const rect = el.getBoundingClientRect();
306
- return {
307
- x: ev.clientX - rect.x,
308
- y: ev.clientY - rect.top
309
- };
310
- };
311
- var testId = (id, inspect = false) => {
312
- if (inspect) {
313
- if (!logged) {
314
- console.log("Open storybook in expanded window;\nthen run INSPECT()");
315
- logged = true;
316
- }
317
- window.INSPECT = () => {
318
- const el = document.querySelector(`[data-test-id="${id}"]`);
319
- window.inspect(el);
320
- console.log(el);
321
- };
322
- }
323
- return {
324
- [DATA_TEST_ID]: id
325
- };
326
- };
327
- var inspectElement = (el) => {
328
- window.INSPECT = () => {
329
- window.inspect(el);
330
- window.element = el;
331
- console.log("Open storybook in expanded window;\nthen run INSPECT()");
332
- console.log(el);
333
- };
334
- };
335
- var DATA_TEST_ID = "data-test-id";
336
-
337
- // src/hooks/useWheel.tsx
338
- var defaultOptions = {
339
- zoom: true
340
- };
341
- var useWheel = (options = defaultOptions) => {
342
- const { root, setProjection } = useCanvasContext();
343
- useEffect2(() => {
344
- if (!root) {
345
- return;
346
- }
347
- return bindAll(root, [
348
- {
349
- type: "wheel",
350
- options: {
351
- capture: true,
352
- passive: false
353
- },
354
- listener: (ev) => {
355
- const zooming = isWheelZooming(ev);
356
- ev.preventDefault();
357
- if (zooming && !options.zoom) {
358
- return;
359
- }
360
- if (ev.ctrlKey) {
361
- if (!root) {
362
- return;
363
- }
364
- setProjection(({ scale, offset }) => {
365
- const pos = getRelativePoint(root, ev);
366
- const scaleSensitivity = 0.01;
367
- const newScale = scale * Math.exp(-ev.deltaY * scaleSensitivity);
368
- return getZoomTransform({
369
- scale,
370
- offset,
371
- newScale,
372
- pos
373
- });
374
- });
375
- } else {
376
- setProjection(({ scale, offset: { x, y } }) => {
377
- return {
378
- scale,
379
- offset: {
380
- x: x - ev.deltaX,
381
- y: y - ev.deltaY
382
- }
383
- };
384
- });
385
- }
386
- }
387
- }
388
- ]);
389
- }, [
390
- root
391
- ]);
392
- };
393
- var isWheelZooming = (ev) => {
394
- if (ev.ctrlKey || ev.metaKey) {
395
- return Math.abs(ev.deltaY) > 0 || Math.abs(ev.deltaZ) > 0;
396
- }
397
- return false;
398
- };
399
-
400
- // src/components/Canvas/Canvas.tsx
401
- var Canvas = /* @__PURE__ */ forwardRef(({ children, classNames, scale: scaleProp = 1, offset: offsetProp = defaultOrigin, ...props }, forwardedRef) => {
402
- const { ref, width = 0, height = 0 } = useResizeDetector();
403
- const [ready, setReady] = useState(false);
404
- const [{ scale, offset }, setProjection] = useState({
405
- scale: scaleProp,
406
- offset: offsetProp
407
- });
408
- useEffect3(() => {
409
- if (width && height && offset === defaultOrigin) {
410
- setProjection({
411
- scale,
412
- offset: {
413
- x: width / 2,
414
- y: height / 2
415
- }
416
- });
417
- }
418
- }, [
419
- width,
420
- height,
421
- scale,
422
- offset
423
- ]);
424
- const projection = useMemo(() => new ProjectionMapper(), []);
425
- useEffect3(() => {
426
- projection.update({
427
- width,
428
- height
429
- }, scale, offset);
430
- if (offset !== defaultOrigin) {
431
- setReady(true);
432
- }
433
- }, [
434
- projection,
435
- scale,
436
- offset,
437
- width,
438
- height
439
- ]);
440
- const styles = useMemo(() => {
441
- return {
442
- // NOTE: Order is important.
443
- transform: `translate(${offset.x}px, ${offset.y}px) scale(${scale})`,
444
- visibility: width && height ? "visible" : "hidden"
445
- };
446
- }, [
447
- scale,
448
- offset
449
- ]);
450
- useImperativeHandle(forwardedRef, () => {
451
- return {
452
- setProjection: async (projection2) => {
453
- setProjection(projection2);
454
- }
455
- };
456
- }, [
457
- ref
458
- ]);
459
- return /* @__PURE__ */ React2.createElement(CanvasContext.Provider, {
460
- value: {
461
- root: ref.current,
462
- ready,
463
- width,
464
- height,
465
- scale,
466
- offset,
467
- styles,
468
- projection,
469
- setProjection
470
- }
471
- }, /* @__PURE__ */ React2.createElement("div", {
472
- ...props,
473
- className: mx2("absolute inset-0 overflow-hidden", classNames),
474
- ref
475
- }, ready ? children : null));
476
- });
477
-
478
- // src/components/CellGrid/CellGrid.tsx
479
- import { RegistryContext } from "@effect-atom/atom-react";
480
- import React5, { useContext as useContext2, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef2, useState as useState2 } from "react";
481
- import { useResizeDetector as useResizeDetector2 } from "react-resize-detector";
482
- import { mx as mx5 } from "@dxos/ui-theme";
483
-
484
- // src/components/CellGrid/headers/Ruler.tsx
485
- import React3, { useMemo as useMemo2 } from "react";
486
- import { mx as mx3 } from "@dxos/ui-theme";
487
-
488
- // src/components/CellGrid/state/viewport.ts
489
- var cellKey = (col, row) => `${col},${row}`;
490
- var cellWidth = (viewport) => viewport.baseCellWidth * viewport.zoomX;
491
- var worldToScreen = (viewport, headers, coord) => {
492
- const w = cellWidth(viewport);
493
- return {
494
- x: headers.left + coord.col * w - viewport.scrollX,
495
- y: headers.top + coord.row * viewport.cellHeight - viewport.scrollY,
496
- w: (coord.length ?? 1) * w,
497
- h: viewport.cellHeight
498
- };
499
- };
500
- var screenToWorld = (viewport, headers, point) => {
501
- const w = cellWidth(viewport);
502
- return {
503
- col: (point.x - headers.left + viewport.scrollX) / w,
504
- row: (point.y - headers.top + viewport.scrollY) / viewport.cellHeight
505
- };
506
- };
507
- var hitTestCell = (viewport, headers, point) => {
508
- if (point.x < headers.left || point.y < headers.top) {
509
- return null;
510
- }
511
- const { col, row } = screenToWorld(viewport, headers, point);
512
- if (col < 0 || row < 0) {
513
- return null;
514
- }
515
- return {
516
- col: Math.floor(col),
517
- row: Math.floor(row)
518
- };
519
- };
520
- var visibleCellRange = (viewport, headers, size) => {
521
- const w = cellWidth(viewport);
522
- const innerW = Math.max(0, size.width - headers.left);
523
- const innerH = Math.max(0, size.height - headers.top);
524
- const minCol = Math.max(0, Math.floor(viewport.scrollX / w));
525
- const maxCol = Math.floor((viewport.scrollX + innerW) / w);
526
- const minRow = Math.max(0, Math.floor(viewport.scrollY / viewport.cellHeight));
527
- const maxRow = Math.floor((viewport.scrollY + innerH) / viewport.cellHeight);
528
- return {
529
- minCol,
530
- maxCol,
531
- minRow,
532
- maxRow
533
- };
534
- };
535
- var visibleCells = function* (cells, range) {
536
- for (const cell of cells.values()) {
537
- if (cell.row < range.minRow || cell.row > range.maxRow) {
538
- continue;
539
- }
540
- const start = cell.col;
541
- const end = cell.col + cell.length - 1;
542
- if (end < range.minCol || start > range.maxCol) {
543
- continue;
544
- }
545
- yield cell;
546
- }
547
- };
548
-
549
- // src/components/CellGrid/headers/Ruler.tsx
550
- var Ruler = ({ viewport, headers, width, majorEvery = 4, classNames }) => {
551
- const safeMajorEvery = Math.max(1, Math.floor(majorEvery));
552
- const ticks = useMemo2(() => {
553
- const w = cellWidth(viewport);
554
- if (w < 1 || width <= headers.left) {
555
- return [];
556
- }
557
- const innerWidth = width - headers.left;
558
- const startCol = Math.floor(viewport.scrollX / w);
559
- const endCol = Math.ceil((viewport.scrollX + innerWidth) / w);
560
- const result = [];
561
- for (let col = startCol; col <= endCol; col++) {
562
- result.push({
563
- col,
564
- x: headers.left + col * w - viewport.scrollX,
565
- major: col % safeMajorEvery === 0
566
- });
567
- }
568
- return result;
569
- }, [
570
- viewport,
571
- headers.left,
572
- width,
573
- safeMajorEvery
574
- ]);
575
- return /* @__PURE__ */ React3.createElement("div", {
576
- className: mx3("absolute top-0 left-0 right-0 border-b border-neutral-200 dark:border-neutral-700 bg-baseSurface select-none overflow-hidden", classNames),
577
- style: {
578
- height: headers.top
579
- }
580
- }, ticks.map(({ col, x, major }) => /* @__PURE__ */ React3.createElement("div", {
581
- key: col,
582
- className: mx3("absolute top-0 bottom-0 text-[10px] text-neutral-500 dark:text-neutral-400", major ? "border-l border-neutral-400 dark:border-neutral-500" : "border-l border-neutral-200 dark:border-neutral-700"),
583
- style: {
584
- transform: `translateX(${x}px)`
585
- }
586
- }, major ? /* @__PURE__ */ React3.createElement("span", {
587
- className: "absolute left-1 top-0"
588
- }, col) : null)));
589
- };
590
-
591
- // src/components/CellGrid/headers/TrackHeader.tsx
592
- import React4 from "react";
593
- import { mx as mx4 } from "@dxos/ui-theme";
594
- var TrackHeader = ({ viewport, headers, rows, height, classNames }) => {
595
- return /* @__PURE__ */ React4.createElement("div", {
596
- className: mx4("absolute left-0 border-r border-neutral-200 dark:border-neutral-700 select-none overflow-hidden", classNames),
597
- style: {
598
- top: headers.top,
599
- width: headers.left,
600
- height: Math.max(0, height - headers.top)
601
- }
602
- }, /* @__PURE__ */ React4.createElement("div", {
603
- style: {
604
- transform: `translateY(${-viewport.scrollY}px)`
605
- }
606
- }, rows.map((row, index) => /* @__PURE__ */ React4.createElement("div", {
607
- key: row.id,
608
- className: "flex items-center px-2 text-xs text-neutral-700 dark:text-neutral-300",
609
- style: {
610
- height: viewport.cellHeight,
611
- // Match the canvas's row-band: a translucent gray overlay on odd rows,
612
- // transparent on even rows. The container's overall background bleeds
613
- // through, so the labels stay legible in both themes.
614
- backgroundColor: index % 2 === 0 ? "transparent" : "rgba(128, 128, 128, 0.08)",
615
- // Match the canvas gridline color (rgba(128, 128, 128, 0.25)). Use a
616
- // half-pixel inset to keep crisp single-pixel rendering on retina.
617
- boxShadow: "inset 0 -1px 0 rgba(128, 128, 128, 0.25)"
618
- }
619
- }, row.label ?? row.id))));
620
- };
621
-
622
- // src/components/CellGrid/input/pointer.ts
623
- var attachPointerHandlers = (element, { registry, atoms, headers, handlers }) => {
624
- let drag = null;
625
- const local = (event) => {
626
- const rect = element.getBoundingClientRect();
627
- return {
628
- x: event.clientX - rect.left,
629
- y: event.clientY - rect.top
630
- };
631
- };
632
- const tryCapture = (pointerId) => {
633
- try {
634
- element.setPointerCapture(pointerId);
635
- } catch {
636
- }
637
- };
638
- const onPointerDown = (event) => {
639
- if (event.button === 1 || event.button === 0 && event.altKey) {
640
- drag = {
641
- kind: "pan",
642
- lastX: event.clientX,
643
- lastY: event.clientY
644
- };
645
- tryCapture(event.pointerId);
646
- event.preventDefault();
647
- return;
648
- }
649
- if (event.button !== 0) {
650
- return;
651
- }
652
- const viewport = registry.get(atoms.viewport);
653
- const point = local(event);
654
- const coord = hitTestCell(viewport, headers, point);
655
- if (!coord) {
656
- return;
657
- }
658
- const tool = registry.get(atoms.tool);
659
- tryCapture(event.pointerId);
660
- switch (tool) {
661
- case "toggle":
662
- case "resize": {
663
- const cells = registry.get(atoms.cells);
664
- const key = cellKey(coord.col, coord.row);
665
- const mode = cells.has(key) ? "unset" : "set";
666
- handlers.onCellToggle?.(coord, mode);
667
- drag = {
668
- kind: "toggle",
669
- mode,
670
- touched: /* @__PURE__ */ new Set([
671
- key
672
- ])
673
- };
674
- break;
675
- }
676
- case "edit": {
677
- drag = {
678
- kind: "draw",
679
- startCoord: coord,
680
- endCoord: coord
681
- };
682
- handlers.onDrawUpdate?.(coord, coord);
683
- break;
684
- }
685
- case "delete": {
686
- const key = cellKey(coord.col, coord.row);
687
- handlers.onCellToggle?.(coord, "unset");
688
- drag = {
689
- kind: "toggle",
690
- mode: "unset",
691
- touched: /* @__PURE__ */ new Set([
692
- key
693
- ])
694
- };
695
- break;
696
- }
697
- case "select": {
698
- drag = {
699
- kind: "select",
700
- origin: coord
701
- };
702
- registry.set(atoms.selection, {
703
- range: {
704
- col0: coord.col,
705
- row0: coord.row,
706
- col1: coord.col,
707
- row1: coord.row
708
- }
709
- });
710
- break;
711
- }
712
- }
713
- };
714
- const onPointerMove = (event) => {
715
- if (!drag) {
716
- return;
717
- }
718
- if (drag.kind === "pan") {
719
- const dx = event.clientX - drag.lastX;
720
- const dy = event.clientY - drag.lastY;
721
- drag.lastX = event.clientX;
722
- drag.lastY = event.clientY;
723
- registry.update(atoms.viewport, (current) => ({
724
- ...current,
725
- scrollX: Math.max(0, current.scrollX - dx),
726
- scrollY: Math.max(0, current.scrollY - dy)
727
- }));
728
- return;
729
- }
730
- const viewport = registry.get(atoms.viewport);
731
- const coord = hitTestCell(viewport, headers, local(event));
732
- if (!coord) {
733
- return;
734
- }
735
- if (drag.kind === "toggle") {
736
- const key = cellKey(coord.col, coord.row);
737
- if (!drag.touched.has(key)) {
738
- drag.touched.add(key);
739
- handlers.onCellToggle?.(coord, drag.mode);
740
- }
741
- } else if (drag.kind === "draw") {
742
- const constrainedCol = coord.col;
743
- if (constrainedCol !== drag.endCoord.col) {
744
- drag.endCoord = {
745
- col: constrainedCol,
746
- row: drag.startCoord.row
747
- };
748
- handlers.onDrawUpdate?.(drag.startCoord, drag.endCoord);
749
- }
750
- } else if (drag.kind === "select") {
751
- registry.set(atoms.selection, {
752
- range: {
753
- col0: drag.origin.col,
754
- row0: drag.origin.row,
755
- col1: coord.col,
756
- row1: coord.row
757
- }
758
- });
759
- }
760
- };
761
- const releaseCapture = (event) => {
762
- if (element.hasPointerCapture(event.pointerId)) {
763
- element.releasePointerCapture(event.pointerId);
764
- }
765
- };
766
- const onPointerUp = (event) => {
767
- if (!drag) {
768
- return;
769
- }
770
- if (drag.kind === "draw") {
771
- handlers.onDrawCommit?.(drag.startCoord, drag.endCoord);
772
- } else if (drag.kind === "select") {
773
- const range = registry.get(atoms.selection).range;
774
- if (range) {
775
- handlers.onSelectionCommit?.(range);
776
- }
777
- }
778
- drag = null;
779
- releaseCapture(event);
780
- };
781
- const onPointerCancel = (event) => {
782
- drag = null;
783
- releaseCapture(event);
784
- };
785
- element.addEventListener("pointerdown", onPointerDown);
786
- element.addEventListener("pointermove", onPointerMove);
787
- element.addEventListener("pointerup", onPointerUp);
788
- element.addEventListener("pointercancel", onPointerCancel);
789
- return () => {
790
- element.removeEventListener("pointerdown", onPointerDown);
791
- element.removeEventListener("pointermove", onPointerMove);
792
- element.removeEventListener("pointerup", onPointerUp);
793
- element.removeEventListener("pointercancel", onPointerCancel);
794
- };
795
- };
796
- var toggleCell = (registry, atoms, coord, factory, mode = "toggle") => {
797
- registry.update(atoms.cells, (current) => {
798
- const next = new Map(current);
799
- const key = cellKey(coord.col, coord.row);
800
- const exists = next.has(key);
801
- if (mode === "set" || mode === "toggle" && !exists) {
802
- next.set(key, factory(coord));
803
- } else if (mode === "unset" || mode === "toggle" && exists) {
804
- next.delete(key);
805
- }
806
- return next;
807
- });
808
- };
809
-
810
- // src/components/CellGrid/input/wheel.ts
811
- var MIN_ZOOM = 0.25;
812
- var MAX_ZOOM = 8;
813
- var attachWheelHandlers = (element, { registry, atoms, headers }) => {
814
- const onWheel = (event) => {
815
- if (event.ctrlKey || event.metaKey) {
816
- event.preventDefault();
817
- const rect = element.getBoundingClientRect();
818
- const x = event.clientX - rect.left;
819
- const factor = Math.exp(-event.deltaY / 200);
820
- registry.update(atoms.viewport, (current2) => {
821
- const nextZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, current2.zoomX * factor));
822
- if (nextZoom === current2.zoomX) {
823
- return current2;
824
- }
825
- const w = cellWidth(current2);
826
- const worldX = (x - headers.left + current2.scrollX) / w;
827
- const nextW = current2.baseCellWidth * nextZoom;
828
- const nextScrollX2 = Math.max(0, worldX * nextW - (x - headers.left));
829
- return {
830
- ...current2,
831
- zoomX: nextZoom,
832
- scrollX: nextScrollX2
833
- };
834
- });
835
- return;
836
- }
837
- const dx = event.shiftKey ? event.deltaY : event.deltaX;
838
- const dy = event.shiftKey ? 0 : event.deltaY;
839
- const current = registry.get(atoms.viewport);
840
- const nextScrollX = Math.max(0, current.scrollX + dx);
841
- const nextScrollY = Math.max(0, current.scrollY + dy);
842
- if (nextScrollX === current.scrollX && nextScrollY === current.scrollY) {
843
- return;
844
- }
845
- event.preventDefault();
846
- registry.set(atoms.viewport, {
847
- ...current,
848
- scrollX: nextScrollX,
849
- scrollY: nextScrollY
850
- });
851
- };
852
- element.addEventListener("wheel", onWheel, {
853
- passive: false
854
- });
855
- return () => element.removeEventListener("wheel", onWheel);
856
- };
857
-
858
- // src/components/CellGrid/render/overlay-layer.ts
859
- var drawOverlay = ({ ctx, size, viewport, headers, selection, playhead, style }) => {
860
- ctx.clearRect(0, 0, size.width, size.height);
861
- ctx.save();
862
- ctx.beginPath();
863
- ctx.rect(headers.left, headers.top, size.width - headers.left, size.height - headers.top);
864
- ctx.clip();
865
- if (selection.range) {
866
- const { col0, row0, col1, row1 } = selection.range;
867
- const minCol = Math.min(col0, col1);
868
- const maxCol = Math.max(col0, col1);
869
- const minRow = Math.min(row0, row1);
870
- const maxRow = Math.max(row0, row1);
871
- const tl = worldToScreen(viewport, headers, {
872
- col: minCol,
873
- row: minRow
874
- });
875
- const br = worldToScreen(viewport, headers, {
876
- col: maxCol + 1,
877
- row: maxRow + 1
878
- });
879
- ctx.fillStyle = style.selectionFill;
880
- ctx.fillRect(tl.x, tl.y, br.x - tl.x, br.y - tl.y);
881
- ctx.strokeStyle = style.selectionStroke;
882
- ctx.setLineDash([
883
- 4,
884
- 3
885
- ]);
886
- ctx.lineWidth = 1;
887
- ctx.strokeRect(tl.x + 0.5, tl.y + 0.5, br.x - tl.x - 1, br.y - tl.y - 1);
888
- ctx.setLineDash([]);
889
- }
890
- if (playhead !== null) {
891
- const w = cellWidth(viewport);
892
- const x = headers.left + playhead * w - viewport.scrollX;
893
- if (x >= headers.left && x <= size.width) {
894
- ctx.strokeStyle = style.playhead;
895
- ctx.lineWidth = 2;
896
- ctx.beginPath();
897
- ctx.moveTo(x, headers.top);
898
- ctx.lineTo(x, size.height);
899
- ctx.stroke();
900
- }
901
- }
902
- ctx.restore();
903
- };
904
-
905
- // src/components/CellGrid/render/static-layer.ts
906
- var drawCells = ({ ctx, size, viewport, headers, rows, cells, renderCell, style }) => {
907
- ctx.clearRect(0, 0, size.width, size.height);
908
- if (style.background) {
909
- ctx.fillStyle = style.background;
910
- ctx.fillRect(0, 0, size.width, size.height);
911
- }
912
- const range = visibleCellRange(viewport, headers, size);
913
- const w = cellWidth(viewport);
914
- const h = viewport.cellHeight;
915
- if (style.rowBand) {
916
- ctx.fillStyle = style.rowBand;
917
- for (let row = range.minRow; row <= Math.min(range.maxRow, rows.length - 1); row++) {
918
- if (row % 2 === 0) {
919
- continue;
920
- }
921
- const y = headers.top + row * h - viewport.scrollY;
922
- ctx.fillRect(headers.left, y, size.width - headers.left, h);
923
- }
924
- }
925
- ctx.strokeStyle = style.gridLine;
926
- ctx.lineWidth = 1;
927
- ctx.beginPath();
928
- for (let col = range.minCol; col <= range.maxCol + 1; col++) {
929
- const x = Math.floor(headers.left + col * w - viewport.scrollX) + 0.5;
930
- if (x < headers.left) {
931
- continue;
932
- }
933
- ctx.moveTo(x, headers.top);
934
- ctx.lineTo(x, size.height);
935
- }
936
- for (let row = range.minRow; row <= Math.min(range.maxRow + 1, rows.length); row++) {
937
- const y = Math.floor(headers.top + row * h - viewport.scrollY) + 0.5;
938
- if (y < headers.top) {
939
- continue;
940
- }
941
- ctx.moveTo(headers.left, y);
942
- ctx.lineTo(size.width, y);
943
- }
944
- ctx.stroke();
945
- ctx.save();
946
- ctx.beginPath();
947
- ctx.rect(headers.left, headers.top, size.width - headers.left, size.height - headers.top);
948
- ctx.clip();
949
- for (const cell of visibleCells(cells, range)) {
950
- if (cell.row >= rows.length) {
951
- continue;
952
- }
953
- const rect = worldToScreen(viewport, headers, cell);
954
- renderCell({
955
- ctx,
956
- ...rect,
957
- cell
958
- });
959
- }
960
- ctx.restore();
961
- };
962
-
963
- // src/components/CellGrid/CellGrid.tsx
964
- var defaultHeaders = {
965
- left: 80,
966
- top: 24
967
- };
968
- var defaultStaticStyle = {
969
- gridLine: "rgba(128,128,128,0.25)",
970
- rowBand: "rgba(128,128,128,0.06)"
971
- };
972
- var defaultOverlayStyle = {
973
- playhead: "rgb(220, 38, 38)",
974
- selectionFill: "rgba(59, 130, 246, 0.15)",
975
- selectionStroke: "rgb(59, 130, 246)"
976
- };
977
- var setupCanvas = (canvas, width, height) => {
978
- const dpr = window.devicePixelRatio || 1;
979
- canvas.width = Math.max(1, Math.floor(width * dpr));
980
- canvas.height = Math.max(1, Math.floor(height * dpr));
981
- canvas.style.width = `${width}px`;
982
- canvas.style.height = `${height}px`;
983
- const ctx = canvas.getContext("2d");
984
- if (!ctx) {
985
- return null;
986
- }
987
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
988
- return ctx;
989
- };
990
- var CellGrid = ({ atoms, rows, renderCell, headers: headersProp, staticStyle: staticStyleProp, overlayStyle: overlayStyleProp, classNames, onCellToggle, onSelectionCommit, onDrawUpdate, onDrawCommit }) => {
991
- const registry = useContext2(RegistryContext);
992
- const headers = useMemo3(() => {
993
- if (headersProp === false) {
994
- return {
995
- left: 0,
996
- top: 0
997
- };
998
- }
999
- return {
1000
- ...defaultHeaders,
1001
- ...headersProp ?? {}
1002
- };
1003
- }, [
1004
- headersProp
1005
- ]);
1006
- const staticStyle = useMemo3(() => ({
1007
- ...defaultStaticStyle,
1008
- ...staticStyleProp ?? {}
1009
- }), [
1010
- staticStyleProp
1011
- ]);
1012
- const overlayStyle = useMemo3(() => ({
1013
- ...defaultOverlayStyle,
1014
- ...overlayStyleProp ?? {}
1015
- }), [
1016
- overlayStyleProp
1017
- ]);
1018
- const { ref: containerRef, width = 0, height = 0 } = useResizeDetector2();
1019
- const staticCanvasRef = useRef2(null);
1020
- const overlayCanvasRef = useRef2(null);
1021
- const overlayInputRef = useRef2(null);
1022
- const [staticCtx, setStaticCtx] = useState2(null);
1023
- const [overlayCtx, setOverlayCtx] = useState2(null);
1024
- const [viewportState, setViewportState] = useState2(() => registry.get(atoms.viewport));
1025
- useEffect4(() => {
1026
- if (!width || !height) {
1027
- return;
1028
- }
1029
- if (staticCanvasRef.current) {
1030
- const ctx = setupCanvas(staticCanvasRef.current, width, height);
1031
- setStaticCtx(ctx);
1032
- }
1033
- if (overlayCanvasRef.current) {
1034
- const ctx = setupCanvas(overlayCanvasRef.current, width, height);
1035
- setOverlayCtx(ctx);
1036
- }
1037
- }, [
1038
- width,
1039
- height
1040
- ]);
1041
- useEffect4(() => registry.subscribe(atoms.viewport, (next) => setViewportState(next)), [
1042
- registry,
1043
- atoms.viewport
1044
- ]);
1045
- useEffect4(() => {
1046
- if (!staticCtx || !width || !height) {
1047
- return;
1048
- }
1049
- let raf = null;
1050
- const schedule = () => {
1051
- if (raf !== null) {
1052
- return;
1053
- }
1054
- raf = requestAnimationFrame(() => {
1055
- raf = null;
1056
- drawCells({
1057
- ctx: staticCtx,
1058
- size: {
1059
- width,
1060
- height
1061
- },
1062
- viewport: registry.get(atoms.viewport),
1063
- headers,
1064
- rows,
1065
- cells: registry.get(atoms.cells),
1066
- renderCell,
1067
- style: staticStyle
1068
- });
1069
- });
1070
- };
1071
- schedule();
1072
- const unsubCells = registry.subscribe(atoms.cells, schedule);
1073
- const unsubViewport = registry.subscribe(atoms.viewport, schedule);
1074
- return () => {
1075
- if (raf !== null) {
1076
- cancelAnimationFrame(raf);
1077
- }
1078
- unsubCells();
1079
- unsubViewport();
1080
- };
1081
- }, [
1082
- staticCtx,
1083
- width,
1084
- height,
1085
- registry,
1086
- atoms.cells,
1087
- atoms.viewport,
1088
- headers,
1089
- rows,
1090
- renderCell,
1091
- staticStyle
1092
- ]);
1093
- useEffect4(() => {
1094
- if (!overlayCtx || !width || !height) {
1095
- return;
1096
- }
1097
- let raf = null;
1098
- let stopped = false;
1099
- const paint = () => {
1100
- drawOverlay({
1101
- ctx: overlayCtx,
1102
- size: {
1103
- width,
1104
- height
1105
- },
1106
- viewport: registry.get(atoms.viewport),
1107
- headers,
1108
- selection: registry.get(atoms.selection),
1109
- playhead: registry.get(atoms.playhead),
1110
- style: overlayStyle
1111
- });
1112
- };
1113
- const isAnimating = () => registry.get(atoms.playhead) !== null;
1114
- const loop = () => {
1115
- if (stopped) {
1116
- return;
1117
- }
1118
- paint();
1119
- if (isAnimating()) {
1120
- raf = requestAnimationFrame(loop);
1121
- } else {
1122
- raf = null;
1123
- }
1124
- };
1125
- const kick = () => {
1126
- paint();
1127
- if (raf === null && isAnimating()) {
1128
- raf = requestAnimationFrame(loop);
1129
- }
1130
- };
1131
- kick();
1132
- const unsubSelection = registry.subscribe(atoms.selection, () => paint());
1133
- const unsubPlayhead = registry.subscribe(atoms.playhead, kick);
1134
- const unsubViewport = registry.subscribe(atoms.viewport, () => paint());
1135
- return () => {
1136
- stopped = true;
1137
- if (raf !== null) {
1138
- cancelAnimationFrame(raf);
1139
- }
1140
- unsubSelection();
1141
- unsubPlayhead();
1142
- unsubViewport();
1143
- };
1144
- }, [
1145
- overlayCtx,
1146
- width,
1147
- height,
1148
- registry,
1149
- atoms.selection,
1150
- atoms.playhead,
1151
- atoms.viewport,
1152
- headers,
1153
- overlayStyle
1154
- ]);
1155
- const callbacksRef = useRef2({
1156
- onCellToggle,
1157
- onSelectionCommit,
1158
- onDrawUpdate,
1159
- onDrawCommit
1160
- });
1161
- callbacksRef.current = {
1162
- onCellToggle,
1163
- onSelectionCommit,
1164
- onDrawUpdate,
1165
- onDrawCommit
1166
- };
1167
- useEffect4(() => {
1168
- const element = overlayInputRef.current;
1169
- if (!element) {
1170
- return;
1171
- }
1172
- const detachPointer = attachPointerHandlers(element, {
1173
- registry,
1174
- atoms,
1175
- headers,
1176
- handlers: {
1177
- onCellToggle: (coord, mode) => callbacksRef.current.onCellToggle?.(coord, mode),
1178
- onSelectionCommit: (range) => callbacksRef.current.onSelectionCommit?.(range),
1179
- onDrawUpdate: (start, end) => callbacksRef.current.onDrawUpdate?.(start, end),
1180
- onDrawCommit: (start, end) => callbacksRef.current.onDrawCommit?.(start, end)
1181
- }
1182
- });
1183
- const detachWheel = attachWheelHandlers(element, {
1184
- registry,
1185
- atoms,
1186
- headers
1187
- });
1188
- return () => {
1189
- detachPointer();
1190
- detachWheel();
1191
- };
1192
- }, [
1193
- registry,
1194
- atoms,
1195
- headers
1196
- ]);
1197
- return /* @__PURE__ */ React5.createElement("div", {
1198
- ref: containerRef,
1199
- className: mx5("relative w-full h-full overflow-hidden bg-baseSurface", classNames)
1200
- }, /* @__PURE__ */ React5.createElement("canvas", {
1201
- ref: staticCanvasRef,
1202
- className: "absolute inset-0 pointer-events-none",
1203
- style: {
1204
- top: -1,
1205
- left: -1
1206
- }
1207
- }), /* @__PURE__ */ React5.createElement("canvas", {
1208
- ref: overlayCanvasRef,
1209
- className: "absolute inset-0 pointer-events-none",
1210
- style: {
1211
- top: -1,
1212
- left: -1
1213
- }
1214
- }), /* @__PURE__ */ React5.createElement("div", {
1215
- ref: overlayInputRef,
1216
- className: "absolute inset-0 touch-none",
1217
- style: {
1218
- paddingLeft: headers.left,
1219
- paddingTop: headers.top
1220
- }
1221
- }), headers.top > 0 && /* @__PURE__ */ React5.createElement(Ruler, {
1222
- viewport: viewportState,
1223
- headers,
1224
- width
1225
- }), headers.left > 0 && /* @__PURE__ */ React5.createElement(TrackHeader, {
1226
- viewport: viewportState,
1227
- headers,
1228
- rows,
1229
- height
1230
- }), headers.top > 0 && headers.left > 0 && /* @__PURE__ */ React5.createElement("div", {
1231
- className: "absolute top-0 left-0 border-b border-r border-neutral-200 dark:border-neutral-700 bg-baseSurface",
1232
- style: {
1233
- width: headers.left,
1234
- height: headers.top
1235
- }
1236
- }));
1237
- };
1238
-
1239
- // src/components/CellGrid/state/atoms.ts
1240
- import { Atom } from "@effect-atom/atom-react";
1241
- var defaultViewport = (options = {}) => ({
1242
- scrollX: 0,
1243
- scrollY: 0,
1244
- baseCellWidth: options.cellWidth ?? 24,
1245
- cellHeight: options.cellHeight ?? 24,
1246
- zoomX: 1
1247
- });
1248
- var createCellGridAtoms = (options = {}) => ({
1249
- cells: Atom.keepAlive(Atom.make(/* @__PURE__ */ new Map())),
1250
- viewport: Atom.keepAlive(Atom.make(defaultViewport(options))),
1251
- selection: Atom.keepAlive(Atom.make({
1252
- range: null
1253
- })),
1254
- playhead: Atom.keepAlive(Atom.make(null)),
1255
- tool: Atom.keepAlive(Atom.make("toggle"))
1256
- });
1257
-
1258
- // src/components/FPS.tsx
1259
- import React6, { useEffect as useEffect5, useReducer, useRef as useRef3 } from "react";
1260
- import { mx as mx6 } from "@dxos/ui-theme";
1261
- var SEC = 1e3;
1262
- var FPS = ({ classNames, width = 60, height = 30, bar = "bg-cyan-500" }) => {
1263
- const [{ fps, max, len }, dispatch] = useReducer((state) => {
1264
- const currentTime = Date.now();
1265
- if (currentTime > state.prevTime + SEC) {
1266
- const nextFPS = [
1267
- ...new Array(Math.floor((currentTime - state.prevTime - SEC) / SEC)).fill(0),
1268
- Math.max(1, Math.round(state.frames * SEC / (currentTime - state.prevTime)))
1269
- ];
1270
- return {
1271
- max: Math.max(state.max, ...nextFPS),
1272
- len: Math.min(state.len + nextFPS.length, width),
1273
- fps: [
1274
- ...state.fps,
1275
- ...nextFPS
1276
- ].slice(-width),
1277
- frames: 1,
1278
- prevTime: currentTime
1279
- };
1280
- } else {
1281
- return {
1282
- ...state,
1283
- frames: state.frames + 1
1284
- };
1285
- }
1286
- }, {
1287
- max: 0,
1288
- len: 0,
1289
- fps: [],
1290
- frames: 0,
1291
- prevTime: Date.now()
1292
- });
1293
- const requestRef = useRef3(null);
1294
- const tick = () => {
1295
- dispatch();
1296
- requestRef.current = requestAnimationFrame(tick);
1297
- };
1298
- useEffect5(() => {
1299
- requestRef.current = requestAnimationFrame(tick);
1300
- return () => {
1301
- if (requestRef.current) {
1302
- cancelAnimationFrame(requestRef.current);
1303
- }
1304
- };
1305
- }, []);
1306
- return /* @__PURE__ */ React6.createElement("div", {
1307
- style: {
1308
- width: width + 6
1309
- },
1310
- className: mx6("relative flex flex-col p-0.5", "bg-base-surface text-xs text-subdued font-thin pointer-events-none border border-separator", classNames)
1311
- }, /* @__PURE__ */ React6.createElement("div", null, fps[len - 1], " FPS"), /* @__PURE__ */ React6.createElement("div", {
1312
- className: "w-full relative",
1313
- style: {
1314
- height
1315
- }
1316
- }, fps.map((frame, i) => /* @__PURE__ */ React6.createElement("div", {
1317
- key: `fps-${i}`,
1318
- className: bar,
1319
- style: {
1320
- position: "absolute",
1321
- bottom: 0,
1322
- right: `${len - 1 - i}px`,
1323
- height: `${height * frame / max}px`,
1324
- width: 1
1325
- }
1326
- }))));
1327
- };
1328
-
1329
- // src/components/Grid/Grid.tsx
1330
- import React7, { forwardRef as forwardRef2, useId, useMemo as useMemo4 } from "react";
1331
- import { useForwardedRef } from "@dxos/react-ui";
1332
- import { mx as mx7 } from "@dxos/ui-theme";
1333
- var gridRatios = [
1334
- 1 / 4,
1335
- 1,
1336
- 4,
1337
- 16
1338
- ];
1339
- var defaultGridSize = 16;
1340
- var defaultOffset = {
1341
- x: 0,
1342
- y: 0
1343
- };
1344
- var createId = (parent, grid) => `dx-canvas-grid-${parent}-${grid}`;
1345
- var Grid = (props) => {
1346
- const { scale, offset } = useCanvasContext();
1347
- return /* @__PURE__ */ React7.createElement(GridComponent, {
1348
- ...props,
1349
- scale,
1350
- offset
1351
- });
1352
- };
1353
- var GridComponent = /* @__PURE__ */ forwardRef2(({ size: gridSize = defaultGridSize, scale = 1, offset = defaultOffset, showAxes = true, classNames }, forwardedRef) => {
1354
- const svgRef = useForwardedRef(forwardedRef);
1355
- const { width = 0, height = 0 } = svgRef.current?.getBoundingClientRect() ?? {};
1356
- const instanceId = useId();
1357
- const grids = useMemo4(() => gridRatios.map((ratio) => ({
1358
- id: ratio,
1359
- size: ratio * gridSize * scale
1360
- })).filter(({ size }) => size >= gridSize && size <= 128), [
1361
- gridSize,
1362
- scale
1363
- ]);
1364
- return /* @__PURE__ */ React7.createElement("svg", {
1365
- ...testId("dx-canvas-grid"),
1366
- ref: svgRef,
1367
- // `dx-fullscreen` (absolute inset-0) does not stretch a replaced <svg> element — without an explicit
1368
- // size it falls back to the intrinsic 300x150, clipping the 100%-sized grid rects. Force full size.
1369
- className: mx7("dx-fullscreen w-full h-full pointer-events-none touch-none select-none", "stroke-neutral-500", classNames)
1370
- }, /* @__PURE__ */ React7.createElement("defs", null, grids.map(({ id, size }) => /* @__PURE__ */ React7.createElement(GridPattern, {
1371
- key: id,
1372
- id: createId(instanceId, id),
1373
- offset,
1374
- size
1375
- }))), showAxes && /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement("line", {
1376
- x1: 0,
1377
- y1: offset.y,
1378
- x2: width,
1379
- y2: offset.y,
1380
- className: "stroke-neutral-500 opacity-40"
1381
- }), /* @__PURE__ */ React7.createElement("line", {
1382
- x1: offset.x,
1383
- y1: 0,
1384
- x2: offset.x,
1385
- y2: height,
1386
- className: "stroke-neutral-500 opacity-40"
1387
- })), /* @__PURE__ */ React7.createElement("g", null, grids.map(({ id }, i) => /* @__PURE__ */ React7.createElement("rect", {
1388
- key: id,
1389
- opacity: 0.1 + i * 0.05,
1390
- fill: `url(#${createId(instanceId, id)})`,
1391
- width: "100%",
1392
- height: "100%"
1393
- }))));
1394
- });
1395
-
1396
- // src/types.ts
1397
- import * as Schema from "effect/Schema";
1398
- var Point = Schema.Struct({
1399
- x: Schema.Number,
1400
- y: Schema.Number
1401
- });
1402
- var Dimension = Schema.Struct({
1403
- width: Schema.Number,
1404
- height: Schema.Number
1405
- });
1406
- var Rect = Schema.extend(Point, Dimension);
1407
- export {
1408
- Arrow,
1409
- Canvas,
1410
- CanvasContext,
1411
- CellGrid,
1412
- DATA_TEST_ID,
1413
- Dimension,
1414
- FPS,
1415
- Grid,
1416
- GridComponent,
1417
- GridPattern,
1418
- Marker,
1419
- Markers,
1420
- Point,
1421
- ProjectionMapper,
1422
- Rect,
1423
- Ruler,
1424
- TrackHeader,
1425
- attachPointerHandlers,
1426
- attachWheelHandlers,
1427
- cellKey,
1428
- cellWidth,
1429
- createCellGridAtoms,
1430
- createPath,
1431
- defaultOrigin,
1432
- defaultViewport,
1433
- drawCells,
1434
- drawOverlay,
1435
- getRelativePoint,
1436
- getZoomTransform,
1437
- hitTestCell,
1438
- inspectElement,
1439
- screenToWorld,
1440
- testId,
1441
- toggleCell,
1442
- useCanvasContext,
1443
- useDrag,
1444
- useWheel,
1445
- visibleCellRange,
1446
- visibleCells,
1447
- worldToScreen,
1448
- zoomInPlace,
1449
- zoomTo
1450
- };
1451
- //# sourceMappingURL=index.mjs.map