@rogieking/figui3 8.9.44 → 8.9.46

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.
@@ -0,0 +1,371 @@
1
+ # FigUI3 lab components (`fig-lab.js`)
2
+
3
+ Experimental. APIs may change. React contract: [../figui3/react.md](../figui3/react.md). Attrs: [reference.md](reference.md).
4
+
5
+ Install `fig-lab.css` + `fig-lab.js`. `propskit-select` prefers `fig-select` — also import `fig-editor.js` + `fig-editor.css` for rich menus. `fig-editor.js` already imports `fig-lab.js`; lab **CSS** is still required.
6
+
7
+ Handlers below assume `onInput` / `onChange` from the React contract.
8
+
9
+ ## Shared propskit
10
+
11
+ Full-surface `fig-field` wrappers. Prefer these over hand-rolled label+control rows.
12
+
13
+ Shared attrs: `label`, `direction` (`horizontal` default), `size` (`""` | `small`; `large` is an alias for default), `disabled`, `variant="minimal"` (no vertical padding; field background on hover), `default` (reset target, may differ from initial `value`).
14
+
15
+ Right-click **Reset**; `resetToDefault()` on a ref. `propskit-slider` also double-click resets. Remaining attrs forward to the inner control. Rows are large by default; `propskit-group size="small"` applies compact sizing to children without an authored size.
16
+
17
+ ```tsx
18
+ const rowRef = useRef<HTMLElement>(null);
19
+ rowRef.current?.resetToDefault();
20
+ ```
21
+
22
+ ## Propskit
23
+
24
+ ### `propskit-switch`
25
+
26
+ ```tsx
27
+ <propskit-switch
28
+ label="Visible"
29
+ checked={on ? "true" : undefined}
30
+ default="true"
31
+ onInput={onInput}
32
+ />
33
+ ```
34
+
35
+ - Inner: `fig-switch`. `checked` / `default` boolean.
36
+
37
+ ### `propskit-color`
38
+
39
+ ```tsx
40
+ <propskit-color
41
+ label="Fill"
42
+ value="#0D99FF"
43
+ alpha="true"
44
+ onInput={onInput}
45
+ onChange={onChange}
46
+ />
47
+ ```
48
+
49
+ - Inner: `fig-fill-picker` + `fig-swatch`. Clicking the field opens the picker anchored to the host. Focus ring on the field, not the swatch.
50
+
51
+ ### `propskit-fill`
52
+
53
+ ```tsx
54
+ <propskit-fill
55
+ label="Fill"
56
+ value='{"type":"solid","color":"#0D99FF"}'
57
+ onInput={onInput}
58
+ onChange={onChange}
59
+ >
60
+ <div slot="mode-shader" label="Shader">
61
+ Shader UI
62
+ </div>
63
+ </propskit-fill>
64
+ ```
65
+
66
+ - Same chrome as color. Value may be fill JSON or a bare video URL. Forwards `mode-*` slots.
67
+
68
+ ### `propskit-gradient`
69
+
70
+ ```tsx
71
+ <propskit-gradient
72
+ label="Gradient"
73
+ value={gradientJson}
74
+ edit="picker"
75
+ onInput={onInput}
76
+ />
77
+ ```
78
+
79
+ - Inner: `fig-input-gradient`. Default `edit="picker"`. `mode="handle|tip"` for inline edit. Clicking the field opens the picker anchored to the host.
80
+
81
+ ### `propskit-select`
82
+
83
+ ```tsx
84
+ <propskit-select
85
+ label="Blend"
86
+ value="multiply"
87
+ options="Normal,Multiply,Screen"
88
+ onChange={onChange}
89
+ />
90
+ ```
91
+
92
+ Rich options (requires editor):
93
+
94
+ ```tsx
95
+ <propskit-select label="Space" value="oklab" onChange={onChange}>
96
+ <fig-select-options slot="panel">
97
+ <fig-select-option value="srgb" label="Classic">
98
+ Classic
99
+ </fig-select-option>
100
+ </fig-select-options>
101
+ </propskit-select>
102
+ ```
103
+
104
+ - Inner: `fig-select` when registered; otherwise a fallback. Authored `fig-select-options slot="panel"` wins. Options stay in light DOM.
105
+
106
+ ### `propskit-text`
107
+
108
+ ```tsx
109
+ <propskit-text
110
+ label="Name"
111
+ value={name}
112
+ onInput={onInput}
113
+ onChange={onChange}
114
+ />
115
+ ```
116
+
117
+ - Inner: `fig-input-text`. `type`, `readonly`.
118
+
119
+ ### `propskit-number`
120
+
121
+ ```tsx
122
+ <propskit-number
123
+ label="Size"
124
+ value="16"
125
+ min="0"
126
+ max="100"
127
+ step="1"
128
+ units="px"
129
+ steppers="true"
130
+ onInput={onInput}
131
+ />
132
+ ```
133
+
134
+ - Inner: `fig-input-number`.
135
+
136
+ ### `propskit-slider`
137
+
138
+ ```tsx
139
+ <propskit-slider
140
+ label="Opacity"
141
+ direction="horizontal"
142
+ type="opacity"
143
+ value="100"
144
+ default="100"
145
+ min="0"
146
+ max="100"
147
+ units="%"
148
+ onInput={onInput}
149
+ onChange={onChange}
150
+ />
151
+ ```
152
+
153
+ - Inner: `fig-slider`. `type` range/hue/delta/stepper/opacity; `elastic` default true. Forward `min` / `max` / `step` / `value`.
154
+
155
+ ### `propskit-wheel`
156
+
157
+ ```tsx
158
+ <propskit-wheel
159
+ label="Duration"
160
+ value="1.5"
161
+ units="seconds"
162
+ onInput={onInput}
163
+ />
164
+ ```
165
+
166
+ - Inner: `fig-input-wheel` + optional `fig-input-number`. Attrs: `label`, `text`, `spin`, `elastic` (row stretch, default true), `precision`, `units`, `default`, `size`, `variant`. Units stay on the wrapper; effective step is applied to the wheel. `spin="false"` updates value without moving ticks.
167
+
168
+ ### `propskit-position`
169
+
170
+ ```tsx
171
+ <propskit-position label="Position" x="50" y="50" units="percent" onInput={onInput} />
172
+ ```
173
+
174
+ - Two numbers. Attrs: `x`, `y`, `units`.
175
+
176
+ ### `propskit-color-point`
177
+
178
+ ```tsx
179
+ <propskit-color-point
180
+ label="Stop"
181
+ value={json}
182
+ collapsible
183
+ open
184
+ onInput={onInput}
185
+ />
186
+ ```
187
+
188
+ - Color + position JSON. `collapsible` / `open` default true (string booleans).
189
+
190
+ ### `propskit-point-radius`
191
+
192
+ ```tsx
193
+ <propskit-point-radius label="Spot" value='{"x":50,"y":50,"radius":60}' onInput={onInput} />
194
+ ```
195
+
196
+ ### `propskit-point-radius-angle`
197
+
198
+ ```tsx
199
+ <propskit-point-radius-angle
200
+ label="Light"
201
+ value='{"x":50,"y":50,"radius":60,"angle":45}'
202
+ onInput={onInput}
203
+ />
204
+ ```
205
+
206
+ ### `propskit-point-point`
207
+
208
+ ```tsx
209
+ <propskit-point-point
210
+ label="Line"
211
+ value='{"x":10,"y":10,"x2":90,"y2":90}'
212
+ onInput={onInput}
213
+ />
214
+ ```
215
+
216
+ ### `propskit-group`
217
+
218
+ ```tsx
219
+ <propskit-group name="Appearance" open show-reset>
220
+ <propskit-slider label="Opacity" value="100" min="0" max="100" />
221
+ </propskit-group>
222
+ ```
223
+
224
+ - Attrs: `name`, `open`, `show-reset`, `size`. Children are React nodes.
225
+
226
+ ### `propskit-oscillator`
227
+
228
+ ```tsx
229
+ <propskit-oscillator
230
+ label="Wave"
231
+ value={wavesJson}
232
+ edit
233
+ onInput={onInput}
234
+ />
235
+ ```
236
+
237
+ - JSON `waves`. Attrs: `edit`, `precision`, `aspect-ratio`, `disabled`.
238
+
239
+ ## Spatial
240
+
241
+ ### `fig-canvas-control`
242
+
243
+ ```tsx
244
+ <div style={{ position: "relative", aspectRatio: "1", width: "100%" }}>
245
+ <fig-canvas-control
246
+ type="point-radius-angle"
247
+ name="Position"
248
+ value='{"x":50,"y":50,"radius":60,"angle":45}'
249
+ snapping="modifier"
250
+ onInput={onInput}
251
+ onChange={onChange}
252
+ />
253
+ </div>
254
+ ```
255
+
256
+ - Parent must be positioned. Types: `point`, `color`, `point-radius`, `point-radius-angle`, `point-point`.
257
+ - Attrs: `type`, `value` (JSON string), `color`, `name`, `tooltips`, `disabled`, `drag-surface`, `snapping` (`false` | `modifier` | `true`)
258
+ - React: JSON `value` as a string attr; do not remount during drag.
259
+
260
+ ### `fig-input-angle`
261
+
262
+ ```tsx
263
+ <fig-input-angle
264
+ value={String(deg)}
265
+ text="true"
266
+ units="°"
267
+ onInput={onInput}
268
+ />
269
+ ```
270
+
271
+ - Attrs: `value`, `precision`, `text`, `min`, `max`, `units`, `dial` (default true), `rotations`, `disabled`
272
+
273
+ ### `fig-input-wheel`
274
+
275
+ ```tsx
276
+ <fig-input-wheel value="50" min="0" max="100" step="1" onInput={onInput} />
277
+ ```
278
+
279
+ - Standalone SVG tick + handle. Attrs: `value` (default `0`), `step` (default `1`), `min`, `max`, `spin` (default true), `disabled`. No `units` / `text` / `label` / reset — use `propskit-wheel` for those.
280
+ - Methods: `focus()`, `spinTo(value)`, `beginScrub()`, `updateScrub()`, `endScrub()`.
281
+
282
+ ### `fig-reorder`
283
+
284
+ ```tsx
285
+ <fig-reorder axis="vertical" handle=".drag">
286
+ {items.map((item) => (
287
+ <div key={item.id} className="row">
288
+ <button className="drag" type="button" aria-label="Reorder" />
289
+ {item.label}
290
+ </div>
291
+ ))}
292
+ </fig-reorder>
293
+ ```
294
+
295
+ Listen for `reorder` on a ref (`onReorder` is not a React-mapped event):
296
+
297
+ ```tsx
298
+ useEffect(() => {
299
+ const el = ref.current;
300
+ if (!el) return;
301
+ const handler = (event: Event) => {
302
+ const { oldIndex, newIndex, item } = (event as CustomEvent).detail;
303
+ onReorder({ oldIndex, newIndex, item });
304
+ };
305
+ el.addEventListener("reorder", handler);
306
+ return () => el.removeEventListener("reorder", handler);
307
+ }, [onReorder]);
308
+ ```
309
+
310
+ - `display: contents`. Reorders **direct children**. `axis` `vertical` | `horizontal`. Omit `handle` to drag whole rows; set it when rows contain sliders/handles.
311
+ - Event `reorder`: `{ oldIndex, newIndex, item }`. Nested drag is ignored for sliders, handles, canvas, and most propskit spatial controls.
312
+
313
+ ## AI composer
314
+
315
+ Layout shells. Wire behavior yourself. Children are React nodes.
316
+
317
+ ### `fig-ai-prompt`
318
+
319
+ ```tsx
320
+ <fig-ai-prompt>
321
+ <fig-input-text multiline placeholder="Describe a change" />
322
+ <fig-footer>
323
+ <fig-button>Send</fig-button>
324
+ </fig-footer>
325
+ </fig-ai-prompt>
326
+ ```
327
+
328
+ ### `fig-ai-context`
329
+
330
+ ```tsx
331
+ <fig-ai-context>
332
+ <fig-attachments>{attachments}</fig-attachments>
333
+ </fig-ai-context>
334
+ ```
335
+
336
+ Open area above the prompt for attachments/status.
337
+
338
+ ### `fig-chat-message`
339
+
340
+ ```tsx
341
+ <fig-chat-message from="user">
342
+ Create a settings panel.
343
+ <fig-attachments aria-label="Message attachments">
344
+ <fig-attachment
345
+ value="settings"
346
+ name="settings.png"
347
+ src={src}
348
+ removable="false"
349
+ />
350
+ </fig-attachments>
351
+ <fig-avatar name="Rogie King" />
352
+ </fig-chat-message>
353
+ <fig-chat-message from="agent">
354
+ <fig-shimmer>
355
+ <span>Thinking…</span>
356
+ </fig-shimmer>
357
+ </fig-chat-message>
358
+ ```
359
+
360
+ - Attrs: `from` (`user` | `agent`)
361
+
362
+ ### `fig-attachments` / `fig-attachment`
363
+
364
+ ```tsx
365
+ <fig-attachments>
366
+ <fig-attachment src={src} name="file.png" value="file" />
367
+ </fig-attachments>
368
+ ```
369
+
370
+ - Attachment attrs: `src`, `name`, `value`, `removable` (default true), `disabled`
371
+ - Events: `remove` — listen natively if React does not map it.
@@ -1,6 +1,6 @@
1
1
  # FigUI3 lab API reference
