@omer-x/svg-viewport 1.0.0 → 2.0.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.
- package/dist/index.cjs +56 -62
- package/dist/index.d.cts +7 -15
- package/dist/index.d.ts +7 -15
- package/dist/index.js +52 -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,62 @@ 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
|
-
|
|
92
|
-
|
|
77
|
+
const isControlled = externalTransformation !== void 0;
|
|
78
|
+
const [internalTransformation, setInternalTransformation] = (0, import_react.useState)(() => {
|
|
79
|
+
if (isControlled) return null;
|
|
80
|
+
return {
|
|
81
|
+
zoom: 1,
|
|
82
|
+
matrix: getFocusedMatrix(initialFocusPoint, width, height)
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
const transformation = isControlled ? externalTransformation : internalTransformation;
|
|
86
|
+
const transformationRef = (0, import_react.useRef)(transformation);
|
|
87
|
+
(0, import_react.useEffect)(() => {
|
|
88
|
+
transformationRef.current = transformation;
|
|
89
|
+
}, [transformation]);
|
|
90
|
+
const pointer = (0, import_react.useRef)({ x: 0, y: 0 });
|
|
91
|
+
const [grabbing, setGrabbing] = (0, import_react.useState)(false);
|
|
92
|
+
const [isPanning, setIsPanning] = (0, import_react.useState)(false);
|
|
93
|
+
const setTransformation = (0, import_react.useCallback)((value) => {
|
|
94
|
+
if (!isControlled) {
|
|
95
|
+
setInternalTransformation(value);
|
|
96
|
+
}
|
|
97
|
+
onTransformationChange?.(value);
|
|
98
|
+
}, [isControlled, onTransformationChange]);
|
|
93
99
|
const stopGrabbing = () => {
|
|
94
100
|
setGrabbing(false);
|
|
95
101
|
};
|
|
96
|
-
(0, import_react2.useEffect)(() => {
|
|
97
|
-
if (setTransformation) return;
|
|
98
|
-
activeSetTransformation({
|
|
99
|
-
zoom: 1,
|
|
100
|
-
matrix: getFocusedMatrix(initialFocusPoint, width, height)
|
|
101
|
-
});
|
|
102
|
-
}, [setTransformation]);
|
|
103
102
|
const down = (e) => {
|
|
104
|
-
if (e.button
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
y: e.clientY
|
|
108
|
-
};
|
|
109
|
-
setActivePanning(true);
|
|
110
|
-
}
|
|
103
|
+
if (e.button !== 0) return;
|
|
104
|
+
pointer.current = { x: e.clientX, y: e.clientY };
|
|
105
|
+
setIsPanning(true);
|
|
111
106
|
setGrabbing(true);
|
|
112
107
|
};
|
|
113
|
-
const move = (0,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
pointer.current
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
108
|
+
const move = (0, import_react.useCallback)((e) => {
|
|
109
|
+
const currentTrans = transformationRef.current;
|
|
110
|
+
if (currentTrans) {
|
|
111
|
+
const x = (e.clientX - pointer.current.x) / currentTrans.zoom;
|
|
112
|
+
const y = (e.clientY - pointer.current.y) / currentTrans.zoom;
|
|
113
|
+
pointer.current = { x: e.clientX, y: e.clientY };
|
|
114
|
+
setTransformation({
|
|
115
|
+
...currentTrans,
|
|
116
|
+
matrix: currentTrans.matrix.translate(x, y)
|
|
117
|
+
});
|
|
122
118
|
}
|
|
123
|
-
}, [
|
|
124
|
-
const up = (0,
|
|
125
|
-
|
|
119
|
+
}, [setTransformation]);
|
|
120
|
+
const up = (0, import_react.useCallback)(() => {
|
|
121
|
+
setIsPanning(false);
|
|
122
|
+
setGrabbing(false);
|
|
126
123
|
}, []);
|
|
127
|
-
(0,
|
|
128
|
-
if (
|
|
124
|
+
(0, import_react.useEffect)(() => {
|
|
125
|
+
if (isPanning) {
|
|
129
126
|
document.addEventListener("mousemove", move);
|
|
130
127
|
document.addEventListener("mouseup", up);
|
|
131
128
|
}
|
|
@@ -133,24 +130,21 @@ var SvgViewport = ({
|
|
|
133
130
|
document.removeEventListener("mousemove", move);
|
|
134
131
|
document.removeEventListener("mouseup", up);
|
|
135
132
|
};
|
|
136
|
-
}, [
|
|
133
|
+
}, [isPanning, move, up]);
|
|
137
134
|
const adjustZoom = (e) => {
|
|
135
|
+
if (!zoomable) return;
|
|
136
|
+
e.preventDefault();
|
|
138
137
|
const scale = e.deltaY < 0 ? 1.25 : 0.8;
|
|
139
138
|
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
|
-
});
|
|
139
|
+
const { clientX, clientY } = e;
|
|
140
|
+
if (transformation && transformation.zoom * scale > minZoom && transformation.zoom * scale < maxZoom) {
|
|
141
|
+
setTransformation({
|
|
142
|
+
zoom: transformation.zoom * scale,
|
|
143
|
+
matrix: adjustWithZoom(transformation.matrix, scale, eventTarget, clientX, clientY)
|
|
144
|
+
});
|
|
145
|
+
}
|
|
152
146
|
};
|
|
153
|
-
const cursor = pannable ? grabbing
|
|
147
|
+
const cursor = pannable ? grabbing ? "grabbing" : "grab" : "auto";
|
|
154
148
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
155
149
|
"svg",
|
|
156
150
|
{
|
|
@@ -163,7 +157,7 @@ var SvgViewport = ({
|
|
|
163
157
|
onContextMenu: (e) => e.preventDefault(),
|
|
164
158
|
style: { ...style, cursor },
|
|
165
159
|
...otherProps,
|
|
166
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { transform: transform(
|
|
160
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { transform: transform(transformation?.matrix), children: transformation && children })
|
|
167
161
|
}
|
|
168
162
|
);
|
|
169
163
|
};
|
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,62 @@ 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(() => {
|
|
54
|
+
if (isControlled) return null;
|
|
55
|
+
return {
|
|
56
|
+
zoom: 1,
|
|
57
|
+
matrix: getFocusedMatrix(initialFocusPoint, width, height)
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
const transformation = isControlled ? externalTransformation : internalTransformation;
|
|
61
|
+
const transformationRef = useRef(transformation);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
transformationRef.current = transformation;
|
|
64
|
+
}, [transformation]);
|
|
64
65
|
const pointer = useRef({ x: 0, y: 0 });
|
|
65
|
-
const [grabbing, setGrabbing] =
|
|
66
|
-
const [
|
|
67
|
-
const
|
|
66
|
+
const [grabbing, setGrabbing] = useState(false);
|
|
67
|
+
const [isPanning, setIsPanning] = useState(false);
|
|
68
|
+
const setTransformation = useCallback((value) => {
|
|
69
|
+
if (!isControlled) {
|
|
70
|
+
setInternalTransformation(value);
|
|
71
|
+
}
|
|
72
|
+
onTransformationChange?.(value);
|
|
73
|
+
}, [isControlled, onTransformationChange]);
|
|
68
74
|
const stopGrabbing = () => {
|
|
69
75
|
setGrabbing(false);
|
|
70
76
|
};
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
if (setTransformation) return;
|
|
73
|
-
activeSetTransformation({
|
|
74
|
-
zoom: 1,
|
|
75
|
-
matrix: getFocusedMatrix(initialFocusPoint, width, height)
|
|
76
|
-
});
|
|
77
|
-
}, [setTransformation]);
|
|
78
77
|
const down = (e) => {
|
|
79
|
-
if (e.button
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
y: e.clientY
|
|
83
|
-
};
|
|
84
|
-
setActivePanning(true);
|
|
85
|
-
}
|
|
78
|
+
if (e.button !== 0) return;
|
|
79
|
+
pointer.current = { x: e.clientX, y: e.clientY };
|
|
80
|
+
setIsPanning(true);
|
|
86
81
|
setGrabbing(true);
|
|
87
82
|
};
|
|
88
83
|
const move = useCallback((e) => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
pointer.current
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
84
|
+
const currentTrans = transformationRef.current;
|
|
85
|
+
if (currentTrans) {
|
|
86
|
+
const x = (e.clientX - pointer.current.x) / currentTrans.zoom;
|
|
87
|
+
const y = (e.clientY - pointer.current.y) / currentTrans.zoom;
|
|
88
|
+
pointer.current = { x: e.clientX, y: e.clientY };
|
|
89
|
+
setTransformation({
|
|
90
|
+
...currentTrans,
|
|
91
|
+
matrix: currentTrans.matrix.translate(x, y)
|
|
92
|
+
});
|
|
97
93
|
}
|
|
98
|
-
}, [
|
|
94
|
+
}, [setTransformation]);
|
|
99
95
|
const up = useCallback(() => {
|
|
100
|
-
|
|
96
|
+
setIsPanning(false);
|
|
97
|
+
setGrabbing(false);
|
|
101
98
|
}, []);
|
|
102
99
|
useEffect(() => {
|
|
103
|
-
if (
|
|
100
|
+
if (isPanning) {
|
|
104
101
|
document.addEventListener("mousemove", move);
|
|
105
102
|
document.addEventListener("mouseup", up);
|
|
106
103
|
}
|
|
@@ -108,24 +105,21 @@ var SvgViewport = ({
|
|
|
108
105
|
document.removeEventListener("mousemove", move);
|
|
109
106
|
document.removeEventListener("mouseup", up);
|
|
110
107
|
};
|
|
111
|
-
}, [
|
|
108
|
+
}, [isPanning, move, up]);
|
|
112
109
|
const adjustZoom = (e) => {
|
|
110
|
+
if (!zoomable) return;
|
|
111
|
+
e.preventDefault();
|
|
113
112
|
const scale = e.deltaY < 0 ? 1.25 : 0.8;
|
|
114
113
|
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
|
-
});
|
|
114
|
+
const { clientX, clientY } = e;
|
|
115
|
+
if (transformation && transformation.zoom * scale > minZoom && transformation.zoom * scale < maxZoom) {
|
|
116
|
+
setTransformation({
|
|
117
|
+
zoom: transformation.zoom * scale,
|
|
118
|
+
matrix: adjustWithZoom(transformation.matrix, scale, eventTarget, clientX, clientY)
|
|
119
|
+
});
|
|
120
|
+
}
|
|
127
121
|
};
|
|
128
|
-
const cursor = pannable ? grabbing
|
|
122
|
+
const cursor = pannable ? grabbing ? "grabbing" : "grab" : "auto";
|
|
129
123
|
return /* @__PURE__ */ jsx(
|
|
130
124
|
"svg",
|
|
131
125
|
{
|
|
@@ -138,7 +132,7 @@ var SvgViewport = ({
|
|
|
138
132
|
onContextMenu: (e) => e.preventDefault(),
|
|
139
133
|
style: { ...style, cursor },
|
|
140
134
|
...otherProps,
|
|
141
|
-
children: /* @__PURE__ */ jsx("g", { transform: transform(
|
|
135
|
+
children: /* @__PURE__ */ jsx("g", { transform: transform(transformation?.matrix), children: transformation && children })
|
|
142
136
|
}
|
|
143
137
|
);
|
|
144
138
|
};
|