@weasel-js/labkit 1.3.0-pre.0 → 1.4.0-pre.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-DtAn_3O-.d.ts +42 -0
- package/dist/_dts/DrawCommand-B3bskUsC.d.ts +564 -0
- package/dist/_dts/{PrefsForm-BYa6cWnO.d.ts → PrefsForm-BkUJZx0A.d.ts} +4 -1
- package/dist/_dts/fitViewToBounds-dZ2UDB6e.d.ts +21 -0
- package/dist/_dts/frac-Cp3NivlC.d.ts +728 -0
- package/dist/_dts/index-BDVzvRzQ.d.ts +236 -0
- package/dist/_dts/shapeKinds-Cx_rxwsa.d.ts +87 -0
- package/dist/_dts/{DrawCommand-DBB45NfN.d.ts → types-C-gh9Ap-.d.ts} +1 -582
- package/dist/_dts/{types-AHlQxBNN.d.ts → types-D6s4b7if.d.ts} +2 -2
- package/dist/_dts/{types-1Sdxy_Pv.d.ts → types-DYMaEvM5.d.ts} +26 -1
- package/dist/_dts/{useTrialState-CsGMjhu9.d.ts → useTrialState-gmMvZqPc.d.ts} +7 -2
- package/dist/canvas/index.d.ts +43 -25
- package/dist/canvas/index.js +5 -3
- package/dist/chrome/index.d.ts +9 -6
- package/dist/chrome/index.js +7 -6
- package/dist/chunk-2AYGEN57.js +32 -0
- package/dist/chunk-2AYGEN57.js.map +1 -0
- package/dist/{chunk-RVNN4CHQ.js → chunk-3P5TTTA7.js} +16 -8
- package/dist/chunk-3P5TTTA7.js.map +1 -0
- package/dist/{chunk-NS54R52R.js → chunk-3ZELRRBV.js} +45 -40
- package/dist/chunk-3ZELRRBV.js.map +1 -0
- package/dist/{chunk-HFDVGF4X.js → chunk-4EY67BKJ.js} +32 -7
- package/dist/chunk-4EY67BKJ.js.map +1 -0
- package/dist/chunk-5TCEVMPI.js +140 -0
- package/dist/chunk-5TCEVMPI.js.map +1 -0
- package/dist/{chunk-O7NVSCLN.js → chunk-6JMU56CU.js} +133 -134
- package/dist/chunk-6JMU56CU.js.map +1 -0
- package/dist/{chunk-CPUJ3QXL.js → chunk-CQLQPQ4P.js} +2 -2
- package/dist/chunk-CQLQPQ4P.js.map +1 -0
- package/dist/chunk-CTRKTLYZ.js +33 -0
- package/dist/chunk-CTRKTLYZ.js.map +1 -0
- package/dist/chunk-EQIB2PGC.js +458 -0
- package/dist/chunk-EQIB2PGC.js.map +1 -0
- package/dist/chunk-HEORBLVT.js +7128 -0
- package/dist/chunk-HEORBLVT.js.map +1 -0
- package/dist/{chunk-V7PRSIRQ.js → chunk-JDDIZSLL.js} +4 -4
- package/dist/chunk-JDDIZSLL.js.map +1 -0
- package/dist/chunk-LITE5YFB.js +669 -0
- package/dist/chunk-LITE5YFB.js.map +1 -0
- package/dist/{chunk-Z6HJ5FEM.js → chunk-ROQDRXKW.js} +732 -315
- package/dist/chunk-ROQDRXKW.js.map +1 -0
- package/dist/chunk-SBXNQW4G.js +3 -0
- package/dist/chunk-SBXNQW4G.js.map +1 -0
- package/dist/{chunk-4TMMVIDM.js → chunk-XJ6N32QP.js} +50 -6
- package/dist/chunk-XJ6N32QP.js.map +1 -0
- package/dist/chunk-YQJNT4XQ.js +293 -0
- package/dist/chunk-YQJNT4XQ.js.map +1 -0
- package/dist/controls/index.d.ts +4 -28
- package/dist/controls/index.js +3 -3
- package/dist/dragdrop/index.d.ts +50 -5
- package/dist/dragdrop/index.js +3 -1
- package/dist/index.d.ts +660 -302
- package/dist/index.js +1793 -587
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +6 -5
- package/dist/layers/index.js +4 -2
- package/dist/loupe/index.d.ts +187 -0
- package/dist/loupe/index.js +7 -0
- package/dist/loupe/index.js.map +1 -0
- package/dist/passthrough/weasel-canvas.d.ts +3 -1
- package/dist/passthrough/weasel-canvas.js +1 -1
- package/dist/passthrough/weasel-ui.d.ts +151 -91
- package/dist/passthrough/weasel-ui.js +3 -2
- package/dist/primitives/index.d.ts +3 -1
- package/dist/primitives/index.js +6 -5
- package/dist/state/index.d.ts +3 -3
- package/dist/state/index.js +2 -2
- package/dist/styles.css +229 -703
- package/dist/surface/index.d.ts +18 -2
- package/dist/surface/index.js +2 -2
- package/dist/ui/layers/index.d.ts +8 -2
- package/dist/ui/layers/index.js +2 -4
- package/dist/undo/index.d.ts +5 -4
- package/package.json +14 -7
- package/src/annotations/AnnotationOverlay.tsx +205 -0
- package/src/annotations/AnnotationTargets.tsx +37 -0
- package/src/annotations/Annotations.less +129 -0
- package/src/annotations/Annotations.meaning.test.tsx +111 -0
- package/src/annotations/Annotations.overlay.test.tsx +142 -0
- package/src/annotations/AnnotationsContext.ts +49 -0
- package/src/annotations/ExportMenu.test.tsx +86 -0
- package/src/annotations/ExportMenu.tsx +182 -0
- package/src/annotations/MarkList.tsx +75 -0
- package/src/annotations/capture.test.ts +140 -0
- package/src/annotations/capture.ts +237 -0
- package/src/annotations/drawOne.test.ts +56 -0
- package/src/annotations/drawOne.ts +47 -0
- package/src/annotations/frac.test.ts +59 -0
- package/src/annotations/frac.ts +66 -0
- package/src/annotations/history.test.ts +82 -0
- package/src/annotations/history.ts +118 -0
- package/src/annotations/index.ts +37 -0
- package/src/annotations/paint.test.ts +125 -0
- package/src/annotations/paint.ts +130 -0
- package/src/annotations/staleness.test.ts +56 -0
- package/src/annotations/staleness.ts +41 -0
- package/src/annotations/store.test.ts +175 -0
- package/src/annotations/store.ts +317 -0
- package/src/annotations/svgNodes.test.ts +77 -0
- package/src/annotations/svgNodes.ts +74 -0
- package/src/annotations/toolMap.test.ts +36 -0
- package/src/annotations/toolMap.ts +54 -0
- package/src/annotations/types.ts +222 -0
- package/src/annotations/view.test.ts +37 -0
- package/src/annotations/view.ts +40 -0
- package/src/canvas/AGENTS.md +55 -5
- package/src/canvas/CanvasStack.test.tsx +27 -1
- package/src/canvas/CanvasStack.tsx +29 -4
- package/src/canvas/CanvasStackContext.ts +23 -4
- package/src/canvas/camera.test.ts +78 -0
- package/src/canvas/camera.ts +62 -0
- package/src/canvas/canvasCoords.test.ts +26 -0
- package/src/canvas/canvasCoords.ts +20 -7
- package/src/canvas/index.ts +5 -1
- package/src/canvas/useLayerScheduler.ts +7 -3
- package/src/canvas/usePanZoom.test.ts +36 -5
- package/src/canvas/usePanZoom.ts +20 -15
- package/src/canvas/worldSpec.test.ts +63 -0
- package/src/canvas/worldSpec.ts +48 -0
- package/src/chrome/builtins.test.ts +31 -1
- package/src/chrome/builtins.tsx +63 -19
- package/src/chrome/regions/PaletteRegion.test.tsx +22 -1
- package/src/chrome/regions/PaletteRegion.tsx +4 -0
- package/src/chrome/regions/SidebarRegion.tsx +37 -13
- package/src/chrome/regions/ToolbarRegion.tsx +2 -1
- package/src/chrome/regions/ViewportRegion.tsx +9 -1
- package/src/chrome/regions/regions.test.tsx +6 -1
- package/src/chrome/types.ts +20 -0
- package/src/config/builder.test.ts +7 -1
- package/src/config/builder.ts +1 -5
- package/src/config/resolve.test.ts +1 -2
- package/src/controls/ControlPanel.stories.tsx +41 -2
- package/src/controls/ControlPanel.test.tsx +69 -9
- package/src/controls/ControlPanel.tsx +106 -25
- package/src/dragdrop/DragDropRuntime.tsx +8 -2
- package/src/dragdrop/dragDrop.test.tsx +29 -2
- package/src/dragdrop/index.ts +11 -0
- package/src/index.test.ts +22 -0
- package/src/index.ts +136 -4
- package/src/instrument/SineWave.smoke.test.tsx +1 -0
- package/src/instrument/types.ts +24 -1
- package/src/lab/Lab.surface.test.tsx +162 -0
- package/src/lab/Lab.tsx +102 -17
- package/src/lab/Lab.undock.test.tsx +85 -0
- package/src/lab/LabFullChrome.stories.tsx +20 -0
- package/src/lab/LabShell.less +11 -0
- package/src/lab/Workspace.less +73 -0
- package/src/lab/Workspace.surface.test.tsx +2 -0
- package/src/lab/Workspace.tsx +97 -8
- package/src/lab/index.ts +1 -1
- package/src/lab/panelHost.ts +37 -0
- package/src/layers/AGENTS.md +1 -1
- package/src/layers/LayerList.tsx +1 -1
- package/src/loupe/AGENTS.md +49 -0
- package/src/loupe/CanvasLoupe.tsx +83 -0
- package/src/loupe/DomLoupe.tsx +62 -0
- package/src/loupe/Loupe.less +38 -0
- package/src/loupe/LoupeBubble.tsx +32 -0
- package/src/loupe/TrialLoupe.tsx +93 -0
- package/src/loupe/canvasLens.test.ts +205 -0
- package/src/loupe/canvasLens.ts +141 -0
- package/src/loupe/index.ts +19 -0
- package/src/loupe/types.test.ts +29 -0
- package/src/loupe/types.ts +90 -0
- package/src/loupe/useHostSize.ts +30 -0
- package/src/loupe/useLoupe.test.tsx +197 -0
- package/src/loupe/useLoupe.ts +189 -0
- package/src/passthrough/weasel-ui.ts +25 -17
- package/src/primitives/FloatingPanel.test.tsx +76 -1
- package/src/primitives/FloatingPanel.tsx +19 -3
- package/src/primitives/ScaleIndicator.test.tsx +7 -2
- package/src/primitives/Sidebar.less +18 -0
- package/src/primitives/Toolbar.less +8 -0
- package/src/primitives/Toolbar.tsx +5 -0
- package/src/primitives/useRovingTabIndex.test.ts +21 -0
- package/src/primitives/useRovingTabIndex.ts +26 -17
- package/src/state/document.test.ts +23 -0
- package/src/state/document.ts +14 -2
- package/src/state/index.ts +2 -0
- package/src/state/store.test.ts +24 -0
- package/src/state/store.ts +35 -1
- package/src/state/types.ts +11 -0
- package/src/state/undock.test.ts +40 -0
- package/src/state/undock.ts +39 -0
- package/src/styles.less +2 -4
- package/src/surface/SurfaceContext.ts +4 -0
- package/src/surface/index.ts +8 -3
- package/src/surface/useSurfaceTile.test.tsx +2 -0
- package/src/surface/useSurfaceTile.ts +7 -1
- package/src/surface/useTiledSurface.test.tsx +58 -0
- package/src/surface/useTiledSurface.ts +38 -3
- package/src/theme/Interstellar.stories.tsx +1 -1
- package/src/trial/Trial.annotations.persist.test.tsx +166 -0
- package/src/trial/Trial.annotations.test.tsx +121 -0
- package/src/trial/Trial.canvas.test.tsx +76 -1
- package/src/trial/Trial.config.test.tsx +3 -8
- package/src/trial/Trial.less +19 -0
- package/src/trial/Trial.loupe.test.tsx +128 -0
- package/src/trial/Trial.stories.tsx +1 -0
- package/src/trial/Trial.test.tsx +12 -0
- package/src/trial/Trial.trialId.test.tsx +36 -0
- package/src/trial/Trial.tsx +215 -40
- package/src/trial/TrialChrome.tsx +35 -0
- package/src/trial/UndockedSections.tsx +65 -0
- package/src/trial/trialOps.test.ts +21 -0
- package/src/trial/trialOps.ts +5 -3
- package/src/ui/layers/index.ts +5 -1
- package/dist/_dts/index-DgcNdEdh.d.ts +0 -314
- package/dist/chunk-4TMMVIDM.js.map +0 -1
- package/dist/chunk-CPUJ3QXL.js.map +0 -1
- package/dist/chunk-HFDVGF4X.js.map +0 -1
- package/dist/chunk-NS54R52R.js.map +0 -1
- package/dist/chunk-O2BULFHX.js +0 -152
- package/dist/chunk-O2BULFHX.js.map +0 -1
- package/dist/chunk-O7NVSCLN.js.map +0 -1
- package/dist/chunk-PO6L3NSH.js +0 -366
- package/dist/chunk-PO6L3NSH.js.map +0 -1
- package/dist/chunk-PQJ5B2U2.js +0 -27
- package/dist/chunk-PQJ5B2U2.js.map +0 -1
- package/dist/chunk-RVNN4CHQ.js.map +0 -1
- package/dist/chunk-TLGRYALP.js +0 -6151
- package/dist/chunk-TLGRYALP.js.map +0 -1
- package/dist/chunk-V7PRSIRQ.js.map +0 -1
- package/dist/chunk-Z6HJ5FEM.js.map +0 -1
- package/dist/chunk-ZZAYVX4X.js +0 -505
- package/dist/chunk-ZZAYVX4X.js.map +0 -1
- package/src/primitives/DragHandleGlyph.tsx +0 -29
- package/src/ui/format.test.ts +0 -33
- package/src/ui/format.ts +0 -30
- package/src/ui/layers/LayerStack.less +0 -143
- package/src/ui/layers/LayerStack.stories.tsx +0 -45
- package/src/ui/layers/LayerStack.test.tsx +0 -170
- package/src/ui/layers/LayerStack.tsx +0 -208
- package/src/ui/properties/CheckboxRow.stories.tsx +0 -27
- package/src/ui/properties/ColorRow.stories.tsx +0 -106
- package/src/ui/properties/CurveField.less +0 -48
- package/src/ui/properties/CurveField.stories.tsx +0 -20
- package/src/ui/properties/CurveField.test.tsx +0 -127
- package/src/ui/properties/CurveField.tsx +0 -184
- package/src/ui/properties/EffectCard.tsx +0 -324
- package/src/ui/properties/Gallery.stories.tsx +0 -178
- package/src/ui/properties/NumberRow.stories.tsx +0 -65
- package/src/ui/properties/PropertyGroup.less +0 -50
- package/src/ui/properties/PropertyGroup.stories.tsx +0 -34
- package/src/ui/properties/PropertyGroup.test.tsx +0 -33
- package/src/ui/properties/PropertyGroup.tsx +0 -45
- package/src/ui/properties/PropertyList.stories.tsx +0 -83
- package/src/ui/properties/PropertyPanel.less +0 -606
- package/src/ui/properties/PropertyPanel.stories.tsx +0 -190
- package/src/ui/properties/PropertyPanel.test.tsx +0 -193
- package/src/ui/properties/PropertyPanel.tsx +0 -469
- package/src/ui/properties/PropertyRow.stories.tsx +0 -53
- package/src/ui/properties/SelectRow.stories.tsx +0 -58
- package/src/ui/properties/SliderRow.stories.tsx +0 -74
- package/src/ui/properties/SpeechBalloonPanels.stories.tsx +0 -398
- package/src/ui/properties/TextRow.stories.tsx +0 -62
- package/src/ui/properties/ToggleRow.stories.tsx +0 -57
- package/src/ui/properties/index.ts +0 -39
- package/src/ui/properties/storyLayouts.tsx +0 -36
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
import { type CSSProperties, type ReactNode, useState } from 'react';
|
|
2
|
-
|
|
3
|
-
// ── Subpanel ─────────────────────────────────────────────────────────
|
|
4
|
-
// Headered group of controls inside a wider panel/list. The header is a
|
|
5
|
-
// label flanked by a horizontal rule — no background, padding, or border;
|
|
6
|
-
// purely a typographic divider. Ported from speech-balloons styles.css:277-313.
|
|
7
|
-
|
|
8
|
-
/** Props for `<Subpanel>`. */
|
|
9
|
-
export interface SubpanelProps {
|
|
10
|
-
title: ReactNode;
|
|
11
|
-
children: ReactNode;
|
|
12
|
-
className?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/** A typographic divider inside a property list: a small title flanked by a
|
|
16
|
-
* rule. Purely a heading — use `<PropertyGroup>` for a bordered section. */
|
|
17
|
-
export function Subpanel({ title, children, className }: SubpanelProps) {
|
|
18
|
-
const cls = className ? `lk-subpanel ${className}` : 'lk-subpanel';
|
|
19
|
-
return (
|
|
20
|
-
<div className={cls}>
|
|
21
|
-
<h4 className="lk-subpanel__title">
|
|
22
|
-
<span>{title}</span>
|
|
23
|
-
<hr />
|
|
24
|
-
</h4>
|
|
25
|
-
{children}
|
|
26
|
-
</div>
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// ── EffectCard ───────────────────────────────────────────────────────
|
|
31
|
-
// Draggable, collapsible card for stacked effect editors. Optional --lk-panel-accent
|
|
32
|
-
// per-instance recolors the title bar, border-left, and (via re-binding --wzl-accent
|
|
33
|
-
// inside the card) every descendant control that reads from the accent token.
|
|
34
|
-
|
|
35
|
-
/** Props for `<EffectCard>`. */
|
|
36
|
-
export interface EffectCardProps {
|
|
37
|
-
/** Card title rendered in the title bar (e.g. effect kind label or a select). */
|
|
38
|
-
title: ReactNode;
|
|
39
|
-
/** Optional badge/chip displayed before the remove button (e.g. primary value summary). */
|
|
40
|
-
primary?: ReactNode;
|
|
41
|
-
/** Optional accent color in any CSS color form. Sets --lk-panel-accent on the card. */
|
|
42
|
-
accent?: string;
|
|
43
|
-
/** Optional ordinal badge that doubles as the drag handle (used by tail cards in SB). */
|
|
44
|
-
index?: number;
|
|
45
|
-
expanded?: boolean;
|
|
46
|
-
onToggleExpanded?: () => void;
|
|
47
|
-
onRemove?: () => void;
|
|
48
|
-
/**
|
|
49
|
-
* Drag-handle wiring. When provided, an inline handle (⋮⋮ icon or `index`)
|
|
50
|
-
* activates HTML5 drag on press. Consumer owns the drag/drop coordination —
|
|
51
|
-
* see EffectCardList for the batteries-included wrapper.
|
|
52
|
-
*/
|
|
53
|
-
draggable?: boolean;
|
|
54
|
-
onPressHandle?: () => void;
|
|
55
|
-
onReleaseHandle?: () => void;
|
|
56
|
-
onDragStart?: (e: React.DragEvent<HTMLDivElement>) => void;
|
|
57
|
-
onDragEnd?: (e: React.DragEvent<HTMLDivElement>) => void;
|
|
58
|
-
onDragOver?: (e: React.DragEvent<HTMLDivElement>) => void;
|
|
59
|
-
onDragLeave?: (e: React.DragEvent<HTMLDivElement>) => void;
|
|
60
|
-
onDrop?: (e: React.DragEvent<HTMLDivElement>) => void;
|
|
61
|
-
dragging?: boolean;
|
|
62
|
-
children?: ReactNode;
|
|
63
|
-
className?: string;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** An accented, collapsible card for one item in a list of like things —
|
|
67
|
-
* effects, layers, tails. The accent color rebinds the theme accent token
|
|
68
|
-
* within the card, so its controls pick it up too. */
|
|
69
|
-
export function EffectCard({
|
|
70
|
-
title,
|
|
71
|
-
primary,
|
|
72
|
-
accent,
|
|
73
|
-
index,
|
|
74
|
-
expanded = true,
|
|
75
|
-
onToggleExpanded,
|
|
76
|
-
onRemove,
|
|
77
|
-
draggable,
|
|
78
|
-
onPressHandle,
|
|
79
|
-
onReleaseHandle,
|
|
80
|
-
onDragStart,
|
|
81
|
-
onDragEnd,
|
|
82
|
-
onDragOver,
|
|
83
|
-
onDragLeave,
|
|
84
|
-
onDrop,
|
|
85
|
-
dragging,
|
|
86
|
-
children,
|
|
87
|
-
className,
|
|
88
|
-
}: EffectCardProps) {
|
|
89
|
-
const stateClass = `${expanded ? 'is-expanded' : 'is-collapsed'}${dragging ? ' is-dragging' : ''}${accent ? ' has-accent' : ''}`;
|
|
90
|
-
const cls = `lk-effect-card ${stateClass}${className ? ` ${className}` : ''}`;
|
|
91
|
-
// Per-instance dynamic color → CSS custom property requires inline style.
|
|
92
|
-
// The card's CSS re-binds --wzl-accent to --lk-panel-accent so descendants tint.
|
|
93
|
-
const style = accent ? ({ '--lk-panel-accent': accent } as CSSProperties) : undefined;
|
|
94
|
-
|
|
95
|
-
// When a toggle handler is supplied the header acts as a disclosure button:
|
|
96
|
-
// role + tab stop + Enter/Space. Bundled together so the interactive props are
|
|
97
|
-
// applied as a set (no half-interactive element when onToggleExpanded is absent).
|
|
98
|
-
const headProps = onToggleExpanded
|
|
99
|
-
? {
|
|
100
|
-
role: 'button' as const,
|
|
101
|
-
tabIndex: 0,
|
|
102
|
-
onClick: onToggleExpanded,
|
|
103
|
-
onKeyDown: (e: React.KeyboardEvent) => {
|
|
104
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
105
|
-
e.preventDefault();
|
|
106
|
-
onToggleExpanded();
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
}
|
|
110
|
-
: {};
|
|
111
|
-
|
|
112
|
-
return (
|
|
113
|
-
// biome-ignore lint/a11y/noStaticElementInteractions: native HTML5 drag-and-drop container — drag events are not click/keyboard interactions and have no semantic element; the in-card handle and header carry the a11y affordances.
|
|
114
|
-
<div
|
|
115
|
-
className={cls}
|
|
116
|
-
style={style}
|
|
117
|
-
draggable={draggable}
|
|
118
|
-
onDragStart={onDragStart}
|
|
119
|
-
onDragEnd={onDragEnd}
|
|
120
|
-
onDragOver={onDragOver}
|
|
121
|
-
onDragLeave={onDragLeave}
|
|
122
|
-
onDrop={onDrop}
|
|
123
|
-
>
|
|
124
|
-
<div className="lk-effect-card__head" {...headProps}>
|
|
125
|
-
{index != null ? (
|
|
126
|
-
<button
|
|
127
|
-
type="button"
|
|
128
|
-
className="lk-effect-card__index-badge lk-effect-card__handle"
|
|
129
|
-
title="Drag to reorder · click to toggle"
|
|
130
|
-
aria-label={`Item ${index + 1} — drag to reorder, click to toggle`}
|
|
131
|
-
onMouseDown={onPressHandle}
|
|
132
|
-
onMouseUp={onReleaseHandle}
|
|
133
|
-
onClick={(e) => {
|
|
134
|
-
e.stopPropagation();
|
|
135
|
-
onToggleExpanded?.();
|
|
136
|
-
}}
|
|
137
|
-
>
|
|
138
|
-
{index + 1}
|
|
139
|
-
</button>
|
|
140
|
-
) : (
|
|
141
|
-
<span
|
|
142
|
-
className="lk-effect-card__handle"
|
|
143
|
-
aria-hidden="true"
|
|
144
|
-
title="Drag to reorder"
|
|
145
|
-
onMouseDown={onPressHandle}
|
|
146
|
-
onMouseUp={onReleaseHandle}
|
|
147
|
-
>
|
|
148
|
-
<DragHandleIcon />
|
|
149
|
-
</span>
|
|
150
|
-
)}
|
|
151
|
-
<span className="lk-effect-card__title">{title}</span>
|
|
152
|
-
{primary != null && <span className="lk-effect-card__primary">{primary}</span>}
|
|
153
|
-
{onRemove && (
|
|
154
|
-
<button
|
|
155
|
-
type="button"
|
|
156
|
-
className="lk-effect-card__remove"
|
|
157
|
-
onClick={(e) => {
|
|
158
|
-
e.stopPropagation();
|
|
159
|
-
onRemove();
|
|
160
|
-
}}
|
|
161
|
-
title="Remove"
|
|
162
|
-
aria-label="Remove"
|
|
163
|
-
>
|
|
164
|
-
✕
|
|
165
|
-
</button>
|
|
166
|
-
)}
|
|
167
|
-
</div>
|
|
168
|
-
{expanded && <div className="lk-effect-card__body">{children}</div>}
|
|
169
|
-
</div>
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function DragHandleIcon() {
|
|
174
|
-
return (
|
|
175
|
-
<svg width="10" height="14" viewBox="0 0 10 14" aria-hidden="true">
|
|
176
|
-
<circle cx="3" cy="3" r="1.3" fill="currentColor" />
|
|
177
|
-
<circle cx="7" cy="3" r="1.3" fill="currentColor" />
|
|
178
|
-
<circle cx="3" cy="7" r="1.3" fill="currentColor" />
|
|
179
|
-
<circle cx="7" cy="7" r="1.3" fill="currentColor" />
|
|
180
|
-
<circle cx="3" cy="11" r="1.3" fill="currentColor" />
|
|
181
|
-
<circle cx="7" cy="11" r="1.3" fill="currentColor" />
|
|
182
|
-
</svg>
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// ── EffectCardList ───────────────────────────────────────────────────
|
|
187
|
-
// Batteries-included list wrapper that coordinates HTML5 drag-and-drop across
|
|
188
|
-
// a list of EffectCards. Consumer supplies items + a renderItem callback;
|
|
189
|
-
// onReorder fires with (sourceId, targetId, position) on drop. Mirrors the
|
|
190
|
-
// LayerStack pattern from speech-balloons Lab.tsx:630-707.
|
|
191
|
-
|
|
192
|
-
/** The minimum an item must carry to appear in an `<EffectCardList>`. */
|
|
193
|
-
export interface EffectCardListItem {
|
|
194
|
-
id: string | number;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/** Props for `<EffectCardList>`. */
|
|
198
|
-
export interface EffectCardListProps<T extends EffectCardListItem> {
|
|
199
|
-
items: ReadonlyArray<T>;
|
|
200
|
-
renderItem: (
|
|
201
|
-
item: T,
|
|
202
|
-
helpers: {
|
|
203
|
-
isExpanded: boolean;
|
|
204
|
-
isDragging: boolean;
|
|
205
|
-
toggleExpanded: () => void;
|
|
206
|
-
cardProps: Pick<
|
|
207
|
-
EffectCardProps,
|
|
208
|
-
| 'expanded'
|
|
209
|
-
| 'dragging'
|
|
210
|
-
| 'draggable'
|
|
211
|
-
| 'onToggleExpanded'
|
|
212
|
-
| 'onPressHandle'
|
|
213
|
-
| 'onReleaseHandle'
|
|
214
|
-
| 'onDragStart'
|
|
215
|
-
| 'onDragEnd'
|
|
216
|
-
| 'onDragOver'
|
|
217
|
-
| 'onDragLeave'
|
|
218
|
-
| 'onDrop'
|
|
219
|
-
>;
|
|
220
|
-
},
|
|
221
|
-
) => ReactNode;
|
|
222
|
-
onReorder: (sourceId: T['id'], targetId: T['id'], position: 'before' | 'after') => void;
|
|
223
|
-
/** Optional rendered when the list is empty. */
|
|
224
|
-
empty?: ReactNode;
|
|
225
|
-
/** IDs to render expanded on first mount. Defaults to all collapsed. */
|
|
226
|
-
defaultExpandedIds?: ReadonlyArray<T['id']>;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/** A drag-reorderable list of `<EffectCard>`s. Owns the expanded and dragging
|
|
230
|
-
* state and hands it back to `renderItem`, so the caller only supplies each
|
|
231
|
-
* card's contents. */
|
|
232
|
-
export function EffectCardList<T extends EffectCardListItem>({
|
|
233
|
-
items,
|
|
234
|
-
renderItem,
|
|
235
|
-
onReorder,
|
|
236
|
-
empty,
|
|
237
|
-
defaultExpandedIds,
|
|
238
|
-
}: EffectCardListProps<T>) {
|
|
239
|
-
const [expanded, setExpanded] = useState<Set<T['id']>>(() => new Set(defaultExpandedIds ?? []));
|
|
240
|
-
const [draggingId, setDraggingId] = useState<T['id'] | null>(null);
|
|
241
|
-
const [pressedId, setPressedId] = useState<T['id'] | null>(null);
|
|
242
|
-
const [dropHint, setDropHint] = useState<{ id: T['id']; position: 'before' | 'after' } | null>(
|
|
243
|
-
null,
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
if (items.length === 0) {
|
|
247
|
-
return <>{empty ?? <div className="lk-effect-card-list__empty">No items.</div>}</>;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return (
|
|
251
|
-
<div className="lk-effect-card-list">
|
|
252
|
-
{items.map((item) => {
|
|
253
|
-
const isExpanded = expanded.has(item.id);
|
|
254
|
-
const isDragging = draggingId === item.id;
|
|
255
|
-
const isPressed = pressedId === item.id;
|
|
256
|
-
const showHintBefore =
|
|
257
|
-
dropHint && dropHint.id === item.id && dropHint.position === 'before';
|
|
258
|
-
const showHintAfter = dropHint && dropHint.id === item.id && dropHint.position === 'after';
|
|
259
|
-
|
|
260
|
-
const toggleExpanded = () =>
|
|
261
|
-
setExpanded((prev) => {
|
|
262
|
-
const next = new Set(prev);
|
|
263
|
-
if (next.has(item.id)) next.delete(item.id);
|
|
264
|
-
else next.add(item.id);
|
|
265
|
-
return next;
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
return (
|
|
269
|
-
<div key={String(item.id)} className="lk-effect-card-list__wrap">
|
|
270
|
-
{showHintBefore && <div className="lk-effect-card-list__drop-hint" />}
|
|
271
|
-
{renderItem(item, {
|
|
272
|
-
isExpanded,
|
|
273
|
-
isDragging,
|
|
274
|
-
toggleExpanded,
|
|
275
|
-
cardProps: {
|
|
276
|
-
expanded: isExpanded,
|
|
277
|
-
dragging: isDragging,
|
|
278
|
-
draggable: isPressed,
|
|
279
|
-
onToggleExpanded: toggleExpanded,
|
|
280
|
-
onPressHandle: () => setPressedId(item.id),
|
|
281
|
-
onReleaseHandle: () => setPressedId(null),
|
|
282
|
-
onDragStart: (e) => {
|
|
283
|
-
e.dataTransfer.effectAllowed = 'move';
|
|
284
|
-
e.dataTransfer.setData('text/plain', String(item.id));
|
|
285
|
-
setDraggingId(item.id);
|
|
286
|
-
},
|
|
287
|
-
onDragEnd: () => {
|
|
288
|
-
setDraggingId(null);
|
|
289
|
-
setPressedId(null);
|
|
290
|
-
setDropHint(null);
|
|
291
|
-
},
|
|
292
|
-
onDragOver: (e) => {
|
|
293
|
-
if (draggingId === null || draggingId === item.id) return;
|
|
294
|
-
e.preventDefault();
|
|
295
|
-
e.dataTransfer.dropEffect = 'move';
|
|
296
|
-
const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();
|
|
297
|
-
const position: 'before' | 'after' =
|
|
298
|
-
e.clientY < rect.top + rect.height / 2 ? 'before' : 'after';
|
|
299
|
-
setDropHint({ id: item.id, position });
|
|
300
|
-
},
|
|
301
|
-
onDragLeave: () => {
|
|
302
|
-
setDropHint((h) => (h && h.id === item.id ? null : h));
|
|
303
|
-
},
|
|
304
|
-
onDrop: (e) => {
|
|
305
|
-
e.preventDefault();
|
|
306
|
-
if (draggingId === null || draggingId === item.id) {
|
|
307
|
-
setDropHint(null);
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();
|
|
311
|
-
const position: 'before' | 'after' =
|
|
312
|
-
e.clientY < rect.top + rect.height / 2 ? 'before' : 'after';
|
|
313
|
-
onReorder(draggingId, item.id, position);
|
|
314
|
-
setDropHint(null);
|
|
315
|
-
},
|
|
316
|
-
},
|
|
317
|
-
})}
|
|
318
|
-
{showHintAfter && <div className="lk-effect-card-list__drop-hint" />}
|
|
319
|
-
</div>
|
|
320
|
-
);
|
|
321
|
-
})}
|
|
322
|
-
</div>
|
|
323
|
-
);
|
|
324
|
-
}
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
CheckboxRow,
|
|
5
|
-
ColorRow,
|
|
6
|
-
NumberRow,
|
|
7
|
-
PropertyList,
|
|
8
|
-
PropertyPanel,
|
|
9
|
-
PropertyRow,
|
|
10
|
-
SelectRow,
|
|
11
|
-
SliderRow,
|
|
12
|
-
TextRow,
|
|
13
|
-
ToggleRow,
|
|
14
|
-
} from './PropertyPanel';
|
|
15
|
-
|
|
16
|
-
// Titled "Gallery" so it sorts above PropertyPanel/PropertyList/Rows in the
|
|
17
|
-
// sidebar alphabetically (G < P < R). Acts as the visual root of the section.
|
|
18
|
-
const meta: Meta = {
|
|
19
|
-
title: 'labkit/UI/Properties/Gallery',
|
|
20
|
-
};
|
|
21
|
-
export default meta;
|
|
22
|
-
type Story = StoryObj;
|
|
23
|
-
|
|
24
|
-
function BlockRows() {
|
|
25
|
-
const [opacity, setOpacity] = useState(0.65);
|
|
26
|
-
const [count, setCount] = useState(8);
|
|
27
|
-
const [name, setName] = useState('Untitled');
|
|
28
|
-
const [mode, setMode] = useState<'fill' | 'stroke' | 'both'>('both');
|
|
29
|
-
const [align, setAlign] = useState<'left' | 'center' | 'right'>('center');
|
|
30
|
-
return (
|
|
31
|
-
<PropertyPanel title="Block layout (default)">
|
|
32
|
-
<PropertyList>
|
|
33
|
-
<SliderRow
|
|
34
|
-
label="Opacity"
|
|
35
|
-
value={opacity}
|
|
36
|
-
min={0}
|
|
37
|
-
max={1}
|
|
38
|
-
step={0.01}
|
|
39
|
-
onChange={setOpacity}
|
|
40
|
-
format={(v) => v.toFixed(2)}
|
|
41
|
-
/>
|
|
42
|
-
<NumberRow label="Count" value={count} onChange={setCount} min={0} max={100} />
|
|
43
|
-
<TextRow label="Name" value={name} onChange={setName} />
|
|
44
|
-
<SelectRow
|
|
45
|
-
label="Mode"
|
|
46
|
-
value={mode}
|
|
47
|
-
onChange={setMode}
|
|
48
|
-
options={[
|
|
49
|
-
{ value: 'fill', label: 'Fill only' },
|
|
50
|
-
{ value: 'stroke', label: 'Stroke only' },
|
|
51
|
-
{ value: 'both', label: 'Fill + stroke' },
|
|
52
|
-
]}
|
|
53
|
-
/>
|
|
54
|
-
<ToggleRow
|
|
55
|
-
label="Align"
|
|
56
|
-
value={align}
|
|
57
|
-
onChange={setAlign}
|
|
58
|
-
options={[
|
|
59
|
-
{ value: 'left', label: 'L' },
|
|
60
|
-
{ value: 'center', label: 'C' },
|
|
61
|
-
{ value: 'right', label: 'R' },
|
|
62
|
-
]}
|
|
63
|
-
/>
|
|
64
|
-
</PropertyList>
|
|
65
|
-
</PropertyPanel>
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function InlineRows() {
|
|
70
|
-
const [opacity, setOpacity] = useState(0.5);
|
|
71
|
-
const [count, setCount] = useState(8);
|
|
72
|
-
const [name, setName] = useState('Untitled');
|
|
73
|
-
const [mode, setMode] = useState<'fill' | 'stroke' | 'both'>('both');
|
|
74
|
-
const [align, setAlign] = useState<'left' | 'center' | 'right'>('center');
|
|
75
|
-
return (
|
|
76
|
-
<PropertyPanel title="Inline layout">
|
|
77
|
-
<PropertyList>
|
|
78
|
-
<SliderRow
|
|
79
|
-
label="Opacity"
|
|
80
|
-
layout="inline"
|
|
81
|
-
value={opacity}
|
|
82
|
-
min={0}
|
|
83
|
-
max={1}
|
|
84
|
-
step={0.01}
|
|
85
|
-
onChange={setOpacity}
|
|
86
|
-
format={(v) => v.toFixed(2)}
|
|
87
|
-
/>
|
|
88
|
-
<NumberRow
|
|
89
|
-
label="Count"
|
|
90
|
-
layout="inline"
|
|
91
|
-
value={count}
|
|
92
|
-
onChange={setCount}
|
|
93
|
-
min={0}
|
|
94
|
-
max={100}
|
|
95
|
-
/>
|
|
96
|
-
<TextRow label="Name" layout="inline" value={name} onChange={setName} />
|
|
97
|
-
<SelectRow
|
|
98
|
-
label="Mode"
|
|
99
|
-
layout="inline"
|
|
100
|
-
value={mode}
|
|
101
|
-
onChange={setMode}
|
|
102
|
-
options={[
|
|
103
|
-
{ value: 'fill', label: 'Fill only' },
|
|
104
|
-
{ value: 'stroke', label: 'Stroke only' },
|
|
105
|
-
{ value: 'both', label: 'Fill + stroke' },
|
|
106
|
-
]}
|
|
107
|
-
/>
|
|
108
|
-
<ToggleRow
|
|
109
|
-
label="Align"
|
|
110
|
-
layout="inline"
|
|
111
|
-
value={align}
|
|
112
|
-
onChange={setAlign}
|
|
113
|
-
options={[
|
|
114
|
-
{ value: 'left', label: 'L' },
|
|
115
|
-
{ value: 'center', label: 'C' },
|
|
116
|
-
{ value: 'right', label: 'R' },
|
|
117
|
-
]}
|
|
118
|
-
/>
|
|
119
|
-
</PropertyList>
|
|
120
|
-
</PropertyPanel>
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function IntrinsicRows() {
|
|
125
|
-
const [fill, setFill] = useState('#b08adb');
|
|
126
|
-
const [stroke, setStroke] = useState('#1a1428');
|
|
127
|
-
const [highlight, setHighlight] = useState('#7fb069');
|
|
128
|
-
const [shadow, setShadow] = useState('#000000');
|
|
129
|
-
const [visible, setVisible] = useState(true);
|
|
130
|
-
const [snap, setSnap] = useState(false);
|
|
131
|
-
return (
|
|
132
|
-
<PropertyPanel title="Intrinsic-layout variants">
|
|
133
|
-
<PropertyList>
|
|
134
|
-
<ColorRow label="Fill" value={fill} onChange={setFill} />
|
|
135
|
-
<ColorRow label="Stroke" value={stroke} onChange={setStroke} />
|
|
136
|
-
<ColorRow label="Highlight" value={highlight} onChange={setHighlight} />
|
|
137
|
-
<ColorRow label="Shadow" value={shadow} onChange={setShadow} />
|
|
138
|
-
<CheckboxRow label="Visible" value={visible} onChange={setVisible} />
|
|
139
|
-
<CheckboxRow label="Snap to grid" value={snap} onChange={setSnap} />
|
|
140
|
-
</PropertyList>
|
|
141
|
-
</PropertyPanel>
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function CustomRows() {
|
|
146
|
-
return (
|
|
147
|
-
<PropertyPanel title="Custom (PropertyRow)">
|
|
148
|
-
<PropertyList>
|
|
149
|
-
<PropertyRow label="Tempo" readout="120 bpm">
|
|
150
|
-
<button type="button">Tap to set</button>
|
|
151
|
-
</PropertyRow>
|
|
152
|
-
<PropertyRow label="Free-form" layout="inline">
|
|
153
|
-
<code style={{ background: 'rgba(0,0,0,0.35)', padding: '2px 6px', borderRadius: 3 }}>
|
|
154
|
-
arbitrary children
|
|
155
|
-
</code>
|
|
156
|
-
</PropertyRow>
|
|
157
|
-
</PropertyList>
|
|
158
|
-
</PropertyPanel>
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export const All: Story = {
|
|
163
|
-
render: () => (
|
|
164
|
-
<div
|
|
165
|
-
style={{
|
|
166
|
-
display: 'grid',
|
|
167
|
-
gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))',
|
|
168
|
-
gap: 16,
|
|
169
|
-
maxWidth: 1100,
|
|
170
|
-
}}
|
|
171
|
-
>
|
|
172
|
-
<BlockRows />
|
|
173
|
-
<InlineRows />
|
|
174
|
-
<IntrinsicRows />
|
|
175
|
-
<CustomRows />
|
|
176
|
-
</div>
|
|
177
|
-
),
|
|
178
|
-
};
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { NumberRow } from './PropertyPanel';
|
|
4
|
-
import { SideBySide } from './storyLayouts';
|
|
5
|
-
|
|
6
|
-
const meta: Meta<typeof NumberRow> = {
|
|
7
|
-
title: 'labkit/UI/Properties/Rows/NumberRow',
|
|
8
|
-
component: NumberRow,
|
|
9
|
-
};
|
|
10
|
-
export default meta;
|
|
11
|
-
type Story = StoryObj<typeof NumberRow>;
|
|
12
|
-
|
|
13
|
-
function Controlled({
|
|
14
|
-
initial,
|
|
15
|
-
label,
|
|
16
|
-
min,
|
|
17
|
-
max,
|
|
18
|
-
step,
|
|
19
|
-
layout,
|
|
20
|
-
}: {
|
|
21
|
-
initial: number;
|
|
22
|
-
label: string;
|
|
23
|
-
min?: number;
|
|
24
|
-
max?: number;
|
|
25
|
-
step?: number;
|
|
26
|
-
layout?: 'block' | 'inline';
|
|
27
|
-
}) {
|
|
28
|
-
const [value, setValue] = useState(initial);
|
|
29
|
-
return (
|
|
30
|
-
<NumberRow
|
|
31
|
-
label={label}
|
|
32
|
-
value={value}
|
|
33
|
-
onChange={setValue}
|
|
34
|
-
min={min}
|
|
35
|
-
max={max}
|
|
36
|
-
step={step}
|
|
37
|
-
layout={layout}
|
|
38
|
-
/>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export const BothLayouts: Story = {
|
|
43
|
-
render: () => (
|
|
44
|
-
<SideBySide
|
|
45
|
-
block={<Controlled initial={8} label="Count" min={0} max={100} step={1} />}
|
|
46
|
-
inline={<Controlled initial={8} label="Count" min={0} max={100} step={1} layout="inline" />}
|
|
47
|
-
/>
|
|
48
|
-
),
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export const Float: Story = {
|
|
52
|
-
render: () => (
|
|
53
|
-
<div style={{ width: 280 }}>
|
|
54
|
-
<Controlled initial={1.5} label="Factor" min={0} max={10} step={0.1} />
|
|
55
|
-
</div>
|
|
56
|
-
),
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export const Unbounded: Story = {
|
|
60
|
-
render: () => (
|
|
61
|
-
<div style={{ width: 280 }}>
|
|
62
|
-
<Controlled initial={0} label="Offset" />
|
|
63
|
-
</div>
|
|
64
|
-
),
|
|
65
|
-
};
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
.lk-property-group {
|
|
2
|
-
grid-column: 1 / -1;
|
|
3
|
-
background: var(--wzl-surface-sunken);
|
|
4
|
-
border-radius: var(--wzl-radius-md);
|
|
5
|
-
padding: 10px 12px 12px;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
.lk-property-group__title {
|
|
9
|
-
display: grid;
|
|
10
|
-
grid-template-columns: 1fr auto 1fr;
|
|
11
|
-
align-items: center;
|
|
12
|
-
gap: 10px;
|
|
13
|
-
margin: 0 0 8px;
|
|
14
|
-
font-family: var(--wzl-font-display);
|
|
15
|
-
font-weight: var(--wzl-font-weight-light);
|
|
16
|
-
font-size: var(--wzl-font-size-sm);
|
|
17
|
-
letter-spacing: var(--wzl-tracking-wider);
|
|
18
|
-
text-transform: uppercase;
|
|
19
|
-
color: var(--wzl-fg-subtle);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
.lk-property-group__title hr {
|
|
23
|
-
border: 0;
|
|
24
|
-
border-top: 1px solid var(--wzl-border);
|
|
25
|
-
margin: 0;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.lk-property-group__body {
|
|
29
|
-
display: grid;
|
|
30
|
-
grid-template-columns: 1fr 1fr;
|
|
31
|
-
column-gap: 16px;
|
|
32
|
-
row-gap: 10px;
|
|
33
|
-
// Stretch so paired rows match in height — slider rows then bottom-align
|
|
34
|
-
// their tracks (via `margin-top: auto`) and stay lined up even when one
|
|
35
|
-
// sibling's label wraps to two lines.
|
|
36
|
-
align-items: stretch;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.lk-property-group:not(.lk-property-group--pairs) .lk-property-group__body > :not(.lk-property-row--color) {
|
|
40
|
-
grid-column: 1 / -1;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.lk-property-group--pairs .lk-property-group__body > * {
|
|
44
|
-
grid-column: auto;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.lk-property-group--pairs .lk-property-group__body > h3,
|
|
48
|
-
.lk-property-group--pairs .lk-property-group__body > .lk-property-group__span {
|
|
49
|
-
grid-column: 1 / -1;
|
|
50
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import { PropertyGroup } from './PropertyGroup';
|
|
3
|
-
import { PropertyList, PropertyPanel, SliderRow } from './PropertyPanel';
|
|
4
|
-
|
|
5
|
-
const meta: Meta<typeof PropertyGroup> = {
|
|
6
|
-
title: 'labkit/UI/Properties/PropertyGroup',
|
|
7
|
-
component: PropertyGroup,
|
|
8
|
-
};
|
|
9
|
-
export default meta;
|
|
10
|
-
|
|
11
|
-
export const Basic: StoryObj<typeof PropertyGroup> = {
|
|
12
|
-
render: () => (
|
|
13
|
-
<PropertyPanel title="Fill">
|
|
14
|
-
<PropertyList>
|
|
15
|
-
<SliderRow label="Amount" value={0.6} min={0} max={1} step={0.02} onChange={() => {}} />
|
|
16
|
-
<PropertyGroup title="Aqua">
|
|
17
|
-
<SliderRow
|
|
18
|
-
label="Light angle"
|
|
19
|
-
value={270}
|
|
20
|
-
min={0}
|
|
21
|
-
max={359}
|
|
22
|
-
step={1}
|
|
23
|
-
unit="°"
|
|
24
|
-
onChange={() => {}}
|
|
25
|
-
/>
|
|
26
|
-
<SliderRow label="Gloss" value={0.55} min={0} max={1} step={0.02} onChange={() => {}} />
|
|
27
|
-
</PropertyGroup>
|
|
28
|
-
<PropertyGroup title="Bevel" hidden>
|
|
29
|
-
<SliderRow label="Rings" value={32} min={4} max={96} step={1} onChange={() => {}} />
|
|
30
|
-
</PropertyGroup>
|
|
31
|
-
</PropertyList>
|
|
32
|
-
</PropertyPanel>
|
|
33
|
-
),
|
|
34
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/react';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import { PropertyGroup } from './PropertyGroup';
|
|
4
|
-
|
|
5
|
-
describe('PropertyGroup', () => {
|
|
6
|
-
it('renders title and children', () => {
|
|
7
|
-
render(
|
|
8
|
-
<PropertyGroup title="Aqua">
|
|
9
|
-
<div>child</div>
|
|
10
|
-
</PropertyGroup>,
|
|
11
|
-
);
|
|
12
|
-
expect(screen.getByText('Aqua')).toBeInTheDocument();
|
|
13
|
-
expect(screen.getByText('child')).toBeInTheDocument();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('renders nothing when hidden is true', () => {
|
|
17
|
-
const { container } = render(
|
|
18
|
-
<PropertyGroup title="Bevel" hidden>
|
|
19
|
-
<div>child</div>
|
|
20
|
-
</PropertyGroup>,
|
|
21
|
-
);
|
|
22
|
-
expect(container).toBeEmptyDOMElement();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('applies subpanel class', () => {
|
|
26
|
-
const { container } = render(
|
|
27
|
-
<PropertyGroup title="Dome">
|
|
28
|
-
<div>x</div>
|
|
29
|
-
</PropertyGroup>,
|
|
30
|
-
);
|
|
31
|
-
expect(container.firstChild).toHaveClass('lk-property-group');
|
|
32
|
-
});
|
|
33
|
-
});
|