2
2
 
3
- Playground: `/propskit/lab#{id}`. Rules: `playground/src/lib/attributeRules.ts`.
3
+ React recipes: [components.md](components.md).
4
4
 
5
5
  ## Propskit reset
6
6
 
@@ -14,6 +14,8 @@ Playground: `/propskit/lab#{id}`. Rules: `playground/src/lib/attributeRules.ts`.
14
14
 
15
15
  `variant="minimal"` is available on switch, color, fill, gradient, select, text, number, slider, position, and wheel controls. It removes vertical host padding, keeps the inner field transparent at rest, and restores the secondary field background on hover.
16
16
 
17
+ ## Propskit sizes
18
+
17
19
  PropsKit rows are large by default; explicit `size="large"` remains supported.
18
20
  `propskit-group size="small"` applies `size="small"` to nested controls that do
19
21
  not define their own size and removes generated sizes when the group returns to
@@ -29,7 +31,7 @@ Options attr: JSON array, comma, or newline. Authored `fig-select-options slot="
29
31
 
30
32
  ## `propskit-slider`
31
33
 
32
- Playground attrs: `type` (`range`, `hue`, `delta`, `stepper`, `opacity`), `color`, `label`, `default`, `units`, `elastic` (default true), `size`, `steppers`, `disabled`.
34
+ Attrs: `type` (`range`, `hue`, `delta`, `stepper`, `opacity`), `color`, `label`, `default`, `units`, `elastic` (default true), `size`, `steppers`, `disabled`.
33
35
 
34
36
  Inner `fig-slider` still needs `min` / `max` / `step` / `value` as forwarded attrs.
35
37
 
@@ -75,7 +77,7 @@ Collapsible point groups: `collapsible` and `open` default true (string booleans
75
77
 
76
78
  Observed: `type`, `value`, `color`, `name`, `tooltips`, `disabled`, `drag-surface`, `snapping`.
77
79
 
78
- Parent must be `position: relative` (or similar) so the control can fill it. Playground wraps in an aspect-ratio box.
80
+ Parent must be `position: relative` (or similar) so the control can fill it. Wrap in an aspect-ratio box.
79
81
 
80
82
  Types: `point`, `color`, `point-radius`, `point-radius-angle`, `point-point`.
81
83
 
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: figui3
3
3
  description: >-
4
- Guides FigUI3 core (`fig.js` / `fig.css`) web components for Figma-style plugin UIs.
5
- Use when adding, using, or debugging fig-* elements from the core bundle—buttons,
6
- fields, overlays, menus, sliders, color/fill inputs, media, dialogs, popups, toasts—or
7
- when working in the /figui3 playground. Not for fig-select or fig-fill-picker
8
- (fig-editor), propskit-* / AI / canvas / angle / wheel / reorder (fig-lab), or fig-layer.
4
+ Guides FigUI3 core (`fig.js` / `fig.css`) web components for Figma-style plugin UIs,
5
+ including React JSX usage. Use when adding, using, or debugging fig-* elements from
6
+ the core bundle—buttons, fields, overlays, menus, sliders, color/fill inputs, media,
7
+ dialogs, popups, toasts. Not for fig-select or fig-fill-picker (fig-editor),
8
+ propskit-* / AI / canvas / angle / wheel / reorder (fig-lab), or fig-layer.
9
9
  user-invocable: false
10
10
  ---
11
11
 
@@ -13,19 +13,13 @@ user-invocable: false
13
13
 
14
14
  Zero-dependency web components for Figma UI3 plugin and widget UIs.
15
15
 
16
- Canonical examples live in the **playground**, not `index.html`.
16
+ Public API: `README.md`. React: [react.md](react.md). Per-tag JSX: [components.md](components.md). Attrs: [reference.md](reference.md).
17
17
 
18
- - Live: https://rog.ie/figui3/
19
- - Local: `npm run dev:playground` → `/figui3`
20
- - Sections: `playground/src/data/figui3Sections.ts`
21
- - Attribute inspector: `playground/src/lib/attributeRules.ts`
22
- - Public API: `README.md`
23
-
24
- Related skills: `fig-editor` (`fig-select`, `fig-fill-picker`), `fig-lab` (`propskit-*`, AI, canvas), `propkit` (`/propskit` field composition).
18
+ Related skills: `fig-editor` (`fig-select`, `fig-fill-picker`), `fig-lab` (`propskit-*`, AI, canvas), `propkit` (property-row composition).
25
19
 
26
20
  ## Bundles
27
21
 
28
- Always import CSS with JS. Register before first render.
22
+ Always import CSS with JS. Register before first render. In React, see [react.md](react.md).
29
23
 
30
24
  ```js
