@weasel-js/labkit 1.2.0 → 1.3.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.
Files changed (227) hide show
  1. package/dist/_dts/{DrawCommand-C_XboUpz.d.ts → DrawCommand-DBB45NfN.d.ts} +196 -52
  2. package/dist/_dts/PrefsForm-BYa6cWnO.d.ts +201 -0
  3. package/dist/_dts/types-1Sdxy_Pv.d.ts +90 -0
  4. package/dist/_dts/types-BttTIent.d.ts +314 -0
  5. package/dist/_dts/types-DCGEcMO_.d.ts +162 -0
  6. package/dist/_dts/{useTrialState-BMNIx3Cy.d.ts → useTrialState-CsGMjhu9.d.ts} +5 -87
  7. package/dist/canvas/index.d.ts +7 -3
  8. package/dist/canvas/index.js +3 -1
  9. package/dist/chrome/index.d.ts +86 -0
  10. package/dist/chrome/index.js +9 -0
  11. package/dist/chrome/index.js.map +1 -0
  12. package/dist/chunk-3XXPU73K.js +138 -0
  13. package/dist/chunk-3XXPU73K.js.map +1 -0
  14. package/dist/{chunk-VUU5UXHE.js → chunk-4TMMVIDM.js} +8 -5
  15. package/dist/chunk-4TMMVIDM.js.map +1 -0
  16. package/dist/chunk-6X5RBC6G.js +366 -0
  17. package/dist/chunk-6X5RBC6G.js.map +1 -0
  18. package/dist/{chunk-574LJAV4.js → chunk-BDJXIBRZ.js} +33 -35
  19. package/dist/chunk-BDJXIBRZ.js.map +1 -0
  20. package/dist/{chunk-SFL7NFKN.js → chunk-BMW4TDP5.js} +15 -7
  21. package/dist/chunk-BMW4TDP5.js.map +1 -0
  22. package/dist/chunk-JDROYQM3.js +266 -0
  23. package/dist/chunk-JDROYQM3.js.map +1 -0
  24. package/dist/{chunk-THBG7FQZ.js → chunk-LGKRIUSW.js} +15 -12
  25. package/dist/chunk-LGKRIUSW.js.map +1 -0
  26. package/dist/{chunk-BOHF3PQO.js → chunk-QCICIHEO.js} +8129 -5196
  27. package/dist/chunk-QCICIHEO.js.map +1 -0
  28. package/dist/chunk-QRYPXSGP.js +212 -0
  29. package/dist/chunk-QRYPXSGP.js.map +1 -0
  30. package/dist/chunk-RGT4EJIB.js +6961 -0
  31. package/dist/chunk-RGT4EJIB.js.map +1 -0
  32. package/dist/chunk-SBXNQW4G.js +3 -0
  33. package/dist/chunk-SBXNQW4G.js.map +1 -0
  34. package/dist/chunk-T66HU3HX.js +17 -0
  35. package/dist/chunk-T66HU3HX.js.map +1 -0
  36. package/dist/chunk-WTI26YTM.js +266 -0
  37. package/dist/chunk-WTI26YTM.js.map +1 -0
  38. package/dist/controls/index.d.ts +19 -5
  39. package/dist/controls/index.js +3 -1
  40. package/dist/dragdrop/index.d.ts +46 -3
  41. package/dist/dragdrop/index.js +3 -1
  42. package/dist/index.d.ts +735 -565
  43. package/dist/index.js +661 -1019
  44. package/dist/index.js.map +1 -1
  45. package/dist/layers/index.d.ts +5 -3
  46. package/dist/layers/index.js +4 -1
  47. package/dist/passthrough/weasel-canvas.d.ts +69 -13
  48. package/dist/passthrough/weasel-canvas.js +1 -1
  49. package/dist/passthrough/weasel-ui.d.ts +3811 -3671
  50. package/dist/passthrough/weasel-ui.js +3 -2
  51. package/dist/primitives/index.d.ts +102 -8
  52. package/dist/primitives/index.js +5 -1
  53. package/dist/state/index.d.ts +3 -2
  54. package/dist/state/index.js +2 -2
  55. package/dist/styles.css +490 -803
  56. package/dist/surface/index.js +2 -1
  57. package/dist/ui/layers/index.d.ts +8 -2
  58. package/dist/ui/layers/index.js +2 -3
  59. package/dist/undo/index.d.ts +4 -2
  60. package/package.json +16 -7
  61. package/src/canvas/AGENTS.md +4 -0
  62. package/src/canvas/CanvasStack.tsx +6 -2
  63. package/src/canvas/useLayerScheduler.ts +22 -15
  64. package/src/canvas/usePanZoom.test.ts +80 -0
  65. package/src/canvas/usePanZoom.ts +16 -2
  66. package/src/chrome/ChromeRegions.stories.tsx +106 -0
  67. package/src/chrome/builtins.test.ts +153 -0
  68. package/src/chrome/builtins.tsx +205 -0
  69. package/src/chrome/index.ts +25 -0
  70. package/src/chrome/merge.test.ts +51 -0
  71. package/src/chrome/merge.ts +52 -0
  72. package/src/chrome/regions/PaletteRegion.less +14 -0
  73. package/src/chrome/regions/PaletteRegion.test.tsx +36 -0
  74. package/src/chrome/regions/PaletteRegion.tsx +41 -0
  75. package/src/chrome/regions/SidebarRegion.tsx +56 -0
  76. package/src/chrome/regions/StatusRegion.tsx +31 -0
  77. package/src/chrome/regions/TitleBarRegion.tsx +41 -0
  78. package/src/chrome/regions/ToolbarRegion.test.tsx +82 -0
  79. package/src/chrome/regions/ToolbarRegion.tsx +71 -0
  80. package/src/chrome/regions/ViewportRegion.less +42 -0
  81. package/src/chrome/regions/ViewportRegion.tsx +37 -0
  82. package/src/chrome/regions/regions.test.tsx +81 -0
  83. package/src/chrome/types.ts +127 -0
  84. package/src/config/builder.test.ts +93 -0
  85. package/src/config/builder.ts +220 -0
  86. package/src/config/fromConfigField.test.ts +93 -0
  87. package/src/config/fromConfigField.ts +74 -0
  88. package/src/config/index.ts +22 -0
  89. package/src/config/resolve.test.ts +119 -0
  90. package/src/config/resolve.ts +67 -0
  91. package/src/config/rules.ts +68 -0
  92. package/src/config/types.ts +108 -0
  93. package/src/config/useConfigSchema.ts +30 -0
  94. package/src/config/visible.ts +20 -0
  95. package/src/controls/ControlPanel.less +4 -58
  96. package/src/controls/ControlPanel.stories.tsx +125 -0
  97. package/src/controls/ControlPanel.test.tsx +245 -10
  98. package/src/controls/ControlPanel.tsx +236 -203
  99. package/src/dragdrop/DragDropRuntime.tsx +36 -18
  100. package/src/dragdrop/DragGhost.less +1 -1
  101. package/src/dragdrop/Palette.less +1 -1
  102. package/src/dragdrop/index.ts +11 -0
  103. package/src/index.ts +74 -5
  104. package/src/instrument/SineWave.smoke.test.tsx +8 -1
  105. package/src/instrument/defineInstrument.test.ts +38 -1
  106. package/src/instrument/defineInstrument.ts +31 -4
  107. package/src/instrument/index.ts +1 -0
  108. package/src/instrument/types.ts +27 -4
  109. package/src/instrument/validateConfigSchema.test.ts +13 -3
  110. package/src/instrument/validateConfigSchema.ts +7 -10
  111. package/src/lab/Lab.chrome.test.tsx +65 -0
  112. package/src/lab/Lab.tsx +58 -15
  113. package/src/lab/LabContext.ts +6 -0
  114. package/src/lab/LabFullChrome.stories.tsx +308 -0
  115. package/src/lab/LabHeader.test.tsx +42 -0
  116. package/src/lab/LabHeader.tsx +57 -0
  117. package/src/lab/LabPalette.test.tsx +35 -0
  118. package/src/lab/LabPalette.tsx +34 -0
  119. package/src/lab/LabShell.less +50 -3
  120. package/src/lab/Workspace.less +1 -30
  121. package/src/lab/Workspace.test.tsx +20 -10
  122. package/src/lab/Workspace.tsx +5 -8
  123. package/src/lab/index.ts +3 -1
  124. package/src/layers/AGENTS.md +1 -1
  125. package/src/layers/LayerList.less +31 -7
  126. package/src/layers/LayerList.tsx +17 -4
  127. package/src/passthrough/weasel-ui.ts +28 -0
  128. package/src/primitives/FloatingPanel.less +28 -0
  129. package/src/primitives/FloatingPanel.stories.tsx +46 -0
  130. package/src/primitives/FloatingPanel.test.tsx +240 -0
  131. package/src/primitives/FloatingPanel.tsx +190 -0
  132. package/src/primitives/FpsMeter.test.tsx +44 -0
  133. package/src/primitives/FpsMeter.tsx +20 -9
  134. package/src/primitives/JobProgress.less +69 -0
  135. package/src/primitives/JobProgress.stories.tsx +48 -0
  136. package/src/primitives/JobProgress.tsx +65 -0
  137. package/src/primitives/Legend.less +47 -0
  138. package/src/primitives/Legend.stories.tsx +32 -0
  139. package/src/primitives/Legend.test.tsx +57 -0
  140. package/src/primitives/Legend.tsx +40 -0
  141. package/src/primitives/Sidebar.less +32 -2
  142. package/src/primitives/StatusBar.less +7 -0
  143. package/src/primitives/StatusBar.tsx +10 -3
  144. package/src/primitives/Toolbar.less +50 -3
  145. package/src/primitives/Toolbar.test.tsx +67 -1
  146. package/src/primitives/Toolbar.tsx +72 -6
  147. package/src/primitives/ZoomControl.less +31 -0
  148. package/src/primitives/ZoomControl.tsx +76 -0
  149. package/src/primitives/floating.entry.test.tsx +14 -0
  150. package/src/primitives/index.ts +10 -2
  151. package/src/primitives/useRovingTabIndex.test.ts +27 -0
  152. package/src/primitives/useRovingTabIndex.ts +79 -0
  153. package/src/state/store.ts +11 -3
  154. package/src/state/toolSlot.test.ts +36 -0
  155. package/src/state/types.ts +4 -0
  156. package/src/styles.less +6 -4
  157. package/src/surface/useTiledSurface.ts +14 -10
  158. package/src/test-setup.ts +26 -0
  159. package/src/theme/Interstellar.stories.tsx +1 -1
  160. package/src/theme/base.less +19 -2
  161. package/src/theme/interstellar.tokens.json +6 -2
  162. package/src/tools/types.ts +24 -0
  163. package/src/trial/Trial.config.test.tsx +108 -0
  164. package/src/trial/Trial.job.test.tsx +13 -15
  165. package/src/trial/Trial.less +115 -10
  166. package/src/trial/Trial.stories.tsx +1 -0
  167. package/src/trial/Trial.test.tsx +67 -96
  168. package/src/trial/Trial.tsx +80 -39
  169. package/src/trial/TrialChrome.tsx +99 -80
  170. package/src/trial/TrialDragContext.ts +10 -0
  171. package/src/trial/TrialTitleBar.tsx +41 -0
  172. package/src/trial/index.ts +1 -15
  173. package/src/trial/trialChrome.borders.test.ts +38 -0
  174. package/src/ui/layers/index.ts +5 -1
  175. package/dist/_dts/index-JFAYj5Tv.d.ts +0 -144
  176. package/dist/_dts/types-x92Kfeme.d.ts +0 -62
  177. package/dist/chunk-574LJAV4.js.map +0 -1
  178. package/dist/chunk-5R2ATYPJ.js +0 -201
  179. package/dist/chunk-5R2ATYPJ.js.map +0 -1
  180. package/dist/chunk-73KA7WBO.js +0 -5992
  181. package/dist/chunk-73KA7WBO.js.map +0 -1
  182. package/dist/chunk-BOHF3PQO.js.map +0 -1
  183. package/dist/chunk-G5TJVQQT.js +0 -161
  184. package/dist/chunk-G5TJVQQT.js.map +0 -1
  185. package/dist/chunk-PMAU3SEE.js +0 -126
  186. package/dist/chunk-PMAU3SEE.js.map +0 -1
  187. package/dist/chunk-SFL7NFKN.js.map +0 -1
  188. package/dist/chunk-THBG7FQZ.js.map +0 -1
  189. package/dist/chunk-VUU5UXHE.js.map +0 -1
  190. package/src/instrument/capabilityDetector.test.ts +0 -64
  191. package/src/instrument/capabilityDetector.ts +0 -20
  192. package/src/trial/DefaultSidebar.tsx +0 -29
  193. package/src/trial/DefaultStatusBar.tsx +0 -19
  194. package/src/trial/DefaultToolbar.tsx +0 -92
  195. package/src/trial/slotTypes.ts +0 -54
  196. package/src/ui/format.test.ts +0 -33
  197. package/src/ui/format.ts +0 -30
  198. package/src/ui/layers/LayerStack.less +0 -143
  199. package/src/ui/layers/LayerStack.stories.tsx +0 -45
  200. package/src/ui/layers/LayerStack.test.tsx +0 -170
  201. package/src/ui/layers/LayerStack.tsx +0 -220
  202. package/src/ui/properties/CheckboxRow.stories.tsx +0 -27
  203. package/src/ui/properties/ColorRow.stories.tsx +0 -106
  204. package/src/ui/properties/CurveField.less +0 -48
  205. package/src/ui/properties/CurveField.stories.tsx +0 -20
  206. package/src/ui/properties/CurveField.test.tsx +0 -127
  207. package/src/ui/properties/CurveField.tsx +0 -184
  208. package/src/ui/properties/EffectCard.tsx +0 -324
  209. package/src/ui/properties/Gallery.stories.tsx +0 -178
  210. package/src/ui/properties/NumberRow.stories.tsx +0 -65
  211. package/src/ui/properties/PropertyGroup.less +0 -50
  212. package/src/ui/properties/PropertyGroup.stories.tsx +0 -34
  213. package/src/ui/properties/PropertyGroup.test.tsx +0 -33
  214. package/src/ui/properties/PropertyGroup.tsx +0 -45
  215. package/src/ui/properties/PropertyList.stories.tsx +0 -83
  216. package/src/ui/properties/PropertyPanel.less +0 -592
  217. package/src/ui/properties/PropertyPanel.stories.tsx +0 -190
  218. package/src/ui/properties/PropertyPanel.test.tsx +0 -193
  219. package/src/ui/properties/PropertyPanel.tsx +0 -469
  220. package/src/ui/properties/PropertyRow.stories.tsx +0 -53
  221. package/src/ui/properties/SelectRow.stories.tsx +0 -58
  222. package/src/ui/properties/SliderRow.stories.tsx +0 -74
  223. package/src/ui/properties/SpeechBalloonPanels.stories.tsx +0 -396
  224. package/src/ui/properties/TextRow.stories.tsx +0 -62
  225. package/src/ui/properties/ToggleRow.stories.tsx +0 -57
  226. package/src/ui/properties/index.ts +0 -39
  227. package/src/ui/properties/storyLayouts.tsx +0 -36
