@weasel-js/labkit 1.6.1 → 1.7.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 +3 -3
- package/dist/_dts/{CanvasStackContext-Wo6OwIVM.d.ts → CanvasStackContext-Cjy6I-Iv.d.ts} +4 -1
- package/dist/_dts/{Stage-DWBBi8IU.d.ts → Stage-D6t7o0pF.d.ts} +59 -18
- package/dist/_dts/{index-CKyX0ME_.d.ts → index-BTmaRy6a.d.ts} +10 -6
- package/dist/_dts/{types-B7yBTTF9.d.ts → index-DCMA4Fy1.d.ts} +59 -113
- package/dist/_dts/{types-DTuViBvc.d.ts → types-BUBSIZuV.d.ts} +18 -8
- package/dist/_dts/{useTrialState-CMaV6UD4.d.ts → useTrialState-Bh8bzFgE.d.ts} +1 -1
- package/dist/canvas/index.d.ts +8 -6
- package/dist/canvas/index.js +6 -6
- package/dist/chrome/index.d.ts +6 -4
- package/dist/chrome/index.js +11 -10
- package/dist/chunk-2ES4W47N.js +8 -0
- package/dist/chunk-2ES4W47N.js.map +1 -0
- package/dist/{chunk-WPLAANH4.js → chunk-2N7UBSS2.js} +12 -12
- package/dist/chunk-2N7UBSS2.js.map +1 -0
- package/dist/chunk-4KSC7NXL.js +15491 -0
- package/dist/chunk-4KSC7NXL.js.map +1 -0
- package/dist/{chunk-SLAQPUI4.js → chunk-CEGFXTBD.js} +10 -63
- package/dist/chunk-CEGFXTBD.js.map +1 -0
- package/dist/chunk-CZZPXUOU.js +3 -0
- package/dist/{chunk-SBXNQW4G.js.map → chunk-CZZPXUOU.js.map} +1 -1
- package/dist/{chunk-QGMOA2YE.js → chunk-E23Q6A3Y.js} +296 -104
- package/dist/chunk-E23Q6A3Y.js.map +1 -0
- package/dist/{chunk-GHGO52SO.js → chunk-E6NARGU7.js} +5 -5
- package/dist/{chunk-GHGO52SO.js.map → chunk-E6NARGU7.js.map} +1 -1
- package/dist/{chunk-IJJUK4FU.js → chunk-EESJUSYR.js} +5 -6
- package/dist/chunk-EESJUSYR.js.map +1 -0
- package/dist/{chunk-HXXTULDN.js → chunk-EFXRM6XQ.js} +8 -11
- package/dist/chunk-EFXRM6XQ.js.map +1 -0
- package/dist/chunk-F4ACIK7Z.js +210 -0
- package/dist/chunk-F4ACIK7Z.js.map +1 -0
- package/dist/{chunk-6FLXL3I7.js → chunk-FB4MIZOO.js} +77 -45
- package/dist/chunk-FB4MIZOO.js.map +1 -0
- package/dist/{chunk-AWQG7FCC.js → chunk-FG3EXZDV.js} +3 -3
- package/dist/{chunk-AWQG7FCC.js.map → chunk-FG3EXZDV.js.map} +1 -1
- package/dist/{chunk-4TT3PNPR.js → chunk-GAHELLJT.js} +95 -95
- package/dist/chunk-GAHELLJT.js.map +1 -0
- package/dist/{chunk-HKV7LRHY.js → chunk-IBVRMHW5.js} +5 -5
- package/dist/{chunk-HKV7LRHY.js.map → chunk-IBVRMHW5.js.map} +1 -1
- package/dist/{chunk-ASOK76ZH.js → chunk-JFOJESJF.js} +72 -29
- package/dist/chunk-JFOJESJF.js.map +1 -0
- package/dist/{chunk-RWNDDKOH.js → chunk-PMLZP7RT.js} +18 -15
- package/dist/chunk-PMLZP7RT.js.map +1 -0
- package/dist/{chunk-TJ77A4A3.js → chunk-READTDQX.js} +5 -9
- package/dist/chunk-READTDQX.js.map +1 -0
- package/dist/{chunk-JVBYV6TL.js → chunk-ROGJWDQA.js} +3 -3
- package/dist/{chunk-JVBYV6TL.js.map → chunk-ROGJWDQA.js.map} +1 -1
- package/dist/{chunk-CGHF6RZV.js → chunk-T6DLGEJB.js} +18 -18
- package/dist/chunk-T6DLGEJB.js.map +1 -0
- package/dist/{chunk-24W5VEJW.js → chunk-ULYYNWGP.js} +2 -2
- package/dist/{chunk-24W5VEJW.js.map → chunk-ULYYNWGP.js.map} +1 -1
- package/dist/{chunk-DWMSB4CA.js → chunk-VYVGVBGY.js} +95 -32
- package/dist/chunk-VYVGVBGY.js.map +1 -0
- package/dist/{chunk-IRTGUQ5O.js → chunk-WLMDTI2Q.js} +16 -6
- package/dist/chunk-WLMDTI2Q.js.map +1 -0
- package/dist/{chunk-NEXR37YJ.js → chunk-XE5HJOPL.js} +6 -8
- package/dist/chunk-XE5HJOPL.js.map +1 -0
- package/dist/chunk-XVUKDSYV.js +38 -0
- package/dist/chunk-XVUKDSYV.js.map +1 -0
- package/dist/{chunk-O6FFFOHS.js → chunk-YCFKW5RX.js} +16 -8
- package/dist/chunk-YCFKW5RX.js.map +1 -0
- package/dist/{chunk-CQLQPQ4P.js → chunk-ZQIHJXP4.js} +2 -2
- package/dist/chunk-ZQIHJXP4.js.map +1 -0
- package/dist/config/index.d.ts +28 -13
- package/dist/config/index.js +4 -4
- package/dist/controls/index.d.ts +4 -7
- package/dist/controls/index.js +6 -5
- package/dist/dragdrop/index.d.ts +23 -3
- package/dist/dragdrop/index.js +4 -3
- package/dist/fonts/oswald-tabular-digits.woff2 +0 -0
- package/dist/index.d.ts +72 -487
- package/dist/index.js +603 -441
- package/dist/index.js.map +1 -1
- package/dist/job/index.d.ts +8 -0
- package/dist/job/index.js +1 -1
- package/dist/layers/index.d.ts +6 -5
- package/dist/layers/index.js +3 -2
- package/dist/loupe/index.d.ts +100 -30
- package/dist/loupe/index.js +585 -6
- package/dist/loupe/index.js.map +1 -1
- package/dist/overview/index.d.ts +4 -2
- package/dist/overview/index.js +20 -18
- package/dist/overview/index.js.map +1 -1
- package/dist/passthrough/weasel-canvas.d.ts +2 -0
- package/dist/passthrough/weasel-ui.d.ts +2 -2701
- package/dist/passthrough/weasel-ui.js +4 -3
- package/dist/primitives/index.d.ts +37 -2
- package/dist/primitives/index.js +9 -8
- package/dist/state/index.d.ts +7 -5
- package/dist/state/index.js +6 -6
- package/dist/styles.css +150 -126
- package/dist/surface/index.d.ts +10 -2
- package/dist/surface/index.js +1 -1
- package/dist/undo/index.d.ts +5 -3
- package/package.json +14 -13
- package/src/annotations/AnnotationOverlay.abandon.test.tsx +143 -0
- package/src/annotations/AnnotationOverlay.tsx +52 -33
- package/src/annotations/Annotations.less +0 -10
- package/src/annotations/Annotations.overlay.test.tsx +31 -0
- package/src/annotations/MarkList.tsx +4 -8
- package/src/annotations/capture.browser.test.ts +41 -0
- package/src/annotations/capture.ts +14 -7
- package/src/annotations/drawOne.ts +2 -2
- package/src/annotations/frac.test.ts +44 -1
- package/src/annotations/frac.ts +46 -18
- package/src/annotations/index.ts +1 -0
- package/src/annotations/paint.test.ts +76 -46
- package/src/annotations/paint.ts +65 -43
- package/src/annotations/store.test.ts +212 -2
- package/src/annotations/store.ts +130 -27
- package/src/annotations/svgNodes.test.ts +28 -19
- package/src/annotations/svgNodes.ts +6 -6
- package/src/annotations/types.ts +29 -10
- package/src/canvas/AGENTS.md +1 -0
- package/src/canvas/CameraInput.test.tsx +30 -0
- package/src/canvas/CameraInput.tsx +60 -48
- package/src/canvas/CanvasStack.tsx +11 -6
- package/src/canvas/CanvasStackContext.ts +4 -1
- package/src/canvas/LinkedCursor.less +1 -1
- package/src/canvas/Stage.test.tsx +33 -4
- package/src/canvas/Stage.tsx +9 -4
- package/src/canvas/cameraRegistry.test.ts +55 -0
- package/src/canvas/cameraRegistry.ts +87 -0
- package/src/canvas/cameraZoom.test.ts +46 -0
- package/src/canvas/cameraZoom.ts +24 -0
- package/src/canvas/index.ts +14 -2
- package/src/canvas/sharedScope.test.tsx +94 -0
- package/src/canvas/worldSpec.ts +3 -1
- package/src/chrome/ChromeRegions.stories.tsx +1 -0
- package/src/chrome/builtins.test.ts +7 -6
- package/src/chrome/builtins.tsx +5 -1
- package/src/chrome/index.ts +1 -1
- package/src/chrome/merge.test.ts +5 -0
- package/src/chrome/merge.ts +4 -2
- package/src/chrome/regions/PaletteRegion.less +0 -7
- package/src/chrome/regions/PaletteRegion.test.tsx +2 -3
- package/src/chrome/regions/PaletteRegion.tsx +3 -4
- package/src/chrome/regions/SidebarRegion.less +12 -2
- package/src/chrome/regions/ViewportRegion.browser.test.less +6 -0
- package/src/chrome/regions/ViewportRegion.browser.test.tsx +38 -0
- package/src/chrome/regions/ViewportRegion.less +6 -0
- package/src/chrome/regions/ViewportRegion.tsx +10 -4
- package/src/chrome/types.ts +4 -2
- package/src/config/auto.ts +1 -0
- package/src/config/builder.test.ts +2 -1
- package/src/config/builder.ts +18 -5
- package/src/config/fromConfigField.test.ts +14 -0
- package/src/config/fromConfigField.ts +18 -4
- package/src/config/path.ts +2 -5
- package/src/config/resolve.ts +4 -0
- package/src/config/types.ts +14 -9
- package/src/controls/ControlPanel.less +1 -1
- package/src/controls/ControlPanel.stories.tsx +3 -2
- package/src/controls/ControlPanel.test.tsx +98 -11
- package/src/controls/ControlPanel.tsx +61 -2
- package/src/dragdrop/DragDropRuntime.tsx +22 -13
- package/src/dragdrop/DragGhost.tsx +3 -0
- package/src/dragdrop/Palette.tsx +2 -0
- package/src/index.test.ts +5 -0
- package/src/index.ts +16 -2
- package/src/instrument/types.ts +0 -6
- package/src/job/useJob.ts +14 -9
- package/src/lab/Lab.tsx +88 -73
- package/src/lab/{LabFit.stories.less → LabFit.browser.test.less} +1 -1
- package/src/lab/{LabFit.stories.tsx → LabFit.browser.test.tsx} +41 -63
- package/src/lab/LabFullChrome.stories.tsx +1 -0
- package/src/lab/LabHeader.test.tsx +24 -14
- package/src/lab/LabHeader.tsx +11 -12
- package/src/lab/LabShell.less +9 -17
- package/src/lab/LabSwitcher.browser.test.tsx +27 -0
- package/src/lab/LabSwitcher.less +8 -6
- package/src/lab/LabZoom.test.tsx +223 -0
- package/src/lab/LabZoom.tsx +170 -0
- package/src/lab/Workspace.tsx +48 -52
- package/src/lab/index.ts +1 -0
- package/src/loupe/AGENTS.md +28 -15
- package/src/loupe/CanvasLoupe.tsx +2 -3
- package/src/loupe/DomLoupe.tsx +1 -7
- package/src/loupe/Loupe.less +16 -0
- package/src/loupe/LoupeGestures.tsx +1 -1
- package/src/loupe/TrialLoupe.tsx +120 -57
- package/src/loupe/index.ts +1 -6
- package/src/loupe/types.test.ts +3 -3
- package/src/loupe/types.ts +24 -27
- package/src/loupe/useLoupe.test.tsx +29 -4
- package/src/loupe/useLoupe.ts +20 -17
- package/src/overview/OverviewMarks.tsx +12 -4
- package/src/overview/TrialOverview.tsx +10 -8
- package/src/passthrough/weasel-ui.test.ts +2 -0
- package/src/passthrough/weasel-ui.ts +17 -5
- package/src/primitives/FloatingPanel.test.tsx +11 -0
- package/src/primitives/FloatingPanel.tsx +8 -7
- package/src/primitives/JobProgress.less +2 -1
- package/src/primitives/Legend.stories.tsx +1 -0
- package/src/primitives/Readout.browser.test.tsx +137 -0
- package/src/primitives/Readout.less +12 -0
- package/src/primitives/Readout.stories.tsx +58 -0
- package/src/primitives/Readout.test.tsx +93 -0
- package/src/primitives/Readout.tsx +56 -0
- package/src/primitives/Split.tsx +69 -41
- package/src/primitives/Toolbar.tsx +3 -3
- package/src/primitives/ZoomControl.less +1 -2
- package/src/primitives/index.ts +2 -0
- package/src/specimen/Specimen.stories.tsx +1 -0
- package/src/specimen/Specimen.tsx +2 -3
- package/src/state/Persistence.browser.test.tsx +47 -0
- package/src/state/store.test.ts +5 -1
- package/src/state/undock.ts +3 -0
- package/src/state/useOpenOnce.ts +20 -16
- package/src/state/usePersistedState.test.tsx +1 -1
- package/src/state/view.ts +1 -0
- package/src/styles.less +1 -0
- package/src/surface/index.ts +1 -1
- package/src/surface/useTiledSurface.test.tsx +14 -0
- package/src/surface/useTiledSurface.ts +11 -3
- package/src/theme/Interstellar.stories.tsx +3 -1
- package/src/theme/base.less +21 -47
- package/src/theme/interstellar.test.ts +1 -1
- package/src/theme/interstellar.theme.json +10 -3
- package/src/trial/Trial.annotations.test.tsx +73 -0
- package/src/trial/Trial.less +0 -29
- package/src/trial/Trial.loupe.test.tsx +28 -11
- package/src/trial/Trial.tsx +85 -67
- package/src/trial/TrialChrome.tsx +27 -6
- package/src/trial/index.ts +2 -0
- package/src/trial/loupeSwitch.ts +15 -0
- package/dist/_dts/LayerList.d-BoELAMsM.d.ts +0 -83
- package/dist/_dts/PrefsRow.d-DFpPIeIC.d.ts +0 -39
- package/dist/_dts/PropertyField.d-D0ndfIIt.d.ts +0 -404
- package/dist/_dts/Select.d-Cr8-zoup.d.ts +0 -569
- package/dist/chunk-4TT3PNPR.js.map +0 -1
- package/dist/chunk-6FLXL3I7.js.map +0 -1
- package/dist/chunk-ASOK76ZH.js.map +0 -1
- package/dist/chunk-CGHF6RZV.js.map +0 -1
- package/dist/chunk-CQLQPQ4P.js.map +0 -1
- package/dist/chunk-DWMSB4CA.js.map +0 -1
- package/dist/chunk-HXXTULDN.js.map +0 -1
- package/dist/chunk-IJJUK4FU.js.map +0 -1
- package/dist/chunk-IRTGUQ5O.js.map +0 -1
- package/dist/chunk-KHK5FGER.js +0 -145
- package/dist/chunk-KHK5FGER.js.map +0 -1
- package/dist/chunk-NEXR37YJ.js.map +0 -1
- package/dist/chunk-O6FFFOHS.js.map +0 -1
- package/dist/chunk-QGMOA2YE.js.map +0 -1
- package/dist/chunk-RWNDDKOH.js.map +0 -1
- package/dist/chunk-S4NCI32V.js +0 -549
- package/dist/chunk-S4NCI32V.js.map +0 -1
- package/dist/chunk-SBXNQW4G.js +0 -3
- package/dist/chunk-SLAQPUI4.js.map +0 -1
- package/dist/chunk-TJ77A4A3.js.map +0 -1
- package/dist/chunk-TYN6MLXX.js +0 -13864
- package/dist/chunk-TYN6MLXX.js.map +0 -1
- package/dist/chunk-WPLAANH4.js.map +0 -1
- package/src/primitives/useRovingTabIndex.test.ts +0 -48
- package/src/primitives/useRovingTabIndex.ts +0 -88
- package/src/state/Persistence.stories.tsx +0 -75
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { openPointerSession, type PointerSession, useVisibleRaf } from '@weasel-js/core';
|
|
1
|
+
import { openPointerSession, type PointerSession, useLatest, useVisibleRaf } from '@weasel-js/core';
|
|
2
|
+
import { boxContainsPoint } from '@weasel-js/geom';
|
|
2
3
|
import {
|
|
3
4
|
type PointerEvent as ReactPointerEvent,
|
|
4
5
|
type RefObject,
|
|
@@ -18,12 +19,16 @@ import type {
|
|
|
18
19
|
} from '../instrument/types';
|
|
19
20
|
import { DragGhost } from './DragGhost';
|
|
20
21
|
|
|
22
|
+
/** A palette drag in progress. `screenPos` is in client pixels; `feedback` is
|
|
23
|
+
* the last `onDragOver` answer — `null` off the canvas or without one. */
|
|
21
24
|
export interface DragState {
|
|
22
25
|
item: PaletteItem;
|
|
23
26
|
screenPos: Point;
|
|
24
27
|
feedback: DragFeedback | null;
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
/** Inputs to `useDragDrop`: the capability, the element that counts as the
|
|
31
|
+
* drop target, and the trial state a drop rewrites. */
|
|
27
32
|
export interface UseDragDropArgs<TS, TC> {
|
|
28
33
|
capability: DragDropCapability<TS, TC>;
|
|
29
34
|
canvasContainerRef: RefObject<HTMLElement | null>;
|
|
@@ -37,11 +42,18 @@ export interface UseDragDropArgs<TS, TC> {
|
|
|
37
42
|
emit: (event: string) => void;
|
|
38
43
|
}
|
|
39
44
|
|
|
45
|
+
/** The live drag, and the handler a palette item calls on pointerdown. */
|
|
40
46
|
export interface UseDragDropResult {
|
|
41
47
|
drag: DragState | null;
|
|
42
48
|
startDrag: (item: PaletteItem, e: ReactPointerEvent) => void;
|
|
43
49
|
}
|
|
44
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Run palette-to-canvas drags for a `DragDropCapability`. Releasing over the
|
|
53
|
+
* container converts the pointer to world coordinates, applies `onDrop`'s
|
|
54
|
+
* state and emits `'canvas.itemAdded'`; releasing anywhere else, or a canceled
|
|
55
|
+
* gesture, drops nothing. `onDragOver` is probed at most once per frame.
|
|
56
|
+
*/
|
|
45
57
|
export function useDragDrop<TS, TC>({
|
|
46
58
|
capability,
|
|
47
59
|
canvasContainerRef,
|
|
@@ -53,8 +65,8 @@ export function useDragDrop<TS, TC>({
|
|
|
53
65
|
worldSpec,
|
|
54
66
|
}: UseDragDropArgs<TS, TC>): UseDragDropResult {
|
|
55
67
|
const [drag, setDrag] = useState<DragState | null>(null);
|
|
68
|
+
// Owned by the handlers below, which write it alongside every `setDrag`.
|
|
56
69
|
const dragRef = useRef<DragState | null>(null);
|
|
57
|
-
dragRef.current = drag;
|
|
58
70
|
/** The move the next frame will resolve. Doubles as the throttle's flag:
|
|
59
71
|
* non-null means a frame is already queued. */
|
|
60
72
|
const pendingPos = useRef<Point | null>(null);
|
|
@@ -62,20 +74,14 @@ export function useDragDrop<TS, TC>({
|
|
|
62
74
|
|
|
63
75
|
/** A session keeps the closure it was opened with, so everything the drop
|
|
64
76
|
* reads has to come from here rather than from that closure. */
|
|
65
|
-
const liveRef =
|
|
66
|
-
liveRef.current = { capability, state, config, setState, emit, view, worldSpec };
|
|
77
|
+
const liveRef = useLatest({ capability, state, config, setState, emit, view, worldSpec });
|
|
67
78
|
|
|
68
79
|
const isOverCanvas = useCallback(
|
|
69
80
|
(screenPos: Point): boolean => {
|
|
70
81
|
const el = canvasContainerRef.current;
|
|
71
82
|
if (!el) return false;
|
|
72
83
|
const r = el.getBoundingClientRect();
|
|
73
|
-
return (
|
|
74
|
-
screenPos.x >= r.left &&
|
|
75
|
-
screenPos.x <= r.right &&
|
|
76
|
-
screenPos.y >= r.top &&
|
|
77
|
-
screenPos.y <= r.bottom
|
|
78
|
-
);
|
|
84
|
+
return boxContainsPoint([r.left, r.top, r.right, r.bottom], screenPos.x, screenPos.y);
|
|
79
85
|
},
|
|
80
86
|
[canvasContainerRef],
|
|
81
87
|
);
|
|
@@ -92,7 +98,7 @@ export function useDragDrop<TS, TC>({
|
|
|
92
98
|
frame,
|
|
93
99
|
);
|
|
94
100
|
},
|
|
95
|
-
[canvasContainerRef],
|
|
101
|
+
[canvasContainerRef, liveRef],
|
|
96
102
|
);
|
|
97
103
|
|
|
98
104
|
const frameLoop = useVisibleRaf(() => {
|
|
@@ -106,7 +112,8 @@ export function useDragDrop<TS, TC>({
|
|
|
106
112
|
const world = screenToWorldFromContainer(screenPos);
|
|
107
113
|
if (world) feedback = live.capability.onDragOver(world, active.item, live.state, live.config);
|
|
108
114
|
}
|
|
109
|
-
|
|
115
|
+
dragRef.current = { ...active, screenPos, feedback };
|
|
116
|
+
setDrag(dragRef.current);
|
|
110
117
|
});
|
|
111
118
|
|
|
112
119
|
const clearDrag = useCallback(() => {
|
|
@@ -154,7 +161,7 @@ export function useDragDrop<TS, TC>({
|
|
|
154
161
|
onCancel: clearDrag,
|
|
155
162
|
});
|
|
156
163
|
},
|
|
157
|
-
[clearDrag, frameLoop, isOverCanvas, screenToWorldFromContainer],
|
|
164
|
+
[clearDrag, frameLoop, isOverCanvas, liveRef, screenToWorldFromContainer],
|
|
158
165
|
);
|
|
159
166
|
|
|
160
167
|
useEffect(() => () => sessionRef.current?.cancel(), []);
|
|
@@ -162,10 +169,12 @@ export function useDragDrop<TS, TC>({
|
|
|
162
169
|
return { drag, startDrag };
|
|
163
170
|
}
|
|
164
171
|
|
|
172
|
+
/** Props for `DragOverlay`. */
|
|
165
173
|
export interface DragOverlayProps {
|
|
166
174
|
drag: DragState | null;
|
|
167
175
|
}
|
|
168
176
|
|
|
177
|
+
/** The ghost for `drag`, or nothing when no drag is live. */
|
|
169
178
|
export function DragOverlay({ drag }: DragOverlayProps) {
|
|
170
179
|
if (!drag) return null;
|
|
171
180
|
return <DragGhost item={drag.item} screenPos={drag.screenPos} />;
|
|
@@ -2,11 +2,14 @@ import { type ReactPortal, useEffect, useState } from 'react';
|
|
|
2
2
|
import { createPortal } from 'react-dom';
|
|
3
3
|
import type { PaletteItem, Point } from '../instrument/types';
|
|
4
4
|
|
|
5
|
+
/** Props for `DragGhost`. `screenPos` is in client pixels. */
|
|
5
6
|
export interface DragGhostProps {
|
|
6
7
|
item: PaletteItem;
|
|
7
8
|
screenPos: Point;
|
|
8
9
|
}
|
|
9
10
|
|
|
11
|
+
/** The dragged item's label, portaled to `document.body` just below-right of
|
|
12
|
+
* `screenPos`. Renders nothing until mounted, so it is SSR-safe. */
|
|
10
13
|
export function DragGhost({ item, screenPos }: DragGhostProps): ReactPortal | null {
|
|
11
14
|
const [mounted, setMounted] = useState(false);
|
|
12
15
|
useEffect(() => setMounted(true), []);
|
package/src/dragdrop/Palette.tsx
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { PointerEvent as ReactPointerEvent } from 'react';
|
|
2
2
|
import type { PaletteItem } from '../instrument/types';
|
|
3
3
|
|
|
4
|
+
/** Props for `Palette`. `onDragStart` is usually `useDragDrop`'s `startDrag`. */
|
|
4
5
|
export interface PaletteProps {
|
|
5
6
|
items: PaletteItem[];
|
|
6
7
|
onDragStart: (item: PaletteItem, e: ReactPointerEvent) => void;
|
|
7
8
|
className?: string;
|
|
8
9
|
}
|
|
9
10
|
|
|
11
|
+
/** One button per item, each starting a drag on pointerdown. */
|
|
10
12
|
export function Palette({ items, onDragStart, className }: PaletteProps) {
|
|
11
13
|
return (
|
|
12
14
|
<div className={className ? `lk-palette ${className}` : 'lk-palette'}>
|
package/src/index.test.ts
CHANGED
|
@@ -49,6 +49,11 @@ describe('public export surface', () => {
|
|
|
49
49
|
expect(labkit).toHaveProperty('useTrialState');
|
|
50
50
|
expect(labkit).toHaveProperty('useTrialId');
|
|
51
51
|
});
|
|
52
|
+
|
|
53
|
+
it('leaves the loupe to its own entry, so a lab without one does not load it', () => {
|
|
54
|
+
expect(labkit).not.toHaveProperty('TrialLoupe');
|
|
55
|
+
expect(labkit).not.toHaveProperty('useLoupe');
|
|
56
|
+
});
|
|
52
57
|
});
|
|
53
58
|
|
|
54
59
|
describe('ui re-exports', () => {
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// The property-panel family, LayerList, Input, Select, TokenPanel, ToggleBar and Button live in
|
|
1
|
+
// The property-panel family, DetailList, LayerList, Input, Select, TokenPanel, ToggleBar and Button live in
|
|
2
2
|
// `@weasel-js/ui`; labkit re-exports them so chrome built on labkit needs no
|
|
3
3
|
// direct ui dependency. Named, not `export *` — a star re-export of an external
|
|
4
4
|
// package emits no binding in the bundle.
|
|
@@ -10,6 +10,11 @@ export type {
|
|
|
10
10
|
ButtonVariant,
|
|
11
11
|
CurveFieldProps,
|
|
12
12
|
CurveMark,
|
|
13
|
+
DetailListLayout,
|
|
14
|
+
DetailListProps,
|
|
15
|
+
DetailListValues,
|
|
16
|
+
DetailRowProps,
|
|
17
|
+
DetailRowStatus,
|
|
13
18
|
IconProps,
|
|
14
19
|
InputProps,
|
|
15
20
|
JogProps,
|
|
@@ -41,6 +46,8 @@ export {
|
|
|
41
46
|
CloseIcon,
|
|
42
47
|
CollapseIcon,
|
|
43
48
|
CurveField,
|
|
49
|
+
DetailList,
|
|
50
|
+
DetailRow,
|
|
44
51
|
ExpandIcon,
|
|
45
52
|
Icon,
|
|
46
53
|
Input,
|
|
@@ -73,6 +80,7 @@ export type {
|
|
|
73
80
|
AnnotationMeaning,
|
|
74
81
|
AnnotationPatch,
|
|
75
82
|
AnnotationQuery,
|
|
83
|
+
AnnotationSelectionMode,
|
|
76
84
|
AnnotationStatus,
|
|
77
85
|
AnnotationStoreOptions,
|
|
78
86
|
AnnotationsApi,
|
|
@@ -131,6 +139,8 @@ export type { ContentSize, PaneSize } from './annotations/view';
|
|
|
131
139
|
export { fitView, fromWeaselView, toWeaselView } from './annotations/view';
|
|
132
140
|
export type {
|
|
133
141
|
CameraContextValue,
|
|
142
|
+
CameraRegistry,
|
|
143
|
+
CameraView,
|
|
134
144
|
CanvasLayerDescriptor,
|
|
135
145
|
CanvasStackContextValue,
|
|
136
146
|
CanvasStackProps,
|
|
@@ -140,20 +150,25 @@ export type {
|
|
|
140
150
|
WorldFrame,
|
|
141
151
|
WorldSpec,
|
|
142
152
|
ZoomAtOptions,
|
|
153
|
+
ZoomRange,
|
|
143
154
|
} from './canvas';
|
|
144
155
|
export {
|
|
145
156
|
applyCamera,
|
|
146
157
|
CameraContext,
|
|
147
158
|
CameraInput,
|
|
159
|
+
CameraPublishContext,
|
|
160
|
+
CameraRegistryContext,
|
|
148
161
|
CameraScope,
|
|
149
162
|
CanvasStack,
|
|
150
163
|
CanvasStackContext,
|
|
151
164
|
centerOn,
|
|
165
|
+
createCameraRegistry,
|
|
152
166
|
DEFAULT_FRAME,
|
|
153
167
|
fitStage,
|
|
154
168
|
resolveFrame,
|
|
155
169
|
Stage,
|
|
156
170
|
screenToWorld,
|
|
171
|
+
usePublishCamera,
|
|
157
172
|
worldToScreen,
|
|
158
173
|
zoomAt,
|
|
159
174
|
} from './canvas';
|
|
@@ -253,7 +268,6 @@ export * from './job';
|
|
|
253
268
|
export * from './lab';
|
|
254
269
|
export type { LayerListItem, LayerListProps, LayerMove } from './layers';
|
|
255
270
|
export { LayerList, moveLayers } from './layers';
|
|
256
|
-
export * from './loupe';
|
|
257
271
|
export * from './primitives';
|
|
258
272
|
export { SPECIMEN_SECTIONS, Specimen } from './specimen/Specimen';
|
|
259
273
|
export {
|
package/src/instrument/types.ts
CHANGED
|
@@ -6,7 +6,6 @@ import type { TrialContribution } from '../chrome/types';
|
|
|
6
6
|
import type { ConfigPath, ConfigSchema } from '../config/types';
|
|
7
7
|
import type { ConfigField } from '../controls/types';
|
|
8
8
|
import type { JobCapability, JobHandle } from '../job/types';
|
|
9
|
-
import type { LoupeDeclaration } from '../loupe/types';
|
|
10
9
|
import type { TrialInfo } from '../state/types';
|
|
11
10
|
import type { ToolCapability } from '../tools/types';
|
|
12
11
|
|
|
@@ -182,11 +181,6 @@ export interface Instrument<TS = unknown, TC = unknown, TItem = unknown> {
|
|
|
182
181
|
* is allowed to mean. Declaring it is what makes the trial provide the
|
|
183
182
|
* annotation overlay and its chrome. */
|
|
184
183
|
annotations?: AnnotationsCapability<TS, TC>;
|
|
185
|
-
/** Magnification. `true` takes every default and re-draws the instrument's
|
|
186
|
-
* canvas layers through a zoomed camera; an instrument whose content is DOM
|
|
187
|
-
* gives a `render` that draws it again at a camera it is handed. A function
|
|
188
|
-
* is re-read as the config changes, so a setting can drive the lens. */
|
|
189
|
-
loupe?: LoupeDeclaration<TS, TC> | ((config: TC) => LoupeDeclaration<TS, TC>);
|
|
190
184
|
/** Chrome this instrument contributes beyond what its capabilities imply. */
|
|
191
185
|
chrome?: TrialContribution[];
|
|
192
186
|
/** Work too slow to do during a render. The runtime starts it, aborts it on
|
package/src/job/useJob.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { useLatest } from '@weasel-js/core';
|
|
1
2
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
3
|
import type { JobCapability, JobFailure, JobHandle, JobStatus } from './types';
|
|
3
4
|
|
|
5
|
+
/** Inputs to `useJob`. `setState` receives each item folded in by the
|
|
6
|
+
* capability's `onItem`. */
|
|
4
7
|
export interface UseJobOptions<TS, TC, TItem> {
|
|
5
8
|
capability: JobCapability<TS, TC, TItem>;
|
|
6
9
|
config: TC;
|
|
@@ -23,6 +26,12 @@ function sameKey(a: readonly unknown[] | null, b: readonly unknown[] | null): bo
|
|
|
23
26
|
return a.length === b.length && a.every((v, i) => Object.is(v, b[i]));
|
|
24
27
|
}
|
|
25
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Drive a `JobCapability` and report its progress. `start` cancels any run in
|
|
31
|
+
* flight and begins again; results from a superseded or canceled run are
|
|
32
|
+
* dropped. With `auto`, it starts on mount and again whenever `key` changes
|
|
33
|
+
* element-wise. Unmounting aborts the run.
|
|
34
|
+
*/
|
|
26
35
|
export function useJob<TS, TC, TItem>({
|
|
27
36
|
capability,
|
|
28
37
|
config,
|
|
@@ -38,14 +47,10 @@ export function useJob<TS, TC, TItem>({
|
|
|
38
47
|
|
|
39
48
|
// Read through refs: `run` is called once per run and must see the values as of
|
|
40
49
|
// that moment rather than re-subscribing on every render.
|
|
41
|
-
const capRef =
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
const stateRef = useRef(state);
|
|
46
|
-
stateRef.current = state;
|
|
47
|
-
const setStateRef = useRef(setState);
|
|
48
|
-
setStateRef.current = setState;
|
|
50
|
+
const capRef = useLatest(capability);
|
|
51
|
+
const configRef = useLatest(config);
|
|
52
|
+
const stateRef = useLatest(state);
|
|
53
|
+
const setStateRef = useLatest(setState);
|
|
49
54
|
|
|
50
55
|
const cancel = useCallback(() => {
|
|
51
56
|
token.current += 1;
|
|
@@ -96,7 +101,7 @@ export function useJob<TS, TC, TItem>({
|
|
|
96
101
|
}));
|
|
97
102
|
}
|
|
98
103
|
})();
|
|
99
|
-
}, []);
|
|
104
|
+
}, [capRef, configRef, setStateRef, stateRef]);
|
|
100
105
|
|
|
101
106
|
// Re-run when the declared key changes. `auto` covers the first mount too.
|
|
102
107
|
const lastKey = useRef<readonly unknown[] | null>(null);
|
package/src/lab/Lab.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
type ReactNode,
|
|
6
6
|
useCallback,
|
|
7
7
|
useEffect,
|
|
8
|
+
useInsertionEffect,
|
|
8
9
|
useLayoutEffect,
|
|
9
10
|
useMemo,
|
|
10
11
|
useRef,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
import { useStore } from 'zustand/react';
|
|
14
15
|
import { AnnotationPreloadContext } from '../annotations/preload';
|
|
15
16
|
import { labAnnotationTools } from '../annotations/toolMap';
|
|
17
|
+
import { CameraRegistryContext, createCameraRegistry } from '../canvas/cameraRegistry';
|
|
16
18
|
import {
|
|
17
19
|
LabAsideRegion,
|
|
18
20
|
LabFooterRegion,
|
|
@@ -50,10 +52,11 @@ import {
|
|
|
50
52
|
} from '../trial/trialOps';
|
|
51
53
|
import { useLabFitWarning } from './fitCheck';
|
|
52
54
|
import { LabContext, type LabContextValue } from './LabContext';
|
|
53
|
-
import { LabHeader } from './LabHeader';
|
|
55
|
+
import { LabHeader, LabThemeSwitcher } from './LabHeader';
|
|
54
56
|
import { LabPalette } from './LabPalette';
|
|
55
57
|
import { LabShell } from './LabShell';
|
|
56
58
|
import type { LabPage } from './LabSwitcher';
|
|
59
|
+
import { LabZoom } from './LabZoom';
|
|
57
60
|
import { createPanelHostRegistry, PanelHostContext } from './panelHost';
|
|
58
61
|
import { type PanelDescriptor, type TrialLayout, Workspace } from './Workspace';
|
|
59
62
|
|
|
@@ -104,6 +107,9 @@ interface LabBaseProps {
|
|
|
104
107
|
/** Offer the header's add-trial control. Default `true`; a lab that opens
|
|
105
108
|
* trials some other way passes `false`. */
|
|
106
109
|
addTrial?: boolean;
|
|
110
|
+
/** Offer the header's zoom controls, which act on the focused trial's camera
|
|
111
|
+
* and answer Mod+=, Mod+- and Mod+0. Default `true`. */
|
|
112
|
+
zoom?: boolean;
|
|
107
113
|
/** Tools offered lab-wide. A trial whose instrument declares none of its own
|
|
108
114
|
* reflects and writes this slot. */
|
|
109
115
|
tools?: readonly TrialTool[];
|
|
@@ -339,6 +345,7 @@ function LabRuntime({
|
|
|
339
345
|
labChrome,
|
|
340
346
|
suppress,
|
|
341
347
|
addTrial,
|
|
348
|
+
zoom = true,
|
|
342
349
|
tools,
|
|
343
350
|
configRules,
|
|
344
351
|
controls,
|
|
@@ -354,6 +361,7 @@ function LabRuntime({
|
|
|
354
361
|
}, [instruments, store]);
|
|
355
362
|
|
|
356
363
|
const trials = useStore(store, (s) => s.trials);
|
|
364
|
+
const [cameras] = useState(createCameraRegistry);
|
|
357
365
|
const [focusPick, setFocusPick] = useState<string | null>(null);
|
|
358
366
|
const focusedTrialId = trials.some((t) => t.id === focusPick)
|
|
359
367
|
? focusPick
|
|
@@ -411,7 +419,10 @@ function LabRuntime({
|
|
|
411
419
|
}, []);
|
|
412
420
|
|
|
413
421
|
const ownSurface = useTiledSurface({ onFrame });
|
|
414
|
-
|
|
422
|
+
// Not `useLatest`: `onFrame` reads it, and is needed before the surface exists.
|
|
423
|
+
useInsertionEffect(() => {
|
|
424
|
+
surfaceRef.current = ownSurface;
|
|
425
|
+
}, [ownSurface]);
|
|
415
426
|
const [labBody, setLabBody] = useState<HTMLDivElement | null>(null);
|
|
416
427
|
const attachOwnSurface = ownSurface.containerRef;
|
|
417
428
|
const labBodyRef = useCallback(
|
|
@@ -612,79 +623,83 @@ function LabRuntime({
|
|
|
612
623
|
<PersistenceContext.Provider value={opened.records}>
|
|
613
624
|
<AnnotationPreloadContext.Provider value={opened.marks}>
|
|
614
625
|
<LabContext.Provider value={contextValue}>
|
|
615
|
-
<
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
626
|
+
<CameraRegistryContext.Provider value={cameras}>
|
|
627
|
+
<ThemeProvider
|
|
628
|
+
theme={theme}
|
|
629
|
+
selection={{ mode: resolvedMode, density: density ?? 'comfortable' }}
|
|
630
|
+
className="lk-lab"
|
|
631
|
+
style={backdropStyle}
|
|
632
|
+
>
|
|
633
|
+
<LabShell
|
|
634
|
+
title={title ?? 'Labkit'}
|
|
635
|
+
mode={modeValue}
|
|
636
|
+
{...(pages ? { pages } : {})}
|
|
637
|
+
{...(path !== undefined ? { path } : {})}
|
|
638
|
+
footer={
|
|
639
|
+
hasFooterChrome ? (
|
|
640
|
+
<>
|
|
641
|
+
{footer}
|
|
642
|
+
<LabFooterRegion contributions={labChromeAll} />
|
|
643
|
+
</>
|
|
644
|
+
) : (
|
|
645
|
+
footer
|
|
646
|
+
)
|
|
647
|
+
}
|
|
648
|
+
header={
|
|
628
649
|
<>
|
|
629
|
-
{
|
|
630
|
-
<
|
|
650
|
+
<LabHeader {...(addTrial !== undefined ? { addTrial } : {})} />
|
|
651
|
+
{zoom ? <LabZoom /> : null}
|
|
652
|
+
{children}
|
|
653
|
+
<LabHeaderRegion contributions={labChromeAll} />
|
|
654
|
+
<LabThemeSwitcher />
|
|
631
655
|
</>
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
<
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
{workspace}
|
|
680
|
-
</>
|
|
681
|
-
)}
|
|
682
|
-
</div>
|
|
683
|
-
</SurfaceCanvasContext.Provider>
|
|
684
|
-
</SurfaceContext.Provider>
|
|
685
|
-
</PanelHostContext.Provider>
|
|
686
|
-
</LabShell>
|
|
687
|
-
</ThemeProvider>
|
|
656
|
+
}
|
|
657
|
+
>
|
|
658
|
+
<PanelHostContext.Provider value={panelHostsRef.current}>
|
|
659
|
+
<SurfaceContext.Provider value={surface}>
|
|
660
|
+
<SurfaceCanvasContext.Provider value={surfaceCanvases}>
|
|
661
|
+
<div className="lk-lab__body" ref={labBodyRef}>
|
|
662
|
+
{outerSurface ? null : (
|
|
663
|
+
// Two buffers stacked around the trials, both inert —
|
|
664
|
+
// each tile takes input from its own box. A tile's marks
|
|
665
|
+
// annotate the instrument's DOM from over it; an opaque
|
|
666
|
+
// renderer sits under it, so the pane can still hold a
|
|
667
|
+
// label. The under one is first so it paints first.
|
|
668
|
+
<>
|
|
669
|
+
<canvas
|
|
670
|
+
className="lk-lab__surface lk-lab__surface--under"
|
|
671
|
+
ref={(el) => {
|
|
672
|
+
ownUnderRef.current = el;
|
|
673
|
+
setOwnUnder(el);
|
|
674
|
+
}}
|
|
675
|
+
/>
|
|
676
|
+
<canvas
|
|
677
|
+
className="lk-lab__surface lk-lab__surface--over"
|
|
678
|
+
ref={(el) => {
|
|
679
|
+
ownOverRef.current = el;
|
|
680
|
+
setOwnOver(el);
|
|
681
|
+
}}
|
|
682
|
+
/>
|
|
683
|
+
</>
|
|
684
|
+
)}
|
|
685
|
+
{hasPaneChrome ? (
|
|
686
|
+
<LabPanes contributions={labChromeAll}>
|
|
687
|
+
<LabPalette contributions={labChromeAll} />
|
|
688
|
+
{workspace}
|
|
689
|
+
</LabPanes>
|
|
690
|
+
) : (
|
|
691
|
+
<>
|
|
692
|
+
<LabPalette contributions={labChromeAll} />
|
|
693
|
+
{workspace}
|
|
694
|
+
</>
|
|
695
|
+
)}
|
|
696
|
+
</div>
|
|
697
|
+
</SurfaceCanvasContext.Provider>
|
|
698
|
+
</SurfaceContext.Provider>
|
|
699
|
+
</PanelHostContext.Provider>
|
|
700
|
+
</LabShell>
|
|
701
|
+
</ThemeProvider>
|
|
702
|
+
</CameraRegistryContext.Provider>
|
|
688
703
|
</LabContext.Provider>
|
|
689
704
|
</AnnotationPreloadContext.Provider>
|
|
690
705
|
</PersistenceContext.Provider>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Host pages for LabFit.
|
|
1
|
+
// Host pages for LabFit.browser.test. Each is scoped by its own class, so a test
|
|
2
2
|
// that mounts no fixture leaves the page untouched.
|
|
3
3
|
|
|
4
4
|
// The mount every example documents: `html, body, #root { margin: 0; height: 100% }`.
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import '@weasel-js/theme/tokens.css';
|
|
2
|
+
import 'windease/styles.css';
|
|
3
|
+
import '../styles.less';
|
|
4
|
+
import './LabFit.browser.test.less';
|
|
5
|
+
import { cleanup, render, screen, waitFor } from '@testing-library/react';
|
|
3
6
|
import { type ReactNode, useLayoutEffect, useState } from 'react';
|
|
4
7
|
import { createPortal } from 'react-dom';
|
|
8
|
+
import { afterEach, expect, test } from 'vitest';
|
|
9
|
+
import { page, userEvent } from 'vitest/browser';
|
|
5
10
|
import type { LabContribution } from '../chrome/labTypes';
|
|
6
11
|
import type { TrialContribution } from '../chrome/types';
|
|
7
12
|
import type { Instrument } from '../instrument/types';
|
|
8
13
|
import { Lab } from './Lab';
|
|
9
|
-
import './LabFit.stories.less';
|
|
10
14
|
|
|
11
|
-
// A lab must fit whatever it is mounted in without scrolling
|
|
12
|
-
// layout
|
|
15
|
+
// A lab must fit whatever it is mounted in without scrolling, which only real
|
|
16
|
+
// layout can show.
|
|
17
|
+
|
|
18
|
+
afterEach(cleanup);
|
|
13
19
|
|
|
14
20
|
const Stub: Instrument = {
|
|
15
21
|
name: 'Stub',
|
|
@@ -158,62 +164,34 @@ function fitProblems(fixture: Fixture, viewport: keyof typeof VIEWPORTS): string
|
|
|
158
164
|
return problems;
|
|
159
165
|
}
|
|
160
166
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
);
|
|
187
|
-
await expect(fitProblems(fixture, viewport)).toEqual([]);
|
|
167
|
+
const FIXTURES: Fixture[] = ['fullscreen', 'floating', 'wrapped', 'framed', 'sidebar'];
|
|
168
|
+
const CASES = FIXTURES.flatMap((fixture) =>
|
|
169
|
+
(Object.keys(VIEWPORTS) as (keyof typeof VIEWPORTS)[]).map((viewport) => ({ fixture, viewport })),
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
test.each(CASES)('$fixture fits a $viewport viewport', async ({ fixture, viewport }) => {
|
|
173
|
+
const { width, height } = VIEWPORTS[viewport].styles;
|
|
174
|
+
await page.viewport(Number.parseInt(width, 10), Number.parseInt(height, 10));
|
|
175
|
+
render(
|
|
176
|
+
<BodyHost host={HOST[fixture]}>
|
|
177
|
+
<Lab
|
|
178
|
+
title={`Fit: ${fixture}`}
|
|
179
|
+
instruments={[Stub]}
|
|
180
|
+
defaultInstrument="Stub"
|
|
181
|
+
chrome={fixture === 'floating' ? [floater] : undefined}
|
|
182
|
+
labChrome={fixture === 'sidebar' ? [sidebarTree] : undefined}
|
|
183
|
+
/>
|
|
184
|
+
</BodyHost>,
|
|
185
|
+
);
|
|
186
|
+
if (fixture === 'floating') {
|
|
187
|
+
await userEvent.click(await screen.findByRole('button', { name: 'Undock Floater' }));
|
|
188
|
+
}
|
|
189
|
+
await waitFor(
|
|
190
|
+
() => {
|
|
191
|
+
const problems = fitProblems(fixture, viewport);
|
|
192
|
+
if (problems.length > 0) throw new Error(problems.join('; '));
|
|
188
193
|
},
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
title: 'labkit/Lab/Fit',
|
|
194
|
-
component: Lab,
|
|
195
|
-
parameters: { layout: 'fullscreen', viewport: { options: VIEWPORTS } },
|
|
196
|
-
};
|
|
197
|
-
export default meta;
|
|
198
|
-
|
|
199
|
-
type Story = StoryObj<typeof Lab>;
|
|
200
|
-
|
|
201
|
-
export const FullscreenWide = fit('fullscreen', 'wide');
|
|
202
|
-
export const FullscreenMedium = fit('fullscreen', 'medium');
|
|
203
|
-
export const FullscreenNarrow = fit('fullscreen', 'narrow');
|
|
204
|
-
|
|
205
|
-
export const FloatingPanelWide = fit('floating', 'wide');
|
|
206
|
-
export const FloatingPanelMedium = fit('floating', 'medium');
|
|
207
|
-
export const FloatingPanelNarrow = fit('floating', 'narrow');
|
|
208
|
-
|
|
209
|
-
export const WrappedNoResetWide = fit('wrapped', 'wide');
|
|
210
|
-
export const WrappedNoResetMedium = fit('wrapped', 'medium');
|
|
211
|
-
export const WrappedNoResetNarrow = fit('wrapped', 'narrow');
|
|
212
|
-
|
|
213
|
-
export const FramedWide = fit('framed', 'wide');
|
|
214
|
-
export const FramedMedium = fit('framed', 'medium');
|
|
215
|
-
export const FramedNarrow = fit('framed', 'narrow');
|
|
216
|
-
|
|
217
|
-
export const SidebarWide = fit('sidebar', 'wide');
|
|
218
|
-
export const SidebarMedium = fit('sidebar', 'medium');
|
|
219
|
-
export const SidebarNarrow = fit('sidebar', 'narrow');
|
|
194
|
+
{ timeout: 2000 },
|
|
195
|
+
);
|
|
196
|
+
expect(fitProblems(fixture, viewport)).toEqual([]);
|
|
197
|
+
});
|
|
@@ -252,6 +252,7 @@ const runLog = defineInstrument<{ lines: string[] }, Record<string, never>>({
|
|
|
252
252
|
const meta: Meta<typeof Lab> = {
|
|
253
253
|
title: 'labkit/Lab/FullChrome',
|
|
254
254
|
component: Lab,
|
|
255
|
+
tags: ['gallery'],
|
|
255
256
|
parameters: { layout: 'fullscreen' },
|
|
256
257
|
};
|
|
257
258
|
export default meta;
|