31
25
  import "@rogieking/figui3/fig.css";
@@ -34,14 +28,12 @@ import "@rogieking/figui3/fig.js";
34
28
 
35
29
  | Bundle | CSS + JS | Components |
36
30
  |---|---|---|
37
- | **Core** (this skill) | `fig.css` + `fig.js` | All `fig-*` below |
31
+ | **Core** (this skill) | `fig.css` + `fig.js` | All `fig-*` in [components.md](components.md) |
38
32
  | **Editor** | `fig-editor.css` + `fig-editor.js` | `fig-select*`, `fig-fill-picker`, `fig-interpolation-swatch` |
39
33
  | **Lab** (unstable) | `fig-lab.css` + `fig-lab.js` | `propskit-*`, `fig-ai-*`, `fig-canvas-control`, `fig-input-angle`, `fig-input-wheel`, `fig-reorder` |
40
34
  | **Layer** | `fig-layer.css` + `fig-layer.js` | `fig-layer` |
41
35
 
42
- `fig-editor.js` also imports `fig.js` and `fig-lab.js`. Lab CSS is still separate. `fig-layer` is **not** registered by `fig-editor.js`.
43
-
44
- Playground “Full editor” toggle reveals `#select`, `#fill-picker`, `#layer`, and `#toast`. That grouping is UI-only: toast is core; layer is `fig-layer.js`.
36
+ `fig-editor.js` also imports `fig.js` and `fig-lab.js`. Lab CSS is still separate. `fig-layer` is **not** registered by `fig-editor.js`. Toast is core; layer is `fig-layer.js`.
45
37
 
