@dxos/react-ui-canvas 0.7.5-main.ff8607b → 0.7.5-staging.b81e783
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.
- package/README.md +1 -3
- package/dist/lib/browser/index.mjs +113 -33
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +114 -32
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +113 -33
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Canvas/Canvas.d.ts +1 -1
- package/dist/types/src/components/Canvas/Canvas.d.ts.map +1 -1
- package/dist/types/src/components/Canvas/Canvas.stories.d.ts +1 -0
- package/dist/types/src/components/Canvas/Canvas.stories.d.ts.map +1 -1
- package/dist/types/src/components/FPS.d.ts +1 -2
- package/dist/types/src/components/FPS.d.ts.map +1 -1
- package/dist/types/src/components/Grid/Grid.d.ts +6 -5
- package/dist/types/src/components/Grid/Grid.d.ts.map +1 -1
- package/dist/types/src/components/Grid/Grid.stories.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +1 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/projection.d.ts +2 -2
- package/dist/types/src/hooks/projection.d.ts.map +1 -1
- package/dist/types/src/hooks/{useProjection.d.ts → useCanvasContext.d.ts} +3 -2
- package/dist/types/src/hooks/useCanvasContext.d.ts.map +1 -0
- package/dist/types/src/hooks/useWheel.d.ts +4 -3
- package/dist/types/src/hooks/useWheel.d.ts.map +1 -1
- package/dist/types/src/util/svg.d.ts +5 -5
- package/dist/types/src/util/svg.d.ts.map +1 -1
- package/dist/types/src/util/util.d.ts +1 -0
- package/dist/types/src/util/util.d.ts.map +1 -1
- package/package.json +14 -13
- package/src/components/Canvas/Canvas.stories.tsx +51 -24
- package/src/components/Canvas/Canvas.tsx +12 -6
- package/src/components/Grid/Grid.stories.tsx +4 -6
- package/src/components/Grid/Grid.tsx +34 -8
- package/src/hooks/index.ts +1 -1
- package/src/hooks/projection.tsx +16 -9
- package/src/hooks/{useProjection.tsx → useCanvasContext.tsx} +2 -1
- package/src/hooks/useWheel.tsx +60 -8
- package/src/util/svg.stories.tsx +1 -1
- package/src/util/svg.tsx +2 -2
- package/src/util/util.ts +11 -0
- package/dist/types/src/hooks/useProjection.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Grid.d.ts","sourceRoot":"","sources":["../../../../../src/components/Grid/Grid.tsx"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"Grid.d.ts","sourceRoot":"","sources":["../../../../../src/components/Grid/Grid.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAqC,MAAM,OAAO,CAAC;AAE1D,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAIvE,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAUzC,MAAM,MAAM,SAAS,GAAG,eAAe,CAAC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC,CAAC;AAEH,eAAO,MAAM,aAAa;WANjB,MAAM;YACL,MAAM;aACL,KAAK;eACH,OAAO;;;uCAwDnB,CAAC;AAGF,eAAO,MAAM,IAAI,UAAW,SAAS,4CAGpC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Grid.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/Grid/Grid.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAKvD,OAAO,
|
|
1
|
+
{"version":3,"file":"Grid.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/Grid/Grid.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAKvD,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAcvD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS,CAKzB,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;AAEjC,eAAO,MAAM,OAAO,EAAE,KAIrB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/hooks/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC;AAC7B,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/hooks/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Point, type Dimension } from '../types';
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const defaultOrigin: Point;
|
|
3
3
|
export type ProjectionState = {
|
|
4
4
|
scale: number;
|
|
5
5
|
offset: Point;
|
|
@@ -54,5 +54,5 @@ export declare const zoomInPlace: (setTransform: (state: ProjectionState) => voi
|
|
|
54
54
|
/**
|
|
55
55
|
* Zoom to new scale and position.
|
|
56
56
|
*/
|
|
57
|
-
export declare const zoomTo: (setTransform: (state: ProjectionState) => void, current: ProjectionState, next: ProjectionState, delay?: number) => void;
|
|
57
|
+
export declare const zoomTo: (setTransform: (state: ProjectionState) => void, current: ProjectionState, next: ProjectionState, delay?: number, cb?: () => void) => void;
|
|
58
58
|
//# sourceMappingURL=projection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../../../../src/hooks/projection.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAEtD,eAAO,MAAM,aAAa,EAAE,KAAsB,CAAC;AAGnD,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,CAAC;CACf,CAAC;
|
|
1
|
+
{"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../../../../src/hooks/projection.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAEtD,eAAO,MAAM,aAAa,EAAE,KAAsB,CAAC;AAGnD,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,CAAC;CACf,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,MAAM,IAAI,SAAS,CAAC;IACxB,IAAI,KAAK,IAAI,MAAM,CAAC;IACpB,IAAI,MAAM,IAAI,KAAK,CAAC;IAEpB;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC;IAEnC;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC;CACnC;AAED,qBAAa,gBAAiB,YAAW,UAAU;IACjD,OAAO,CAAC,OAAO,CAAsC;IACrD,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,QAAQ,CAAsB;gBAE1B,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK;IAM9D,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK;IAetD,IAAI,MAAM;;;MAET;IAED,IAAI,KAAK,WAER;IAED,IAAI,MAAM;;;MAET;IAED,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE;IAIlC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE;CAGlC;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,sCAK1B,eAAe,GAAG;IAAE,GAAG,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,KAAG,eAQvD,CAAC;AAEF;;GAEG;AAEH,eAAO,MAAM,WAAW,iBACR,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,OACzC,KAAK,UACF,KAAK,WACJ,MAAM,QACT,MAAM,yBAWb,CAAC;AAIF;;GAEG;AAEH,eAAO,MAAM,MAAM,iBACH,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,WACrC,eAAe,QAClB,eAAe,0CAatB,CAAC"}
|
|
@@ -2,11 +2,12 @@ import { type CSSProperties, type Dispatch, type SetStateAction } from 'react';
|
|
|
2
2
|
import { type Projection, type ProjectionState } from './projection';
|
|
3
3
|
export type CanvasContext = ProjectionState & {
|
|
4
4
|
root: HTMLDivElement;
|
|
5
|
+
ready: boolean;
|
|
5
6
|
width: number;
|
|
6
7
|
height: number;
|
|
7
8
|
styles: CSSProperties;
|
|
8
9
|
projection: Projection;
|
|
9
10
|
setProjection: Dispatch<SetStateAction<ProjectionState>>;
|
|
10
11
|
};
|
|
11
|
-
export declare const
|
|
12
|
-
//# sourceMappingURL=
|
|
12
|
+
export declare const useCanvasContext: () => CanvasContext;
|
|
13
|
+
//# sourceMappingURL=useCanvasContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCanvasContext.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useCanvasContext.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,OAAO,CAAC;IACf,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,gBAAgB,QAAO,aAEnC,CAAC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export type WheelOptions = {
|
|
2
|
+
zoom?: boolean;
|
|
3
|
+
};
|
|
3
4
|
/**
|
|
4
5
|
* Handle wheel events to update the transform state (zoom and offset).
|
|
5
6
|
*/
|
|
6
|
-
export declare const useWheel: (
|
|
7
|
+
export declare const useWheel: (options?: WheelOptions) => void;
|
|
7
8
|
//# sourceMappingURL=useWheel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWheel.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useWheel.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useWheel.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useWheel.tsx"],"names":[],"mappings":"AAWA,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAMF;;GAEG;AACH,eAAO,MAAM,QAAQ,aAAa,YAAY,SAkD7C,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type PropsWithChildren, type SVGProps } from 'react';
|
|
2
2
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
3
3
|
import { type Dimension, type Point } from '../types';
|
|
4
4
|
export declare const createPath: (points: Point[], join?: boolean) => string;
|
|
@@ -8,7 +8,7 @@ export declare const createPath: (points: Point[], join?: boolean) => string;
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const Markers: ({ id, classNames }: ThemedClassName<{
|
|
10
10
|
id?: string;
|
|
11
|
-
}>) =>
|
|
11
|
+
}>) => import("react/jsx-runtime").JSX.Element;
|
|
12
12
|
export type MarkerProps = SVGProps<SVGMarkerElement> & PropsWithChildren<ThemedClassName<{
|
|
13
13
|
id: string;
|
|
14
14
|
pos: Point;
|
|
@@ -18,16 +18,16 @@ export type MarkerProps = SVGProps<SVGMarkerElement> & PropsWithChildren<ThemedC
|
|
|
18
18
|
/**
|
|
19
19
|
* https://www.w3.org/TR/SVG2/painting.html#Markers
|
|
20
20
|
*/
|
|
21
|
-
export declare const Marker: ({ id, className, children, pos: { x: refX, y: refY }, size: { width: markerWidth, height: markerHeight }, fill, ...rest }: MarkerProps) =>
|
|
21
|
+
export declare const Marker: ({ id, className, children, pos: { x: refX, y: refY }, size: { width: markerWidth, height: markerHeight }, fill, ...rest }: MarkerProps) => import("react/jsx-runtime").JSX.Element;
|
|
22
22
|
export declare const Arrow: ({ classNames, id, size, dir, closed, }: ThemedClassName<{
|
|
23
23
|
id: string;
|
|
24
24
|
size?: number;
|
|
25
25
|
dir?: "start" | "end";
|
|
26
26
|
closed?: boolean;
|
|
27
|
-
}>) =>
|
|
27
|
+
}>) => import("react/jsx-runtime").JSX.Element;
|
|
28
28
|
export declare const GridPattern: ({ classNames, id, size, offset, }: ThemedClassName<{
|
|
29
29
|
id: string;
|
|
30
30
|
size: number;
|
|
31
31
|
offset: Point;
|
|
32
|
-
}>) =>
|
|
32
|
+
}>) => import("react/jsx-runtime").JSX.Element;
|
|
33
33
|
//# sourceMappingURL=svg.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"svg.d.ts","sourceRoot":"","sources":["../../../../src/util/svg.tsx"],"names":[],"mappings":"AAIA,
|
|
1
|
+
{"version":3,"file":"svg.d.ts","sourceRoot":"","sources":["../../../../src/util/svg.tsx"],"names":[],"mappings":"AAIA,OAAc,EAAE,KAAK,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAC;AAErE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGtD,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,KAAK,EAAE,MAAM,UAAU,CAAC;AAMtD,eAAO,MAAM,UAAU,WAAY,KAAK,EAAE,2BAEzC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,OAAO,uBAAsC,eAAe,CAAC;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,4CAYzF,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,gBAAgB,CAAC,GAClD,iBAAiB,CACf,eAAe,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,KAAK,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC,CACH,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,MAAM,8HAQhB,WAAW,4CAgBb,CAAC;AAEF,eAAO,MAAM,KAAK,2CAMf,eAAe,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,4CA0BzF,CAAC;AAEF,eAAO,MAAM,WAAW,sCAKrB,eAAe,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,KAAK,CAAA;CAAE,CAAC,4CAe9D,CAAC"}
|
|
@@ -12,5 +12,6 @@ export declare const getRelativePoint: (el: HTMLElement, ev: MouseEvent) => {
|
|
|
12
12
|
export declare const testId: <ID = string>(id: ID, inspect?: boolean) => {
|
|
13
13
|
"data-test-id": ID;
|
|
14
14
|
};
|
|
15
|
+
export declare const inspectElement: (el: Element) => void;
|
|
15
16
|
export declare const DATA_TEST_ID = "data-test-id";
|
|
16
17
|
//# sourceMappingURL=util.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../../src/util/util.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,eAAO,MAAM,gBAAgB,OAAQ,WAAW,MAAM,UAAU;;;CAG/D,CAAC;AAEF;;GAEG;AAEH,eAAO,MAAM,MAAM,GAAI,EAAE,eAAe,EAAE;;CAiBzC,CAAC;AAEF,eAAO,MAAM,YAAY,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../../src/util/util.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,eAAO,MAAM,gBAAgB,OAAQ,WAAW,MAAM,UAAU;;;CAG/D,CAAC;AAEF;;GAEG;AAEH,eAAO,MAAM,MAAM,GAAI,EAAE,eAAe,EAAE;;CAiBzC,CAAC;AAEF,eAAO,MAAM,cAAc,OAAQ,OAAO,SASzC,CAAC;AAEF,eAAO,MAAM,YAAY,iBAAiB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-canvas",
|
|
3
|
-
"version": "0.7.5-
|
|
3
|
+
"version": "0.7.5-staging.b81e783",
|
|
4
4
|
"description": "A canvas component.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"author": "DXOS.org",
|
|
9
|
+
"type": "module",
|
|
9
10
|
"exports": {
|
|
10
11
|
".": {
|
|
11
12
|
"types": "./dist/types/src/index.d.ts",
|
|
@@ -26,14 +27,14 @@
|
|
|
26
27
|
"@radix-ui/react-context": "1.1.1",
|
|
27
28
|
"bind-event-listener": "^3.0.0",
|
|
28
29
|
"d3": "^7.9.0",
|
|
29
|
-
"effect": "^3.12.
|
|
30
|
+
"effect": "^3.12.3",
|
|
30
31
|
"react-resize-detector": "^11.0.1",
|
|
31
32
|
"transformation-matrix": "^2.16.1",
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/
|
|
34
|
-
"@dxos/
|
|
35
|
-
"@dxos/log": "0.7.5-
|
|
36
|
-
"@dxos/util": "0.7.5-
|
|
33
|
+
"@dxos/invariant": "0.7.5-staging.b81e783",
|
|
34
|
+
"@dxos/debug": "0.7.5-staging.b81e783",
|
|
35
|
+
"@dxos/effect": "0.7.5-staging.b81e783",
|
|
36
|
+
"@dxos/log": "0.7.5-staging.b81e783",
|
|
37
|
+
"@dxos/util": "0.7.5-staging.b81e783"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
40
|
"@effect/schema": "^0.75.5",
|
|
@@ -43,17 +44,17 @@
|
|
|
43
44
|
"react": "~18.2.0",
|
|
44
45
|
"react-dom": "~18.2.0",
|
|
45
46
|
"vite": "5.4.7",
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/react-ui
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/storybook-utils": "0.7.5-
|
|
47
|
+
"@dxos/random": "0.7.5-staging.b81e783",
|
|
48
|
+
"@dxos/react-ui": "0.7.5-staging.b81e783",
|
|
49
|
+
"@dxos/react-ui-theme": "0.7.5-staging.b81e783",
|
|
50
|
+
"@dxos/storybook-utils": "0.7.5-staging.b81e783"
|
|
50
51
|
},
|
|
51
52
|
"peerDependencies": {
|
|
52
53
|
"@effect/schema": "^0.75.5",
|
|
53
54
|
"react": "~18.2.0",
|
|
54
55
|
"react-dom": "~18.2.0",
|
|
55
|
-
"@dxos/react-ui": "0.7.5-
|
|
56
|
-
"@dxos/react-ui-theme": "0.7.5-
|
|
56
|
+
"@dxos/react-ui": "0.7.5-staging.b81e783",
|
|
57
|
+
"@dxos/react-ui-theme": "0.7.5-staging.b81e783"
|
|
57
58
|
},
|
|
58
59
|
"publishConfig": {
|
|
59
60
|
"access": "public"
|
|
@@ -10,38 +10,62 @@ import React from 'react';
|
|
|
10
10
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
11
11
|
|
|
12
12
|
import { Canvas } from './Canvas';
|
|
13
|
-
import {
|
|
13
|
+
import { useCanvasContext, useWheel } from '../../hooks';
|
|
14
14
|
import { type Point } from '../../types';
|
|
15
15
|
import { testId } from '../../util';
|
|
16
16
|
import { Grid, type GridProps } from '../Grid';
|
|
17
17
|
|
|
18
|
+
const size = 128;
|
|
19
|
+
|
|
20
|
+
const points: Point[] = [0, (2 * Math.PI) / 3, (2 * Math.PI * 2) / 3].map((a, i) => ({
|
|
21
|
+
x: Math.round(Math.cos(a - Math.PI / 2) * size * 1.5),
|
|
22
|
+
y: Math.round(Math.sin(a - Math.PI / 2) * size * 1.5),
|
|
23
|
+
}));
|
|
24
|
+
|
|
18
25
|
const Render = (props: GridProps) => {
|
|
19
26
|
return (
|
|
20
27
|
<Canvas>
|
|
21
|
-
<
|
|
28
|
+
<Grid {...props} />
|
|
29
|
+
<Content />
|
|
22
30
|
</Canvas>
|
|
23
31
|
);
|
|
24
32
|
};
|
|
25
33
|
|
|
26
|
-
const
|
|
27
|
-
const { root, scale, offset, styles, setProjection } = useProjection();
|
|
28
|
-
useWheel(root, setProjection);
|
|
29
|
-
|
|
34
|
+
const TwoCanvases = (props: GridProps) => {
|
|
30
35
|
return (
|
|
31
|
-
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
<div className='grid grid-cols-2 gap-2 w-full h-full'>
|
|
37
|
+
<div className='h-full relative'>
|
|
38
|
+
<Canvas>
|
|
39
|
+
<Grid {...props} />
|
|
40
|
+
<Content />
|
|
41
|
+
</Canvas>
|
|
42
|
+
</div>
|
|
43
|
+
<div className='h-full relative'>
|
|
44
|
+
<Canvas>
|
|
45
|
+
<Grid {...props} />
|
|
46
|
+
<Content />
|
|
47
|
+
</Canvas>
|
|
37
48
|
</div>
|
|
38
|
-
|
|
49
|
+
</div>
|
|
39
50
|
);
|
|
40
51
|
};
|
|
41
52
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
const Content = () => {
|
|
54
|
+
useWheel();
|
|
55
|
+
return (
|
|
56
|
+
<div>
|
|
57
|
+
{points.map(({ x, y }, i) => (
|
|
58
|
+
<Item key={i} x={x} y={y} />
|
|
59
|
+
))}
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const Item = (p: Point) => {
|
|
65
|
+
const { projection } = useCanvasContext();
|
|
66
|
+
const r = (projection.scale * size) / 2;
|
|
67
|
+
const [{ x, y }] = projection.toScreen([p]);
|
|
68
|
+
const rect = {
|
|
45
69
|
left: x - size / 2,
|
|
46
70
|
top: y - size / 2,
|
|
47
71
|
width: size,
|
|
@@ -50,14 +74,15 @@ const Item = ({ x, y }: Point) => {
|
|
|
50
74
|
|
|
51
75
|
return (
|
|
52
76
|
<div {...testId('dx-test', true)}>
|
|
53
|
-
<div className='absolute flex
|
|
77
|
+
<div className='absolute flex justify-center items-center' style={rect}>
|
|
54
78
|
<div className='font-mono'>
|
|
55
|
-
({x},{y})
|
|
79
|
+
({p.x},{p.y})
|
|
56
80
|
</div>
|
|
57
81
|
</div>
|
|
82
|
+
|
|
58
83
|
{/* NOTE: Width and height are not important since overflow-visible. */}
|
|
59
|
-
<svg className='absolute overflow-visible'
|
|
60
|
-
<circle cx={
|
|
84
|
+
<svg className='absolute overflow-visible'>
|
|
85
|
+
<circle cx={x} cy={y} r={r} className='stroke-red-500 storke-width-2 fill-none' />
|
|
61
86
|
</svg>
|
|
62
87
|
</div>
|
|
63
88
|
);
|
|
@@ -75,8 +100,10 @@ export default meta;
|
|
|
75
100
|
type Story = StoryObj<GridProps>;
|
|
76
101
|
|
|
77
102
|
export const Default: Story = {
|
|
78
|
-
args: {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
103
|
+
args: { size: 16 },
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const SideBySide: Story = {
|
|
107
|
+
args: { size: 16 },
|
|
108
|
+
render: TwoCanvases,
|
|
82
109
|
};
|
|
@@ -4,20 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
import React, {
|
|
6
6
|
type CSSProperties,
|
|
7
|
+
type HTMLAttributes,
|
|
7
8
|
type PropsWithChildren,
|
|
8
9
|
forwardRef,
|
|
9
10
|
useEffect,
|
|
10
11
|
useImperativeHandle,
|
|
11
12
|
useMemo,
|
|
12
13
|
useState,
|
|
13
|
-
type HTMLAttributes,
|
|
14
14
|
} from 'react';
|
|
15
15
|
import { useResizeDetector } from 'react-resize-detector';
|
|
16
16
|
|
|
17
17
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
18
18
|
import { mx } from '@dxos/react-ui-theme';
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { defaultOrigin, CanvasContext, ProjectionMapper, type ProjectionState } from '../../hooks';
|
|
21
21
|
|
|
22
22
|
export interface CanvasController {
|
|
23
23
|
setProjection(projection: ProjectionState): Promise<void>;
|
|
@@ -30,14 +30,17 @@ export type CanvasProps = ThemedClassName<PropsWithChildren<Partial<ProjectionSt
|
|
|
30
30
|
* Manages CSS projection.
|
|
31
31
|
*/
|
|
32
32
|
export const Canvas = forwardRef<CanvasController, CanvasProps>(
|
|
33
|
-
({ children, classNames, scale: _scale = 1, offset: _offset =
|
|
33
|
+
({ children, classNames, scale: _scale = 1, offset: _offset = defaultOrigin, ...props }, forwardedRef) => {
|
|
34
34
|
// Size.
|
|
35
35
|
const { ref, width = 0, height = 0 } = useResizeDetector();
|
|
36
36
|
|
|
37
|
+
// Ready when initially resized.
|
|
38
|
+
const [ready, setReady] = useState(false);
|
|
39
|
+
|
|
37
40
|
// Projection.
|
|
38
41
|
const [{ scale, offset }, setProjection] = useState<ProjectionState>({ scale: _scale, offset: _offset });
|
|
39
42
|
useEffect(() => {
|
|
40
|
-
if (width && height && offset ===
|
|
43
|
+
if (width && height && offset === defaultOrigin) {
|
|
41
44
|
setProjection({ scale, offset: { x: width / 2, y: height / 2 } });
|
|
42
45
|
}
|
|
43
46
|
}, [width, height, scale, offset]);
|
|
@@ -46,6 +49,9 @@ export const Canvas = forwardRef<CanvasController, CanvasProps>(
|
|
|
46
49
|
const projection = useMemo(() => new ProjectionMapper(), []);
|
|
47
50
|
useEffect(() => {
|
|
48
51
|
projection.update({ width, height }, scale, offset);
|
|
52
|
+
if (offset !== defaultOrigin) {
|
|
53
|
+
setReady(true);
|
|
54
|
+
}
|
|
49
55
|
}, [projection, scale, offset, width, height]);
|
|
50
56
|
|
|
51
57
|
// CSS transforms.
|
|
@@ -72,10 +78,10 @@ export const Canvas = forwardRef<CanvasController, CanvasProps>(
|
|
|
72
78
|
|
|
73
79
|
return (
|
|
74
80
|
<CanvasContext.Provider
|
|
75
|
-
value={{ root: ref.current, width, height, scale, offset, styles, projection, setProjection }}
|
|
81
|
+
value={{ root: ref.current, ready, width, height, scale, offset, styles, projection, setProjection }}
|
|
76
82
|
>
|
|
77
83
|
<div role='none' {...props} className={mx('absolute inset-0 overflow-hidden', classNames)} ref={ref}>
|
|
78
|
-
{children}
|
|
84
|
+
{ready ? children : null}
|
|
79
85
|
</div>
|
|
80
86
|
</CanvasContext.Provider>
|
|
81
87
|
);
|
|
@@ -9,25 +9,23 @@ import React, { useRef, useState } from 'react';
|
|
|
9
9
|
|
|
10
10
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { GridComponent, type GridProps } from './Grid';
|
|
13
13
|
import { type ProjectionState } from '../../hooks';
|
|
14
|
-
import { useWheel } from '../../hooks';
|
|
15
14
|
|
|
16
15
|
const Render = (props: GridProps) => {
|
|
17
16
|
const ref = useRef<HTMLDivElement>(null);
|
|
18
|
-
const [{ scale, offset }
|
|
19
|
-
useWheel(ref.current, setProjection);
|
|
17
|
+
const [{ scale, offset }] = useState<ProjectionState>({ scale: 1, offset: { x: 0, y: 0 } });
|
|
20
18
|
|
|
21
19
|
return (
|
|
22
20
|
<div ref={ref} className='grow'>
|
|
23
|
-
<
|
|
21
|
+
<GridComponent scale={scale} offset={offset} {...props} />
|
|
24
22
|
</div>
|
|
25
23
|
);
|
|
26
24
|
};
|
|
27
25
|
|
|
28
26
|
const meta: Meta<GridProps> = {
|
|
29
27
|
title: 'ui/react-ui-canvas/Grid',
|
|
30
|
-
component:
|
|
28
|
+
component: GridComponent,
|
|
31
29
|
render: Render,
|
|
32
30
|
decorators: [withTheme, withLayout({ fullscreen: true })],
|
|
33
31
|
};
|
|
@@ -2,24 +2,36 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React, { forwardRef, useMemo } from 'react';
|
|
5
|
+
import React, { forwardRef, useMemo, useId } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type ThemedClassName } from '@dxos/react-ui';
|
|
7
|
+
import { useForwardedRef, type ThemedClassName } from '@dxos/react-ui';
|
|
8
8
|
import { mx } from '@dxos/react-ui-theme';
|
|
9
9
|
|
|
10
|
+
import { useCanvasContext } from '../../hooks';
|
|
10
11
|
import { type Point } from '../../types';
|
|
11
12
|
import { GridPattern, testId } from '../../util';
|
|
12
13
|
|
|
13
14
|
const gridRatios = [1 / 4, 1, 4, 16];
|
|
14
15
|
|
|
16
|
+
const defaultGridSize = 16;
|
|
15
17
|
const defaultOffset: Point = { x: 0, y: 0 };
|
|
16
18
|
|
|
17
19
|
const createId = (parent: string, grid: number) => `dx-canvas-grid-${parent}-${grid}`;
|
|
18
20
|
|
|
19
|
-
export type GridProps = ThemedClassName<{
|
|
21
|
+
export type GridProps = ThemedClassName<{
|
|
22
|
+
size?: number;
|
|
23
|
+
scale?: number;
|
|
24
|
+
offset?: Point;
|
|
25
|
+
showAxes?: boolean;
|
|
26
|
+
}>;
|
|
20
27
|
|
|
21
|
-
export const
|
|
22
|
-
(
|
|
28
|
+
export const GridComponent = forwardRef<SVGSVGElement, GridProps>(
|
|
29
|
+
(
|
|
30
|
+
{ size: gridSize = defaultGridSize, scale = 1, offset = defaultOffset, showAxes = true, classNames },
|
|
31
|
+
forwardedRef,
|
|
32
|
+
) => {
|
|
33
|
+
const svgRef = useForwardedRef(forwardedRef);
|
|
34
|
+
const instanceId = useId();
|
|
23
35
|
const grids = useMemo(
|
|
24
36
|
() =>
|
|
25
37
|
gridRatios
|
|
@@ -28,10 +40,12 @@ export const Grid = forwardRef<SVGSVGElement, GridProps>(
|
|
|
28
40
|
[gridSize, scale],
|
|
29
41
|
);
|
|
30
42
|
|
|
43
|
+
const { width = 0, height = 0 } = svgRef.current?.getBoundingClientRect() ?? {};
|
|
44
|
+
|
|
31
45
|
return (
|
|
32
46
|
<svg
|
|
33
47
|
{...testId('dx-canvas-grid')}
|
|
34
|
-
ref={
|
|
48
|
+
ref={svgRef}
|
|
35
49
|
className={mx(
|
|
36
50
|
'absolute inset-0 w-full h-full pointer-events-none touch-none select-none',
|
|
37
51
|
'stroke-neutral-500',
|
|
@@ -41,15 +55,21 @@ export const Grid = forwardRef<SVGSVGElement, GridProps>(
|
|
|
41
55
|
{/* NOTE: The pattern is offset so that the middle of the pattern aligns with the grid. */}
|
|
42
56
|
<defs>
|
|
43
57
|
{grids.map(({ id, size }) => (
|
|
44
|
-
<GridPattern key={id} id={createId(
|
|
58
|
+
<GridPattern key={id} id={createId(instanceId, id)} offset={offset} size={size} />
|
|
45
59
|
))}
|
|
46
60
|
</defs>
|
|
61
|
+
{showAxes && (
|
|
62
|
+
<>
|
|
63
|
+
<line x1={0} y1={offset.y} x2={width} y2={offset.y} className='stroke-neutral-500 opacity-40' />
|
|
64
|
+
<line x1={offset.x} y1={0} x2={offset.x} y2={height} className='stroke-neutral-500 opacity-40' />
|
|
65
|
+
</>
|
|
66
|
+
)}
|
|
47
67
|
<g>
|
|
48
68
|
{grids.map(({ id }, i) => (
|
|
49
69
|
<rect
|
|
50
70
|
key={id}
|
|
51
71
|
opacity={0.1 + i * 0.05}
|
|
52
|
-
fill={`url(#${createId(
|
|
72
|
+
fill={`url(#${createId(instanceId, id)})`}
|
|
53
73
|
width='100%'
|
|
54
74
|
height='100%'
|
|
55
75
|
/>
|
|
@@ -59,3 +79,9 @@ export const Grid = forwardRef<SVGSVGElement, GridProps>(
|
|
|
59
79
|
);
|
|
60
80
|
},
|
|
61
81
|
);
|
|
82
|
+
|
|
83
|
+
// TODO(burdon): Use id of parent canvas.
|
|
84
|
+
export const Grid = (props: GridProps) => {
|
|
85
|
+
const { scale, offset } = useCanvasContext();
|
|
86
|
+
return <GridComponent {...props} scale={scale} offset={offset} />;
|
|
87
|
+
};
|
package/src/hooks/index.ts
CHANGED
package/src/hooks/projection.tsx
CHANGED
|
@@ -7,15 +7,15 @@ import {
|
|
|
7
7
|
type Matrix,
|
|
8
8
|
applyToPoints,
|
|
9
9
|
compose,
|
|
10
|
-
inverse,
|
|
11
|
-
translate as translateMatrix,
|
|
12
10
|
identity,
|
|
11
|
+
inverse,
|
|
13
12
|
scale as scaleMatrix,
|
|
13
|
+
translate as translateMatrix,
|
|
14
14
|
} from 'transformation-matrix';
|
|
15
15
|
|
|
16
16
|
import { type Point, type Dimension } from '../types';
|
|
17
17
|
|
|
18
|
-
export const
|
|
18
|
+
export const defaultOrigin: Point = { x: 0, y: 0 };
|
|
19
19
|
|
|
20
20
|
// TODO(burdon): Rotation also?
|
|
21
21
|
export type ProjectionState = {
|
|
@@ -23,9 +23,6 @@ export type ProjectionState = {
|
|
|
23
23
|
offset: Point;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
// TODO(burdon): Tradeoff between stable ProjectionMapping object that can be used with live values within a closure,
|
|
27
|
-
// vs. a reactive object that can trigger updates?
|
|
28
|
-
|
|
29
26
|
/**
|
|
30
27
|
* Maps between screen and model coordinates.
|
|
31
28
|
*/
|
|
@@ -48,7 +45,7 @@ export interface Projection {
|
|
|
48
45
|
export class ProjectionMapper implements Projection {
|
|
49
46
|
private _bounds: Dimension = { width: 0, height: 0 };
|
|
50
47
|
private _scale: number = 1;
|
|
51
|
-
private _offset: Point =
|
|
48
|
+
private _offset: Point = defaultOrigin;
|
|
52
49
|
private _toScreen: Matrix = identity();
|
|
53
50
|
private _toModel: Matrix = identity();
|
|
54
51
|
|
|
@@ -62,7 +59,13 @@ export class ProjectionMapper implements Projection {
|
|
|
62
59
|
this._bounds = bounds;
|
|
63
60
|
this._scale = scale;
|
|
64
61
|
this._offset = offset;
|
|
65
|
-
this._toScreen = compose(
|
|
62
|
+
this._toScreen = compose(
|
|
63
|
+
// NOTE: Order is important.
|
|
64
|
+
translateMatrix(this._offset.x, this._offset.y),
|
|
65
|
+
scaleMatrix(this._scale),
|
|
66
|
+
// TODO(burdon): Flip.
|
|
67
|
+
// flipX(),
|
|
68
|
+
);
|
|
66
69
|
this._toModel = inverse(this._toScreen);
|
|
67
70
|
return this;
|
|
68
71
|
}
|
|
@@ -128,6 +131,8 @@ export const zoomInPlace = (
|
|
|
128
131
|
});
|
|
129
132
|
};
|
|
130
133
|
|
|
134
|
+
const noop = () => {};
|
|
135
|
+
|
|
131
136
|
/**
|
|
132
137
|
* Zoom to new scale and position.
|
|
133
138
|
*/
|
|
@@ -137,6 +142,7 @@ export const zoomTo = (
|
|
|
137
142
|
current: ProjectionState,
|
|
138
143
|
next: ProjectionState,
|
|
139
144
|
delay = 200,
|
|
145
|
+
cb = noop,
|
|
140
146
|
) => {
|
|
141
147
|
const is = d3.interpolateObject({ scale: current.scale, ...current.offset }, { scale: next.scale, ...next.offset });
|
|
142
148
|
d3.transition()
|
|
@@ -145,5 +151,6 @@ export const zoomTo = (
|
|
|
145
151
|
.tween('zoom', () => (t) => {
|
|
146
152
|
const { scale, x, y } = is(t);
|
|
147
153
|
setTransform({ scale, offset: { x, y } });
|
|
148
|
-
})
|
|
154
|
+
})
|
|
155
|
+
.on('end', cb);
|
|
149
156
|
};
|
|
@@ -10,6 +10,7 @@ import { type Projection, type ProjectionState } from './projection';
|
|
|
10
10
|
|
|
11
11
|
export type CanvasContext = ProjectionState & {
|
|
12
12
|
root: HTMLDivElement;
|
|
13
|
+
ready: boolean;
|
|
13
14
|
width: number;
|
|
14
15
|
height: number;
|
|
15
16
|
styles: CSSProperties;
|
|
@@ -23,6 +24,6 @@ export type CanvasContext = ProjectionState & {
|
|
|
23
24
|
// TODO(burdon): Use radix?
|
|
24
25
|
export const CanvasContext = createContext<CanvasContext | null>(null);
|
|
25
26
|
|
|
26
|
-
export const
|
|
27
|
+
export const useCanvasContext = (): CanvasContext => {
|
|
27
28
|
return useContext(CanvasContext) ?? raise(new Error('Missing CanvasContext'));
|
|
28
29
|
};
|