@@ -1,396 +0,0 @@
1
- // Visual parity check: rebuild a slice of the speech-balloons lab side panels
2
- // using only labkit primitives. If the readouts, units, alpha, two-per-row
3
- // effect bodies, subpanels, and accent-tinted effect cards look the same as
4
- // the SB reference, we've captured the styling correctly.
5
-
6
- import type { Meta, StoryObj } from '@storybook/react-vite';
7
- import { useState } from 'react';
8
- import {
9
- CheckboxRow,
10
- ColorRow,
11
- EffectCard,
12
- EffectCardList,
13
- PropertyList,
14
- PropertyPanel,
15
- SelectRow,
16
- SliderRow,
17
- Subpanel,
18
- } from './';
19
-
20
- const meta: Meta = {
21
- title: 'labkit/UI/Properties/PropertyPanel/SpeechBalloonPanels',
22
- };
23
- export default meta;
24
- type Story = StoryObj;
25
-
26
- function BodyPanel() {
27
- const [base, setBase] = useState<'rectangle' | 'oval' | 'polygon' | 'cloud'>('rectangle');
28
- const [width, setWidth] = useState(220);
29
- const [height, setHeight] = useState(140);
30
- const [lean, setLean] = useState(0);
31
- const [textColor, setTextColor] = useState('#e6e7ec');
32
- const [textAlpha, setTextAlpha] = useState(1);
33
-
34
- return (
35
- <PropertyPanel title={`Body — ${base}`}>
36
- <PropertyList>
37
- <SelectRow
38
- label="Base shape"
39
- value={base}
40
- onChange={(v) => setBase(v as typeof base)}
41
- options={[
42
- { value: 'rectangle', label: 'rectangle' },
43
- { value: 'oval', label: 'oval' },
44
- { value: 'polygon', label: 'polygon' },
45
- { value: 'cloud', label: 'cloud' },
46
- ]}
47
- />
48
- <SliderRow
49
- label="Width"
50
- value={width}
51
- min={60}
52
- max={500}
53
- step={2}
54
- unit="px"
55
- onChange={setWidth}
56
- />
57
- <SliderRow
58
- label="Height"
59
- value={height}
60
- min={20}
61
- max={500}
62
- step={2}
63
- unit="px"
64
- onChange={setHeight}
65
- />
66
- <SliderRow
67
- label="Italic lean"
68
- value={lean}
69
- min={-25}
70
- max={25}
71
- step={0.5}
72
- unit={<sup>°</sup>}
73
- onChange={setLean}
74
- />
75
- <ColorRow
76
- label="Text color"
77
- value={textColor}
78
- onChange={setTextColor}
79
- alpha={textAlpha}
80
- onAlphaChange={setTextAlpha}
81
- />
82
- </PropertyList>
83
- </PropertyPanel>
84
- );
85
- }
86
-
87
- // A single tail's editor body — demonstrates pack='pairs' + a Subpanel
88
- // divider for the shape-specific section (e.g. Bubbles parameters).
89
- function TailBody() {
90
- const [shape, setShape] = useState<'classic' | 'bubbles' | 'lightning' | 'wavy'>('bubbles');
91
- const [angle, setAngle] = useState(115);
92
- const [outAngle, setOutAngle] = useState(0);
93
- const [arc, setArc] = useState(0);
94
- const [size, setSize] = useState(60);
95
- const [bubbleDiameter, setBubbleDiameter] = useState(30);
96
- const [count, setCount] = useState(3);
97
- const [gap, setGap] = useState(0.15);
98
- const [radial, setRadial] = useState(0);
99
-
100
- return (
101
- <PropertyList pack="pairs">
102
- <SelectRow
103
- label="Shape"
104
- value={shape}
105
- onChange={(v) => setShape(v as typeof shape)}
106
- options={[
107
- { value: 'classic', label: 'classic' },
108
- { value: 'bubbles', label: 'bubbles' },
109
- { value: 'lightning', label: 'lightning' },
110
- { value: 'wavy', label: 'wavy' },
111
- ]}
112
- />
113
- <SliderRow
114
- label="Angle"
115
- value={angle}
116
- min={0}
117
- max={359}
118
- unit={<sup>°</sup>}
119
- onChange={setAngle}
120
- />
121
- <SliderRow
122
- label="Tip angle"
123
- value={outAngle}
124
- min={-90}
125
- max={90}
126
- unit={<sup>°</sup>}
127
- onChange={setOutAngle}
128
- />
129
- <SliderRow
130
- label="Bend"
131
- value={arc}
132
- min={-1}
133
- max={1}
134
- step={0.02}
135
- format={(v) => v.toFixed(2)}
136
- onChange={setArc}
137
- />
138
- <SliderRow
139
- label="Length"
140
- value={size}
141
- min={8}
142
- max={220}
143
- step={0.5}
144
- unit="px"
145
- onChange={setSize}
146
- />
147
- {shape === 'bubbles' && (
148
- <Subpanel title="Bubbles">
149
- <SliderRow
150
- label="Size"
151
- value={bubbleDiameter}
152
- min={8}
153
- max={120}
154
- unit="px"
155
- onChange={setBubbleDiameter}
156
- />
157
- <SliderRow label="Count" value={count} min={1} max={8} onChange={setCount} />
158
- <SliderRow
159
- label="Gap"
160
- value={gap}
161
- min={-1}
162
- max={1}
163
- step={0.02}
164
- format={(v) => v.toFixed(2)}
165
- onChange={setGap}
166
- />
167
- <SliderRow
168
- label="Base distance"
169
- value={radial}
170
- min={-60}
171
- max={60}
172
- step={0.5}
173
- unit="px"
174
- onChange={setRadial}
175
- />
176
- </Subpanel>
177
- )}
178
- </PropertyList>
179
- );
180
- }
181
-
182
- function StrokePanel() {
183
- const [width, setWidth] = useState(2);
184
- const [color, setColor] = useState('#161921');
185
- const [alpha, setAlpha] = useState(1);
186
- return (
187
- <PropertyPanel title="Stroke">
188
- <PropertyList pack="pairs">
189
- <SliderRow
190
- label="Width"
191
- value={width}
192
- min={0.5}
193
- max={12}
194
- step={0.5}
195
- unit="px"
196
- format={(v) => v.toFixed(1)}
197
- onChange={setWidth}
198
- />
199
- <ColorRow
200
- label="Color"
201
- value={color}
202
- onChange={setColor}
203
- alpha={alpha}
204
- onAlphaChange={setAlpha}
205
- />
206
- </PropertyList>
207
- </PropertyPanel>
208
- );
209
- }
210
-
211
- function ShadowPanel() {
212
- const [dx, setDx] = useState(4);
213
- const [dy, setDy] = useState(8);
214
- const [blur, setBlur] = useState(10);
215
- const [opacity, setOpacity] = useState(0.4);
216
- const [enabled, setEnabled] = useState(true);
217
- return (
218
- <PropertyPanel title="Shadow">
219
- <PropertyList pack="pairs">
220
- <div className="lk-property-list__span">
221
- <CheckboxRow label="Enabled" value={enabled} onChange={setEnabled} />
222
- </div>
223
- <SliderRow
224
- label="Offset X"
225
- value={dx}
226
- min={-20}
227
- max={20}
228
- step={0.5}
229
- unit="px"
230
- format={(v) => v.toFixed(1)}
231
- onChange={setDx}
232
- />
233
- <SliderRow
234
- label="Offset Y"
235
- value={dy}
236
- min={-20}
237
- max={20}
238
- step={0.5}
239
- unit="px"
240
- format={(v) => v.toFixed(1)}
241
- onChange={setDy}
242
- />
243
- <SliderRow
244
- label="Blur"
245
- value={blur}
246
- min={0}
247
- max={30}
248
- step={0.5}
249
- unit="px"
250
- format={(v) => v.toFixed(1)}
251
- onChange={setBlur}
252
- />
253
- <SliderRow
254
- label="Opacity"
255
- value={opacity}
256
- min={0}
257
- max={1}
258
- step={0.05}
259
- format={(v) => v.toFixed(2)}
260
- onChange={setOpacity}
261
- />
262
- </PropertyList>
263
- </PropertyPanel>
264
- );
265
- }
266
-
267
- export const LeftSidebar: Story = {
268
- render: () => (
269
- <div style={{ width: 360, display: 'flex', flexDirection: 'column', gap: 8 }}>
270
- <BodyPanel />
271
- <StrokePanel />
272
- <ShadowPanel />
273
- </div>
274
- ),
275
- };
276
-
277
- // ── Right sidebar: tails ───────────────────────────────────────────
278
- // Each tail is an EffectCard with a per-instance accent (--lk-panel-accent)
279
- // that re-binds --wzl-accent inside the card. Drag the badge handle to reorder.
280
-
281
- interface TailItem {
282
- id: number;
283
- shape: string;
284
- accent: string;
285
- }
286
-
287
- const TAIL_PALETTE = ['#7ec8e3', '#f0a35c', '#a48bd4', '#7fb069', '#d46aaa'];
288
-
289
- export const RightSidebarTails: Story = {
290
- render: () => {
291
- function TailsList() {
292
- const [tails, setTails] = useState<TailItem[]>([
293
- { id: 1, shape: 'classic', accent: TAIL_PALETTE[0] },
294
- { id: 2, shape: 'bubbles', accent: TAIL_PALETTE[1] },
295
- { id: 3, shape: 'wavy', accent: TAIL_PALETTE[2] },
296
- ]);
297
- const reorder = (sourceId: number, targetId: number, position: 'before' | 'after') => {
298
- setTails((prev) => {
299
- const next = prev.filter((t) => t.id !== sourceId);
300
- const idx = next.findIndex((t) => t.id === targetId);
301
- const insertAt = position === 'before' ? idx : idx + 1;
302
- const src = prev.find((t) => t.id === sourceId)!;
303
- next.splice(insertAt, 0, src);
304
- return next;
305
- });
306
- };
307
- const remove = (id: number) => setTails((prev) => prev.filter((t) => t.id !== id));
308
- return (
309
- <EffectCardList
310
- items={tails}
311
- onReorder={reorder}
312
- defaultExpandedIds={tails.map((t) => t.id)}
313
- renderItem={(tail, { cardProps }) => (
314
- <EffectCard
315
- {...cardProps}
316
- accent={tail.accent}
317
- index={tails.findIndex((t) => t.id === tail.id)}
318
- title={tail.shape}
319
- primary={<>{Math.round(60 + tail.id * 30)}°</>}
320
- onRemove={() => remove(tail.id)}
321
- >
322
- <TailBody />
323
- </EffectCard>
324
- )}
325
- />
326
- );
327
- }
328
- return (
329
- <div style={{ width: 360, display: 'flex', flexDirection: 'column', gap: 8 }}>
330
- <TailsList />
331
- </div>
332
- );
333
- },
334
- };
335
-
336
- // ── Effect cards without accent — for fill / stroke / shadow stacks ─
337
-
338
- export const LayerStack: Story = {
339
- render: () => {
340
- function Stack() {
341
- const [items, setItems] = useState([
342
- { id: 1, kind: 'fill' },
343
- { id: 2, kind: 'stroke' },
344
- { id: 3, kind: 'shadow' },
345
- ]);
346
- const reorder = (sourceId: number, targetId: number, position: 'before' | 'after') => {
347
- setItems((prev) => {
348
- const next = prev.filter((t) => t.id !== sourceId);
349
- const idx = next.findIndex((t) => t.id === targetId);
350
- const insertAt = position === 'before' ? idx : idx + 1;
351
- const src = prev.find((t) => t.id === sourceId)!;
352
- next.splice(insertAt, 0, src);
353
- return next;
354
- });
355
- };
356
- return (
357
- <EffectCardList
358
- items={items}
359
- onReorder={reorder}
360
- defaultExpandedIds={items.map((i) => i.id)}
361
- renderItem={(item, { cardProps }) => (
362
- <EffectCard
363
- {...cardProps}
364
- title={item.kind}
365
- onRemove={() => setItems((prev) => prev.filter((t) => t.id !== item.id))}
366
- >
367
- <PropertyList pack="pairs">
368
- <SliderRow
369
- label="Sample A"
370
- value={12}
371
- min={0}
372
- max={64}
373
- unit="px"
374
- onChange={() => {}}
375
- />
376
- <SliderRow
377
- label="Sample B"
378
- value={45}
379
- min={-180}
380
- max={180}
381
- unit={<sup>°</sup>}
382
- onChange={() => {}}
383
- />
384
- </PropertyList>
385
- </EffectCard>
386
- )}
387
- />
388
- );
389
- }
390
- return (
391
- <div style={{ width: 360, display: 'flex', flexDirection: 'column', gap: 8 }}>
392
- <Stack />
393
- </div>
394
- );
395
- },
396
- };
@@ -1,62 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { useState } from 'react';
3
- import { TextRow } from './PropertyPanel';
4
- import { SideBySide } from './storyLayouts';
5
-
6
- const meta: Meta<typeof TextRow> = {
7
- title: 'labkit/UI/Properties/Rows/TextRow',
8
- component: TextRow,
9
- };
10
- export default meta;
11
- type Story = StoryObj<typeof TextRow>;
12
-
13
- function Controlled({
14
- initial,
15
- label,
16
- placeholder,
17
- maxLength,
18
- layout,
19
- }: {
20
- initial: string;
21
- label: string;
22
- placeholder?: string;
23
- maxLength?: number;
24
- layout?: 'block' | 'inline';
25
- }) {
26
- const [value, setValue] = useState(initial);
27
- return (
28
- <TextRow
29
- label={label}
30
- value={value}
31
- onChange={setValue}
32
- placeholder={placeholder}
33
- maxLength={maxLength}
34
- layout={layout}
35
- />
36
- );
37
- }
38
-
39
- export const BothLayouts: Story = {
40
- render: () => (
41
- <SideBySide
42
- block={<Controlled initial="Untitled" label="Name" />}
43
- inline={<Controlled initial="Untitled" label="Name" layout="inline" />}
44
- />
45
- ),
46
- };
47
-
48
- export const Empty: Story = {
49
- render: () => (
50
- <div style={{ width: 280 }}>
51
- <Controlled initial="" label="Name" placeholder="Type a name…" />
52
- </div>
53
- ),
54
- };
55
-
56
- export const Capped: Story = {
57
- render: () => (
58
- <div style={{ width: 280 }}>
59
- <Controlled initial="ABCD" label="Tag" maxLength={4} />
60
- </div>
61
- ),
62
- };
@@ -1,57 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { useState } from 'react';
3
- import { ToggleRow } from './PropertyPanel';
4
- import { SideBySide } from './storyLayouts';
5
-
6
- const meta: Meta<typeof ToggleRow> = {
7
- title: 'labkit/UI/Properties/Rows/ToggleRow',
8
- component: ToggleRow,
9
- };
10
- export default meta;
11
- type Story = StoryObj<typeof ToggleRow>;
12
-
13
- const ALIGN_OPTIONS = [
14
- { value: 'left', label: 'L' },
15
- { value: 'center', label: 'C' },
16
- { value: 'right', label: 'R' },
17
- ] as const;
18
-
19
- function Align({ layout }: { layout?: 'block' | 'inline' }) {
20
- const [value, setValue] = useState<'left' | 'center' | 'right'>('center');
21
- return (
22
- <ToggleRow
23
- label="Align"
24
- value={value}
25
- onChange={setValue}
26
- options={ALIGN_OPTIONS}
27
- layout={layout}
28
- />
29
- );
30
- }
31
-
32
- export const BothLayouts: Story = {
33
- render: () => <SideBySide block={<Align />} inline={<Align layout="inline" />} />,
34
- };
35
-
36
- function TwoOptions() {
37
- const [value, setValue] = useState<'on' | 'off'>('on');
38
- return (
39
- <ToggleRow
40
- label="Snapping"
41
- value={value}
42
- onChange={setValue}
43
- options={[
44
- { value: 'on', label: 'On' },
45
- { value: 'off', label: 'Off' },
46
- ]}
47
- />
48
- );
49
- }
50
-
51
- export const TwoOption: Story = {
52
- render: () => (
53
- <div style={{ width: 280 }}>
54
- <TwoOptions />
55
- </div>
56
- ),
57
- };
@@ -1,39 +0,0 @@
1
- export type { CurveFieldProps, CurveMark } from './CurveField';
2
- export { CurveField } from './CurveField';
3
- export type {
4
- EffectCardListItem,
5
- EffectCardListProps,
6
- EffectCardProps,
7
- SubpanelProps,
8
- } from './EffectCard';
9
- export { EffectCard, EffectCardList, Subpanel } from './EffectCard';
10
- export type { PropertyGroupProps } from './PropertyGroup';
11
- export { PropertyGroup } from './PropertyGroup';
12
- export type {
13
- CheckboxRowProps,
14
- ColorRowProps,
15
- NumberRowProps,
16
- PropertyListPack,
17
- PropertyListProps,
18
- PropertyPanelProps,
19
- PropertyRowLayout,
20
- PropertyRowProps,
21
- PropertyRowVariant,
22
- SelectOption,
23
- SelectRowProps,
24
- SliderRowProps,
25
- TextRowProps,
26
- ToggleRowProps,
27
- } from './PropertyPanel';
28
- export {
29
- CheckboxRow,
30
- ColorRow,
31
- NumberRow,
32
- PropertyList,
33
- PropertyPanel,
34
- PropertyRow,
35
- SelectRow,
36
- SliderRow,
37
- TextRow,
38
- ToggleRow,
39
- } from './PropertyPanel';
@@ -1,36 +0,0 @@
1
- import type { ReactNode } from 'react';
2
-
3
- /**
4
- * Side-by-side comparison used by row stories to show block + inline layouts
5
- * at the same time. Not a stories file — name has no .stories. suffix so
6
- * Storybook's glob skips it.
7
- */
8
- export function SideBySide({ block, inline }: { block: ReactNode; inline: ReactNode }) {
9
- const labelStyle = {
10
- font: '300 0.72rem/1 Oswald, system-ui',
11
- color: 'var(--wzl-fg-muted)',
12
- textTransform: 'uppercase' as const,
13
- letterSpacing: '0.08em',
14
- margin: '0 0 8px',
15
- };
16
- return (
17
- <div
18
- style={{
19
- display: 'grid',
20
- gridTemplateColumns: '1fr 1fr',
21
- gap: 24,
22
- width: 720,
23
- alignItems: 'start',
24
- }}
25
- >
26
- <div>
27
- <div style={labelStyle}>Block</div>
28
- {block}
29
- </div>
30
- <div>
31
- <div style={labelStyle}>Inline</div>
32
- {inline}
33
- </div>
34
- </div>
35
- );
36
- }