@dxos/react-ui-canvas 0.7.5-main.9d2a38b → 0.7.5-main.b19bfc8

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 (45) hide show
  1. package/README.md +1 -3
  2. package/dist/lib/browser/index.mjs +114 -34
  3. package/dist/lib/browser/index.mjs.map +4 -4
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/node/index.cjs +115 -33
  6. package/dist/lib/node/index.cjs.map +4 -4
  7. package/dist/lib/node/meta.json +1 -1
  8. package/dist/lib/node-esm/index.mjs +114 -34
  9. package/dist/lib/node-esm/index.mjs.map +4 -4
  10. package/dist/lib/node-esm/meta.json +1 -1
  11. package/dist/types/src/components/Canvas/Canvas.d.ts +2 -2
  12. package/dist/types/src/components/Canvas/Canvas.d.ts.map +1 -1
  13. package/dist/types/src/components/Canvas/Canvas.stories.d.ts +1 -0
  14. package/dist/types/src/components/Canvas/Canvas.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/FPS.d.ts +1 -2
  16. package/dist/types/src/components/FPS.d.ts.map +1 -1
  17. package/dist/types/src/components/Grid/Grid.d.ts +7 -6
  18. package/dist/types/src/components/Grid/Grid.d.ts.map +1 -1
  19. package/dist/types/src/components/Grid/Grid.stories.d.ts.map +1 -1
  20. package/dist/types/src/hooks/index.d.ts +1 -1
  21. package/dist/types/src/hooks/index.d.ts.map +1 -1
  22. package/dist/types/src/hooks/projection.d.ts +2 -2
  23. package/dist/types/src/hooks/projection.d.ts.map +1 -1
  24. package/dist/types/src/hooks/{useProjection.d.ts → useCanvasContext.d.ts} +3 -2
  25. package/dist/types/src/hooks/useCanvasContext.d.ts.map +1 -0
  26. package/dist/types/src/hooks/useWheel.d.ts +4 -3
  27. package/dist/types/src/hooks/useWheel.d.ts.map +1 -1
  28. package/dist/types/src/util/svg.d.ts +5 -5
  29. package/dist/types/src/util/svg.d.ts.map +1 -1
  30. package/dist/types/src/util/util.d.ts +1 -0
  31. package/dist/types/src/util/util.d.ts.map +1 -1
  32. package/package.json +15 -14
  33. package/src/components/Canvas/Canvas.stories.tsx +51 -24
  34. package/src/components/Canvas/Canvas.tsx +12 -6
  35. package/src/components/FPS.tsx +1 -1
  36. package/src/components/Grid/Grid.stories.tsx +4 -6
  37. package/src/components/Grid/Grid.tsx +34 -8
  38. package/src/hooks/index.ts +1 -1
  39. package/src/hooks/projection.tsx +16 -9
  40. package/src/hooks/{useProjection.tsx → useCanvasContext.tsx} +2 -1
  41. package/src/hooks/useWheel.tsx +60 -8
  42. package/src/util/svg.stories.tsx +1 -1
  43. package/src/util/svg.tsx +2 -2
  44. package/src/util/util.ts +11 -0
  45. package/dist/types/src/hooks/useProjection.d.ts.map +0 -1
@@ -3,36 +3,54 @@
3
3
  //
4
4
 
5
5
  import { bindAll } from 'bind-event-listener';
6
- import { type Dispatch, type SetStateAction, useEffect } from 'react';
6
+ import { useEffect } from 'react';
7
7
 
8
- import { getZoomTransform, type ProjectionState } from './projection';
8
+ import { getZoomTransform } from './projection';
9
+ import { useCanvasContext } from './useCanvasContext';
9
10
  import { getRelativePoint } from '../util';
10
11
 
12
+ export type WheelOptions = {
13
+ zoom?: boolean;
14
+ };
15
+
16
+ const defaultOptions: WheelOptions = {
17
+ zoom: true,
18
+ };
19
+
11
20
  /**
12
21
  * Handle wheel events to update the transform state (zoom and offset).
13
22
  */
