@k2b/ui 0.5.1 → 0.6.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/browser/actions/Button.js +11 -2
- package/dist/browser/actions/Button.js.map +1 -1
- package/dist/browser/actions/SplitButton.js.map +1 -1
- package/dist/browser/actions/index.js.map +1 -1
- package/dist/browser/content/ZoomPanViewport.js +396 -0
- package/dist/browser/content/ZoomPanViewport.js.map +1 -0
- package/dist/browser/content/index.js +1 -0
- package/dist/browser/content/index.js.map +1 -1
- package/dist/browser/content/zoom-pan.js +54 -0
- package/dist/browser/content/zoom-pan.js.map +1 -0
- package/dist/browser/feedback/prompts.js.map +1 -1
- package/dist/browser/inputs/TextInput.js +2 -1
- package/dist/browser/inputs/TextInput.js.map +1 -1
- package/dist/browser/intl/messages.js +6 -0
- package/dist/browser/intl/messages.js.map +1 -1
- package/dist/browser/layout/PullToRefresh.js +8 -7
- package/dist/browser/layout/PullToRefresh.js.map +1 -1
- package/dist/ssr/actions/Button.js +11 -2
- package/dist/ssr/actions/Button.js.map +1 -1
- package/dist/ssr/actions/SplitButton.js.map +1 -1
- package/dist/ssr/actions/index.js.map +1 -1
- package/dist/ssr/content/ZoomPanViewport.js +300 -0
- package/dist/ssr/content/ZoomPanViewport.js.map +1 -0
- package/dist/ssr/content/index.js +1 -0
- package/dist/ssr/content/index.js.map +1 -1
- package/dist/ssr/content/zoom-pan.js +54 -0
- package/dist/ssr/content/zoom-pan.js.map +1 -0
- package/dist/ssr/feedback/prompts.js.map +1 -1
- package/dist/ssr/inputs/TextInput.js +2 -1
- package/dist/ssr/inputs/TextInput.js.map +1 -1
- package/dist/ssr/intl/messages.js +6 -0
- package/dist/ssr/intl/messages.js.map +1 -1
- package/dist/ssr/layout/PullToRefresh.js +1 -1
- package/dist/ssr/layout/PullToRefresh.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/actions/Button.d.ts +5 -0
- package/dist/types/actions/SplitButton.d.ts +1 -1
- package/dist/types/actions/index.d.ts +1 -1
- package/dist/types/content/ZoomPanViewport.d.ts +33 -0
- package/dist/types/content/index.d.ts +2 -0
- package/dist/types/content/zoom-pan.d.ts +29 -0
- package/dist/types/feedback/prompts.d.ts +2 -1
- package/dist/types/intl/messages.d.ts +3 -0
- package/package.json +3 -4
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { ssrStyleProperty as _$ssrStyleProperty } from "solid-js/web";
|
|
2
|
+
import { ssrStyle as _$ssrStyle } from "solid-js/web";
|
|
3
|
+
import { ssr as _$ssr } from "solid-js/web";
|
|
4
|
+
import { ssrAttribute as _$ssrAttribute } from "solid-js/web";
|
|
5
|
+
import { escape as _$escape } from "solid-js/web";
|
|
6
|
+
import { createComponent as _$createComponent } from "solid-js/web";
|
|
7
|
+
var _tmpl$ = ["<i", " aria-hidden=\"true\"></i>"],
|
|
8
|
+
_tmpl$2 = ["<footer class=\"k2b-dialog__actions\">", "</footer>"],
|
|
9
|
+
_tmpl$3 = ["<div class=\"k2b-dialog__panel k2b-dialog__stack\">", "<div class=\"k2b-zoom-pan-dialog\">", "", "</div></div>"],
|
|
10
|
+
_tmpl$4 = ["<button type=\"button\" class=\"k2b-button k2b-icon-button\" data-variant=\"secondary\"", " title=\"", "\"><i class=\"", "\" aria-hidden=\"true\"></i></button>"],
|
|
11
|
+
_tmpl$5 = ["<div class=\"", "\" style=\"", "\" role=\"group\"", " tabindex=\"0\"", "><div class=\"k2b-zoom-pan__stage\"", " style=\"", "\">", "</div><div class=\"k2b-zoom-pan__controls\">", "", "", "", "</div></div>"];
|
|
12
|
+
import { createSignal, For, onCleanup, onMount, Show } from "solid-js";
|
|
13
|
+
import { Button } from "../actions/Button";
|
|
14
|
+
import { dialogCore } from "../feedback/dialog-core";
|
|
15
|
+
import { DialogHeader } from "../feedback/prompts";
|
|
16
|
+
import { LocaleProvider, useLocale } from "../intl/locale";
|
|
17
|
+
import { useUiMessages } from "../intl/messages";
|
|
18
|
+
import { clampZoomPan, panZoomPan, ZOOM_PAN_FIT, ZOOM_PAN_STEP, zoomZoomPanAt } from "./zoom-pan";
|
|
19
|
+
const PAN_KEY_STEP = 48;
|
|
20
|
+
const DRAG_THRESHOLD = 4;
|
|
21
|
+
const PAN_KEYS = {
|
|
22
|
+
ArrowLeft: [PAN_KEY_STEP, 0],
|
|
23
|
+
ArrowRight: [-PAN_KEY_STEP, 0],
|
|
24
|
+
ArrowUp: [0, PAN_KEY_STEP],
|
|
25
|
+
ArrowDown: [0, -PAN_KEY_STEP]
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** Current screen transform of the stage, including a running CSS transition. */
|
|
29
|
+
const renderedTransform = (stage, fallback) => {
|
|
30
|
+
const match = /^matrix\(([^)]+)\)$/.exec(getComputedStyle(stage).transform ?? "");
|
|
31
|
+
const values = match?.[1]?.split(",").map(Number);
|
|
32
|
+
if (!values || values.length !== 6 || values.some(value => !Number.isFinite(value)) || !values[0]) return fallback;
|
|
33
|
+
return {
|
|
34
|
+
scale: values[0],
|
|
35
|
+
x: values[4] ?? 0,
|
|
36
|
+
y: values[5] ?? 0
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const ActionButton = props => {
|
|
40
|
+
const [pending, setPending] = createSignal(false);
|
|
41
|
+
const run = async () => {
|
|
42
|
+
if (pending()) return;
|
|
43
|
+
setPending(true);
|
|
44
|
+
try {
|
|
45
|
+
await props.action.onSelect();
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error("Zoom-pan action failed", error);
|
|
48
|
+
} finally {
|
|
49
|
+
setPending(false);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
return _$createComponent(Button, {
|
|
53
|
+
variant: "secondary",
|
|
54
|
+
get loading() {
|
|
55
|
+
return pending();
|
|
56
|
+
},
|
|
57
|
+
onClick: () => void run(),
|
|
58
|
+
get children() {
|
|
59
|
+
return [_$createComponent(Show, {
|
|
60
|
+
get when() {
|
|
61
|
+
return props.action.icon;
|
|
62
|
+
},
|
|
63
|
+
children: icon => _$ssr(_tmpl$, _$ssrAttribute("class", _$escape(icon(), true), false))
|
|
64
|
+
}), props.action.label];
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Makes any content zoomable (fit to 8×) and pannable with buttons, Ctrl/Cmd +
|
|
71
|
+
* wheel, pinch, drag, and keys. It applies a CSS transform, so vector content
|
|
72
|
+
* stays crisp and is never re-rendered. The view always starts at fit.
|
|
73
|
+
*/
|
|
74
|
+
export function ZoomPanViewport(props) {
|
|
75
|
+
const messages = useUiMessages();
|
|
76
|
+
const locale = useLocale();
|
|
77
|
+
const [transform, setTransform] = createSignal(ZOOM_PAN_FIT);
|
|
78
|
+
const [smooth, setSmooth] = createSignal(false);
|
|
79
|
+
const [dragging, setDragging] = createSignal(false);
|
|
80
|
+
let root;
|
|
81
|
+
let stage;
|
|
82
|
+
const pointers = new Map();
|
|
83
|
+
let drag;
|
|
84
|
+
let pinch;
|
|
85
|
+
let suppressClick = false;
|
|
86
|
+
const zoomed = () => transform().scale > 1;
|
|
87
|
+
const geometry = () => {
|
|
88
|
+
const rect = root?.getBoundingClientRect();
|
|
89
|
+
const width = rect?.width ?? 0;
|
|
90
|
+
const height = rect?.height ?? 0;
|
|
91
|
+
const content = stage?.firstElementChild?.getBoundingClientRect();
|
|
92
|
+
if (!rect || !stage || !content || content.width <= 0 || content.height <= 0) {
|
|
93
|
+
return {
|
|
94
|
+
width,
|
|
95
|
+
height,
|
|
96
|
+
content: {
|
|
97
|
+
left: 0,
|
|
98
|
+
top: 0,
|
|
99
|
+
width,
|
|
100
|
+
height
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const current = renderedTransform(stage, transform());
|
|
105
|
+
return {
|
|
106
|
+
width,
|
|
107
|
+
height,
|
|
108
|
+
content: {
|
|
109
|
+
left: (content.left - rect.left - current.x) / current.scale,
|
|
110
|
+
top: (content.top - rect.top - current.y) / current.scale,
|
|
111
|
+
width: content.width / current.scale,
|
|
112
|
+
height: content.height / current.scale
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
const apply = (next, animate) => {
|
|
117
|
+
setSmooth(animate);
|
|
118
|
+
setTransform(next);
|
|
119
|
+
};
|
|
120
|
+
const local = event => {
|
|
121
|
+
const rect = root?.getBoundingClientRect();
|
|
122
|
+
return {
|
|
123
|
+
x: event.clientX - (rect?.left ?? 0),
|
|
124
|
+
y: event.clientY - (rect?.top ?? 0)
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
const zoomBy = (factor, point, animate = true) => {
|
|
128
|
+
const box = geometry();
|
|
129
|
+
const current = transform();
|
|
130
|
+
apply(zoomZoomPanAt(current, current.scale * factor, point ?? {
|
|
131
|
+
x: box.width / 2,
|
|
132
|
+
y: box.height / 2
|
|
133
|
+
}, box), animate);
|
|
134
|
+
};
|
|
135
|
+
const reset = () => apply(ZOOM_PAN_FIT, true);
|
|
136
|
+
const openFullscreen = () => {
|
|
137
|
+
const fullscreen = props.fullscreen;
|
|
138
|
+
if (!fullscreen) return;
|
|
139
|
+
const currentLocale = locale();
|
|
140
|
+
void dialogCore.open(close => _$createComponent(LocaleProvider, {
|
|
141
|
+
locale: currentLocale,
|
|
142
|
+
get children() {
|
|
143
|
+
return _$ssr(_tmpl$3, _$escape(_$createComponent(DialogHeader, {
|
|
144
|
+
get title() {
|
|
145
|
+
return fullscreen.title;
|
|
146
|
+
},
|
|
147
|
+
close: () => close()
|
|
148
|
+
})), _$escape(_$createComponent(ZoomPanViewport, {
|
|
149
|
+
get label() {
|
|
150
|
+
return props.label;
|
|
151
|
+
},
|
|
152
|
+
get children() {
|
|
153
|
+
return fullscreen.content();
|
|
154
|
+
}
|
|
155
|
+
})), _$escape(_$createComponent(Show, {
|
|
156
|
+
get when() {
|
|
157
|
+
return fullscreen.actions?.length;
|
|
158
|
+
},
|
|
159
|
+
get children() {
|
|
160
|
+
return _$ssr(_tmpl$2, _$escape(_$createComponent(For, {
|
|
161
|
+
get each() {
|
|
162
|
+
return fullscreen.actions;
|
|
163
|
+
},
|
|
164
|
+
children: action => _$createComponent(ActionButton, {
|
|
165
|
+
action: action
|
|
166
|
+
})
|
|
167
|
+
})));
|
|
168
|
+
}
|
|
169
|
+
})));
|
|
170
|
+
}
|
|
171
|
+
}), {
|
|
172
|
+
panelClassName: "k2b-dialog k2b-dialog--full",
|
|
173
|
+
ariaLabel: fullscreen.title,
|
|
174
|
+
initialFocus: dialog => dialog.querySelector(".k2b-zoom-pan")
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
const handleKeyDown = event => {
|
|
178
|
+
if (event.target !== root || event.ctrlKey || event.metaKey || event.altKey) return;
|
|
179
|
+
const key = event.key;
|
|
180
|
+
if (key === "+" || key === "=") zoomBy(ZOOM_PAN_STEP);else if (key === "-" || key === "_") zoomBy(1 / ZOOM_PAN_STEP);else if (key === "0") reset();else if ((key === "f" || key === "F") && props.fullscreen) openFullscreen();else {
|
|
181
|
+
const delta = PAN_KEYS[key];
|
|
182
|
+
// At fit the arrow keys keep scrolling the page.
|
|
183
|
+
if (!delta || !zoomed()) return;
|
|
184
|
+
apply(panZoomPan(transform(), delta[0], delta[1], geometry()), true);
|
|
185
|
+
}
|
|
186
|
+
event.preventDefault();
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Plain wheel keeps scrolling the page; Ctrl/Cmd + wheel (and trackpad pinch) zooms toward the pointer.
|
|
190
|
+
const handleWheel = event => {
|
|
191
|
+
if (!event.ctrlKey && !event.metaKey) return;
|
|
192
|
+
event.preventDefault();
|
|
193
|
+
const lines = event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? 400 : 1;
|
|
194
|
+
const delta = Math.max(-50, Math.min(50, event.deltaY * lines));
|
|
195
|
+
zoomBy(Math.exp(-delta / 100), local(event), false);
|
|
196
|
+
};
|
|
197
|
+
const handlePointerDown = event => {
|
|
198
|
+
if (event.button !== 0 || event.target.closest("button, a, input, select, textarea")) return;
|
|
199
|
+
pointers.set(event.pointerId, local(event));
|
|
200
|
+
if (pointers.size === 2) {
|
|
201
|
+
const [first, second] = [...pointers.values()];
|
|
202
|
+
pinch = {
|
|
203
|
+
distance: Math.max(1, Math.hypot(second.x - first.x, second.y - first.y)),
|
|
204
|
+
center: {
|
|
205
|
+
x: (first.x + second.x) / 2,
|
|
206
|
+
y: (first.y + second.y) / 2
|
|
207
|
+
},
|
|
208
|
+
origin: transform()
|
|
209
|
+
};
|
|
210
|
+
drag = undefined;
|
|
211
|
+
} else if (pointers.size === 1 && zoomed()) {
|
|
212
|
+
// Panning starts only above fit, so clicks and text selection keep working at fit.
|
|
213
|
+
drag = {
|
|
214
|
+
id: event.pointerId,
|
|
215
|
+
x: event.clientX,
|
|
216
|
+
y: event.clientY,
|
|
217
|
+
origin: transform(),
|
|
218
|
+
moved: false
|
|
219
|
+
};
|
|
220
|
+
} else return;
|
|
221
|
+
try {
|
|
222
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
223
|
+
} catch {
|
|
224
|
+
// A synthetic or already released pointer cannot be captured; moves still arrive while inside.
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
const handlePointerMove = event => {
|
|
228
|
+
if (!pointers.has(event.pointerId)) return;
|
|
229
|
+
pointers.set(event.pointerId, local(event));
|
|
230
|
+
if (pinch && pointers.size >= 2) {
|
|
231
|
+
const [first, second] = [...pointers.values()];
|
|
232
|
+
const distance = Math.hypot(second.x - first.x, second.y - first.y);
|
|
233
|
+
const center = {
|
|
234
|
+
x: (first.x + second.x) / 2,
|
|
235
|
+
y: (first.y + second.y) / 2
|
|
236
|
+
};
|
|
237
|
+
const box = geometry();
|
|
238
|
+
const scaled = zoomZoomPanAt(pinch.origin, pinch.origin.scale * (distance / pinch.distance), pinch.center, box);
|
|
239
|
+
apply(panZoomPan(scaled, center.x - pinch.center.x, center.y - pinch.center.y, box), false);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (drag?.id !== event.pointerId) return;
|
|
243
|
+
const dx = event.clientX - drag.x;
|
|
244
|
+
const dy = event.clientY - drag.y;
|
|
245
|
+
if (!drag.moved && Math.hypot(dx, dy) < DRAG_THRESHOLD) return;
|
|
246
|
+
drag.moved = true;
|
|
247
|
+
setDragging(true);
|
|
248
|
+
apply(panZoomPan(drag.origin, dx, dy, geometry()), false);
|
|
249
|
+
};
|
|
250
|
+
const handlePointerEnd = event => {
|
|
251
|
+
pointers.delete(event.pointerId);
|
|
252
|
+
if (pointers.size < 2) pinch = undefined;
|
|
253
|
+
if (drag?.id !== event.pointerId) return;
|
|
254
|
+
if (drag.moved) {
|
|
255
|
+
// The click that follows this pointerup (if the browser sends one) comes in the same task.
|
|
256
|
+
suppressClick = true;
|
|
257
|
+
setTimeout(() => {
|
|
258
|
+
suppressClick = false;
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
drag = undefined;
|
|
262
|
+
setDragging(false);
|
|
263
|
+
};
|
|
264
|
+
onMount(() => {
|
|
265
|
+
const element = root;
|
|
266
|
+
if (!element) return;
|
|
267
|
+
// A drag must not also count as a click on the content or its owner.
|
|
268
|
+
const click = event => {
|
|
269
|
+
if (!suppressClick) return;
|
|
270
|
+
suppressClick = false;
|
|
271
|
+
event.preventDefault();
|
|
272
|
+
event.stopPropagation();
|
|
273
|
+
};
|
|
274
|
+
// Keep a two-finger gesture for pinch zoom instead of page panning.
|
|
275
|
+
const touchMove = event => {
|
|
276
|
+
if (event.touches.length > 1) event.preventDefault();
|
|
277
|
+
};
|
|
278
|
+
element.addEventListener("click", click, true);
|
|
279
|
+
element.addEventListener("touchmove", touchMove, {
|
|
280
|
+
passive: false
|
|
281
|
+
});
|
|
282
|
+
const observer = typeof ResizeObserver === "undefined" ? undefined : new ResizeObserver(() => apply(clampZoomPan(transform(), geometry()), false));
|
|
283
|
+
observer?.observe(element);
|
|
284
|
+
onCleanup(() => {
|
|
285
|
+
element.removeEventListener("click", click, true);
|
|
286
|
+
element.removeEventListener("touchmove", touchMove);
|
|
287
|
+
observer?.disconnect();
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
const control = (label, shortcut, icon, action) => _$ssr(_tmpl$4, _$ssrAttribute("aria-label", _$escape(label, true), false), `${_$escape(label, true)} (${_$escape(shortcut, true)})`, `ti ${_$escape(icon, true)}`);
|
|
291
|
+
return _$ssr(_tmpl$5, `k2b-zoom-pan ${_$escape(props.class ?? "", true)}`, _$ssrStyle(props.style), _$ssrAttribute("aria-label", _$escape(props.label, true), false) + _$ssrAttribute("aria-description", _$escape(messages().zoomPanKeys, true), false), _$ssrAttribute("data-controls", _$escape(props.controls ?? "visible", true), false) + _$ssrAttribute("data-zoomed", zoomed() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-dragging", dragging() ? "true" : _$escape(undefined, true), false), _$ssrAttribute("data-smooth", smooth() ? "true" : _$escape(undefined, true), false), _$ssrStyleProperty("transform:", `translate(${_$escape(transform().x, true)}px, ${_$escape(transform().y, true)}px) scale(${_$escape(transform().scale, true)})`), _$escape(props.children), _$escape(control(messages().zoomIn, "+", "ti-plus", () => zoomBy(ZOOM_PAN_STEP))), _$escape(control(messages().zoomOut, "-", "ti-minus", () => zoomBy(1 / ZOOM_PAN_STEP))), _$escape(control(messages().resetZoom, "0", "ti-focus-centered", reset)), _$escape(_$createComponent(Show, {
|
|
292
|
+
get when() {
|
|
293
|
+
return props.fullscreen;
|
|
294
|
+
},
|
|
295
|
+
get children() {
|
|
296
|
+
return control(messages().openFullscreen, "F", "ti-maximize", openFullscreen);
|
|
297
|
+
}
|
|
298
|
+
})));
|
|
299
|
+
}
|
|
300
|
+
//# sourceMappingURL=ZoomPanViewport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["content/ZoomPanViewport.tsx"],"sourcesContent":["import { createSignal, For, type JSX, onCleanup, onMount, Show } from \"solid-js\";\nimport { Button } from \"../actions/Button\";\nimport { dialogCore } from \"../feedback/dialog-core\";\nimport { DialogHeader } from \"../feedback/prompts\";\nimport { LocaleProvider, useLocale } from \"../intl/locale\";\nimport { useUiMessages } from \"../intl/messages\";\nimport {\n clampZoomPan,\n panZoomPan,\n ZOOM_PAN_FIT,\n ZOOM_PAN_STEP,\n type ZoomPanGeometry,\n type ZoomPanTransform,\n zoomZoomPanAt,\n} from \"./zoom-pan\";\n\nexport type ZoomPanAction = {\n label: string;\n icon?: string;\n /** Runs the action; the button shows a pending state until a returned promise settles. Handle failures here. */\n onSelect: () => void | Promise<void>;\n};\n\nexport type ZoomPanFullscreen = {\n /** Heading and accessible name of the fullscreen dialog. */\n title: string;\n /** Renders a fresh copy of the content for the dialog. Called once per opening. */\n content: () => JSX.Element;\n /** Actions shown in the fullscreen dialog, for example exports. */\n actions?: readonly ZoomPanAction[];\n};\n\nexport type ZoomPanViewportProps = {\n /** Accessible name of the focusable viewport. */\n label: string;\n /** The zoomable content. Its first element is measured to keep it on screen. */\n children: JSX.Element;\n /** Enables the fullscreen control and the `F` shortcut. */\n fullscreen?: ZoomPanFullscreen;\n /** `hover` reveals the controls on hover, on focus, and while zoomed in. Touch screens always show them. */\n controls?: \"visible\" | \"hover\";\n class?: string;\n style?: JSX.CSSProperties;\n};\n\nconst PAN_KEY_STEP = 48;\nconst DRAG_THRESHOLD = 4;\nconst PAN_KEYS: Record<string, readonly [number, number]> = {\n ArrowLeft: [PAN_KEY_STEP, 0],\n ArrowRight: [-PAN_KEY_STEP, 0],\n ArrowUp: [0, PAN_KEY_STEP],\n ArrowDown: [0, -PAN_KEY_STEP],\n};\n\n/** Current screen transform of the stage, including a running CSS transition. */\nconst renderedTransform = (stage: HTMLElement, fallback: ZoomPanTransform): ZoomPanTransform => {\n const match = /^matrix\\(([^)]+)\\)$/.exec(getComputedStyle(stage).transform ?? \"\");\n const values = match?.[1]?.split(\",\").map(Number);\n if (!values || values.length !== 6 || values.some((value) => !Number.isFinite(value)) || !values[0]) return fallback;\n return { scale: values[0], x: values[4] ?? 0, y: values[5] ?? 0 };\n};\n\nconst ActionButton = (props: { action: ZoomPanAction }): JSX.Element => {\n const [pending, setPending] = createSignal(false);\n const run = async () => {\n if (pending()) return;\n setPending(true);\n try {\n await props.action.onSelect();\n } catch (error) {\n console.error(\"Zoom-pan action failed\", error);\n } finally {\n setPending(false);\n }\n };\n return (\n <Button variant=\"secondary\" loading={pending()} onClick={() => void run()}>\n <Show when={props.action.icon}>{(icon) => <i class={icon()} aria-hidden=\"true\" />}</Show>\n {props.action.label}\n </Button>\n );\n};\n\n/**\n * Makes any content zoomable (fit to 8×) and pannable with buttons, Ctrl/Cmd +\n * wheel, pinch, drag, and keys. It applies a CSS transform, so vector content\n * stays crisp and is never re-rendered. The view always starts at fit.\n */\nexport function ZoomPanViewport(props: ZoomPanViewportProps): JSX.Element {\n const messages = useUiMessages();\n const locale = useLocale();\n const [transform, setTransform] = createSignal<ZoomPanTransform>(ZOOM_PAN_FIT);\n const [smooth, setSmooth] = createSignal(false);\n const [dragging, setDragging] = createSignal(false);\n let root: HTMLDivElement | undefined;\n let stage: HTMLDivElement | undefined;\n const pointers = new Map<number, { x: number; y: number }>();\n let drag: { id: number; x: number; y: number; origin: ZoomPanTransform; moved: boolean } | undefined;\n let pinch: { distance: number; center: { x: number; y: number }; origin: ZoomPanTransform } | undefined;\n let suppressClick = false;\n\n const zoomed = () => transform().scale > 1;\n\n const geometry = (): ZoomPanGeometry => {\n const rect = root?.getBoundingClientRect();\n const width = rect?.width ?? 0;\n const height = rect?.height ?? 0;\n const content = stage?.firstElementChild?.getBoundingClientRect();\n if (!rect || !stage || !content || content.width <= 0 || content.height <= 0) {\n return { width, height, content: { left: 0, top: 0, width, height } };\n }\n const current = renderedTransform(stage, transform());\n return {\n width,\n height,\n content: {\n left: (content.left - rect.left - current.x) / current.scale,\n top: (content.top - rect.top - current.y) / current.scale,\n width: content.width / current.scale,\n height: content.height / current.scale,\n },\n };\n };\n\n const apply = (next: ZoomPanTransform, animate: boolean) => {\n setSmooth(animate);\n setTransform(next);\n };\n\n const local = (event: { clientX: number; clientY: number }) => {\n const rect = root?.getBoundingClientRect();\n return { x: event.clientX - (rect?.left ?? 0), y: event.clientY - (rect?.top ?? 0) };\n };\n\n const zoomBy = (factor: number, point?: { x: number; y: number }, animate = true) => {\n const box = geometry();\n const current = transform();\n apply(zoomZoomPanAt(current, current.scale * factor, point ?? { x: box.width / 2, y: box.height / 2 }, box), animate);\n };\n\n const reset = () => apply(ZOOM_PAN_FIT, true);\n\n const openFullscreen = () => {\n const fullscreen = props.fullscreen;\n if (!fullscreen) return;\n const currentLocale = locale();\n void dialogCore.open<void>(\n (close) => (\n <LocaleProvider locale={currentLocale}>\n <div class=\"k2b-dialog__panel k2b-dialog__stack\">\n <DialogHeader title={fullscreen.title} close={() => close()} />\n <div class=\"k2b-zoom-pan-dialog\">\n <ZoomPanViewport label={props.label}>{fullscreen.content()}</ZoomPanViewport>\n <Show when={fullscreen.actions?.length}>\n <footer class=\"k2b-dialog__actions\">\n <For each={fullscreen.actions}>{(action) => <ActionButton action={action} />}</For>\n </footer>\n </Show>\n </div>\n </div>\n </LocaleProvider>\n ),\n {\n panelClassName: \"k2b-dialog k2b-dialog--full\",\n ariaLabel: fullscreen.title,\n initialFocus: (dialog) => dialog.querySelector<HTMLElement>(\".k2b-zoom-pan\"),\n },\n );\n };\n\n const handleKeyDown: JSX.EventHandler<HTMLDivElement, KeyboardEvent> = (event) => {\n if (event.target !== root || event.ctrlKey || event.metaKey || event.altKey) return;\n const key = event.key;\n if (key === \"+\" || key === \"=\") zoomBy(ZOOM_PAN_STEP);\n else if (key === \"-\" || key === \"_\") zoomBy(1 / ZOOM_PAN_STEP);\n else if (key === \"0\") reset();\n else if ((key === \"f\" || key === \"F\") && props.fullscreen) openFullscreen();\n else {\n const delta = PAN_KEYS[key];\n // At fit the arrow keys keep scrolling the page.\n if (!delta || !zoomed()) return;\n apply(panZoomPan(transform(), delta[0], delta[1], geometry()), true);\n }\n event.preventDefault();\n };\n\n // Plain wheel keeps scrolling the page; Ctrl/Cmd + wheel (and trackpad pinch) zooms toward the pointer.\n const handleWheel: JSX.EventHandler<HTMLDivElement, WheelEvent> = (event) => {\n if (!event.ctrlKey && !event.metaKey) return;\n event.preventDefault();\n const lines = event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? 400 : 1;\n const delta = Math.max(-50, Math.min(50, event.deltaY * lines));\n zoomBy(Math.exp(-delta / 100), local(event), false);\n };\n\n const handlePointerDown: JSX.EventHandler<HTMLDivElement, PointerEvent> = (event) => {\n if (event.button !== 0 || (event.target as Element).closest(\"button, a, input, select, textarea\")) return;\n pointers.set(event.pointerId, local(event));\n if (pointers.size === 2) {\n const [first, second] = [...pointers.values()] as [{ x: number; y: number }, { x: number; y: number }];\n pinch = {\n distance: Math.max(1, Math.hypot(second.x - first.x, second.y - first.y)),\n center: { x: (first.x + second.x) / 2, y: (first.y + second.y) / 2 },\n origin: transform(),\n };\n drag = undefined;\n } else if (pointers.size === 1 && zoomed()) {\n // Panning starts only above fit, so clicks and text selection keep working at fit.\n drag = { id: event.pointerId, x: event.clientX, y: event.clientY, origin: transform(), moved: false };\n } else return;\n try {\n event.currentTarget.setPointerCapture(event.pointerId);\n } catch {\n // A synthetic or already released pointer cannot be captured; moves still arrive while inside.\n }\n };\n\n const handlePointerMove: JSX.EventHandler<HTMLDivElement, PointerEvent> = (event) => {\n if (!pointers.has(event.pointerId)) return;\n pointers.set(event.pointerId, local(event));\n if (pinch && pointers.size >= 2) {\n const [first, second] = [...pointers.values()] as [{ x: number; y: number }, { x: number; y: number }];\n const distance = Math.hypot(second.x - first.x, second.y - first.y);\n const center = { x: (first.x + second.x) / 2, y: (first.y + second.y) / 2 };\n const box = geometry();\n const scaled = zoomZoomPanAt(pinch.origin, pinch.origin.scale * (distance / pinch.distance), pinch.center, box);\n apply(panZoomPan(scaled, center.x - pinch.center.x, center.y - pinch.center.y, box), false);\n return;\n }\n if (drag?.id !== event.pointerId) return;\n const dx = event.clientX - drag.x;\n const dy = event.clientY - drag.y;\n if (!drag.moved && Math.hypot(dx, dy) < DRAG_THRESHOLD) return;\n drag.moved = true;\n setDragging(true);\n apply(panZoomPan(drag.origin, dx, dy, geometry()), false);\n };\n\n const handlePointerEnd: JSX.EventHandler<HTMLDivElement, PointerEvent> = (event) => {\n pointers.delete(event.pointerId);\n if (pointers.size < 2) pinch = undefined;\n if (drag?.id !== event.pointerId) return;\n if (drag.moved) {\n // The click that follows this pointerup (if the browser sends one) comes in the same task.\n suppressClick = true;\n setTimeout(() => {\n suppressClick = false;\n });\n }\n drag = undefined;\n setDragging(false);\n };\n\n onMount(() => {\n const element = root;\n if (!element) return;\n // A drag must not also count as a click on the content or its owner.\n const click = (event: MouseEvent) => {\n if (!suppressClick) return;\n suppressClick = false;\n event.preventDefault();\n event.stopPropagation();\n };\n // Keep a two-finger gesture for pinch zoom instead of page panning.\n const touchMove = (event: TouchEvent) => {\n if (event.touches.length > 1) event.preventDefault();\n };\n element.addEventListener(\"click\", click, true);\n element.addEventListener(\"touchmove\", touchMove, { passive: false });\n const observer =\n typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(() => apply(clampZoomPan(transform(), geometry()), false));\n observer?.observe(element);\n onCleanup(() => {\n element.removeEventListener(\"click\", click, true);\n element.removeEventListener(\"touchmove\", touchMove);\n observer?.disconnect();\n });\n });\n\n const control = (label: string, shortcut: string, icon: string, action: () => void) => (\n <button\n type=\"button\"\n class=\"k2b-button k2b-icon-button\"\n data-variant=\"secondary\"\n aria-label={label}\n title={`${label} (${shortcut})`}\n onClick={action}\n >\n <i class={`ti ${icon}`} aria-hidden=\"true\" />\n </button>\n );\n\n return (\n <div\n ref={root}\n class={`k2b-zoom-pan ${props.class ?? \"\"}`}\n style={props.style}\n role=\"group\"\n aria-label={props.label}\n aria-description={messages().zoomPanKeys}\n tabIndex={0}\n data-controls={props.controls ?? \"visible\"}\n data-zoomed={zoomed() ? \"true\" : undefined}\n data-dragging={dragging() ? \"true\" : undefined}\n onKeyDown={handleKeyDown}\n onWheel={handleWheel}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerEnd}\n onPointerCancel={handlePointerEnd}\n >\n <div\n ref={stage}\n class=\"k2b-zoom-pan__stage\"\n data-smooth={smooth() ? \"true\" : undefined}\n style={{ transform: `translate(${transform().x}px, ${transform().y}px) scale(${transform().scale})` }}\n >\n {props.children}\n </div>\n <div class=\"k2b-zoom-pan__controls\">\n {control(messages().zoomIn, \"+\", \"ti-plus\", () => zoomBy(ZOOM_PAN_STEP))}\n {control(messages().zoomOut, \"-\", \"ti-minus\", () => zoomBy(1 / ZOOM_PAN_STEP))}\n {control(messages().resetZoom, \"0\", \"ti-focus-centered\", reset)}\n <Show when={props.fullscreen}>{control(messages().openFullscreen, \"F\", \"ti-maximize\", openFullscreen)}</Show>\n </div>\n </div>\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -22,4 +22,5 @@ export { Pagination } from "./Pagination";
|
|
|
22
22
|
export { default as PdfPreview } from "./PdfPreview";
|
|
23
23
|
export { default as RangePicker } from "./RangePicker";
|
|
24
24
|
export { default as StructuredDataPreview, isStructuredDataValue } from "./StructuredDataPreview";
|
|
25
|
+
export { ZoomPanViewport } from "./ZoomPanViewport";
|
|
25
26
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["content/index.ts"],"sourcesContent":["export type {\n CalendarAttendee,\n CalendarDayBadge,\n CalendarEvent,\n CalendarEventColor,\n CalendarEventRenderContext,\n CalendarEventTimeChange,\n CalendarLabels,\n CalendarProps,\n CalendarRecurrence,\n CalendarResource,\n CalendarView,\n} from \"./Calendar\";\nexport { default as Calendar } from \"./Calendar\";\n\nexport type { ChartKind, ChartLabels, ChartProps, ChartRenderOptions } from \"./Chart\";\nexport { default as Chart } from \"./Chart\";\nexport type { ChartExplorerColumn, ChartExplorerProps } from \"./ChartExplorer\";\nexport { ChartExplorer } from \"./ChartExplorer\";\nexport type { ChartExplorerControlsProps } from \"./ChartExplorerControls\";\nexport { ChartExplorerControls } from \"./ChartExplorerControls\";\nexport { ChartFilterControls, type ChartFilterControlsProps } from \"./ChartFilterControls\";\nexport { ChartSnapshotView } from \"./ChartSnapshotView\";\nexport type { CodeDisplayLanguage, CodeDisplayProps } from \"./CodeDisplay\";\nexport { default as CodeDisplay } from \"./CodeDisplay\";\nexport { type ChartCursor, createChartCursor } from \"./chart-cursor\";\nexport type {\n ChartExplorerCharts,\n ChartExplorerController,\n ChartExplorerData,\n ChartExplorerOptions,\n ChartExplorerRequest,\n ChartExplorerRow,\n ChartExplorerSnapshot,\n} from \"./chart-explorer\";\nexport { createChartExplorer } from \"./chart-explorer\";\nexport type { ChartDatumRef, ChartSelection, ChartTooltip, ChartTooltipFormatter } from \"./chart-inspection\";\nexport type { ChartSnapshot } from \"./chart-snapshot\";\nexport { prepareChartSnapshot } from \"./chart-snapshot\";\nexport type {\n StateTimelineChartOptions,\n StateTimelineDomain,\n StateTimelineInterval,\n StateTimelineRow,\n StateTimelineState,\n} from \"./chart-state-timeline\";\nexport { type CollectionSelection, createCollectionSelection, type SelectionModifiers } from \"./collection-selection\";\nexport type {\n DataTableColumn,\n DataTableControlsProps,\n DataTableFooter,\n DataTableHeaderProps,\n DataTablePanelFooterProps,\n DataTablePanelProps,\n DataTableProps,\n DataTableRenderCell,\n DataTableRenderHeader,\n DataTableSort,\n} from \"./DataTable\";\nexport { default as DataTable } from \"./DataTable\";\nexport type { DocCodeHighlighter, DocCodeProps, DocConcept, DocNoteVariant, DocRow } from \"./Docs\";\nexport { DocCode, DocConceptGrid, DocInlineCode, DocLead, DocNote, DocPage, DocRows, DocSection } from \"./Docs\";\nexport { FileBrowserPanel, type FileBrowserPanelProps, type FileSource, openFileBrowser } from \"./FileBrowser\";\nexport { FileGrid, type FileGridProps } from \"./FileGrid\";\nexport { default as FileTree, type FileTreeActions, type FileTreeEntry, type FileTreeProps } from \"./FileTree\";\nexport {\n canPreviewFile,\n default as FileView,\n type FileViewContent,\n type FileViewFile,\n type FileViewPreviewKind,\n type FileViewProps,\n type FileViewRenderer,\n type FileViewRendererProps,\n formatFileViewSize,\n getFileViewPreviewKind,\n} from \"./FileView\";\nexport type { LightboxImage } from \"./Lightbox\";\nexport { default as Lightbox } from \"./Lightbox\";\nexport type { LogTableEntry } from \"./LogEntriesTable\";\nexport { default as LogEntriesTable } from \"./LogEntriesTable\";\nexport { default as MarkdownView, type MarkdownRenderOptions, type MarkdownViewProps, renderSafeMarkdown } from \"./MarkdownView\";\nexport { Pagination, type PaginationProps } from \"./Pagination\";\nexport type { PdfPreviewProps, PdfPreviewRequest } from \"./PdfPreview\";\nexport { default as PdfPreview } from \"./PdfPreview\";\nexport type { RangeOption, RangePickerProps } from \"./RangePicker\";\nexport { default as RangePicker } from \"./RangePicker\";\nexport type { StructuredDataPreviewMode, StructuredDataPreviewProps, StructuredDataValue } from \"./StructuredDataPreview\";\nexport { default as StructuredDataPreview, isStructuredDataValue } from \"./StructuredDataPreview\";\n"],"mappings":"AAaA,SAAS,OAAO,IAAI,QAAQ,QAAQ,YAAY;AAGhD,SAAS,OAAO,IAAI,KAAK,QAAQ,SAAS;AAE1C,SAAS,aAAa,QAAQ,iBAAiB;AAE/C,SAAS,qBAAqB,QAAQ,yBAAyB;AAC/D,SAAS,mBAAmB,QAAuC,uBAAuB;AAC1F,SAAS,iBAAiB,QAAQ,qBAAqB;AAEvD,SAAS,OAAO,IAAI,WAAW,QAAQ,eAAe;AACtD,SAA2B,iBAAiB,QAAQ,gBAAgB;AAUpE,SAAS,mBAAmB,QAAQ,kBAAkB;AAGtD,SAAS,oBAAoB,QAAQ,kBAAkB;AAQvD,SAAmC,yBAAyB,QAAiC,wBAAwB;AAarH,SAAS,OAAO,IAAI,SAAS,QAAQ,aAAa;AAElD,SAAS,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,QAAQ,QAAQ;AAC/G,SAAS,gBAAgB,EAA+C,eAAe,QAAQ,eAAe;AAC9G,SAAS,QAAQ,QAA4B,YAAY;AACzD,SAAS,OAAO,IAAI,QAAQ,QAAsE,YAAY;AAC9G,SACE,cAAc,EACd,OAAO,IAAI,QAAQ,EAOnB,kBAAkB,EAClB,sBAAsB,QACjB,YAAY;AAEnB,SAAS,OAAO,IAAI,QAAQ,QAAQ,YAAY;AAEhD,SAAS,OAAO,IAAI,eAAe,QAAQ,mBAAmB;AAC9D,SAAS,OAAO,IAAI,YAAY,EAAsD,kBAAkB,QAAQ,gBAAgB;AAChI,SAAS,UAAU,QAA8B,cAAc;AAE/D,SAAS,OAAO,IAAI,UAAU,QAAQ,cAAc;AAEpD,SAAS,OAAO,IAAI,WAAW,QAAQ,eAAe;AAEtD,SAAS,OAAO,IAAI,qBAAqB,EAAE,qBAAqB,QAAQ,yBAAyB","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["content/index.ts"],"sourcesContent":["export type {\n CalendarAttendee,\n CalendarDayBadge,\n CalendarEvent,\n CalendarEventColor,\n CalendarEventRenderContext,\n CalendarEventTimeChange,\n CalendarLabels,\n CalendarProps,\n CalendarRecurrence,\n CalendarResource,\n CalendarView,\n} from \"./Calendar\";\nexport { default as Calendar } from \"./Calendar\";\n\nexport type { ChartKind, ChartLabels, ChartProps, ChartRenderOptions } from \"./Chart\";\nexport { default as Chart } from \"./Chart\";\nexport type { ChartExplorerColumn, ChartExplorerProps } from \"./ChartExplorer\";\nexport { ChartExplorer } from \"./ChartExplorer\";\nexport type { ChartExplorerControlsProps } from \"./ChartExplorerControls\";\nexport { ChartExplorerControls } from \"./ChartExplorerControls\";\nexport { ChartFilterControls, type ChartFilterControlsProps } from \"./ChartFilterControls\";\nexport { ChartSnapshotView } from \"./ChartSnapshotView\";\nexport type { CodeDisplayLanguage, CodeDisplayProps } from \"./CodeDisplay\";\nexport { default as CodeDisplay } from \"./CodeDisplay\";\nexport { type ChartCursor, createChartCursor } from \"./chart-cursor\";\nexport type {\n ChartExplorerCharts,\n ChartExplorerController,\n ChartExplorerData,\n ChartExplorerOptions,\n ChartExplorerRequest,\n ChartExplorerRow,\n ChartExplorerSnapshot,\n} from \"./chart-explorer\";\nexport { createChartExplorer } from \"./chart-explorer\";\nexport type { ChartDatumRef, ChartSelection, ChartTooltip, ChartTooltipFormatter } from \"./chart-inspection\";\nexport type { ChartSnapshot } from \"./chart-snapshot\";\nexport { prepareChartSnapshot } from \"./chart-snapshot\";\nexport type {\n StateTimelineChartOptions,\n StateTimelineDomain,\n StateTimelineInterval,\n StateTimelineRow,\n StateTimelineState,\n} from \"./chart-state-timeline\";\nexport { type CollectionSelection, createCollectionSelection, type SelectionModifiers } from \"./collection-selection\";\nexport type {\n DataTableColumn,\n DataTableControlsProps,\n DataTableFooter,\n DataTableHeaderProps,\n DataTablePanelFooterProps,\n DataTablePanelProps,\n DataTableProps,\n DataTableRenderCell,\n DataTableRenderHeader,\n DataTableSort,\n} from \"./DataTable\";\nexport { default as DataTable } from \"./DataTable\";\nexport type { DocCodeHighlighter, DocCodeProps, DocConcept, DocNoteVariant, DocRow } from \"./Docs\";\nexport { DocCode, DocConceptGrid, DocInlineCode, DocLead, DocNote, DocPage, DocRows, DocSection } from \"./Docs\";\nexport { FileBrowserPanel, type FileBrowserPanelProps, type FileSource, openFileBrowser } from \"./FileBrowser\";\nexport { FileGrid, type FileGridProps } from \"./FileGrid\";\nexport { default as FileTree, type FileTreeActions, type FileTreeEntry, type FileTreeProps } from \"./FileTree\";\nexport {\n canPreviewFile,\n default as FileView,\n type FileViewContent,\n type FileViewFile,\n type FileViewPreviewKind,\n type FileViewProps,\n type FileViewRenderer,\n type FileViewRendererProps,\n formatFileViewSize,\n getFileViewPreviewKind,\n} from \"./FileView\";\nexport type { LightboxImage } from \"./Lightbox\";\nexport { default as Lightbox } from \"./Lightbox\";\nexport type { LogTableEntry } from \"./LogEntriesTable\";\nexport { default as LogEntriesTable } from \"./LogEntriesTable\";\nexport { default as MarkdownView, type MarkdownRenderOptions, type MarkdownViewProps, renderSafeMarkdown } from \"./MarkdownView\";\nexport { Pagination, type PaginationProps } from \"./Pagination\";\nexport type { PdfPreviewProps, PdfPreviewRequest } from \"./PdfPreview\";\nexport { default as PdfPreview } from \"./PdfPreview\";\nexport type { RangeOption, RangePickerProps } from \"./RangePicker\";\nexport { default as RangePicker } from \"./RangePicker\";\nexport type { StructuredDataPreviewMode, StructuredDataPreviewProps, StructuredDataValue } from \"./StructuredDataPreview\";\nexport { default as StructuredDataPreview, isStructuredDataValue } from \"./StructuredDataPreview\";\nexport type { ZoomPanAction, ZoomPanFullscreen, ZoomPanViewportProps } from \"./ZoomPanViewport\";\nexport { ZoomPanViewport } from \"./ZoomPanViewport\";\n"],"mappings":"AAaA,SAAS,OAAO,IAAI,QAAQ,QAAQ,YAAY;AAGhD,SAAS,OAAO,IAAI,KAAK,QAAQ,SAAS;AAE1C,SAAS,aAAa,QAAQ,iBAAiB;AAE/C,SAAS,qBAAqB,QAAQ,yBAAyB;AAC/D,SAAS,mBAAmB,QAAuC,uBAAuB;AAC1F,SAAS,iBAAiB,QAAQ,qBAAqB;AAEvD,SAAS,OAAO,IAAI,WAAW,QAAQ,eAAe;AACtD,SAA2B,iBAAiB,QAAQ,gBAAgB;AAUpE,SAAS,mBAAmB,QAAQ,kBAAkB;AAGtD,SAAS,oBAAoB,QAAQ,kBAAkB;AAQvD,SAAmC,yBAAyB,QAAiC,wBAAwB;AAarH,SAAS,OAAO,IAAI,SAAS,QAAQ,aAAa;AAElD,SAAS,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,QAAQ,QAAQ;AAC/G,SAAS,gBAAgB,EAA+C,eAAe,QAAQ,eAAe;AAC9G,SAAS,QAAQ,QAA4B,YAAY;AACzD,SAAS,OAAO,IAAI,QAAQ,QAAsE,YAAY;AAC9G,SACE,cAAc,EACd,OAAO,IAAI,QAAQ,EAOnB,kBAAkB,EAClB,sBAAsB,QACjB,YAAY;AAEnB,SAAS,OAAO,IAAI,QAAQ,QAAQ,YAAY;AAEhD,SAAS,OAAO,IAAI,eAAe,QAAQ,mBAAmB;AAC9D,SAAS,OAAO,IAAI,YAAY,EAAsD,kBAAkB,QAAQ,gBAAgB;AAChI,SAAS,UAAU,QAA8B,cAAc;AAE/D,SAAS,OAAO,IAAI,UAAU,QAAQ,cAAc;AAEpD,SAAS,OAAO,IAAI,WAAW,QAAQ,eAAe;AAEtD,SAAS,OAAO,IAAI,qBAAqB,EAAE,qBAAqB,QAAQ,yBAAyB;AAEjG,SAAS,eAAe,QAAQ,mBAAmB","ignoreList":[]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Screen transform of zoomable content: `translate(x, y) scale(scale)` from the viewport origin. */
|
|
2
|
+
|
|
3
|
+
/** Viewport size and the content's untransformed box inside it, all in CSS pixels. */
|
|
4
|
+
|
|
5
|
+
/** Fit is the untransformed layout; zoom never goes below it. */
|
|
6
|
+
export const ZOOM_PAN_FIT = {
|
|
7
|
+
scale: 1,
|
|
8
|
+
x: 0,
|
|
9
|
+
y: 0
|
|
10
|
+
};
|
|
11
|
+
export const ZOOM_PAN_MAX_SCALE = 8;
|
|
12
|
+
/** Button and key steps: six steps of √2 reach the maximum from fit. */
|
|
13
|
+
export const ZOOM_PAN_STEP = Math.SQRT2;
|
|
14
|
+
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Keep content on screen: content larger than the viewport must cover it, and
|
|
18
|
+
* smaller content must stay fully inside it. Fit is always a valid position.
|
|
19
|
+
*/
|
|
20
|
+
const clampAxis = (offset, scale, start, size, viewport) => {
|
|
21
|
+
const first = -scale * start;
|
|
22
|
+
const last = viewport - scale * (start + size);
|
|
23
|
+
return clamp(offset, Math.min(first, last), Math.max(first, last));
|
|
24
|
+
};
|
|
25
|
+
export const clampZoomPan = (transform, geometry) => {
|
|
26
|
+
const scale = clamp(Number.isFinite(transform.scale) ? transform.scale : 1, 1, ZOOM_PAN_MAX_SCALE);
|
|
27
|
+
// Snap to the exact fit so a zoom-out round trip leaves no residual offset.
|
|
28
|
+
if (scale <= 1.0001) return ZOOM_PAN_FIT;
|
|
29
|
+
const {
|
|
30
|
+
content
|
|
31
|
+
} = geometry;
|
|
32
|
+
return {
|
|
33
|
+
scale,
|
|
34
|
+
x: clampAxis(transform.x, scale, content.left, content.width, geometry.width),
|
|
35
|
+
y: clampAxis(transform.y, scale, content.top, content.height, geometry.height)
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** Zoom to `scale` while the content point under `point` (viewport pixels) stays under it. */
|
|
40
|
+
export const zoomZoomPanAt = (transform, scale, point, geometry) => {
|
|
41
|
+
const next = clamp(scale, 1, ZOOM_PAN_MAX_SCALE);
|
|
42
|
+
const ratio = next / transform.scale;
|
|
43
|
+
return clampZoomPan({
|
|
44
|
+
scale: next,
|
|
45
|
+
x: point.x - (point.x - transform.x) * ratio,
|
|
46
|
+
y: point.y - (point.y - transform.y) * ratio
|
|
47
|
+
}, geometry);
|
|
48
|
+
};
|
|
49
|
+
export const panZoomPan = (transform, deltaX, deltaY, geometry) => clampZoomPan({
|
|
50
|
+
scale: transform.scale,
|
|
51
|
+
x: transform.x + deltaX,
|
|
52
|
+
y: transform.y + deltaY
|
|
53
|
+
}, geometry);
|
|
54
|
+
//# sourceMappingURL=zoom-pan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["content/zoom-pan.ts"],"sourcesContent":["/** Screen transform of zoomable content: `translate(x, y) scale(scale)` from the viewport origin. */\nexport type ZoomPanTransform = { scale: number; x: number; y: number };\n\n/** Viewport size and the content's untransformed box inside it, all in CSS pixels. */\nexport type ZoomPanGeometry = {\n width: number;\n height: number;\n content: { left: number; top: number; width: number; height: number };\n};\n\n/** Fit is the untransformed layout; zoom never goes below it. */\nexport const ZOOM_PAN_FIT: ZoomPanTransform = { scale: 1, x: 0, y: 0 };\nexport const ZOOM_PAN_MAX_SCALE = 8;\n/** Button and key steps: six steps of √2 reach the maximum from fit. */\nexport const ZOOM_PAN_STEP = Math.SQRT2;\n\nconst clamp = (value: number, min: number, max: number): number => Math.min(max, Math.max(min, value));\n\n/**\n * Keep content on screen: content larger than the viewport must cover it, and\n * smaller content must stay fully inside it. Fit is always a valid position.\n */\nconst clampAxis = (offset: number, scale: number, start: number, size: number, viewport: number): number => {\n const first = -scale * start;\n const last = viewport - scale * (start + size);\n return clamp(offset, Math.min(first, last), Math.max(first, last));\n};\n\nexport const clampZoomPan = (transform: ZoomPanTransform, geometry: ZoomPanGeometry): ZoomPanTransform => {\n const scale = clamp(Number.isFinite(transform.scale) ? transform.scale : 1, 1, ZOOM_PAN_MAX_SCALE);\n // Snap to the exact fit so a zoom-out round trip leaves no residual offset.\n if (scale <= 1.0001) return ZOOM_PAN_FIT;\n const { content } = geometry;\n return {\n scale,\n x: clampAxis(transform.x, scale, content.left, content.width, geometry.width),\n y: clampAxis(transform.y, scale, content.top, content.height, geometry.height),\n };\n};\n\n/** Zoom to `scale` while the content point under `point` (viewport pixels) stays under it. */\nexport const zoomZoomPanAt = (\n transform: ZoomPanTransform,\n scale: number,\n point: { x: number; y: number },\n geometry: ZoomPanGeometry,\n): ZoomPanTransform => {\n const next = clamp(scale, 1, ZOOM_PAN_MAX_SCALE);\n const ratio = next / transform.scale;\n return clampZoomPan(\n {\n scale: next,\n x: point.x - (point.x - transform.x) * ratio,\n y: point.y - (point.y - transform.y) * ratio,\n },\n geometry,\n );\n};\n\nexport const panZoomPan = (transform: ZoomPanTransform, deltaX: number, deltaY: number, geometry: ZoomPanGeometry): ZoomPanTransform =>\n clampZoomPan({ scale: transform.scale, x: transform.x + deltaX, y: transform.y + deltaY }, geometry);\n"],"mappings":"AAAA;;AAGA;;AAOA;AACA,OAAO,MAAM,YAA8B,GAAG;EAAE,KAAK,EAAE,CAAC;EAAE,CAAC,EAAE,CAAC;EAAE,CAAC,EAAE;AAAE,CAAC;AACtE,OAAO,MAAM,kBAAkB,GAAG,CAAC;AACnC;AACA,OAAO,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK;AAEvC,MAAM,KAAK,GAAG,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW,KAAa,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;;AAEtG;AACA;AACA;AACA;AACA,MAAM,SAAS,GAAG,CAAC,MAAc,EAAE,KAAa,EAAE,KAAa,EAAE,IAAY,EAAE,QAAgB,KAAa;EAC1G,MAAM,KAAK,GAAG,CAAC,KAAK,GAAG,KAAK;EAC5B,MAAM,IAAI,GAAG,QAAQ,GAAG,KAAK,IAAI,KAAK,GAAG,IAAI,CAAC;EAC9C,OAAO,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACpE,CAAC;AAED,OAAO,MAAM,YAAY,GAAG,CAAC,SAA2B,EAAE,QAAyB,KAAuB;EACxG,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,kBAAkB,CAAC;EAClG;EACA,IAAI,KAAK,IAAI,MAAM,EAAE,OAAO,YAAY;EACxC,MAAM;IAAE;EAAQ,CAAC,GAAG,QAAQ;EAC5B,OAAO;IACL,KAAK;IACL,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC;IAC7E,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM;EAC/E,CAAC;AACH,CAAC;;AAED;AACA,OAAO,MAAM,aAAa,GAAG,CAC3B,SAA2B,EAC3B,KAAa,EACb,KAA+B,EAC/B,QAAyB,KACJ;EACrB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,kBAAkB,CAAC;EAChD,MAAM,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC,KAAK;EACpC,OAAO,YAAY,CACjB;IACE,KAAK,EAAE,IAAI;IACX,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,KAAK;IAC5C,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI;EACzC,CAAC,EACD,QACF,CAAC;AACH,CAAC;AAED,OAAO,MAAM,UAAU,GAAG,CAAC,SAA2B,EAAE,MAAc,EAAE,MAAc,EAAE,QAAyB,KAC/G,YAAY,CAAC;EAAE,KAAK,EAAE,SAAS,CAAC,KAAK;EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,GAAG,MAAM;EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,GAAG;AAAO,CAAC,EAAE,QAAQ,CAAC","ignoreList":[]}
|