46
38
  ## Principles
47
39
 
@@ -49,25 +41,7 @@ Playground “Full editor” toggle reveals `#select`, `#fill-picker`, `#layer`,
49
41
  2. Use design tokens (`--figma-color-*`, `--radius-*`, `--spacer-*`). Do not hardcode Figma colors.
50
42
  3. Emit `input` while interacting and `change` on commit. Do not fire `input` from programmatic attribute writes.
51
43
  4. Preserve a11y: labels, keyboard, ARIA, disabled. See the `a11y` skill.
52
- 5. Keep components framework-agnostic. No React internals.
53
-
54
- ## React + Vite
55
-
56
- ```tsx
57
- import "@rogieking/figui3/fig.css";
58
-
59
- const bootstrap = async () => {
60
- await import("@rogieking/figui3/fig.js");
61
- createRoot(document.getElementById("app")!).render(<App />);
62
- };
63
- bootstrap();
64
- ```
65
-
66
- - Use DOM attrs (`text="true"`). Read values from `e.target` / `e.detail`.
67
- - On `fig-*` and `<dialog is="fig-...">`, use `class` not `className`.
68
- - Prefer refs + `addEventListener` for `input`/`change`.
69
-
70
- Color picker modes: `fig-fill-picker` is optional editor. Do not use `picker` / `picker-anchor` on `fig-input-color`. `picker-*` attrs forward to the picker only when it is registered. See `fig-editor`.
44
+ 5. Keep components framework-agnostic. No React internals in `fig.js`.
71
45
 
