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