@omer-x/svg-viewport 0.3.1 → 1.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 +173 -0
- package/dist/index.d.cts +65 -0
- package/dist/index.d.ts +45 -7
- package/dist/index.js +132 -91
- package/package.json +27 -23
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
default: () => index_default
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/components/SvgViewport.tsx
|
|
29
|
+
var import_react2 = require("react");
|
|
30
|
+
|
|
31
|
+
// src/core/matrix.ts
|
|
32
|
+
function transform(matrix) {
|
|
33
|
+
if (!matrix) return void 0;
|
|
34
|
+
const { a, b, c, d, e, f } = matrix;
|
|
35
|
+
return `matrix(${a}, ${b}, ${c}, ${d}, ${e}, ${f})`;
|
|
36
|
+
}
|
|
37
|
+
function focusTo(matrix, { x, y }, width, height, zoom) {
|
|
38
|
+
return matrix.translate(
|
|
39
|
+
width / 2 / zoom - x - matrix.m41 / zoom,
|
|
40
|
+
height / 2 / zoom - y - matrix.m42 / zoom
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
function adjustWithZoom(matrix, scale, svgElement, eX, eY) {
|
|
44
|
+
const rect = svgElement.getBoundingClientRect();
|
|
45
|
+
const focusPoint = new DOMPoint(eX - rect.left, eY - rect.top);
|
|
46
|
+
const relativePoint = focusPoint.matrixTransform(matrix.inverse());
|
|
47
|
+
const modifier = new DOMMatrix().translate(relativePoint.x, relativePoint.y).scale(scale).translate(-relativePoint.x, -relativePoint.y);
|
|
48
|
+
return matrix.multiply(modifier);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/core/initial-focus.ts
|
|
52
|
+
function getFocusedMatrix(focusPoint, width, height) {
|
|
53
|
+
switch (focusPoint) {
|
|
54
|
+
case "center":
|
|
55
|
+
return focusTo(new DOMMatrix(), { x: 0, y: 0 }, width, height, 1);
|
|
56
|
+
case "top-left":
|
|
57
|
+
return focusTo(new DOMMatrix(), { x: 0, y: 0 }, 0, 0, 1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
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
|
+
// src/components/SvgViewport.tsx
|
|
72
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
73
|
+
var SvgViewport = ({
|
|
74
|
+
width,
|
|
75
|
+
height,
|
|
76
|
+
pannable = false,
|
|
77
|
+
zoomable = false,
|
|
78
|
+
minZoom = 0.5,
|
|
79
|
+
maxZoom = 2,
|
|
80
|
+
panning = false,
|
|
81
|
+
setPanning,
|
|
82
|
+
transformation = null,
|
|
83
|
+
setTransformation,
|
|
84
|
+
initialFocusPoint = "center",
|
|
85
|
+
style,
|
|
86
|
+
children,
|
|
87
|
+
...otherProps
|
|
88
|
+
}) => {
|
|
89
|
+
const pointer = (0, import_react2.useRef)({ x: 0, y: 0 });
|
|
90
|
+
const [grabbing, setGrabbing] = (0, import_react2.useState)(false);
|
|
91
|
+
const [activeTransformation, activeSetTransformation] = usePolyfillState(transformation, setTransformation);
|
|
92
|
+
const [activePanning, setActivePanning] = usePolyfillState(panning, setPanning);
|
|
93
|
+
const stopGrabbing = () => {
|
|
94
|
+
setGrabbing(false);
|
|
95
|
+
};
|
|
96
|
+
(0, import_react2.useEffect)(() => {
|
|
97
|
+
if (setTransformation) return;
|
|
98
|
+
activeSetTransformation({
|
|
99
|
+
zoom: 1,
|
|
100
|
+
matrix: getFocusedMatrix(initialFocusPoint, width, height)
|
|
101
|
+
});
|
|
102
|
+
}, [setTransformation]);
|
|
103
|
+
const down = (e) => {
|
|
104
|
+
if (e.button === 0) {
|
|
105
|
+
pointer.current = {
|
|
106
|
+
x: e.clientX,
|
|
107
|
+
y: e.clientY
|
|
108
|
+
};
|
|
109
|
+
setActivePanning(true);
|
|
110
|
+
}
|
|
111
|
+
setGrabbing(true);
|
|
112
|
+
};
|
|
113
|
+
const move = (0, import_react2.useCallback)((e) => {
|
|
114
|
+
if (activePanning && activeTransformation) {
|
|
115
|
+
const x = (e.clientX - pointer.current.x) / activeTransformation.zoom;
|
|
116
|
+
const y = (e.clientY - pointer.current.y) / activeTransformation.zoom;
|
|
117
|
+
pointer.current = {
|
|
118
|
+
x: e.clientX,
|
|
119
|
+
y: e.clientY
|
|
120
|
+
};
|
|
121
|
+
activeSetTransformation((t) => t ? { ...t, matrix: t.matrix.translate(x, y) } : t);
|
|
122
|
+
}
|
|
123
|
+
}, [activePanning, activeTransformation]);
|
|
124
|
+
const up = (0, import_react2.useCallback)(() => {
|
|
125
|
+
setActivePanning(false);
|
|
126
|
+
}, []);
|
|
127
|
+
(0, import_react2.useEffect)(() => {
|
|
128
|
+
if (activePanning) {
|
|
129
|
+
document.addEventListener("mousemove", move);
|
|
130
|
+
document.addEventListener("mouseup", up);
|
|
131
|
+
}
|
|
132
|
+
return () => {
|
|
133
|
+
document.removeEventListener("mousemove", move);
|
|
134
|
+
document.removeEventListener("mouseup", up);
|
|
135
|
+
};
|
|
136
|
+
}, [activePanning]);
|
|
137
|
+
const adjustZoom = (e) => {
|
|
138
|
+
const scale = e.deltaY < 0 ? 1.25 : 0.8;
|
|
139
|
+
const eventTarget = e.currentTarget;
|
|
140
|
+
const eventClientX = e.clientX;
|
|
141
|
+
const eventClientY = e.clientY;
|
|
142
|
+
activeSetTransformation((t) => {
|
|
143
|
+
if (t && t.zoom * scale > minZoom && t.zoom * scale < maxZoom) {
|
|
144
|
+
return {
|
|
145
|
+
...t,
|
|
146
|
+
zoom: t.zoom * scale,
|
|
147
|
+
matrix: adjustWithZoom(t.matrix, scale, eventTarget, eventClientX, eventClientY)
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return t;
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
const cursor = pannable ? grabbing || panning ? "grabbing" : "grab" : "auto";
|
|
154
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
155
|
+
"svg",
|
|
156
|
+
{
|
|
157
|
+
width,
|
|
158
|
+
height,
|
|
159
|
+
onMouseDown: pannable ? down : void 0,
|
|
160
|
+
onMouseUp: pannable ? stopGrabbing : void 0,
|
|
161
|
+
onMouseLeave: pannable ? stopGrabbing : void 0,
|
|
162
|
+
onWheel: zoomable ? adjustZoom : void 0,
|
|
163
|
+
onContextMenu: (e) => e.preventDefault(),
|
|
164
|
+
style: { ...style, cursor },
|
|
165
|
+
...otherProps,
|
|
166
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { transform: transform(activeTransformation?.matrix), children: activeTransformation && children })
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
var SvgViewport_default = SvgViewport;
|
|
171
|
+
|
|
172
|
+
// src/index.ts
|
|
173
|
+
var index_default = SvgViewport_default;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ComponentProps, Dispatch, SetStateAction } from 'react';
|
|
3
|
+
|
|
4
|
+
type FocusPoint = "center" | "top-left";
|
|
5
|
+
|
|
6
|
+
type ViewportTransform = {
|
|
7
|
+
zoom: number;
|
|
8
|
+
matrix: DOMMatrix;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Props for the SvgViewport component.
|
|
13
|
+
*/
|
|
14
|
+
type SvgViewportProps = ComponentProps<"svg"> & {
|
|
15
|
+
/**
|
|
16
|
+
* Width of the SVG viewport.
|
|
17
|
+
*/
|
|
18
|
+
width: number;
|
|
19
|
+
/**
|
|
20
|
+
* Height of the SVG viewport.
|
|
21
|
+
*/
|
|
22
|
+
height: number;
|
|
23
|
+
/**
|
|
24
|
+
* Enable or disable panning functionality.
|
|
25
|
+
*/
|
|
26
|
+
pannable?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Enable or disable zooming functionality.
|
|
29
|
+
*/
|
|
30
|
+
zoomable?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Minimum zoom level.
|
|
33
|
+
*/
|
|
34
|
+
minZoom?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Maximum zoom level.
|
|
37
|
+
*/
|
|
38
|
+
maxZoom?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Indicates if panning is currently active.
|
|
41
|
+
*/
|
|
42
|
+
panning?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Setter for the panning state.
|
|
45
|
+
*/
|
|
46
|
+
setPanning?: Dispatch<SetStateAction<boolean>>;
|
|
47
|
+
/**
|
|
48
|
+
* Current transformation state.
|
|
49
|
+
*/
|
|
50
|
+
transformation?: ViewportTransform | null;
|
|
51
|
+
/**
|
|
52
|
+
* Setter for the transformation state.
|
|
53
|
+
*/
|
|
54
|
+
setTransformation?: Dispatch<SetStateAction<ViewportTransform | null>>;
|
|
55
|
+
/**
|
|
56
|
+
* Initial focus point of the viewport.
|
|
57
|
+
*/
|
|
58
|
+
initialFocusPoint?: FocusPoint;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* SVG Viewport component that supports panning and zooming.
|
|
62
|
+
*/
|
|
63
|
+
declare const SvgViewport: ({ width, height, pannable, zoomable, minZoom, maxZoom, panning, setPanning, transformation, setTransformation, initialFocusPoint, style, children, ...otherProps }: SvgViewportProps) => react_jsx_runtime.JSX.Element;
|
|
64
|
+
|
|
65
|
+
export { SvgViewport as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,27 +1,65 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ComponentProps, Dispatch, SetStateAction } from 'react';
|
|
2
3
|
|
|
3
4
|
type FocusPoint = "center" | "top-left";
|
|
4
5
|
|
|
5
6
|
type ViewportTransform = {
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
zoom: number;
|
|
8
|
+
matrix: DOMMatrix;
|
|
8
9
|
};
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Props for the SvgViewport component.
|
|
13
|
+
*/
|
|
14
|
+
type SvgViewportProps = ComponentProps<"svg"> & {
|
|
15
|
+
/**
|
|
16
|
+
* Width of the SVG viewport.
|
|
17
|
+
*/
|
|
11
18
|
width: number;
|
|
19
|
+
/**
|
|
20
|
+
* Height of the SVG viewport.
|
|
21
|
+
*/
|
|
12
22
|
height: number;
|
|
23
|
+
/**
|
|
24
|
+
* Enable or disable panning functionality.
|
|
25
|
+
*/
|
|
13
26
|
pannable?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Enable or disable zooming functionality.
|
|
29
|
+
*/
|
|
14
30
|
zoomable?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Minimum zoom level.
|
|
33
|
+
*/
|
|
15
34
|
minZoom?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Maximum zoom level.
|
|
37
|
+
*/
|
|
16
38
|
maxZoom?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Indicates if panning is currently active.
|
|
41
|
+
*/
|
|
17
42
|
panning?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Setter for the panning state.
|
|
45
|
+
*/
|
|
18
46
|
setPanning?: Dispatch<SetStateAction<boolean>>;
|
|
47
|
+
/**
|
|
48
|
+
* Current transformation state.
|
|
49
|
+
*/
|
|
19
50
|
transformation?: ViewportTransform | null;
|
|
51
|
+
/**
|
|
52
|
+
* Setter for the transformation state.
|
|
53
|
+
*/
|
|
20
54
|
setTransformation?: Dispatch<SetStateAction<ViewportTransform | null>>;
|
|
55
|
+
/**
|
|
56
|
+
* Initial focus point of the viewport.
|
|
57
|
+
*/
|
|
21
58
|
initialFocusPoint?: FocusPoint;
|
|
22
|
-
className?: string;
|
|
23
|
-
children: ReactNode;
|
|
24
59
|
};
|
|
25
|
-
|
|
60
|
+
/**
|
|
61
|
+
* SVG Viewport component that supports panning and zooming.
|
|
62
|
+
*/
|
|
63
|
+
declare const SvgViewport: ({ width, height, pannable, zoomable, minZoom, maxZoom, panning, setPanning, transformation, setTransformation, initialFocusPoint, style, children, ...otherProps }: SvgViewportProps) => react_jsx_runtime.JSX.Element;
|
|
26
64
|
|
|
27
65
|
export { SvgViewport as default };
|
package/dist/index.js
CHANGED
|
@@ -1,110 +1,151 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
|
3
2
|
|
|
3
|
+
// src/components/SvgViewport.tsx
|
|
4
|
+
import { useCallback, useEffect, useRef, useState as useState2 } from "react";
|
|
5
|
+
|
|
6
|
+
// src/core/matrix.ts
|
|
4
7
|
function transform(matrix) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
return `matrix(${a}, ${b}, ${c}, ${d}, ${e}, ${f})`;
|
|
8
|
+
if (!matrix) return void 0;
|
|
9
|
+
const { a, b, c, d, e, f } = matrix;
|
|
10
|
+
return `matrix(${a}, ${b}, ${c}, ${d}, ${e}, ${f})`;
|
|
9
11
|
}
|
|
10
12
|
function focusTo(matrix, { x, y }, width, height, zoom) {
|
|
11
|
-
|
|
13
|
+
return matrix.translate(
|
|
14
|
+
width / 2 / zoom - x - matrix.m41 / zoom,
|
|
15
|
+
height / 2 / zoom - y - matrix.m42 / zoom
|
|
16
|
+
);
|
|
12
17
|
}
|
|
13
18
|
function adjustWithZoom(matrix, scale, svgElement, eX, eY) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
.scale(scale)
|
|
20
|
-
.translate(-relativePoint.x, -relativePoint.y);
|
|
21
|
-
return matrix.multiply(modifier);
|
|
19
|
+
const rect = svgElement.getBoundingClientRect();
|
|
20
|
+
const focusPoint = new DOMPoint(eX - rect.left, eY - rect.top);
|
|
21
|
+
const relativePoint = focusPoint.matrixTransform(matrix.inverse());
|
|
22
|
+
const modifier = new DOMMatrix().translate(relativePoint.x, relativePoint.y).scale(scale).translate(-relativePoint.x, -relativePoint.y);
|
|
23
|
+
return matrix.multiply(modifier);
|
|
22
24
|
}
|
|
23
25
|
|
|
26
|
+
// src/core/initial-focus.ts
|
|
24
27
|
function getFocusedMatrix(focusPoint, width, height) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
switch (focusPoint) {
|
|
29
|
+
case "center":
|
|
30
|
+
return focusTo(new DOMMatrix(), { x: 0, y: 0 }, width, height, 1);
|
|
31
|
+
case "top-left":
|
|
32
|
+
return focusTo(new DOMMatrix(), { x: 0, y: 0 }, 0, 0, 1);
|
|
33
|
+
}
|
|
31
34
|
}
|
|
32
35
|
|
|
36
|
+
// src/hooks/polyfill-state.ts
|
|
37
|
+
import { useState } from "react";
|
|
33
38
|
function usePolyfillState(state, dispatch) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
const [polyfill, setPolyfill] = useState(state);
|
|
40
|
+
if (dispatch) {
|
|
41
|
+
return [state, dispatch];
|
|
42
|
+
}
|
|
43
|
+
return [polyfill, setPolyfill];
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
46
|
+
// src/components/SvgViewport.tsx
|
|
47
|
+
import { jsx } from "react/jsx-runtime";
|
|
48
|
+
var SvgViewport = ({
|
|
49
|
+
width,
|
|
50
|
+
height,
|
|
51
|
+
pannable = false,
|
|
52
|
+
zoomable = false,
|
|
53
|
+
minZoom = 0.5,
|
|
54
|
+
maxZoom = 2,
|
|
55
|
+
panning = false,
|
|
56
|
+
setPanning,
|
|
57
|
+
transformation = null,
|
|
58
|
+
setTransformation,
|
|
59
|
+
initialFocusPoint = "center",
|
|
60
|
+
style,
|
|
61
|
+
children,
|
|
62
|
+
...otherProps
|
|
63
|
+
}) => {
|
|
64
|
+
const pointer = useRef({ x: 0, y: 0 });
|
|
65
|
+
const [grabbing, setGrabbing] = useState2(false);
|
|
66
|
+
const [activeTransformation, activeSetTransformation] = usePolyfillState(transformation, setTransformation);
|
|
67
|
+
const [activePanning, setActivePanning] = usePolyfillState(panning, setPanning);
|
|
68
|
+
const stopGrabbing = () => {
|
|
69
|
+
setGrabbing(false);
|
|
70
|
+
};
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (setTransformation) return;
|
|
73
|
+
activeSetTransformation({
|
|
74
|
+
zoom: 1,
|
|
75
|
+
matrix: getFocusedMatrix(initialFocusPoint, width, height)
|
|
76
|
+
});
|
|
77
|
+
}, [setTransformation]);
|
|
78
|
+
const down = (e) => {
|
|
79
|
+
if (e.button === 0) {
|
|
80
|
+
pointer.current = {
|
|
81
|
+
x: e.clientX,
|
|
82
|
+
y: e.clientY
|
|
83
|
+
};
|
|
84
|
+
setActivePanning(true);
|
|
85
|
+
}
|
|
86
|
+
setGrabbing(true);
|
|
87
|
+
};
|
|
88
|
+
const move = useCallback((e) => {
|
|
89
|
+
if (activePanning && activeTransformation) {
|
|
90
|
+
const x = (e.clientX - pointer.current.x) / activeTransformation.zoom;
|
|
91
|
+
const y = (e.clientY - pointer.current.y) / activeTransformation.zoom;
|
|
92
|
+
pointer.current = {
|
|
93
|
+
x: e.clientX,
|
|
94
|
+
y: e.clientY
|
|
95
|
+
};
|
|
96
|
+
activeSetTransformation((t) => t ? { ...t, matrix: t.matrix.translate(x, y) } : t);
|
|
97
|
+
}
|
|
98
|
+
}, [activePanning, activeTransformation]);
|
|
99
|
+
const up = useCallback(() => {
|
|
100
|
+
setActivePanning(false);
|
|
101
|
+
}, []);
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (activePanning) {
|
|
104
|
+
document.addEventListener("mousemove", move);
|
|
105
|
+
document.addEventListener("mouseup", up);
|
|
106
|
+
}
|
|
107
|
+
return () => {
|
|
108
|
+
document.removeEventListener("mousemove", move);
|
|
109
|
+
document.removeEventListener("mouseup", up);
|
|
67
110
|
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
setActivePanning(false);
|
|
81
|
-
}, []);
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
if (activePanning) {
|
|
84
|
-
document.addEventListener("mousemove", move);
|
|
85
|
-
document.addEventListener("mouseup", up);
|
|
86
|
-
}
|
|
87
|
-
return () => {
|
|
88
|
-
document.removeEventListener("mousemove", move);
|
|
89
|
-
document.removeEventListener("mouseup", up);
|
|
111
|
+
}, [activePanning]);
|
|
112
|
+
const adjustZoom = (e) => {
|
|
113
|
+
const scale = e.deltaY < 0 ? 1.25 : 0.8;
|
|
114
|
+
const eventTarget = e.currentTarget;
|
|
115
|
+
const eventClientX = e.clientX;
|
|
116
|
+
const eventClientY = e.clientY;
|
|
117
|
+
activeSetTransformation((t) => {
|
|
118
|
+
if (t && t.zoom * scale > minZoom && t.zoom * scale < maxZoom) {
|
|
119
|
+
return {
|
|
120
|
+
...t,
|
|
121
|
+
zoom: t.zoom * scale,
|
|
122
|
+
matrix: adjustWithZoom(t.matrix, scale, eventTarget, eventClientX, eventClientY)
|
|
90
123
|
};
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
124
|
+
}
|
|
125
|
+
return t;
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
const cursor = pannable ? grabbing || panning ? "grabbing" : "grab" : "auto";
|
|
129
|
+
return /* @__PURE__ */ jsx(
|
|
130
|
+
"svg",
|
|
131
|
+
{
|
|
132
|
+
width,
|
|
133
|
+
height,
|
|
134
|
+
onMouseDown: pannable ? down : void 0,
|
|
135
|
+
onMouseUp: pannable ? stopGrabbing : void 0,
|
|
136
|
+
onMouseLeave: pannable ? stopGrabbing : void 0,
|
|
137
|
+
onWheel: zoomable ? adjustZoom : void 0,
|
|
138
|
+
onContextMenu: (e) => e.preventDefault(),
|
|
139
|
+
style: { ...style, cursor },
|
|
140
|
+
...otherProps,
|
|
141
|
+
children: /* @__PURE__ */ jsx("g", { transform: transform(activeTransformation?.matrix), children: activeTransformation && children })
|
|
142
|
+
}
|
|
143
|
+
);
|
|
108
144
|
};
|
|
145
|
+
var SvgViewport_default = SvgViewport;
|
|
109
146
|
|
|
110
|
-
|
|
147
|
+
// src/index.ts
|
|
148
|
+
var index_default = SvgViewport_default;
|
|
149
|
+
export {
|
|
150
|
+
index_default as default
|
|
151
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omer-x/svg-viewport",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Provides a simple React component for displaying SVG content with zooming and panning capabilities",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -23,9 +23,6 @@
|
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
|
-
"files": [
|
|
27
|
-
"dist/"
|
|
28
|
-
],
|
|
29
26
|
"author": {
|
|
30
27
|
"name": "Omer Mecitoglu",
|
|
31
28
|
"email": "omer.mecitoglu@gmail.com",
|
|
@@ -33,28 +30,35 @@
|
|
|
33
30
|
},
|
|
34
31
|
"license": "MIT",
|
|
35
32
|
"type": "module",
|
|
36
|
-
"
|
|
37
|
-
|
|
33
|
+
"files": [
|
|
34
|
+
"dist/"
|
|
35
|
+
],
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"import": "./dist/index.js",
|
|
40
|
+
"require": "./dist/index.cjs"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
38
43
|
"scripts": {
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
44
|
+
"lint": "eslint --flag unstable_native_nodejs_ts_config",
|
|
45
|
+
"lint:fix": "eslint --fix --flag unstable_native_nodejs_ts_config",
|
|
46
|
+
"test": "vitest run --coverage",
|
|
47
|
+
"test:watch": "vitest --coverage",
|
|
48
|
+
"dev": "tsup --watch",
|
|
49
|
+
"build": "tsup"
|
|
43
50
|
},
|
|
44
|
-
"
|
|
45
|
-
"react": "^
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"react": "^19"
|
|
46
53
|
},
|
|
47
54
|
"devDependencies": {
|
|
48
|
-
"@omer-x/eslint-config": "^
|
|
49
|
-
"@
|
|
50
|
-
"@
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"ts-unused-exports": "^10.0.1",
|
|
57
|
-
"tslib": "^2.6.2",
|
|
58
|
-
"typescript": "^5.3.3"
|
|
55
|
+
"@omer-x/eslint-config": "^2.2.6",
|
|
56
|
+
"@types/react": "^19.2.10",
|
|
57
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
58
|
+
"eslint": "^9.39.2",
|
|
59
|
+
"semantic-release": "^25.0.3",
|
|
60
|
+
"tsup": "^8.5.1",
|
|
61
|
+
"typescript": "^5.9.3",
|
|
62
|
+
"vitest": "^4.0.18"
|
|
59
63
|
}
|
|
60
64
|
}
|