@weasel-js/labkit 1.4.0-pre.0 → 1.4.0-pre.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/README.md +226 -55
- package/dist/_dts/{CanvasStackContext-DtAn_3O-.d.ts → CanvasStackContext-LnCfqNBA.d.ts} +1 -1
- package/dist/_dts/{frac-Cp3NivlC.d.ts → frac-C-2c72Ij.d.ts} +9 -2
- package/dist/canvas/index.d.ts +73 -7
- package/dist/canvas/index.js +2 -2
- package/dist/chrome/index.d.ts +2 -2
- package/dist/chrome/index.js +5 -5
- package/dist/{chunk-LITE5YFB.js → chunk-6OEYTYML.js} +6 -6
- package/dist/{chunk-LITE5YFB.js.map → chunk-6OEYTYML.js.map} +1 -1
- package/dist/{chunk-JDDIZSLL.js → chunk-AE5CNVRU.js} +3 -3
- package/dist/{chunk-JDDIZSLL.js.map → chunk-AE5CNVRU.js.map} +1 -1
- package/dist/{chunk-EQIB2PGC.js → chunk-E2UQVZ44.js} +3 -3
- package/dist/{chunk-EQIB2PGC.js.map → chunk-E2UQVZ44.js.map} +1 -1
- package/dist/{chunk-4EY67BKJ.js → chunk-ESIQNQ6K.js} +3 -3
- package/dist/{chunk-4EY67BKJ.js.map → chunk-ESIQNQ6K.js.map} +1 -1
- package/dist/{chunk-6JMU56CU.js → chunk-HHGVISVZ.js} +4 -4
- package/dist/{chunk-6JMU56CU.js.map → chunk-HHGVISVZ.js.map} +1 -1
- package/dist/{chunk-3ZELRRBV.js → chunk-HMFODCOX.js} +86 -6
- package/dist/chunk-HMFODCOX.js.map +1 -0
- package/dist/{chunk-ROQDRXKW.js → chunk-L5QJOOLV.js} +126 -12
- package/dist/chunk-L5QJOOLV.js.map +1 -0
- package/dist/{chunk-5TCEVMPI.js → chunk-M75ZU6ZZ.js} +3 -3
- package/dist/{chunk-5TCEVMPI.js.map → chunk-M75ZU6ZZ.js.map} +1 -1
- package/dist/{chunk-3P5TTTA7.js → chunk-QB5SM4BM.js} +3 -3
- package/dist/{chunk-3P5TTTA7.js.map → chunk-QB5SM4BM.js.map} +1 -1
- package/dist/{chunk-HEORBLVT.js → chunk-RBGKL7NF.js} +3 -3
- package/dist/{chunk-HEORBLVT.js.map → chunk-RBGKL7NF.js.map} +1 -1
- package/dist/{chunk-YQJNT4XQ.js → chunk-TO2FUOKF.js} +4 -4
- package/dist/{chunk-YQJNT4XQ.js.map → chunk-TO2FUOKF.js.map} +1 -1
- package/dist/controls/index.js +3 -3
- package/dist/dragdrop/index.d.ts +1 -1
- package/dist/dragdrop/index.js +2 -2
- package/dist/index.d.ts +7 -52
- package/dist/index.js +49 -101
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +2 -2
- package/dist/layers/index.js +3 -3
- package/dist/loupe/index.d.ts +3 -3
- package/dist/loupe/index.js +2 -2
- package/dist/passthrough/weasel-canvas.js +1 -1
- package/dist/passthrough/weasel-ui.js +2 -2
- package/dist/primitives/index.js +4 -4
- package/dist/surface/index.js +2 -2
- package/dist/ui/layers/index.js +2 -2
- package/dist/undo/index.d.ts +1 -1
- package/package.json +7 -7
- package/src/annotations/store.test.ts +72 -0
- package/src/annotations/store.ts +30 -0
- package/src/annotations/types.ts +9 -2
- package/src/canvas/index.ts +11 -0
- package/src/index.test.ts +1 -0
- package/src/index.ts +13 -9
- package/dist/chunk-3ZELRRBV.js.map +0 -1
- package/dist/chunk-ROQDRXKW.js.map +0 -1
package/dist/layers/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { E as LayerDescriptor } from '../_dts/frac-
|
|
2
|
-
export { B as LayerCapability } from '../_dts/frac-
|
|
1
|
+
import { E as LayerDescriptor } from '../_dts/frac-C-2c72Ij.js';
|
|
2
|
+
export { B as LayerCapability } from '../_dts/frac-C-2c72Ij.js';
|
|
3
3
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
4
|
import 'react';
|
|
5
5
|
import '../_dts/types-D6s4b7if.js';
|
package/dist/layers/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { LayerList } from '../chunk-
|
|
1
|
+
export { LayerList } from '../chunk-AE5CNVRU.js';
|
|
2
2
|
import '../chunk-SBXNQW4G.js';
|
|
3
|
-
import '../chunk-
|
|
4
|
-
import '../chunk-
|
|
3
|
+
import '../chunk-RBGKL7NF.js';
|
|
4
|
+
import '../chunk-L5QJOOLV.js';
|
|
5
5
|
//# sourceMappingURL=index.js.map
|
|
6
6
|
//# sourceMappingURL=index.js.map
|
package/dist/loupe/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ae as LoupeMode, V as ViewTransform, W as WorldFrame, c as WorldSpec, a2 as ViewportSize, K as LoupeRenderArgs, N as ResolvedLoupe } from '../_dts/frac-
|
|
3
|
-
export { L as LOUPE_DEFAULTS, G as LoupeCapability, J as LoupeDeclaration, ab as resolveLoupe } from '../_dts/frac-
|
|
4
|
-
import { c as CanvasStackSurface, C as CanvasLayerDescriptor } from '../_dts/CanvasStackContext-
|
|
2
|
+
import { ae as LoupeMode, V as ViewTransform, W as WorldFrame, c as WorldSpec, a2 as ViewportSize, K as LoupeRenderArgs, N as ResolvedLoupe } from '../_dts/frac-C-2c72Ij.js';
|
|
3
|
+
export { L as LOUPE_DEFAULTS, G as LoupeCapability, J as LoupeDeclaration, ab as resolveLoupe } from '../_dts/frac-C-2c72Ij.js';
|
|
4
|
+
import { c as CanvasStackSurface, C as CanvasLayerDescriptor } from '../_dts/CanvasStackContext-LnCfqNBA.js';
|
|
5
5
|
import { RefObject, ReactNode } from 'react';
|
|
6
6
|
import '../_dts/types-D6s4b7if.js';
|
|
7
7
|
import '../_dts/PrefsForm-BkUJZx0A.js';
|
package/dist/loupe/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { CanvasLoupe, DomLoupe, LOUPE_DEFAULTS, LoupeBubble, TrialLoupe, drawCanvasLens, lensCamera, lensSourceRect, resolveLoupe, sampleStack, useLoupe } from '../chunk-
|
|
1
|
+
export { CanvasLoupe, DomLoupe, LOUPE_DEFAULTS, LoupeBubble, TrialLoupe, drawCanvasLens, lensCamera, lensSourceRect, resolveLoupe, sampleStack, useLoupe } from '../chunk-E2UQVZ44.js';
|
|
2
2
|
import '../chunk-CTRKTLYZ.js';
|
|
3
3
|
import '../chunk-2AYGEN57.js';
|
|
4
4
|
import '../chunk-CQLQPQ4P.js';
|
|
5
|
-
import '../chunk-
|
|
5
|
+
import '../chunk-L5QJOOLV.js';
|
|
6
6
|
//# sourceMappingURL=index.js.map
|
|
7
7
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { FALLBACK_FIT_VIEW, MinimapCanvas, SceneViewCanvas, buildSceneViewCommands, computeFitView, computeIndicatorCommand, renderSceneToCanvas } from '../chunk-
|
|
1
|
+
export { FALLBACK_FIT_VIEW, MinimapCanvas, SceneViewCanvas, buildSceneViewCommands, computeFitView, computeIndicatorCommand, renderSceneToCanvas } from '../chunk-L5QJOOLV.js';
|
|
2
2
|
//# sourceMappingURL=weasel-canvas.js.map
|
|
3
3
|
//# sourceMappingURL=weasel-canvas.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import '../chunk-SBXNQW4G.js';
|
|
2
|
-
export { m as ActionBar, r4 as ActionsBar, It as Badge, a3 as Button, h2 as Callout, c5 as CalloutTrigger, o as Checkbox, p3 as ComboBox, m3 as ComboBoxItem, F as CurveEditor, s as DataGrid, c as Dialog, n2 as DragHandleGlyph, Je as EDGE_PROFILES, n as Field, u as Input, s2 as KeyCap, c2 as KeySequence, c3 as LayerStack, e2 as MINUS_SIGN, f as NumberField, r3 as OptionsBar, f2 as Plot2D, n5 as PointPlotter, r5 as Powerline, u2 as Radio, l as RadioGroup, l2 as RangeSlider, p as Select, h as SelectItem, n3 as Sidebar, s3 as SidebarPanel, _2 as Slider, a2 as Switch, c4 as Tab, s4 as TabList, l3 as TabPanel, o4 as Tabs, r2 as ToggleBar, o3 as ToolButton, n4 as ToolGroup, _4 as ToolPalette, In as chromaAt, u3 as detectPlatform, a as dlog, r as fieldClasses, t as formatNumber, d2 as formatShortcut, u4 as formatShortcutParts, o2 as inferKeycapKind, i as isDebugEnabled, b2 as isPrefLeaf, e as keyGlyph, p2 as keySpecFromKey, d as keySpecsFromMods, Fn as oklchToHex, v2 as paintGradientTrack, x2 as prefValueAtPath, i2 as useReorderDragList, a4 as useRovingTabIndex, S as visiblePrefSubtree } from '../chunk-
|
|
3
|
-
import '../chunk-
|
|
2
|
+
export { m as ActionBar, r4 as ActionsBar, It as Badge, a3 as Button, h2 as Callout, c5 as CalloutTrigger, o as Checkbox, p3 as ComboBox, m3 as ComboBoxItem, F as CurveEditor, s as DataGrid, c as Dialog, n2 as DragHandleGlyph, Je as EDGE_PROFILES, n as Field, u as Input, s2 as KeyCap, c2 as KeySequence, c3 as LayerStack, e2 as MINUS_SIGN, f as NumberField, r3 as OptionsBar, f2 as Plot2D, n5 as PointPlotter, r5 as Powerline, u2 as Radio, l as RadioGroup, l2 as RangeSlider, p as Select, h as SelectItem, n3 as Sidebar, s3 as SidebarPanel, _2 as Slider, a2 as Switch, c4 as Tab, s4 as TabList, l3 as TabPanel, o4 as Tabs, r2 as ToggleBar, o3 as ToolButton, n4 as ToolGroup, _4 as ToolPalette, In as chromaAt, u3 as detectPlatform, a as dlog, r as fieldClasses, t as formatNumber, d2 as formatShortcut, u4 as formatShortcutParts, o2 as inferKeycapKind, i as isDebugEnabled, b2 as isPrefLeaf, e as keyGlyph, p2 as keySpecFromKey, d as keySpecsFromMods, Fn as oklchToHex, v2 as paintGradientTrack, x2 as prefValueAtPath, i2 as useReorderDragList, a4 as useRovingTabIndex, S as visiblePrefSubtree } from '../chunk-RBGKL7NF.js';
|
|
3
|
+
import '../chunk-L5QJOOLV.js';
|
|
4
4
|
//# sourceMappingURL=weasel-ui.js.map
|
|
5
5
|
//# sourceMappingURL=weasel-ui.js.map
|
package/dist/primitives/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { FloatingPanel, JobProgress, Legend, Sidebar } from '../chunk-
|
|
2
|
-
export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl } from '../chunk-
|
|
1
|
+
export { FloatingPanel, JobProgress, Legend, Sidebar } from '../chunk-QB5SM4BM.js';
|
|
2
|
+
export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl } from '../chunk-HHGVISVZ.js';
|
|
3
3
|
import '../chunk-SBXNQW4G.js';
|
|
4
|
-
import '../chunk-
|
|
4
|
+
import '../chunk-RBGKL7NF.js';
|
|
5
5
|
import '../chunk-CQLQPQ4P.js';
|
|
6
|
-
import '../chunk-
|
|
6
|
+
import '../chunk-L5QJOOLV.js';
|
|
7
7
|
//# sourceMappingURL=index.js.map
|
|
8
8
|
//# sourceMappingURL=index.js.map
|
package/dist/surface/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { SurfaceCanvasContext, SurfaceContext, composeRects, rectsEqual, toDeviceRect, useSurface, useSurfaceCanvas, useSurfaceOptional, useSurfaceTile, useTiledSurface } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
export { SurfaceCanvasContext, SurfaceContext, composeRects, rectsEqual, toDeviceRect, useSurface, useSurfaceCanvas, useSurfaceOptional, useSurfaceTile, useTiledSurface } from '../chunk-ESIQNQ6K.js';
|
|
2
|
+
import '../chunk-L5QJOOLV.js';
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/dist/ui/layers/index.js
CHANGED
package/dist/undo/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasel-js/labkit",
|
|
3
|
-
"version": "1.4.0-pre.
|
|
3
|
+
"version": "1.4.0-pre.1",
|
|
4
4
|
"description": "React widgets for building self-contained interactive lab pages",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "orochi235",
|
|
@@ -114,11 +114,11 @@
|
|
|
114
114
|
"react-dom": "^19.0.0"
|
|
115
115
|
},
|
|
116
116
|
"dependencies": {
|
|
117
|
-
"@weasel-js/core": "1.4.0-pre.
|
|
118
|
-
"@weasel-js/loupe": "1.4.0-pre.
|
|
119
|
-
"@weasel-js/svg": "1.4.0-pre.
|
|
120
|
-
"@weasel-js/theme": "1.4.0-pre.
|
|
121
|
-
"@weasel-js/ui": "1.4.0-pre.
|
|
117
|
+
"@weasel-js/core": "1.4.0-pre.1",
|
|
118
|
+
"@weasel-js/loupe": "1.4.0-pre.1",
|
|
119
|
+
"@weasel-js/svg": "1.4.0-pre.1",
|
|
120
|
+
"@weasel-js/theme": "1.4.0-pre.1",
|
|
121
|
+
"@weasel-js/ui": "1.4.0-pre.1",
|
|
122
122
|
"earcut": "2.2.4",
|
|
123
123
|
"polygon-clipping": "^0.15.7",
|
|
124
124
|
"react-aria-components": "^1.5.0",
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
"@biomejs/biome": "^2.4.12",
|
|
130
130
|
"@rollup/plugin-alias": "^6.0.0",
|
|
131
131
|
"@testing-library/jest-dom": "^6.6.0",
|
|
132
|
-
"@weasel-js/modes": "1.4.0-pre.
|
|
132
|
+
"@weasel-js/modes": "1.4.0-pre.1",
|
|
133
133
|
"less": "^4.2.0",
|
|
134
134
|
"rollup-plugin-dts": "^6.4.1",
|
|
135
135
|
"tsx": "^4.19.0",
|
|
@@ -155,6 +155,78 @@ describe('the annotation store', () => {
|
|
|
155
155
|
expect(fn.mock.calls.length).toBe(before);
|
|
156
156
|
});
|
|
157
157
|
|
|
158
|
+
// What the overlay's canvas does to a selection is `scene.setSelection` —
|
|
159
|
+
// weasel binds `useSelection` to the scene it is handed, so the selection is
|
|
160
|
+
// scene state, not React state, and these exercise the real write. What they
|
|
161
|
+
// do NOT reach is the pointer half: no click, marquee or handle runs here,
|
|
162
|
+
// because jsdom has no WebGL2 and the overlay's canvas never paints or
|
|
163
|
+
// hit-tests. That the canvas is bound to this scene is core's claim.
|
|
164
|
+
it('merges the selection across targets, and maps it back to annotation ids', () => {
|
|
165
|
+
const store = makeStore();
|
|
166
|
+
const onNaive = store.add(RING);
|
|
167
|
+
const onOcct = store.add({ ...RING, target: 'occt' });
|
|
168
|
+
|
|
169
|
+
store.setSelection([onOcct, onNaive]);
|
|
170
|
+
// Declaration order, not call order: one merged answer over several scenes.
|
|
171
|
+
expect(store.selection()).toEqual([onNaive, onOcct]);
|
|
172
|
+
expect(store.selection().map((id) => store.get(id)?.target)).toEqual(['naive', 'occt']);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('reads back what the canvas would have written into the scene', () => {
|
|
176
|
+
const store = makeStore();
|
|
177
|
+
const id = store.add(RING);
|
|
178
|
+
const [node] = [...store.sceneFor('naive').renderOrder()];
|
|
179
|
+
if (node === undefined) throw new Error('unreachable');
|
|
180
|
+
|
|
181
|
+
store.sceneFor('naive').setSelection([node]);
|
|
182
|
+
expect(store.selection()).toEqual([id]);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('replaces rather than adds, clearing a target the new selection omits', () => {
|
|
186
|
+
const store = makeStore();
|
|
187
|
+
const onNaive = store.add(RING);
|
|
188
|
+
const onOcct = store.add({ ...RING, target: 'occt' });
|
|
189
|
+
|
|
190
|
+
store.setSelection([onNaive]);
|
|
191
|
+
store.setSelection([onOcct]);
|
|
192
|
+
expect(store.selection()).toEqual([onOcct]);
|
|
193
|
+
expect(store.sceneFor('naive').getSelection()).toEqual([]);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('drops an id it cannot resolve instead of throwing', () => {
|
|
197
|
+
const store = makeStore();
|
|
198
|
+
const id = store.add(RING);
|
|
199
|
+
|
|
200
|
+
expect(() => store.setSelection(['naive/nope', 'ghost/1', 'noslash', ''])).not.toThrow();
|
|
201
|
+
expect(store.selection()).toEqual([]);
|
|
202
|
+
|
|
203
|
+
store.setSelection([id, id]);
|
|
204
|
+
expect(store.selection()).toEqual([id]);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('stops reporting a selected mark once it is removed', () => {
|
|
208
|
+
const store = makeStore();
|
|
209
|
+
const id = store.add(RING);
|
|
210
|
+
store.setSelection([id]);
|
|
211
|
+
store.remove(id);
|
|
212
|
+
expect(store.selection()).toEqual([]);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('notifies subscribers when the selection changes', () => {
|
|
216
|
+
const store = makeStore();
|
|
217
|
+
const id = store.add(RING);
|
|
218
|
+
const fn = vi.fn();
|
|
219
|
+
store.subscribe(fn);
|
|
220
|
+
|
|
221
|
+
store.setSelection([id]);
|
|
222
|
+
expect(fn).toHaveBeenCalled();
|
|
223
|
+
|
|
224
|
+
// A write that changes nothing is not an event.
|
|
225
|
+
const before = fn.mock.calls.length;
|
|
226
|
+
store.setSelection([id]);
|
|
227
|
+
expect(fn.mock.calls.length).toBe(before);
|
|
228
|
+
});
|
|
229
|
+
|
|
158
230
|
it('round-trips through JSON, and the snapshot is JSON-clean', () => {
|
|
159
231
|
const store = makeStore();
|
|
160
232
|
const id = store.add(RING, { angle: 'iso', shading: 'outline' });
|
package/src/annotations/store.ts
CHANGED
|
@@ -221,6 +221,36 @@ export function createAnnotationStore(opts: AnnotationStoreOptions): Annotations
|
|
|
221
221
|
return history.redo(historySceneAt);
|
|
222
222
|
},
|
|
223
223
|
|
|
224
|
+
selection() {
|
|
225
|
+
const out: string[] = [];
|
|
226
|
+
for (const target of liveTargets()) {
|
|
227
|
+
const scene = scenes.get(target);
|
|
228
|
+
if (!scene) continue;
|
|
229
|
+
for (const node of scene.getSelection()) {
|
|
230
|
+
if (scene.get(node)) out.push(`${target}/${String(node)}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return out;
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
setSelection(ids) {
|
|
237
|
+
const wanted = new Map<string, string[]>();
|
|
238
|
+
for (const id of ids) {
|
|
239
|
+
const found = nodeOf(id);
|
|
240
|
+
if (!found) continue;
|
|
241
|
+
const node = String(found.node.id);
|
|
242
|
+
const on = wanted.get(found.target);
|
|
243
|
+
if (!on) wanted.set(found.target, [node]);
|
|
244
|
+
else if (!on.includes(node)) on.push(node);
|
|
245
|
+
}
|
|
246
|
+
for (const [target, scene] of scenes) {
|
|
247
|
+
const next = wanted.get(target) ?? [];
|
|
248
|
+
const now = scene.getSelection();
|
|
249
|
+
if (now.length === next.length && next.every((n, i) => String(now[i]) === n)) continue;
|
|
250
|
+
scene.setSelection(next.map(asNodeId));
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
|
|
224
254
|
subscribe(fn) {
|
|
225
255
|
subs.add(fn);
|
|
226
256
|
return () => {
|
package/src/annotations/types.ts
CHANGED
|
@@ -196,8 +196,15 @@ export interface AnnotationsApi {
|
|
|
196
196
|
within(target: string, box: FracRect): Annotation[];
|
|
197
197
|
/** Whether `a`'s position still describes the picture `config` produces. */
|
|
198
198
|
isStale(a: Annotation, config: unknown): boolean;
|
|
199
|
-
/**
|
|
200
|
-
*
|
|
199
|
+
/** The marks the user currently has selected, across every target, as
|
|
200
|
+
* annotation ids. Every one resolves through `get`. */
|
|
201
|
+
selection(): readonly string[];
|
|
202
|
+
/** Replace the selection. An id naming a target or a mark that is not
|
|
203
|
+
* there is dropped, the way `update` and `remove` ignore one. */
|
|
204
|
+
setSelection(ids: readonly string[]): void;
|
|
205
|
+
/** Fires after every mutation *and* after a selection change — weasel keeps
|
|
206
|
+
* a canvas's selection on the scene, so both already arrive on this one
|
|
207
|
+
* channel. No delta: re-query, and re-read `selection()`. */
|
|
201
208
|
subscribe(fn: () => void): () => void;
|
|
202
209
|
|
|
203
210
|
/** Whether the last mark change on any target can be taken back. Weasel
|
package/src/canvas/index.ts
CHANGED
|
@@ -6,5 +6,16 @@ export type { ZoomAtOptions } from './camera';
|
|
|
6
6
|
export { centerOn, zoomAt } from './camera';
|
|
7
7
|
export { screenToWorld, worldToScreen } from './canvasCoords';
|
|
8
8
|
export type { CanvasLayerDescriptor } from './useLayerScheduler';
|
|
9
|
+
export type { OrbitHandlers, OrbitView, UseOrbitOptions, Vec3 } from './useOrbit';
|
|
10
|
+
export {
|
|
11
|
+
clampPitch,
|
|
12
|
+
orbitAfterDrag,
|
|
13
|
+
orbitAfterWheel,
|
|
14
|
+
PITCH_LIMIT,
|
|
15
|
+
useOrbit,
|
|
16
|
+
wrapYaw,
|
|
17
|
+
} from './useOrbit';
|
|
18
|
+
export type { PanZoomHandlers, UsePanZoomOptions } from './usePanZoom';
|
|
19
|
+
export { usePanZoom } from './usePanZoom';
|
|
9
20
|
export type { ViewportSize, WorldFrame, WorldSpec } from './worldSpec';
|
|
10
21
|
export { applyCamera, DEFAULT_FRAME, resolveFrame } from './worldSpec';
|
package/src/index.test.ts
CHANGED
|
@@ -67,6 +67,7 @@ describe('surface, job and orbit entry points', () => {
|
|
|
67
67
|
expect(typeof kit.fracToWorld).toBe('function');
|
|
68
68
|
expect(typeof kit.useOrbit).toBe('function');
|
|
69
69
|
expect(typeof kit.orbitAfterDrag).toBe('function');
|
|
70
|
+
expect(typeof kit.usePanZoom).toBe('function');
|
|
70
71
|
expect(typeof kit.useJob).toBe('function');
|
|
71
72
|
expect(typeof kit.as2DView).toBe('function');
|
|
72
73
|
expect(kit.SurfaceContext).toBeDefined();
|
package/src/index.ts
CHANGED
|
@@ -115,6 +115,12 @@ export type {
|
|
|
115
115
|
CanvasStackContextValue,
|
|
116
116
|
CanvasStackProps,
|
|
117
117
|
CanvasStackSurface,
|
|
118
|
+
OrbitHandlers,
|
|
119
|
+
OrbitView,
|
|
120
|
+
PanZoomHandlers,
|
|
121
|
+
UseOrbitOptions,
|
|
122
|
+
UsePanZoomOptions,
|
|
123
|
+
Vec3,
|
|
118
124
|
ViewportSize,
|
|
119
125
|
WorldFrame,
|
|
120
126
|
WorldSpec,
|
|
@@ -125,21 +131,19 @@ export {
|
|
|
125
131
|
CanvasStack,
|
|
126
132
|
CanvasStackContext,
|
|
127
133
|
centerOn,
|
|
128
|
-
DEFAULT_FRAME,
|
|
129
|
-
resolveFrame,
|
|
130
|
-
screenToWorld,
|
|
131
|
-
worldToScreen,
|
|
132
|
-
zoomAt,
|
|
133
|
-
} from './canvas';
|
|
134
|
-
export type { OrbitHandlers, OrbitView, UseOrbitOptions, Vec3 } from './canvas/useOrbit';
|
|
135
|
-
export {
|
|
136
134
|
clampPitch,
|
|
135
|
+
DEFAULT_FRAME,
|
|
137
136
|
orbitAfterDrag,
|
|
138
137
|
orbitAfterWheel,
|
|
139
138
|
PITCH_LIMIT,
|
|
139
|
+
resolveFrame,
|
|
140
|
+
screenToWorld,
|
|
140
141
|
useOrbit,
|
|
142
|
+
usePanZoom,
|
|
143
|
+
worldToScreen,
|
|
141
144
|
wrapYaw,
|
|
142
|
-
|
|
145
|
+
zoomAt,
|
|
146
|
+
} from './canvas';
|
|
143
147
|
export * from './chrome';
|
|
144
148
|
export { f } from './config/builder';
|
|
145
149
|
export { fromConfigFields } from './config/fromConfigField';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/canvas/useLayerScheduler.ts","../src/canvas/usePanZoom.ts","../src/canvas/CanvasStack.tsx"],"names":["useRef","useEffect"],"mappings":";;;;;;;AAyBO,SAAS,iBAAA,CAAkB;AAAA,EAChC,MAAA;AAAA,EACA,IAAA;AAAA,EACA,KAAA,GAAQ,aAAA;AAAA,EACR,UAAA;AAAA,EACA,IAAA;AAAA,EACA;AACF,CAAA,EAA2B;AACzB,EAAA,MAAM,KAAA,GAAQ,MAAA,iBAAoB,IAAI,GAAA,EAAK,CAAA;AAC3C,EAAA,MAAM,aAAA,GAAgB,MAAA,iBAAqD,IAAI,GAAA,EAAK,CAAA;AAEpF,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,MAAA,MAAM,IAAA,GAAO,aAAA,CAAc,OAAA,CAAQ,GAAA,CAAI,MAAM,EAAE,CAAA;AAC/C,MAAA,IAAI,SAAS,KAAA,CAAM,MAAA,QAAc,OAAA,CAAQ,GAAA,CAAI,MAAM,EAAE,CAAA;AACrD,MAAA,aAAA,CAAc,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,EAAA,EAAI,MAAM,MAAM,CAAA;AAAA,IAClD;AACA,IAAA,KAAA,MAAW,MAAM,CAAC,GAAG,cAAc,OAAA,CAAQ,IAAA,EAAM,CAAA,EAAG;AAClD,MAAA,IAAI,CAAC,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,EAAE,CAAA,EAAG,aAAA,CAAc,OAAA,CAAQ,MAAA,CAAO,EAAE,CAAA;AAAA,IACvE;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,KAAA,MAAW,SAAS,MAAA,EAAQ,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,MAAM,EAAE,CAAA;AAAA,EACxD,GAAG,CAAC,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,MAAM,CAAC,CAAA;AAK9B,EAAA,MAAM,SAAA,GAAY,aAAA;AAAA,IAChB,MAAM;AACJ,MAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,CAAA,EAAG;AAC9B,MAAA,MAAM,MAAM,UAAA,CAAW,OAAA;AACvB,MAAA,IAAI,CAAC,GAAA,EAAK;AACR,QAAA,KAAA,CAAM,QAAQ,KAAA,EAAM;AACpB,QAAA;AAAA,MACF;AACA,MAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,QAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,EAAE,CAAA,EAAG;AAClC,QAAA,IAAI,CAAC,MAAM,OAAA,EAAS;AACpB,QAAA,MAAM,MAAA,GAAS,GAAA,CAAI,GAAA,CAAI,KAAA,CAAM,EAAE,CAAA;AAC/B,QAAA,IAAI,CAAC,MAAA,EAAQ;AACb,QAAA,MAAM,GAAA,GAAM,MAAA,CAAO,UAAA,CAAW,IAAI,CAAA;AAClC,QAAA,IAAI,CAAC,GAAA,EAAK;AACV,QAAA,GAAA,CAAI,IAAA,EAAK;AACT,QAAA,GAAA,CAAI,YAAA,CAAa,KAAK,GAAA,EAAK,CAAA,EAAG,GAAG,IAAA,CAAK,GAAA,EAAK,GAAG,CAAC,CAAA;AAC/C,QAAA,GAAA,CAAI,UAAU,CAAA,EAAG,CAAA,EAAG,IAAA,CAAK,KAAA,EAAO,KAAK,MAAM,CAAA;AAC3C,QAAA,KAAA,CAAM,MAAA,CAAO,GAAA,EAAK,IAAA,EAAM,KAAK,CAAA;AAC7B,QAAA,GAAA,CAAI,OAAA,EAAQ;AAAA,MACd;AACA,MAAA,KAAA,CAAM,QAAQ,KAAA,EAAM;AAAA,IACtB,CAAA;AAAA,IACA,EAAE,QAAQ,IAAA;AAAK,GACjB;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,IAAA,GAAO,CAAA,YAAa,OAAA,EAAQ;AAAA,EAChD,CAAC,CAAA;AACH;AClDA,IAAM,cAAA,GAAiB,CAAA;AAEvB,SAAS,iBAAiB,CAAA,EAAoB;AAC5C,EAAA,OAAO,MAAA,CAAO,QAAA,CAAS,CAAC,CAAA,IAAK,CAAA,GAAI,CAAA;AACnC;AAEO,SAAS,UAAA,CAAW;AAAA,EACzB,IAAA;AAAA,EACA,YAAA;AAAA,EACA,OAAA,GAAU,GAAA;AAAA,EACV,OAAA,GAAU,EAAA;AAAA,EACV,KAAA,GAAQ;AACV,CAAA,EAAuC;AACrC,EAAA,MAAM,OAAA,GAAUA,OAAyB,IAAI,CAAA;AAC7C,EAAA,MAAM,OAAA,GAAUA,OAAO,IAAI,CAAA;AAC3B,EAAA,OAAA,CAAQ,OAAA,GAAU,IAAA;AAIlB,EAAA,MAAM,cAAA,GAAiBA,OAAO,gBAAA,CAAiB,IAAA,CAAK,IAAI,CAAA,GAAI,IAAA,CAAK,OAAO,IAAI,CAAA;AAE5E,EAAA,MAAM,OAAA,GAAU,WAAA;AAAA,IACd,CAAC,CAAA,KAA+B;AAC9B,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,MAAM,IAAA,GAAO,CAAA,CAAE,aAAA,CAAc,qBAAA,EAAsB;AACnD,MAAA,MAAM,cAAc,cAAA,CAAe,OAAA;AACnC,MAAA,YAAA;AAAA,QACE,MAAA;AAAA,UACE,OAAA,CAAQ,OAAA;AAAA,UACR,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,CAAE,SAAS,IAAK,CAAA;AAAA,UAC1B,EAAE,CAAA,EAAG,CAAA,CAAE,OAAA,GAAU,IAAA,CAAK,MAAM,CAAA,EAAG,CAAA,CAAE,OAAA,GAAU,IAAA,CAAK,GAAA,EAAI;AAAA,UACpD;AAAA,YACE,KAAA;AAAA,YACA,KAAK,WAAA,IAAe,IAAA,GAAO,UAAU,IAAA,CAAK,GAAA,CAAI,SAAS,WAAW,CAAA;AAAA,YAClE,KAAK,WAAA,IAAe,IAAA,GAAO,UAAU,IAAA,CAAK,GAAA,CAAI,SAAS,WAAW;AAAA;AACpE;AACF,OACF;AAAA,IACF,CAAA;AAAA,IACA,CAAC,YAAA,EAAc,OAAA,EAAS,OAAA,EAAS,KAAK;AAAA,GACxC;AAEA,EAAA,MAAM,aAAA,GAAgB,WAAA,CAAY,CAAC,CAAA,KAAiC;AAClE,IAAA,IAAI,CAAA,CAAE,WAAW,CAAA,EAAG;AACpB,IAAA,CAAA,CAAE,aAAA,CAAc,iBAAA,CAAkB,CAAA,CAAE,SAAS,CAAA;AAC7C,IAAA,OAAA,CAAQ,OAAA,GAAU;AAAA,MAChB,WAAW,CAAA,CAAE,SAAA;AAAA,MACb,cAAc,CAAA,CAAE,OAAA;AAAA,MAChB,cAAc,CAAA,CAAE,OAAA;AAAA,MAChB,QAAA,EAAU,EAAE,GAAG,OAAA,CAAQ,QAAQ,GAAA,EAAI;AAAA,MACnC,KAAA,EAAO;AAAA,KACT;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,aAAA,GAAgB,WAAA;AAAA,IACpB,CAAC,CAAA,KAAiC;AAChC,MAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,MAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,SAAA,KAAc,EAAE,SAAA,EAAW;AAC7C,MAAA,MAAM,EAAA,GAAK,CAAA,CAAE,OAAA,GAAU,IAAA,CAAK,YAAA;AAC5B,MAAA,MAAM,EAAA,GAAK,CAAA,CAAE,OAAA,GAAU,IAAA,CAAK,YAAA;AAC5B,MAAA,IAAI,CAAC,KAAK,KAAA,IAAS,IAAA,CAAK,MAAM,EAAA,EAAI,EAAE,IAAI,cAAA,EAAgB;AACxD,MAAA,IAAA,CAAK,KAAA,GAAQ,IAAA;AACb,MAAA,YAAA,CAAa;AAAA,QACX,IAAA,EAAM,QAAQ,OAAA,CAAQ,IAAA;AAAA,QACtB,GAAA,EAAK,EAAE,CAAA,EAAG,IAAA,CAAK,QAAA,CAAS,CAAA,GAAI,EAAA,EAAI,CAAA,EAAG,IAAA,CAAK,QAAA,CAAS,CAAA,GAAI,EAAA;AAAG,OACzD,CAAA;AAAA,IACH,CAAA;AAAA,IACA,CAAC,YAAY;AAAA,GACf;AAEA,EAAA,MAAM,WAAA,GAAc,WAAA,CAAY,CAAC,CAAA,KAAiC;AAChE,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,SAAA,KAAc,EAAE,SAAA,EAAW;AAC7C,IAAA,IAAI,CAAA,CAAE,aAAA,CAAc,iBAAA,CAAkB,CAAA,CAAE,SAAS,CAAA,EAAG;AAClD,MAAA,CAAA,CAAE,aAAA,CAAc,qBAAA,CAAsB,CAAA,CAAE,SAAS,CAAA;AAAA,IACnD;AACA,IAAA,OAAA,CAAQ,OAAA,GAAU,IAAA;AAAA,EACpB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,UAAA,GAAa,YAAY,MAAM,OAAA,CAAQ,SAAS,KAAA,KAAU,IAAA,EAAM,EAAE,CAAA;AAExE,EAAA,OAAO,EAAE,OAAA,EAAS,aAAA,EAAe,aAAA,EAAe,aAAa,UAAA,EAAW;AAC1E;ACrFO,SAAS,WAAA,CAAY;AAAA,EAC1B,MAAA;AAAA,EACA,IAAA;AAAA,EACA,YAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA,GAAQ,MAAA;AAAA,EACR,MAAA,GAAS,MAAA;AAAA,EACT,SAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAAqB;AACnB,EAAA,MAAM,YAAA,GAAeA,OAA8B,IAAI,CAAA;AACvD,EAAA,MAAM,SAAA,GAAYA,MAAAA,iBAAuC,IAAI,GAAA,EAAK,CAAA;AAClE,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,QAAA,CAAS,EAAE,KAAA,EAAO,CAAA,EAAG,MAAA,EAAQ,CAAA,EAAG,GAAA,EAAK,CAAA,EAAG,CAAA;AAEhE,EAAA,MAAM,YAAA,GAAe,CAAC,EAAA,EAAY,EAAA,KAAuC;AACvE,IAAA,IAAI,EAAA,EAAI,SAAA,CAAU,OAAA,CAAQ,GAAA,CAAI,IAAI,EAAE,CAAA;AAAA,SAC/B,SAAA,CAAU,OAAA,CAAQ,MAAA,CAAO,EAAE,CAAA;AAAA,EAClC,CAAA;AAEA,EAAAC,UAAU,MAAM;AACd,IAAA,MAAM,KAAK,YAAA,CAAa,OAAA;AACxB,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,MAAM,SAAS,MAAM;AACnB,MAAA,MAAM,IAAA,GAAO,GAAG,qBAAA,EAAsB;AACtC,MAAA,MAAM,GAAA,GAAM,OAAO,gBAAA,IAAoB,CAAA;AACvC,MAAA,OAAA,CAAQ,CAAC,IAAA,KAAS;AAChB,QAAA,IAAI,IAAA,CAAK,KAAA,KAAU,IAAA,CAAK,KAAA,IAAS,IAAA,CAAK,WAAW,IAAA,CAAK,MAAA,IAAU,IAAA,CAAK,GAAA,KAAQ,GAAA,EAAK;AAChF,UAAA,OAAO,IAAA;AAAA,QACT;AACA,QAAA,OAAO,EAAE,KAAA,EAAO,IAAA,CAAK,OAAO,MAAA,EAAQ,IAAA,CAAK,QAAQ,GAAA,EAAI;AAAA,MACvD,CAAC,CAAA;AAAA,IACH,CAAA;AACA,IAAA,MAAA,EAAO;AACP,IAAA,IAAI,OAAO,mBAAmB,WAAA,EAAa;AAC3C,IAAA,MAAM,EAAA,GAAK,IAAI,cAAA,CAAe,MAAM,CAAA;AACpC,IAAA,EAAA,CAAG,QAAQ,EAAE,CAAA;AACb,IAAA,OAAO,MAAM,GAAG,UAAA,EAAW;AAAA,EAC7B,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,WAAA,GAAcD,OAAO,QAAQ,CAAA;AACnC,EAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AACtB,EAAAC,UAAU,MAAM;AACd,IAAA,IAAI,IAAA,CAAK,KAAA,KAAU,CAAA,IAAK,IAAA,CAAK,WAAW,CAAA,EAAG;AAC3C,IAAA,WAAA,CAAY,OAAA,GAAU,EAAE,KAAA,EAAO,IAAA,CAAK,OAAO,MAAA,EAAQ,IAAA,CAAK,QAAQ,CAAA;AAAA,EAClE,GAAG,CAAC,IAAA,CAAK,KAAA,EAAO,IAAA,CAAK,MAAM,CAAC,CAAA;AAE5B,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,MAAM,YAAA,CAAa,SAAA,EAAW,IAAI,CAAA,EAAG,CAAC,SAAA,EAAW,IAAI,CAAC,CAAA;AAE5E,EAAA,MAAM,QAAA,GAAW,WAAW,EAAE,IAAA,EAAM,cAAc,OAAA,EAAS,OAAA,EAAS,OAAO,CAAA;AAC3E,EAAA,iBAAA,CAAkB,EAAE,QAAQ,IAAA,EAAM,KAAA,EAAO,YAAY,SAAA,EAAW,IAAA,EAAM,IAAA,EAAM,YAAA,EAAc,CAAA;AAE1F,EAAA,MAAM,QAAA,GAAW,OAAA;AAAA,IACf,OAAO;AAAA,MACL,IAAA;AAAA,MACA,KAAA;AAAA,MACA,SAAS,EAAE,OAAA,EAAS,cAAc,IAAA,EAAM,QAAA,EAAU,WAAW,MAAA;AAAO,KACtE,CAAA;AAAA,IACA,CAAC,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,MAAM;AAAA,GAC5B;AAEA,EAAA,MAAM,cAAA,GAAgC,EAAE,KAAA,EAAO,MAAA,EAAO;AACtD,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,KAAA,EAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,MAAM,IAAA,CAAK,KAAA,GAAQ,IAAA,CAAK,GAAG,CAAC,CAAA;AAAA,IACpD,MAAA,EAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,MAAM,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,GAAG,CAAC;AAAA,GACxD;AACA,EAAA,MAAM,SAAA,GAA2B;AAAA,IAC/B,OAAO,IAAA,CAAK,KAAA;AAAA,IACZ,QAAQ,IAAA,CAAK;AAAA,GACf;AAEA,EAAA,MAAM,eAAA,GAAkB,CAAC,CAAA,KAA0C;AACjE,IAAA,MAAM,WAAA,GAAc,SAAS,UAAA,EAAW;AACxC,IAAA,QAAA,CAAS,YAAY,CAAC,CAAA;AACtB,IAAA,IAAI,CAAC,WAAA,IAAe,SAAA,IAAa,YAAA,CAAa,OAAA,EAAS;AACrD,MAAA,MAAM,IAAA,GAAO,YAAA,CAAa,OAAA,CAAQ,qBAAA,EAAsB;AACxD,MAAA,MAAM,MAAA,GAAS,EAAE,CAAA,EAAG,CAAA,CAAE,OAAA,GAAU,IAAA,CAAK,IAAA,EAAM,CAAA,EAAG,CAAA,CAAE,OAAA,GAAU,IAAA,CAAK,GAAA,EAAI;AACnE,MAAA,SAAA,CAAU,aAAA,CAAc,MAAA,EAAQ,IAAA,EAAM,KAAK,CAAC,CAAA;AAAA,IAC9C;AAAA,EACF,CAAA;AAEA,EAAA,uBACE,GAAA,CAAC,kBAAA,CAAmB,QAAA,EAAnB,EAA4B,OAAO,QAAA,EAClC,QAAA,kBAAA,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,YAAA;AAAA,MACL,SAAA,EAAW,SAAA,GAAY,CAAA,gBAAA,EAAmB,SAAS,CAAA,CAAA,GAAK,iBAAA;AAAA,MACxD,KAAA,EAAO,cAAA;AAAA,MACP,SAAS,QAAA,CAAS,OAAA;AAAA,MAClB,eAAe,QAAA,CAAS,aAAA;AAAA,MACxB,eAAe,QAAA,CAAS,aAAA;AAAA,MACxB,WAAA,EAAa,eAAA;AAAA,MAEZ,QAAA,EAAA;AAAA,QAAA,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,qBACX,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YAEC,KAAK,CAAC,EAAA,KAAO,YAAA,CAAa,KAAA,CAAM,IAAI,EAAE,CAAA;AAAA,YACtC,SAAA,EAAU,yBAAA;AAAA,YACV,OAAO,QAAA,CAAS,KAAA;AAAA,YAChB,QAAQ,QAAA,CAAS,MAAA;AAAA,YACjB,KAAA,EAAO,EAAE,GAAG,SAAA,EAAW,SAAS,KAAA,CAAM,OAAA,GAAU,UAAU,MAAA;AAAO,WAAA;AAAA,UAL5D,KAAA,CAAM;AAAA,SAOd,CAAA;AAAA,wBACD,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,0BAAA,EAA4B,QAAA,EAAS;AAAA;AAAA;AAAA,GACtD,EACF,CAAA;AAEJ","file":"chunk-3ZELRRBV.js","sourcesContent":["import { useVisibleRaf } from '@weasel-js/core';\nimport { type RefObject, useEffect, useRef } from 'react';\nimport type { ViewTransform } from '../instrument/types';\nimport { DEFAULT_FRAME, type WorldFrame } from './worldSpec';\n\n/** One layer of a canvas stack: its id, whether it is currently shown, and how\n * it paints itself. */\nexport interface CanvasLayerDescriptor {\n id: string;\n visible: boolean;\n render: (ctx: CanvasRenderingContext2D, view: ViewTransform, frame: WorldFrame) => void;\n}\n\ninterface SchedulerOptions {\n layers: CanvasLayerDescriptor[];\n view: ViewTransform;\n /** The instrument's coordinate system, resolved against `size`. */\n frame?: WorldFrame;\n canvasRefs: RefObject<Map<string, HTMLCanvasElement>>;\n size: { width: number; height: number; dpr: number };\n /** The element the stack occupies. Given one, the scheduler also stops while\n * the stack sits outside the viewport. */\n host?: RefObject<Element | null>;\n}\n\nexport function useLayerScheduler({\n layers,\n view,\n frame = DEFAULT_FRAME,\n canvasRefs,\n size,\n host,\n}: SchedulerOptions): void {\n const dirty = useRef<Set<string>>(new Set());\n const lastRenderRef = useRef<Map<string, CanvasLayerDescriptor['render']>>(new Map());\n\n useEffect(() => {\n for (const layer of layers) {\n const prev = lastRenderRef.current.get(layer.id);\n if (prev !== layer.render) dirty.current.add(layer.id);\n lastRenderRef.current.set(layer.id, layer.render);\n }\n for (const id of [...lastRenderRef.current.keys()]) {\n if (!layers.find((l) => l.id === id)) lastRenderRef.current.delete(id);\n }\n }, [layers]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-mark dirty when view or size changes\n useEffect(() => {\n for (const layer of layers) dirty.current.add(layer.id);\n }, [view, frame, size, layers]);\n\n // Painting only what is dirty is not the same as doing nothing: a hidden tab\n // still commits React updates, and the effect above marks every layer dirty\n // on any view or size change. The gate is what actually stops the work.\n const frameLoop = useVisibleRaf(\n () => {\n if (dirty.current.size === 0) return;\n const map = canvasRefs.current;\n if (!map) {\n dirty.current.clear();\n return;\n }\n for (const layer of layers) {\n if (!dirty.current.has(layer.id)) continue;\n if (!layer.visible) continue;\n const canvas = map.get(layer.id);\n if (!canvas) continue;\n const ctx = canvas.getContext('2d');\n if (!ctx) continue;\n ctx.save();\n ctx.setTransform(size.dpr, 0, 0, size.dpr, 0, 0);\n ctx.clearRect(0, 0, size.width, size.height);\n layer.render(ctx, view, frame);\n ctx.restore();\n }\n dirty.current.clear();\n },\n { target: host },\n );\n\n useEffect(() => {\n if (dirty.current.size > 0) frameLoop.request();\n });\n}\n","import { type PointerEvent, useCallback, useRef, type WheelEvent } from 'react';\nimport type { ViewTransform } from '../instrument/types';\nimport { zoomAt } from './camera';\nimport { DEFAULT_FRAME, type WorldFrame } from './worldSpec';\n\nexport interface UsePanZoomOptions {\n view: ViewTransform;\n onViewChange: (v: ViewTransform) => void;\n /** The opening `view.zoom` always stays reachable, widening these past\n * whatever is passed here if it would otherwise exclude it. */\n minZoom?: number;\n maxZoom?: number;\n /** The instrument's coordinate system, resolved against the viewport. The\n * wheel anchors in this frame; omitting it anchors at the element's\n * top-left, which is what labkit did before a frame could be declared. */\n frame?: WorldFrame;\n}\n\nexport interface PanZoomHandlers {\n onWheel: (e: WheelEvent<HTMLElement>) => void;\n onPointerDown: (e: PointerEvent<HTMLElement>) => void;\n onPointerMove: (e: PointerEvent<HTMLElement>) => void;\n onPointerUp: (e: PointerEvent<HTMLElement>) => void;\n isDragging: () => boolean;\n}\n\ninterface DragState {\n pointerId: number;\n startScreenX: number;\n startScreenY: number;\n startPan: { x: number; y: number };\n moved: boolean;\n}\n\nconst DRAG_THRESHOLD = 3;\n\nfunction isPositiveFinite(n: number): boolean {\n return Number.isFinite(n) && n > 0;\n}\n\nexport function usePanZoom({\n view,\n onViewChange,\n minZoom = 0.1,\n maxZoom = 32,\n frame = DEFAULT_FRAME,\n}: UsePanZoomOptions): PanZoomHandlers {\n const dragRef = useRef<DragState | null>(null);\n const viewRef = useRef(view);\n viewRef.current = view;\n // Captured once, from the view the canvas opened at — not the current view,\n // which would let a prior zoom-out shrink the range and trap the opening\n // view unreachable behind it.\n const initialZoomRef = useRef(isPositiveFinite(view.zoom) ? view.zoom : null);\n\n const onWheel = useCallback(\n (e: WheelEvent<HTMLElement>) => {\n e.preventDefault();\n const rect = e.currentTarget.getBoundingClientRect();\n const initialZoom = initialZoomRef.current;\n onViewChange(\n zoomAt(\n viewRef.current,\n Math.exp(-e.deltaY * 0.001),\n { x: e.clientX - rect.left, y: e.clientY - rect.top },\n {\n frame,\n min: initialZoom == null ? minZoom : Math.min(minZoom, initialZoom),\n max: initialZoom == null ? maxZoom : Math.max(maxZoom, initialZoom),\n },\n ),\n );\n },\n [onViewChange, minZoom, maxZoom, frame],\n );\n\n const onPointerDown = useCallback((e: PointerEvent<HTMLElement>) => {\n if (e.button !== 0) return;\n e.currentTarget.setPointerCapture(e.pointerId);\n dragRef.current = {\n pointerId: e.pointerId,\n startScreenX: e.clientX,\n startScreenY: e.clientY,\n startPan: { ...viewRef.current.pan },\n moved: false,\n };\n }, []);\n\n const onPointerMove = useCallback(\n (e: PointerEvent<HTMLElement>) => {\n const drag = dragRef.current;\n if (!drag || drag.pointerId !== e.pointerId) return;\n const dx = e.clientX - drag.startScreenX;\n const dy = e.clientY - drag.startScreenY;\n if (!drag.moved && Math.hypot(dx, dy) < DRAG_THRESHOLD) return;\n drag.moved = true;\n onViewChange({\n zoom: viewRef.current.zoom,\n pan: { x: drag.startPan.x + dx, y: drag.startPan.y + dy },\n });\n },\n [onViewChange],\n );\n\n const onPointerUp = useCallback((e: PointerEvent<HTMLElement>) => {\n const drag = dragRef.current;\n if (!drag || drag.pointerId !== e.pointerId) return;\n if (e.currentTarget.hasPointerCapture(e.pointerId)) {\n e.currentTarget.releasePointerCapture(e.pointerId);\n }\n dragRef.current = null;\n }, []);\n\n const isDragging = useCallback(() => dragRef.current?.moved === true, []);\n\n return { onWheel, onPointerDown, onPointerMove, onPointerUp, isDragging };\n}\n","import { type CSSProperties, type ReactNode, useEffect, useMemo, useRef, useState } from 'react';\nimport type { Point, ViewTransform } from '../instrument/types';\nimport { CanvasStackContext } from './CanvasStackContext';\nimport { screenToWorld } from './canvasCoords';\nimport { type CanvasLayerDescriptor, useLayerScheduler } from './useLayerScheduler';\nimport { usePanZoom } from './usePanZoom';\nimport { resolveFrame, type ViewportSize, type WorldSpec } from './worldSpec';\n\n/** Props for `<CanvasStack>`. */\nexport interface CanvasStackProps {\n layers: CanvasLayerDescriptor[];\n view: ViewTransform;\n onViewChange: (v: ViewTransform) => void;\n /** The instrument's coordinate system. Omitted, world (0,0) sits at the\n * element's top-left with y running down. */\n worldSpec?: WorldSpec;\n minZoom?: number;\n maxZoom?: number;\n width?: number | string;\n height?: number | string;\n className?: string;\n /** Fired whenever the stack is measured, so a consumer can place a view that\n * only makes sense in terms of the viewport. */\n onResize?: (size: ViewportSize) => void;\n onHitTest?: (worldPos: Point) => void;\n children?: ReactNode;\n}\n\n/** Stacks one `<canvas>` per layer and drives them from a shared view, so a\n * layer that changes rarely is not redrawn with one that changes every frame.\n * Handles sizing, device pixel ratio, and pan/zoom. */\nexport function CanvasStack({\n layers,\n view,\n onViewChange,\n worldSpec,\n minZoom,\n maxZoom,\n width = '100%',\n height = '100%',\n className,\n onResize,\n onHitTest,\n children,\n}: CanvasStackProps) {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const canvasMap = useRef<Map<string, HTMLCanvasElement>>(new Map());\n const [size, setSize] = useState({ width: 0, height: 0, dpr: 1 });\n\n const setCanvasRef = (id: string, el: HTMLCanvasElement | null): void => {\n if (el) canvasMap.current.set(id, el);\n else canvasMap.current.delete(id);\n };\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n const update = () => {\n const rect = el.getBoundingClientRect();\n const dpr = window.devicePixelRatio ?? 1;\n setSize((prev) => {\n if (prev.width === rect.width && prev.height === rect.height && prev.dpr === dpr) {\n return prev;\n }\n return { width: rect.width, height: rect.height, dpr };\n });\n };\n update();\n if (typeof ResizeObserver === 'undefined') return;\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const onResizeRef = useRef(onResize);\n onResizeRef.current = onResize;\n useEffect(() => {\n if (size.width === 0 && size.height === 0) return;\n onResizeRef.current?.({ width: size.width, height: size.height });\n }, [size.width, size.height]);\n\n const frame = useMemo(() => resolveFrame(worldSpec, size), [worldSpec, size]);\n\n const handlers = usePanZoom({ view, onViewChange, minZoom, maxZoom, frame });\n useLayerScheduler({ layers, view, frame, canvasRefs: canvasMap, size, host: containerRef });\n\n const ctxValue = useMemo(\n () => ({\n view,\n frame,\n surface: { element: containerRef, size, canvases: canvasMap, layers },\n }),\n [view, frame, size, layers],\n );\n\n const containerStyle: CSSProperties = { width, height };\n const canvasPx = {\n width: Math.max(0, Math.round(size.width * size.dpr)),\n height: Math.max(0, Math.round(size.height * size.dpr)),\n };\n const canvasCss: CSSProperties = {\n width: size.width,\n height: size.height,\n };\n\n const handlePointerUp = (e: React.PointerEvent<HTMLDivElement>) => {\n const wasDragging = handlers.isDragging();\n handlers.onPointerUp(e);\n if (!wasDragging && onHitTest && containerRef.current) {\n const rect = containerRef.current.getBoundingClientRect();\n const screen = { x: e.clientX - rect.left, y: e.clientY - rect.top };\n onHitTest(screenToWorld(screen, view, frame));\n }\n };\n\n return (\n <CanvasStackContext.Provider value={ctxValue}>\n <div\n ref={containerRef}\n className={className ? `lk-canvas-stack ${className}` : 'lk-canvas-stack'}\n style={containerStyle}\n onWheel={handlers.onWheel}\n onPointerDown={handlers.onPointerDown}\n onPointerMove={handlers.onPointerMove}\n onPointerUp={handlePointerUp}\n >\n {layers.map((layer) => (\n <canvas\n key={layer.id}\n ref={(el) => setCanvasRef(layer.id, el)}\n className=\"lk-canvas-stack__canvas\"\n width={canvasPx.width}\n height={canvasPx.height}\n style={{ ...canvasCss, display: layer.visible ? 'block' : 'none' }}\n />\n ))}\n <div className=\"lk-canvas-stack__overlay\">{children}</div>\n </div>\n </CanvasStackContext.Provider>\n );\n}\n"]}
|