72
46
  ## Overlay rules
73
47
 
@@ -76,34 +50,28 @@ Color picker modes: `fig-fill-picker` is optional editor. Do not use `picker` /
76
50
  - `<dialog is="fig-toast">` — call `showToast()`. `theme`, `duration`, `live`, `dismiss`, `icon`.
77
51
  - `fig-menu` and `fig-select` use `popover="manual"` so lists escape filter-containing popups to the top layer. Nested menus inside popovers must keep that. `fig-menu` slots items (does not relocate them); triggers get `slot="trigger"`.
78
52
 
79
- ```html
53
+ ```tsx
80
54
  <dialog is="fig-dialog" drag handle="fig-header">
81
55
  <fig-header>
82
56
  Title
83
57
  <fig-button variant="ghost" icon close-dialog aria-label="Close">
84
- <fig-icon name="close"></fig-icon>
58
+ <fig-icon name="close" />
85
59
  </fig-button>
86
60
  </fig-header>
87
61
  <fig-content>Body</fig-content>
88
62
  </dialog>
89
-
90
- <dialog is="fig-popup" anchor="#trigger" position="bottom left" offset="8 8">
91
- Popup content
92
- </dialog>
93
63
  ```
94
64
 
95
65
  ## Field composition
96
66
 
97
- Default property row:
98
-
99
- ```html
67
+ ```tsx
100
68
  <fig-field direction="horizontal">
101
69
  <label>Opacity</label>
102
- <fig-slider value="75" min="0" max="100" text="true" units="%" full></fig-slider>
70
+ <fig-slider value="75" min="0" max="100" text="true" units="%" full />
103
71
  </fig-field>
104
72
  ```
