@weasel-js/labkit 1.6.0 → 1.7.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/_dts/{CanvasStackContext-DQgcH1ny.d.ts → CanvasStackContext-D2jnIwfk.d.ts} +1 -1
- package/dist/_dts/FloatingPanel-7j0IcXwz.d.ts +30 -0
- package/dist/_dts/{LayerList.d-2c9yup5Y.d.ts → LayerList.d-DiuEArKq.d.ts} +4 -4
- package/dist/_dts/PrefsRow.d-TOZ7BgD9.d.ts +79 -0
- package/dist/_dts/PropertyPanel.d-D-1paCbE.d.ts +141 -0
- package/dist/_dts/{Select.d-BchGMC2Y.d.ts → Select.d-Bensr_MT.d.ts} +15 -6
- package/dist/_dts/Stage-CCWfQQWM.d.ts +202 -0
- package/dist/_dts/index-B2Yj5aA0.d.ts +274 -0
- package/dist/_dts/{index-B0--8PEM.d.ts → index-CsYqUHD3.d.ts} +12 -13
- package/dist/_dts/{types-B7y8hX1D.d.ts → types-CRPSGFAp.d.ts} +33 -5
- package/dist/_dts/{frac-DTRq7Xpb.d.ts → types-CSm9jgol.d.ts} +700 -650
- package/dist/_dts/{useTrialState-CP4jfuKl.d.ts → useTrialState-CTZPgeZ7.d.ts} +2 -3
- package/dist/canvas/index.d.ts +40 -8
- package/dist/canvas/index.js +5 -3
- package/dist/chrome/index.d.ts +5 -6
- package/dist/chrome/index.js +9 -7
- package/dist/chunk-24W5VEJW.js +18 -0
- package/dist/chunk-24W5VEJW.js.map +1 -0
- package/dist/chunk-275JRRVD.js +728 -0
- package/dist/chunk-275JRRVD.js.map +1 -0
- package/dist/chunk-3RY6WL5T.js +38 -0
- package/dist/chunk-3RY6WL5T.js.map +1 -0
- package/dist/chunk-4LJSC7MB.js +143 -0
- package/dist/chunk-4LJSC7MB.js.map +1 -0
- package/dist/{chunk-3FI7Y4R4.js → chunk-7BO7HNGF.js} +5 -5
- package/dist/{chunk-3FI7Y4R4.js.map → chunk-7BO7HNGF.js.map} +1 -1
- package/dist/chunk-AWQG7FCC.js +19 -0
- package/dist/chunk-AWQG7FCC.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-ZORVNOKX.js → chunk-FMK7UQ6V.js} +24 -145
- package/dist/chunk-FMK7UQ6V.js.map +1 -0
- package/dist/{chunk-BVL7Y574.js → chunk-FZ7YFQN7.js} +26 -10
- package/dist/chunk-FZ7YFQN7.js.map +1 -0
- package/dist/{chunk-USO6TERP.js → chunk-GOZLNM4L.js} +11 -9
- package/dist/chunk-GOZLNM4L.js.map +1 -0
- package/dist/{chunk-H5VIV7MQ.js → chunk-I5DUQ5HM.js} +88 -82
- package/dist/chunk-I5DUQ5HM.js.map +1 -0
- package/dist/chunk-J7OWWTCW.js +143 -0
- package/dist/chunk-J7OWWTCW.js.map +1 -0
- package/dist/{chunk-H6ZAOWNE.js → chunk-JVBYV6TL.js} +3 -3
- package/dist/{chunk-H6ZAOWNE.js.map → chunk-JVBYV6TL.js.map} +1 -1
- package/dist/{chunk-VQ47KWD5.js → chunk-KMJ7RWD3.js} +18 -7
- package/dist/chunk-KMJ7RWD3.js.map +1 -0
- package/dist/{chunk-ERFMC6WU.js → chunk-M7HP3FTK.js} +20 -11
- package/dist/chunk-M7HP3FTK.js.map +1 -0
- package/dist/chunk-MIM5R4ZT.js +14972 -0
- package/dist/chunk-MIM5R4ZT.js.map +1 -0
- package/dist/{chunk-HFRE4QQV.js → chunk-T5IBSPAM.js} +96 -32
- package/dist/chunk-T5IBSPAM.js.map +1 -0
- package/dist/chunk-UNAZSLWE.js +294 -0
- package/dist/chunk-UNAZSLWE.js.map +1 -0
- package/dist/{chunk-RS3HRQWU.js → chunk-WPLAANH4.js} +4 -3
- package/dist/chunk-WPLAANH4.js.map +1 -0
- package/dist/{chunk-AWH3SRPW.js → chunk-Y23G2VZL.js} +5 -37
- package/dist/chunk-Y23G2VZL.js.map +1 -0
- package/dist/{chunk-MOLIUNVH.js → chunk-YT75VYRD.js} +4 -4
- package/dist/{chunk-MOLIUNVH.js.map → chunk-YT75VYRD.js.map} +1 -1
- package/dist/chunk-ZIFPHIWZ.js +302 -0
- package/dist/chunk-ZIFPHIWZ.js.map +1 -0
- package/dist/config/index.d.ts +48 -40
- package/dist/config/index.js +4 -5
- package/dist/controls/index.d.ts +91 -7
- package/dist/controls/index.js +5 -4
- package/dist/dragdrop/index.d.ts +9 -7
- package/dist/dragdrop/index.js +3 -2
- package/dist/index.d.ts +294 -82
- package/dist/index.js +425 -402
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +4 -5
- package/dist/layers/index.js +2 -1
- package/dist/loupe/index.d.ts +20 -17
- package/dist/loupe/index.js +5 -3
- package/dist/overview/index.d.ts +56 -0
- package/dist/overview/index.js +289 -0
- package/dist/overview/index.js.map +1 -0
- package/dist/passthrough/weasel-ui.d.ts +239 -69
- package/dist/passthrough/weasel-ui.js +3 -2
- package/dist/primitives/index.d.ts +5 -204
- package/dist/primitives/index.js +6 -4
- package/dist/state/index.d.ts +8 -8
- package/dist/state/index.js +4 -5
- package/dist/state/index.js.map +1 -1
- package/dist/styles.css +152 -82
- package/dist/undo/index.d.ts +4 -4
- package/package.json +17 -12
- package/src/annotations/AnnotationOverlay.tsx +2 -2
- package/src/annotations/Annotations.less +0 -10
- package/src/annotations/MarkList.tsx +4 -8
- package/src/annotations/index.ts +1 -0
- package/src/annotations/paint.test.ts +3 -14
- package/src/annotations/paint.ts +3 -19
- package/src/annotations/store.test.ts +26 -0
- package/src/annotations/store.ts +25 -6
- package/src/annotations/svgNodes.test.ts +6 -9
- package/src/annotations/svgNodes.ts +3 -3
- package/src/annotations/toolMap.test.ts +37 -2
- package/src/annotations/toolMap.ts +13 -0
- package/src/annotations/types.ts +27 -0
- package/src/canvas/AGENTS.md +9 -10
- package/src/canvas/CameraInput.test.tsx +136 -0
- package/src/canvas/CameraInput.tsx +322 -0
- package/src/canvas/CanvasStack.tsx +47 -58
- package/src/canvas/LinkedCursor.less +16 -0
- package/src/canvas/LinkedCursor.tsx +54 -0
- package/src/canvas/Stage.test.tsx +33 -4
- package/src/canvas/Stage.tsx +47 -37
- package/src/canvas/cameraRegistry.test.ts +55 -0
- package/src/canvas/cameraRegistry.ts +86 -0
- package/src/canvas/cameraView.test.ts +60 -0
- package/src/canvas/cameraView.ts +61 -0
- package/src/canvas/cameraZoom.test.ts +46 -0
- package/src/canvas/cameraZoom.ts +24 -0
- package/src/canvas/index.ts +23 -2
- package/src/canvas/sharedScope.test.tsx +94 -0
- package/src/chrome/ChromeRegions.stories.tsx +1 -0
- package/src/chrome/regions/PaletteRegion.less +0 -7
- package/src/chrome/regions/SidebarRegion.less +12 -2
- package/src/chrome/regions/SidebarRegion.test.tsx +9 -2
- package/src/config/builder.test.ts +2 -1
- package/src/config/builder.ts +15 -8
- package/src/config/entry.test.ts +3 -1
- package/src/config/fromConfigField.test.ts +14 -0
- package/src/config/fromConfigField.ts +19 -4
- package/src/config/index.ts +4 -1
- package/src/config/path.ts +2 -5
- package/src/config/resolve.ts +15 -1
- package/src/config/sectionTree.test.ts +12 -6
- package/src/config/sectionTree.ts +15 -5
- package/src/config/types.ts +34 -9
- package/src/controls/ControlPanel.less +1 -1
- package/src/controls/ControlPanel.stories.tsx +3 -2
- package/src/controls/ControlPanel.test.tsx +76 -13
- package/src/controls/ControlPanel.tsx +80 -11
- package/src/controls/inDialog.test.tsx +18 -0
- package/src/controls/inDialog.tsx +2 -10
- package/src/index.test.ts +1 -1
- package/src/index.ts +23 -5
- package/src/instrument/SineWave.smoke.test.tsx +2 -0
- package/src/instrument/types.ts +10 -1
- package/src/lab/Lab.nebula.test.tsx +11 -2
- package/src/lab/Lab.test.tsx +3 -1
- package/src/lab/Lab.tsx +87 -76
- 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 +8 -17
- package/src/lab/LabSwitcher.less +6 -6
- package/src/lab/LabZoom.test.tsx +223 -0
- package/src/lab/LabZoom.tsx +170 -0
- package/src/lab/index.ts +1 -0
- package/src/loupe/AGENTS.md +7 -6
- package/src/loupe/LoupeGestures.tsx +15 -10
- package/src/loupe/TrialLoupe.tsx +5 -5
- package/src/loupe/index.ts +1 -0
- package/src/loupe/loupeActions.ts +6 -2
- package/src/overview/AGENTS.md +38 -0
- package/src/overview/OverviewMarks.test.tsx +100 -0
- package/src/overview/OverviewMarks.tsx +93 -0
- package/src/overview/TrialOverview.less +53 -0
- package/src/overview/TrialOverview.stories.tsx +50 -0
- package/src/overview/TrialOverview.test.tsx +116 -0
- package/src/overview/TrialOverview.tsx +376 -0
- package/src/overview/index.ts +2 -0
- package/src/passthrough/weasel-ui.test.ts +2 -0
- package/src/passthrough/weasel-ui.ts +23 -4
- package/src/primitives/FloatingPanel.test.tsx +11 -0
- package/src/primitives/FloatingPanel.tsx +2 -5
- package/src/primitives/JobProgress.less +1 -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/index.ts +2 -0
- package/src/specimen/Specimen.stories.tsx +1 -0
- package/src/specimen/Specimen.tsx +21 -4
- package/src/state/Persistence.browser.test.tsx +47 -0
- package/src/state/labRecords.test.ts +4 -1
- package/src/state/store.test.ts +9 -5
- package/src/state/usePersistedState.test.tsx +1 -1
- package/src/state/useTrialState.test.tsx +1 -1
- package/src/styles.less +3 -0
- package/src/theme/Interstellar.stories.tsx +4 -6
- 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/tools/labTool.ts +6 -2
- package/src/trial/Trial.annotations.test.tsx +16 -0
- package/src/trial/Trial.less +0 -13
- package/src/trial/Trial.loupe.test.tsx +73 -1
- package/src/trial/Trial.stories.tsx +2 -0
- package/src/trial/Trial.tsx +23 -1
- package/src/trial/TrialChrome.tsx +21 -3
- package/src/trial/trialOps.test.ts +4 -4
- package/dist/_dts/PrefsRow.d-DFpPIeIC.d.ts +0 -39
- package/dist/_dts/inDialog-D5ZKGjiD.d.ts +0 -23
- package/dist/_dts/index-DINmCuLA.d.ts +0 -453
- package/dist/_dts/number.d-OeQYQoUO.d.ts +0 -28
- package/dist/_dts/usePanZoom-Bq73pwFe.d.ts +0 -117
- package/dist/chunk-2AYGEN57.js +0 -32
- package/dist/chunk-2AYGEN57.js.map +0 -1
- package/dist/chunk-AWH3SRPW.js.map +0 -1
- package/dist/chunk-BVL7Y574.js.map +0 -1
- package/dist/chunk-ERFMC6WU.js.map +0 -1
- package/dist/chunk-H5VIV7MQ.js.map +0 -1
- package/dist/chunk-HFRE4QQV.js.map +0 -1
- package/dist/chunk-RS3HRQWU.js.map +0 -1
- package/dist/chunk-SBXNQW4G.js +0 -3
- package/dist/chunk-TT5C5S6R.js +0 -368
- package/dist/chunk-TT5C5S6R.js.map +0 -1
- package/dist/chunk-USO6TERP.js.map +0 -1
- package/dist/chunk-VQ47KWD5.js.map +0 -1
- package/dist/chunk-WUJXJC5O.js +0 -14568
- package/dist/chunk-WUJXJC5O.js.map +0 -1
- package/dist/chunk-ZORVNOKX.js.map +0 -1
- package/src/canvas/usePanZoom.test.ts +0 -244
- package/src/canvas/usePanZoom.ts +0 -147
- package/src/state/Persistence.stories.tsx +0 -75
package/src/lab/Lab.tsx
CHANGED
|
@@ -12,7 +12,8 @@ import {
|
|
|
12
12
|
} from 'react';
|
|
13
13
|
import { useStore } from 'zustand/react';
|
|
14
14
|
import { AnnotationPreloadContext } from '../annotations/preload';
|
|
15
|
-
import {
|
|
15
|
+
import { labAnnotationTools } from '../annotations/toolMap';
|
|
16
|
+
import { CameraRegistryContext, createCameraRegistry } from '../canvas/cameraRegistry';
|
|
16
17
|
import {
|
|
17
18
|
LabAsideRegion,
|
|
18
19
|
LabFooterRegion,
|
|
@@ -50,10 +51,11 @@ import {
|
|
|
50
51
|
} from '../trial/trialOps';
|
|
51
52
|
import { useLabFitWarning } from './fitCheck';
|
|
52
53
|
import { LabContext, type LabContextValue } from './LabContext';
|
|
53
|
-
import { LabHeader } from './LabHeader';
|
|
54
|
+
import { LabHeader, LabThemeSwitcher } from './LabHeader';
|
|
54
55
|
import { LabPalette } from './LabPalette';
|
|
55
56
|
import { LabShell } from './LabShell';
|
|
56
57
|
import type { LabPage } from './LabSwitcher';
|
|
58
|
+
import { LabZoom } from './LabZoom';
|
|
57
59
|
import { createPanelHostRegistry, PanelHostContext } from './panelHost';
|
|
58
60
|
import { type PanelDescriptor, type TrialLayout, Workspace } from './Workspace';
|
|
59
61
|
|
|
@@ -104,6 +106,9 @@ interface LabBaseProps {
|
|
|
104
106
|
/** Offer the header's add-trial control. Default `true`; a lab that opens
|
|
105
107
|
* trials some other way passes `false`. */
|
|
106
108
|
addTrial?: boolean;
|
|
109
|
+
/** Offer the header's zoom controls, which act on the focused trial's camera
|
|
110
|
+
* and answer Mod+=, Mod+- and Mod+0. Default `true`. */
|
|
111
|
+
zoom?: boolean;
|
|
107
112
|
/** Tools offered lab-wide. A trial whose instrument declares none of its own
|
|
108
113
|
* reflects and writes this slot. */
|
|
109
114
|
tools?: readonly TrialTool[];
|
|
@@ -339,6 +344,7 @@ function LabRuntime({
|
|
|
339
344
|
labChrome,
|
|
340
345
|
suppress,
|
|
341
346
|
addTrial,
|
|
347
|
+
zoom = true,
|
|
342
348
|
tools,
|
|
343
349
|
configRules,
|
|
344
350
|
controls,
|
|
@@ -354,6 +360,7 @@ function LabRuntime({
|
|
|
354
360
|
}, [instruments, store]);
|
|
355
361
|
|
|
356
362
|
const trials = useStore(store, (s) => s.trials);
|
|
363
|
+
const [cameras] = useState(createCameraRegistry);
|
|
357
364
|
const [focusPick, setFocusPick] = useState<string | null>(null);
|
|
358
365
|
const focusedTrialId = trials.some((t) => t.id === focusPick)
|
|
359
366
|
? focusPick
|
|
@@ -478,10 +485,10 @@ function LabRuntime({
|
|
|
478
485
|
// of them silently losing.
|
|
479
486
|
// Declaring `annotations` on any instrument puts the drawing tools here, in
|
|
480
487
|
// the lab's rail: one tool is armed across every trial.
|
|
481
|
-
const
|
|
488
|
+
const annotationTools = useMemo(() => labAnnotationTools(instruments), [instruments]);
|
|
482
489
|
const labChromeAll = useMemo(
|
|
483
|
-
() => labContributions([...
|
|
484
|
-
[
|
|
490
|
+
() => labContributions([...annotationTools, ...(tools ?? [])], labChrome),
|
|
491
|
+
[annotationTools, tools, labChrome],
|
|
485
492
|
);
|
|
486
493
|
const hasFooterChrome = labChromeAll.some((c) => c.region === 'footer');
|
|
487
494
|
const hasPaneChrome = labChromeAll.some((c) => c.region === 'sidebar' || c.region === 'aside');
|
|
@@ -612,79 +619,83 @@ function LabRuntime({
|
|
|
612
619
|
<PersistenceContext.Provider value={opened.records}>
|
|
613
620
|
<AnnotationPreloadContext.Provider value={opened.marks}>
|
|
614
621
|
<LabContext.Provider value={contextValue}>
|
|
615
|
-
<
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
622
|
+
<CameraRegistryContext.Provider value={cameras}>
|
|
623
|
+
<ThemeProvider
|
|
624
|
+
theme={theme}
|
|
625
|
+
selection={{ mode: resolvedMode, density: density ?? 'comfortable' }}
|
|
626
|
+
className="lk-lab"
|
|
627
|
+
style={backdropStyle}
|
|
628
|
+
>
|
|
629
|
+
<LabShell
|
|
630
|
+
title={title ?? 'Labkit'}
|
|
631
|
+
mode={modeValue}
|
|
632
|
+
{...(pages ? { pages } : {})}
|
|
633
|
+
{...(path !== undefined ? { path } : {})}
|
|
634
|
+
footer={
|
|
635
|
+
hasFooterChrome ? (
|
|
636
|
+
<>
|
|
637
|
+
{footer}
|
|
638
|
+
<LabFooterRegion contributions={labChromeAll} />
|
|
639
|
+
</>
|
|
640
|
+
) : (
|
|
641
|
+
footer
|
|
642
|
+
)
|
|
643
|
+
}
|
|
644
|
+
header={
|
|
628
645
|
<>
|
|
629
|
-
{
|
|
630
|
-
<
|
|
646
|
+
<LabHeader {...(addTrial !== undefined ? { addTrial } : {})} />
|
|
647
|
+
{zoom ? <LabZoom /> : null}
|
|
648
|
+
{children}
|
|
649
|
+
<LabHeaderRegion contributions={labChromeAll} />
|
|
650
|
+
<LabThemeSwitcher />
|
|
631
651
|
</>
|
|
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>
|
|
652
|
+
}
|
|
653
|
+
>
|
|
654
|
+
<PanelHostContext.Provider value={panelHostsRef.current}>
|
|
655
|
+
<SurfaceContext.Provider value={surface}>
|
|
656
|
+
<SurfaceCanvasContext.Provider value={surfaceCanvases}>
|
|
657
|
+
<div className="lk-lab__body" ref={labBodyRef}>
|
|
658
|
+
{outerSurface ? null : (
|
|
659
|
+
// Two buffers stacked around the trials, both inert —
|
|
660
|
+
// each tile takes input from its own box. A tile's marks
|
|
661
|
+
// annotate the instrument's DOM from over it; an opaque
|
|
662
|
+
// renderer sits under it, so the pane can still hold a
|
|
663
|
+
// label. The under one is first so it paints first.
|
|
664
|
+
<>
|
|
665
|
+
<canvas
|
|
666
|
+
className="lk-lab__surface lk-lab__surface--under"
|
|
667
|
+
ref={(el) => {
|
|
668
|
+
ownUnderRef.current = el;
|
|
669
|
+
setOwnUnder(el);
|
|
670
|
+
}}
|
|
671
|
+
/>
|
|
672
|
+
<canvas
|
|
673
|
+
className="lk-lab__surface lk-lab__surface--over"
|
|
674
|
+
ref={(el) => {
|
|
675
|
+
ownOverRef.current = el;
|
|
676
|
+
setOwnOver(el);
|
|
677
|
+
}}
|
|
678
|
+
/>
|
|
679
|
+
</>
|
|
680
|
+
)}
|
|
681
|
+
{hasPaneChrome ? (
|
|
682
|
+
<LabPanes contributions={labChromeAll}>
|
|
683
|
+
<LabPalette contributions={labChromeAll} />
|
|
684
|
+
{workspace}
|
|
685
|
+
</LabPanes>
|
|
686
|
+
) : (
|
|
687
|
+
<>
|
|
688
|
+
<LabPalette contributions={labChromeAll} />
|
|
689
|
+
{workspace}
|
|
690
|
+
</>
|
|
691
|
+
)}
|
|
692
|
+
</div>
|
|
693
|
+
</SurfaceCanvasContext.Provider>
|
|
694
|
+
</SurfaceContext.Provider>
|
|
695
|
+
</PanelHostContext.Provider>
|
|
696
|
+
</LabShell>
|
|
697
|
+
</ThemeProvider>
|
|
698
|
+
</CameraRegistryContext.Provider>
|
|
688
699
|
</LabContext.Provider>
|
|
689
700
|
</AnnotationPreloadContext.Provider>
|
|
690
701
|
</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;
|
|
@@ -58,28 +58,38 @@ describe('<LabHeader>', () => {
|
|
|
58
58
|
<Lab instruments={[Stub, Other]} defaultInstrument="Stub" addTrial={false} />,
|
|
59
59
|
);
|
|
60
60
|
expect(screen.queryByRole('button', { name: /add trial/i })).toBeNull();
|
|
61
|
-
expect(screen.getByRole('
|
|
61
|
+
expect(screen.getByRole('button', { name: /^Theme: Auto/ })).toBeInTheDocument();
|
|
62
62
|
unmount();
|
|
63
63
|
render(<Lab instruments={[Stub]} defaultInstrument="Stub" addTrial={false} />);
|
|
64
64
|
expect(screen.queryByRole('button', { name: /add trial/i })).toBeNull();
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
it('
|
|
67
|
+
it('rotates the color mode from one button', async () => {
|
|
68
|
+
const { default: userEvent } = await import('@testing-library/user-event');
|
|
69
|
+
const user = userEvent.setup();
|
|
68
70
|
render(<Lab instruments={[Stub]} defaultInstrument="Stub" />);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
expect(screen.queryByRole('radiogroup')).toBeNull();
|
|
72
|
+
for (const [now, next] of [
|
|
73
|
+
['Auto', 'Light'],
|
|
74
|
+
['Light', 'Dark'],
|
|
75
|
+
['Dark', 'Auto'],
|
|
76
|
+
]) {
|
|
77
|
+
const button = screen.getByRole('button', { name: `Theme: ${now} — click for ${next}` });
|
|
78
|
+
expect(button.querySelector('svg')).not.toBeNull();
|
|
79
|
+
expect(button).toHaveTextContent('');
|
|
80
|
+
await user.click(button);
|
|
71
81
|
}
|
|
82
|
+
expect(screen.getByRole('button', { name: /^Theme: Auto/ })).toBeInTheDocument();
|
|
72
83
|
});
|
|
73
84
|
|
|
74
|
-
it('
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
85
|
+
it('puts the theme switch after the header content a consumer passes', () => {
|
|
86
|
+
render(
|
|
87
|
+
<Lab instruments={[Stub]} defaultInstrument="Stub">
|
|
88
|
+
<button type="button">Consumer</button>
|
|
89
|
+
</Lab>,
|
|
90
|
+
);
|
|
91
|
+
const consumer = screen.getByRole('button', { name: 'Consumer' });
|
|
92
|
+
const theme = screen.getByRole('button', { name: /^Theme: / });
|
|
93
|
+
expect(consumer.compareDocumentPosition(theme) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
|
84
94
|
});
|
|
85
95
|
});
|
package/src/lab/LabHeader.tsx
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { AddIcon } from '@weasel-js/ui';
|
|
2
|
-
import {
|
|
2
|
+
import { MenuButton, ThemeSwitcher } from '../passthrough/weasel-ui';
|
|
3
3
|
import { useLabContext } from './LabContext';
|
|
4
4
|
|
|
5
|
-
/** The
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* content, which still lands beside them. */
|
|
5
|
+
/** The control `<Lab>` puts at the start of its header: add a trial. It drives
|
|
6
|
+
* `LabContext`, which carried it with no UI at all — so every consumer rebuilt
|
|
7
|
+
* it. Rendered before a consumer's own header content. */
|
|
9
8
|
export function LabHeader({ addTrial = true }: { addTrial?: boolean }) {
|
|
10
9
|
const lab = useLabContext();
|
|
11
10
|
const only = lab.instruments.length === 1 ? lab.instruments[0] : null;
|
|
@@ -29,13 +28,13 @@ export function LabHeader({ addTrial = true }: { addTrial?: boolean }) {
|
|
|
29
28
|
onAction={(name) => lab.addTrial(name)}
|
|
30
29
|
/>
|
|
31
30
|
)}
|
|
32
|
-
|
|
33
|
-
<ColorModeControl
|
|
34
|
-
className="lk-lab-header__mode"
|
|
35
|
-
variant="flat"
|
|
36
|
-
value={lab.mode}
|
|
37
|
-
onChange={lab.setMode}
|
|
38
|
-
/>
|
|
39
31
|
</>
|
|
40
32
|
);
|
|
41
33
|
}
|
|
34
|
+
|
|
35
|
+
/** The lab's color-mode switch, which `<Lab>` puts at the end of its header,
|
|
36
|
+
* after a consumer's own header content. */
|
|
37
|
+
export function LabThemeSwitcher() {
|
|
38
|
+
const lab = useLabContext();
|
|
39
|
+
return <ThemeSwitcher value={lab.mode} onChange={lab.setMode} />;
|
|
40
|
+
}
|
package/src/lab/LabShell.less
CHANGED
|
@@ -155,21 +155,12 @@
|
|
|
155
155
|
align-self: center;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
// A consumer's own header content shares this row and does not shrink, so
|
|
167
|
-
// without this the segments compress past their labels and the pill track
|
|
168
|
-
// disappears entirely.
|
|
169
|
-
flex-shrink: 0;
|
|
170
|
-
|
|
171
|
-
&:hover,
|
|
172
|
-
&:focus-within {
|
|
173
|
-
opacity: 1;
|
|
174
|
-
}
|
|
158
|
+
// The readout's digit count changes as the zoom does (80% to 125%), so it sits
|
|
159
|
+
// in a cell sized for the widest value the default range reaches, and the
|
|
160
|
+
// buttons either side of it stay put.
|
|
161
|
+
.lk-lab-zoom__readout {
|
|
162
|
+
display: inline-block;
|
|
163
|
+
min-width: 5ch;
|
|
164
|
+
text-align: center;
|
|
165
|
+
font-variant-numeric: tabular-nums;
|
|
175
166
|
}
|
package/src/lab/LabSwitcher.less
CHANGED
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
&:hover { color: var(--wzl-accent-fg); }
|
|
30
30
|
|
|
31
31
|
&:focus-visible {
|
|
32
|
-
outline: 2px solid var(--wzl-focus-ring
|
|
32
|
+
outline: 2px solid var(--wzl-focus-ring);
|
|
33
33
|
outline-offset: 2px;
|
|
34
|
-
border-radius: var(--wzl-radius-sm
|
|
34
|
+
border-radius: var(--wzl-radius-sm);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -64,22 +64,22 @@
|
|
|
64
64
|
// surface, the way `FloatingPanel` does.
|
|
65
65
|
background: var(--wzl-surface);
|
|
66
66
|
border: 1px solid var(--wzl-border);
|
|
67
|
-
border-radius: var(--wzl-radius-md
|
|
67
|
+
border-radius: var(--wzl-radius-md);
|
|
68
68
|
box-shadow: 0 6px 20px var(--wzl-shadow);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
.lk-switcher-item {
|
|
72
72
|
padding: var(--wzl-space-xs) var(--wzl-space-sm);
|
|
73
|
-
border-radius: var(--wzl-radius-sm
|
|
73
|
+
border-radius: var(--wzl-radius-sm);
|
|
74
74
|
color: var(--wzl-fg);
|
|
75
75
|
font-size: var(--wzl-font-size);
|
|
76
76
|
text-decoration: none;
|
|
77
77
|
white-space: nowrap;
|
|
78
78
|
|
|
79
|
-
&:hover { background: var(--wzl-surface-hover
|
|
79
|
+
&:hover { background: var(--wzl-surface-hover); }
|
|
80
80
|
|
|
81
81
|
&:focus-visible {
|
|
82
|
-
outline: 2px solid var(--wzl-focus-ring
|
|
82
|
+
outline: 2px solid var(--wzl-focus-ring);
|
|
83
83
|
outline-offset: -2px;
|
|
84
84
|
}
|
|
85
85
|
}
|