@omer-x/svg-viewport 1.0.0 → 2.0.1
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/dist/index.cjs +58 -62
- package/dist/index.d.cts +7 -15
- package/dist/index.d.ts +7 -15
- package/dist/index.js +54 -58
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -26,7 +26,7 @@ __export(index_exports, {
|
|
|
26
26
|
module.exports = __toCommonJS(index_exports);
|
|
27
27
|
|
|
28
28
|
// src/components/SvgViewport.tsx
|
|
29
|
-
var
|
|
29
|
+
var import_react = require("react");
|
|
30
30
|
|
|
31
31
|
// src/core/matrix.ts
|
|
32
32
|
function transform(matrix) {
|
|
@@ -58,16 +58,6 @@ function getFocusedMatrix(focusPoint, width, height) {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
// src/hooks/polyfill-state.ts
|
|
62
|
-
var import_react = require("react");
|
|
63
|
-
function usePolyfillState(state, dispatch) {
|
|
64
|
-
const [polyfill, setPolyfill] = (0, import_react.useState)(state);
|
|
65
|
-
if (dispatch) {
|
|
66
|
-
return [state, dispatch];
|
|
67
|
-
}
|
|
68
|
-
return [polyfill, setPolyfill];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
61
|
// src/components/SvgViewport.tsx
|
|
72
62
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
73
63
|
var SvgViewport = ({
|
|
@@ -77,55 +67,64 @@ var SvgViewport = ({
|
|
|
77
67
|
zoomable = false,
|
|
78
68
|
minZoom = 0.5,
|
|
79
69
|
maxZoom = 2,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
transformation = null,
|
|
83
|
-
setTransformation,
|
|
70
|
+
transformation: externalTransformation,
|
|
71
|
+
onTransformationChange,
|
|
84
72
|
initialFocusPoint = "center",
|
|
85
73
|
style,
|
|
86
74
|
children,
|
|
87
75
|
...otherProps
|
|
88
76
|
}) => {
|
|
89
|
-
const
|
|
90
|
-
const [
|
|
91
|
-
const
|
|
92
|
-
const [
|
|
77
|
+
const isControlled = externalTransformation !== void 0;
|
|
78
|
+
const [internalTransformation, setInternalTransformation] = (0, import_react.useState)(null);
|
|
79
|
+
const pointer = (0, import_react.useRef)({ x: 0, y: 0 });
|
|
80
|
+
const [grabbing, setGrabbing] = (0, import_react.useState)(false);
|
|
81
|
+
const [isPanning, setIsPanning] = (0, import_react.useState)(false);
|
|
82
|
+
const transformation = isControlled ? externalTransformation : internalTransformation;
|
|
83
|
+
const transformationRef = (0, import_react.useRef)(transformation);
|
|
84
|
+
(0, import_react.useEffect)(() => {
|
|
85
|
+
transformationRef.current = transformation;
|
|
86
|
+
}, [transformation]);
|
|
87
|
+
(0, import_react.useEffect)(() => {
|
|
88
|
+
if (!isControlled && !internalTransformation) {
|
|
89
|
+
setInternalTransformation({
|
|
90
|
+
zoom: 1,
|
|
91
|
+
matrix: getFocusedMatrix(initialFocusPoint, width, height)
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}, []);
|
|
95
|
+
const setTransformation = (0, import_react.useCallback)((value) => {
|
|
96
|
+
if (!isControlled) {
|
|
97
|
+
setInternalTransformation(value);
|
|
98
|
+
}
|
|
99
|
+
onTransformationChange?.(value);
|
|
100
|
+
}, [isControlled, onTransformationChange]);
|
|
93
101
|
const stopGrabbing = () => {
|
|
94
102
|
setGrabbing(false);
|
|
95
103
|
};
|
|
96
|
-
(0, import_react2.useEffect)(() => {
|
|
97
|
-
if (setTransformation) return;
|
|
98
|
-
activeSetTransformation({
|
|
99
|
-
zoom: 1,
|
|
100
|
-
matrix: getFocusedMatrix(initialFocusPoint, width, height)
|
|
101
|
-
});
|
|
102
|
-
}, [setTransformation]);
|
|
103
104
|
const down = (e) => {
|
|
104
|
-
if (e.button
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
y: e.clientY
|
|
108
|
-
};
|
|
109
|
-
setActivePanning(true);
|
|
110
|
-
}
|
|
105
|
+
if (e.button !== 0) return;
|
|
106
|
+
pointer.current = { x: e.clientX, y: e.clientY };
|
|
107
|
+
setIsPanning(true);
|
|
111
108
|
setGrabbing(true);
|
|
112
109
|
};
|
|
113
|
-
const move = (0,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
pointer.current
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
110
|
+
const move = (0, import_react.useCallback)((e) => {
|
|
111
|
+
const currentTrans = transformationRef.current;
|
|
112
|
+
if (currentTrans) {
|
|
113
|
+
const x = (e.clientX - pointer.current.x) / currentTrans.zoom;
|
|
114
|
+
const y = (e.clientY - pointer.current.y) / currentTrans.zoom;
|
|
115
|
+
pointer.current = { x: e.clientX, y: e.clientY };
|
|
116
|
+
setTransformation({
|
|
117
|
+
...currentTrans,
|
|
118
|
+
matrix: currentTrans.matrix.translate(x, y)
|
|
119
|
+
});
|
|
122
120
|
}
|
|
123
|
-
}, [
|
|
124
|
-
const up = (0,
|
|
125
|
-
|
|
121
|
+
}, [setTransformation]);
|
|
122
|
+
const up = (0, import_react.useCallback)(() => {
|
|
123
|
+
setIsPanning(false);
|
|
124
|
+
setGrabbing(false);
|
|
126
125
|
}, []);
|
|
127
|
-
(0,
|
|
128
|
-
if (
|
|
126
|
+
(0, import_react.useEffect)(() => {
|
|
127
|
+
if (isPanning) {
|
|
129
128
|
document.addEventListener("mousemove", move);
|
|
130
129
|
document.addEventListener("mouseup", up);
|
|
131
130
|
}
|
|
@@ -133,24 +132,21 @@ var SvgViewport = ({
|
|
|
133
132
|
document.removeEventListener("mousemove", move);
|
|
134
133
|
document.removeEventListener("mouseup", up);
|
|
135
134
|
};
|
|
136
|
-
}, [
|
|
135
|
+
}, [isPanning, move, up]);
|
|
137
136
|
const adjustZoom = (e) => {
|
|
137
|
+
if (!zoomable) return;
|
|
138
|
+
e.preventDefault();
|
|
138
139
|
const scale = e.deltaY < 0 ? 1.25 : 0.8;
|
|
139
140
|
const eventTarget = e.currentTarget;
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
matrix: adjustWithZoom(t.matrix, scale, eventTarget, eventClientX, eventClientY)
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
return t;
|
|
151
|
-
});
|
|
141
|
+
const { clientX, clientY } = e;
|
|
142
|
+
if (transformation && transformation.zoom * scale > minZoom && transformation.zoom * scale < maxZoom) {
|
|
143
|
+
setTransformation({
|
|
144
|
+
zoom: transformation.zoom * scale,
|
|
145
|
+
matrix: adjustWithZoom(transformation.matrix, scale, eventTarget, clientX, clientY)
|
|
146
|
+
});
|
|
147
|
+
}
|
|
152
148
|
};
|
|
153
|
-
const cursor = pannable ? grabbing
|
|
149
|
+
const cursor = pannable ? grabbing ? "grabbing" : "grab" : "auto";
|
|
154
150
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
155
151
|
"svg",
|
|
156
152
|
{
|
|
@@ -163,7 +159,7 @@ var SvgViewport = ({
|
|
|
163
159
|
onContextMenu: (e) => e.preventDefault(),
|
|
164
160
|
style: { ...style, cursor },
|
|
165
161
|
...otherProps,
|
|
166
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { transform: transform(
|
|
162
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { transform: transform(transformation?.matrix), children: transformation && children })
|
|
167
163
|
}
|
|
168
164
|
);
|
|
169
165
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ComponentProps
|
|
2
|
+
import { ComponentProps } from 'react';
|
|
3
3
|
|
|
4
4
|
type FocusPoint = "center" | "top-left";
|
|
5
5
|
|
|
6
|
-
type
|
|
6
|
+
type ViewportTransformation = {
|
|
7
7
|
zoom: number;
|
|
8
8
|
matrix: DOMMatrix;
|
|
9
9
|
};
|
|
@@ -37,21 +37,13 @@ type SvgViewportProps = ComponentProps<"svg"> & {
|
|
|
37
37
|
*/
|
|
38
38
|
maxZoom?: number;
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
40
|
+
* Current transformation state of the viewport.
|
|
41
41
|
*/
|
|
42
|
-
|
|
42
|
+
transformation?: ViewportTransformation | null;
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
44
|
+
* Callback to update the transformation state.
|
|
45
45
|
*/
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Current transformation state.
|
|
49
|
-
*/
|
|
50
|
-
transformation?: ViewportTransform | null;
|
|
51
|
-
/**
|
|
52
|
-
* Setter for the transformation state.
|
|
53
|
-
*/
|
|
54
|
-
setTransformation?: Dispatch<SetStateAction<ViewportTransform | null>>;
|
|
46
|
+
onTransformationChange?: (tranformation: ViewportTransformation) => void;
|
|
55
47
|
/**
|
|
56
48
|
* Initial focus point of the viewport.
|
|
57
49
|
*/
|
|
@@ -60,6 +52,6 @@ type SvgViewportProps = ComponentProps<"svg"> & {
|
|
|
60
52
|
/**
|
|
61
53
|
* SVG Viewport component that supports panning and zooming.
|
|
62
54
|
*/
|
|
63
|
-
declare const SvgViewport: ({ width, height, pannable, zoomable, minZoom, maxZoom,
|
|
55
|
+
declare const SvgViewport: ({ width, height, pannable, zoomable, minZoom, maxZoom, transformation: externalTransformation, onTransformationChange, initialFocusPoint, style, children, ...otherProps }: SvgViewportProps) => react_jsx_runtime.JSX.Element;
|
|
64
56
|
|
|
65
57
|
export { SvgViewport as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ComponentProps
|
|
2
|
+
import { ComponentProps } from 'react';
|
|
3
3
|
|
|
4
4
|
type FocusPoint = "center" | "top-left";
|
|
5
5
|
|
|
6
|
-
type
|
|
6
|
+
type ViewportTransformation = {
|
|
7
7
|
zoom: number;
|
|
8
8
|
matrix: DOMMatrix;
|
|
9
9
|
};
|
|
@@ -37,21 +37,13 @@ type SvgViewportProps = ComponentProps<"svg"> & {
|
|
|
37
37
|
*/
|
|
38
38
|
maxZoom?: number;
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
40
|
+
* Current transformation state of the viewport.
|
|
41
41
|
*/
|
|
42
|
-
|
|
42
|
+
transformation?: ViewportTransformation | null;
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
44
|
+
* Callback to update the transformation state.
|
|
45
45
|
*/
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Current transformation state.
|
|
49
|
-
*/
|
|
50
|
-
transformation?: ViewportTransform | null;
|
|
51
|
-
/**
|
|
52
|
-
* Setter for the transformation state.
|
|
53
|
-
*/
|
|
54
|
-
setTransformation?: Dispatch<SetStateAction<ViewportTransform | null>>;
|
|
46
|
+
onTransformationChange?: (tranformation: ViewportTransformation) => void;
|
|
55
47
|
/**
|
|
56
48
|
* Initial focus point of the viewport.
|
|
57
49
|
*/
|
|
@@ -60,6 +52,6 @@ type SvgViewportProps = ComponentProps<"svg"> & {
|
|
|
60
52
|
/**
|
|
61
53
|
* SVG Viewport component that supports panning and zooming.
|
|
62
54
|
*/
|
|
63
|
-
declare const SvgViewport: ({ width, height, pannable, zoomable, minZoom, maxZoom,
|
|
55
|
+
declare const SvgViewport: ({ width, height, pannable, zoomable, minZoom, maxZoom, transformation: externalTransformation, onTransformationChange, initialFocusPoint, style, children, ...otherProps }: SvgViewportProps) => react_jsx_runtime.JSX.Element;
|
|
64
56
|
|
|
65
57
|
export { SvgViewport as default };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/components/SvgViewport.tsx
|
|
4
|
-
import { useCallback, useEffect, useRef, useState
|
|
4
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
5
5
|
|
|
6
6
|
// src/core/matrix.ts
|
|
7
7
|
function transform(matrix) {
|
|
@@ -33,16 +33,6 @@ function getFocusedMatrix(focusPoint, width, height) {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
// src/hooks/polyfill-state.ts
|
|
37
|
-
import { useState } from "react";
|
|
38
|
-
function usePolyfillState(state, dispatch) {
|
|
39
|
-
const [polyfill, setPolyfill] = useState(state);
|
|
40
|
-
if (dispatch) {
|
|
41
|
-
return [state, dispatch];
|
|
42
|
-
}
|
|
43
|
-
return [polyfill, setPolyfill];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
36
|
// src/components/SvgViewport.tsx
|
|
47
37
|
import { jsx } from "react/jsx-runtime";
|
|
48
38
|
var SvgViewport = ({
|
|
@@ -52,55 +42,64 @@ var SvgViewport = ({
|
|
|
52
42
|
zoomable = false,
|
|
53
43
|
minZoom = 0.5,
|
|
54
44
|
maxZoom = 2,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
transformation = null,
|
|
58
|
-
setTransformation,
|
|
45
|
+
transformation: externalTransformation,
|
|
46
|
+
onTransformationChange,
|
|
59
47
|
initialFocusPoint = "center",
|
|
60
48
|
style,
|
|
61
49
|
children,
|
|
62
50
|
...otherProps
|
|
63
51
|
}) => {
|
|
52
|
+
const isControlled = externalTransformation !== void 0;
|
|
53
|
+
const [internalTransformation, setInternalTransformation] = useState(null);
|
|
64
54
|
const pointer = useRef({ x: 0, y: 0 });
|
|
65
|
-
const [grabbing, setGrabbing] =
|
|
66
|
-
const [
|
|
67
|
-
const
|
|
55
|
+
const [grabbing, setGrabbing] = useState(false);
|
|
56
|
+
const [isPanning, setIsPanning] = useState(false);
|
|
57
|
+
const transformation = isControlled ? externalTransformation : internalTransformation;
|
|
58
|
+
const transformationRef = useRef(transformation);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
transformationRef.current = transformation;
|
|
61
|
+
}, [transformation]);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (!isControlled && !internalTransformation) {
|
|
64
|
+
setInternalTransformation({
|
|
65
|
+
zoom: 1,
|
|
66
|
+
matrix: getFocusedMatrix(initialFocusPoint, width, height)
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}, []);
|
|
70
|
+
const setTransformation = useCallback((value) => {
|
|
71
|
+
if (!isControlled) {
|
|
72
|
+
setInternalTransformation(value);
|
|
73
|
+
}
|
|
74
|
+
onTransformationChange?.(value);
|
|
75
|
+
}, [isControlled, onTransformationChange]);
|
|
68
76
|
const stopGrabbing = () => {
|
|
69
77
|
setGrabbing(false);
|
|
70
78
|
};
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
if (setTransformation) return;
|
|
73
|
-
activeSetTransformation({
|
|
74
|
-
zoom: 1,
|
|
75
|
-
matrix: getFocusedMatrix(initialFocusPoint, width, height)
|
|
76
|
-
});
|
|
77
|
-
}, [setTransformation]);
|
|
78
79
|
const down = (e) => {
|
|
79
|
-
if (e.button
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
y: e.clientY
|
|
83
|
-
};
|
|
84
|
-
setActivePanning(true);
|
|
85
|
-
}
|
|
80
|
+
if (e.button !== 0) return;
|
|
81
|
+
pointer.current = { x: e.clientX, y: e.clientY };
|
|
82
|
+
setIsPanning(true);
|
|
86
83
|
setGrabbing(true);
|
|
87
84
|
};
|
|
88
85
|
const move = useCallback((e) => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
pointer.current
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
86
|
+
const currentTrans = transformationRef.current;
|
|
87
|
+
if (currentTrans) {
|
|
88
|
+
const x = (e.clientX - pointer.current.x) / currentTrans.zoom;
|
|
89
|
+
const y = (e.clientY - pointer.current.y) / currentTrans.zoom;
|
|
90
|
+
pointer.current = { x: e.clientX, y: e.clientY };
|
|
91
|
+
setTransformation({
|
|
92
|
+
...currentTrans,
|
|
93
|
+
matrix: currentTrans.matrix.translate(x, y)
|
|
94
|
+
});
|
|
97
95
|
}
|
|
98
|
-
}, [
|
|
96
|
+
}, [setTransformation]);
|
|
99
97
|
const up = useCallback(() => {
|
|
100
|
-
|
|
98
|
+
setIsPanning(false);
|
|
99
|
+
setGrabbing(false);
|
|
101
100
|
}, []);
|
|
102
101
|
useEffect(() => {
|
|
103
|
-
if (
|
|
102
|
+
if (isPanning) {
|
|
104
103
|
document.addEventListener("mousemove", move);
|
|
105
104
|
document.addEventListener("mouseup", up);
|
|
106
105
|
}
|
|
@@ -108,24 +107,21 @@ var SvgViewport = ({
|
|
|
108
107
|
document.removeEventListener("mousemove", move);
|
|
109
108
|
document.removeEventListener("mouseup", up);
|
|
110
109
|
};
|
|
111
|
-
}, [
|
|
110
|
+
}, [isPanning, move, up]);
|
|
112
111
|
const adjustZoom = (e) => {
|
|
112
|
+
if (!zoomable) return;
|
|
113
|
+
e.preventDefault();
|
|
113
114
|
const scale = e.deltaY < 0 ? 1.25 : 0.8;
|
|
114
115
|
const eventTarget = e.currentTarget;
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
matrix: adjustWithZoom(t.matrix, scale, eventTarget, eventClientX, eventClientY)
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
return t;
|
|
126
|
-
});
|
|
116
|
+
const { clientX, clientY } = e;
|
|
117
|
+
if (transformation && transformation.zoom * scale > minZoom && transformation.zoom * scale < maxZoom) {
|
|
118
|
+
setTransformation({
|
|
119
|
+
zoom: transformation.zoom * scale,
|
|
120
|
+
matrix: adjustWithZoom(transformation.matrix, scale, eventTarget, clientX, clientY)
|
|
121
|
+
});
|
|
122
|
+
}
|
|
127
123
|
};
|
|
128
|
-
const cursor = pannable ? grabbing
|
|
124
|
+
const cursor = pannable ? grabbing ? "grabbing" : "grab" : "auto";
|
|
129
125
|
return /* @__PURE__ */ jsx(
|
|
130
126
|
"svg",
|
|
131
127
|
{
|
|
@@ -138,7 +134,7 @@ var SvgViewport = ({
|
|
|
138
134
|
onContextMenu: (e) => e.preventDefault(),
|
|
139
135
|
style: { ...style, cursor },
|
|
140
136
|
...otherProps,
|
|
141
|
-
children: /* @__PURE__ */ jsx("g", { transform: transform(
|
|
137
|
+
children: /* @__PURE__ */ jsx("g", { transform: transform(transformation?.matrix), children: transformation && children })
|
|
142
138
|
}
|
|
143
139
|
);
|
|
144
140
|
};
|