105
73
 
106
- Labeled property wrappers (`propskit-*`) are lab. For `/propskit` playground patterns, use the `propkit` skill.
74
+ Labeled property wrappers (`propskit-*`) are lab. See the `propkit` and `fig-lab` skills.
107
75
 
108
76
  ## Select vs dropdown
109
77
 
@@ -115,100 +83,23 @@ Labeled property wrappers (`propskit-*`) are lab. For `/propskit` playground pat
115
83
 
116
84
  Prefer `fig-select` for Figma-style menus. Use `fig-dropdown` only for a native select.
117
85
 
118
- ## Core catalog
119
-
120
- Playground hashes: `/figui3#{id}`. Full attrs: [reference.md](reference.md).
121
-
122
- ### Buttons and inputs
123
-
124
- | Tag | Playground | Notes |
125
- |---|---|---|
126
- | `fig-button` | `#button` | `variant`: secondary, ghost, link, destructive*, overlay, input. `type`: button, toggle, submit, select, upload. `size`, `icon`, `selected` |
127
- | `fig-dropdown` | `#dropdown` | Native select. Options as `<option>` / `<optgroup>`. `variant="ghost"` |
128
- | `fig-combo-input` | `#combo-input` | Text + suggestions (`options`) |
129
- | `fig-input-text` | `#text-input` | `multiline` for textarea |
130
- | `fig-input-number` | `#number-input` | `min`, `max`, `step`, `units`, `precision` |
131
- | `fig-input-file` | `#file-input` | `accepts`, `multiple`, button `variant` |
132
- | `fig-checkbox` / `fig-radio` / `fig-switch` | `#checkbox` `#radio` `#switch` | Switch supports `indeterminate` |
133
- | `fig-slider` | `#slider` | `type`: range, opacity, hue, stepper, delta. `text`, `units`, `transform`, `variant="classic"` |
134
- | `fig-options` | (propkit `#options`) | Option list helper; same option string formats as select |
135
-
136
- ### Color and fill (no picker dialog)
137
-
138
- | Tag | Playground | Notes |
139
- |---|---|---|
140
- | `fig-input-color` | (propkit `#color`) | Solid color. `text`, `alpha`. Auto-detects `fig-fill-picker` |
141
- | `fig-input-fill` | `#fill-input` | Solid/gradient/image/video/webcam/custom JSON `value`. Custom `mode-*` slots forward to the inner picker and use image-style chrome. Same `webcam` / `video.poster` shape as the picker. `webcam-mode`, `default-video`, `picker-*` forwarded if picker registered |
142
- | `fig-input-palette` | (propkit `#palette`) | Multi-color. `fixed`, `open` |
143
- | `fig-input-gradient` | (propkit `#gradient`) | Stops. `edit`, `mode="handle\|tip"` |
144
- | `fig-swatch` | `#swatch` | `size`, `selected`, `alpha` |
145
- | `fig-color-tip` | `#color-tip` | `control="color\|add\|remove"` |
146
- | `fig-chit` | — | Alias-style color chip |
147
-
148
- ### Layout and chrome
149
-
150
- | Tag | Playground | Notes |
151
- |---|---|---|
152
- | `fig-field` | `#field` | `direction="horizontal\|vertical"`, `label` |
153
- | `fig-group` | (containers) | `name`, `collapsible`, `open`, `compact` |
154
- | `fig-header` / `fig-footer` / `fig-content` | (containers) | Header: `borderless`, `compact`. Footer: `sticky` |
155
- | `fig-tabs` / `fig-tab` | `#tabs` | Roving tabs. `content="#id"` for panels |
156
- | `fig-segmented-control` / `fig-segment` | `#segmented-control` | Radio-group pattern |
157
- | `fig-chooser` / `fig-choice` | `#chooser` | Listbox. Omit `value` to select first; `value=""` means none. |
158
- | `fig-separator` / `fig-menu-separator` | `#separator` | Optional `label`, `sticky`, `borderless` |
159
- | `fig-menu` / `fig-menu-item` | `#menu` | `fig-menu-trigger`, `trigger="contextmenu"`, `position`, `offset`. Item also works in `fig-popup` (sticky separators, nested row menus). |
160
- | `fig-icon` | `#icon` | Token mask (`name`, `size="small"`, `color`) |
161
- | `fig-avatar` | `#avatar` | `src` / `name`, `size="large"` |
162
- | `fig-truncate` | `#truncate` | `position="right\|left\|middle"`, `tooltip`, `tail` |
163
-
164
- ### Overlays
165
-
166
- | Tag | Playground | Notes |
167
- |---|---|---|
168
- | `dialog is="fig-dialog"` | `#dialog` | `modal`, `drag`, `resizable`, `autoresize`, `handle`, `closedby`, `position` |
169
- | `dialog is="fig-popup"` | `#popup` | `anchor`, `position`, `offset`, `viewport-margin`, `variant`, `theme` |
170
- | `dialog is="fig-toast"` | `#toast` | `showToast()`. `theme`, `duration`, `live`, `dismiss`, `icon` |
171
- | `fig-tooltip` | `#tooltip` | `text`, `action="hover\|click\|manual"`, `delay`, `theme` |
172
-
173
- ### Media
174
-
175
- | Tag | Playground | Notes |
176
- |---|---|---|
177
- | `fig-preview` | (propkit `#preview`) | `aspect-ratio`, `fit`, `full`, `checkerboard` |
178
- | `fig-media` / `fig-image` / `fig-video` | `#media` `#image` `#video` | Upload via `upload`. Video controls below preview |
179
- | `fig-card` | `#card` | Media + label + selection |
180
- | `fig-media-controls` | `#media-controls` | Play/pause chrome |
181
- | `fig-input-file` | `#file-input` | File picker button |
182
-
183
- ### Specialized
184
-
185
- | Tag | Playground | Notes |
186
- |---|---|---|
187
- | `fig-easing-curve` | (propkit `#easing`) | Bezier/spring |
188
- | `fig-3d-rotate` | (containers) | Cube rotate |
189
- | `fig-origin-grid` | (propkit) | Transform origin |
190
- | `fig-joystick` | (propkit `#joystick`) | 2D position |
191
- | `fig-handle` | `#handle` | `type="default\|minimal\|color\|canvas"`, `drag`, `drag-snapping` |
192
- | `fig-spinner` / `fig-shimmer` / `fig-skeleton` | `#spinner` `#shimmer` | Loading |
193
-
194
- `fig-input-angle` and `fig-input-wheel` are **lab**, not core.
195
-
196
86
  ## Events