14
- export const useWheel = (el: HTMLDivElement | null, setProjection: Dispatch<SetStateAction<ProjectionState>>) => {
23
+ export const useWheel = (options: WheelOptions = defaultOptions) => {
24
+ const { root, setProjection } = useCanvasContext();
15
25
  useEffect(() => {
16
- if (!el) {
26
+ if (!root) {
17
27
  return;
18
28
  }
19
29
 
20
- return bindAll(el, [
30
+ return bindAll(root, [
21
31
  {
22
32
  type: 'wheel',
23
33
  options: { capture: true, passive: false },
24
34
  listener: (ev: WheelEvent) => {
35
+ const zooming = isWheelZooming(ev);
36
+ if (!hasFocus(root) && !zooming) {
37
+ return;
38
+ }
39
+
25
40
  ev.preventDefault();
41
+ if (zooming && !options.zoom) {
42
+ return;
43
+ }
26
44
 
27
45
  // Zoom or pan.
28
46
  if (ev.ctrlKey) {
29
- if (!el) {
47
+ if (!root) {
30
48
  return;
31
49
  }
32
50
 
33
51
  // Keep centered while zooming.
34
52
  setProjection(({ scale, offset }) => {
35
- const pos = getRelativePoint(el, ev);
53
+ const pos = getRelativePoint(root, ev);
36
54
  const scaleSensitivity = 0.01;
37
55
  const newScale = scale * Math.exp(-ev.deltaY * scaleSensitivity);
38
56
  return getZoomTransform({ scale, offset, newScale, pos });
@@ -51,5 +69,39 @@ export const useWheel = (el: HTMLDivElement | null, setProjection: Dispatch<SetS
51
69
  },
52
70
  },
53
71
  ]);
54
- }, [el, setProjection]);
72
+ }, [root]);
73
+ };
74
+
75
+ const isWheelZooming = (ev: WheelEvent): boolean => {
76
+ // Check for ctrl/cmd key + wheel action.
77
+ if (ev.ctrlKey || ev.metaKey) {
78
+ // Some browsers use deltaY, others deltaZ for zoom.
79
+ return Math.abs(ev.deltaY) > 0 || Math.abs(ev.deltaZ) > 0;
80
+ }
81
+
82
+ return false;
83
+ };
84
+
85
+ const hasFocus = (element: HTMLElement): boolean => {
86
+ const activeElement = document.activeElement;
87
+ if (!activeElement) {
88
+ return false;
89
+ }
90
+
91
+ // Handle shadow DOM.
92
+ let shadowActive = activeElement;
93
+ while (shadowActive?.shadowRoot?.activeElement) {
94
+ shadowActive = shadowActive.shadowRoot.activeElement;
95
+ }
96
+
97
+ // Check if element or any parent has focus.
98
+ let current: HTMLElement | null = element;
99
+ while (current) {
100
+ if (current === activeElement || current === shadowActive) {
101
+ return true;
102
+ }
103
+ current = current.parentElement;
104
+ }
105
+
106
+ return false;
55
107
  };
@@ -13,7 +13,7 @@ import { Arrow, createPath } from './svg';
13
13
  import { testId } from './util';
14
14
 
15
15
  const Render = () => (
16
- <svg className='border border-neutral-500 w-[400px] h-[400px]'>
16
+ <svg className='border border-neutral-500 w-[30rem] h-[400px]'>
17
17
  <defs>
18
18
  <Arrow id='arrow-start' classNames='fill-none stroke-red-500' dir='start' />
19
19
  <Arrow id='arrow-end' classNames='fill-none stroke-red-500' dir='end' />
package/src/util/svg.tsx CHANGED
@@ -28,8 +28,8 @@ export const Markers = ({ id = 'dx-marker', classNames }: ThemedClassName<{ id?:
28
28
  <Arrow id={`${id}-arrow-end`} dir='end' classNames={classNames} />
29
29
  <Arrow id={`${id}-triangle-start`} dir='start' closed classNames={classNames} />
30
30
  <Arrow id={`${id}-triangle-end`} dir='end' closed classNames={classNames} />
31
- <Marker id={`${id}-circle`} pos={{ x: 6, y: 6 }} size={{ width: 12, height: 12 }}>
32
- <circle cx={6} cy={6} r={5} stroke={'context-stroke'} className={mx(classNames)} />
31
+ <Marker id={`${id}-circle`} pos={{ x: 8, y: 8 }} size={{ width: 16, height: 16 }}>
32
+ <circle cx={8} cy={8} r={5} stroke={'context-stroke'} className={mx(classNames)} />
33
33
  </Marker>
34
34
  </>
35
35
  );
package/src/util/util.ts CHANGED
@@ -36,4 +36,15 @@ export const testId = <ID = string>(id: ID, inspect = false) => {
36
36
  return { [DATA_TEST_ID]: id };
37
37
  };
38
38
 
39
+ export const inspectElement = (el: Element) => {
40
+ (window as any).INSPECT = () => {
41
+ (window as any).inspect(el);
42
+ (window as any).element = el;
43
+ // eslint-disable-next-line no-console
44
+ console.log('Open storybook in expanded window;\nthen run INSPECT()');
45
+ // eslint-disable-next-line no-console
46
+ console.log(el);
47
+ };
48
+ };
49
+
39
50
  export const DATA_TEST_ID = 'data-test-id';
@@ -1 +0,0 @@
1
- {"version":3,"file":"useProjection.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useProjection.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,cAAc,EAA6B,MAAM,OAAO,CAAC;AAI1G,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG;IAC5C,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;IACvB,aAAa,EAAE,QAAQ,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC;CAC1D,CAAC;AAQF,eAAO,MAAM,aAAa,QAAO,aAEhC,CAAC"}