197
87
 
198
88
  ```txt
199
- fig-slider input/change → e.target.value
89
+ fig-slider input/change → e.currentTarget.value
200
90
  fig-input-color input/change → detail { color, alpha, opacity } plus legacy value/hex/rgba
201
91
  fig-input-fill input/change → fill payload in e.detail
202
92
  fig-menu change → detail { value }
203
93
  fig-dialog/popup native dialog close plus FigUI3 positioning attrs
204
94
  ```
205
95
 
96
+ `fig-input-angle` and `fig-input-wheel` are **lab**, not core.
97
+
206
98
  ## Maintainer workflow
207
99
 
208
100
  1. Read `fig.js` + `components.css` before editing.
209
- 2. Mirror playground examples in `figui3Sections.ts` and `attributeRules.ts`.
210
- 3. Update `README.md` + `CHANGELOG.md` for public API changes.
211
- 4. `bun build` for dist. Never kill `npm run dev:playground`.
212
- 5. Tests: `npm run test:components` (Playwright). Do not start a second playground if one is running.
101
+ 2. Update `README.md` + `CHANGELOG.md` for public API changes.
102
+ 3. `bun build` for dist.
103
+ 4. Tests: `npm run test:components` (Playwright).
213
104
 
214
- Primary files: `fig.js`, `components.css`, `base.css`, `README.md`, `playground/src/data/figui3Sections.ts`.
105
+ Primary files: `fig.js`, `components.css`, `base.css`, `README.md`.