@rogieking/figui3 6.25.0 → 6.27.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.
@@ -87,6 +87,8 @@ export default defineConfig({
87
87
  - Preserve value shape expectations:
88
88
  - `fig-input-color` expects solid color data (`detail.color`, optional `detail.alpha`) from the picker.
89
89
  - `fig-fill-picker` custom modes use JSON with `type` set to mode name and remaining data in payload.
90
+ - Direct color events expose additive `{ color, alpha, opacity }` aliases: opaque `#RRGGBB`, `0–1`, and `0–100`, respectively.
91
+ - Keep `fig-input-color`'s legacy `value`, `hex`, and `rgba` event-detail fields unchanged; consume the aliases for a shared contract.
90
92
 
91
93
  ## Critical Rules
92
94
 
@@ -180,7 +182,7 @@ attributeChangedCallback(name, oldValue, newValue) {
180
182
  ```txt
181
183
  Event contract quick map:
182
184
  - fig-slider: input/change -> current value on e.target.value
183
- - fig-input-color: input/change -> value on e.target.value, structured color via e.detail (when available)
185
+ - fig-input-color: input/change -> legacy value/hex/rgba plus color/alpha/opacity aliases in e.detail
184
186
  - fig-input-fill / fig-fill-picker: input/change -> fill payload in e.detail
185
187
  ```
186
188
 
@@ -113,6 +113,8 @@ bootstrap();
113
113
  - Default variant for most property panels.
114
114
  - `variant="classic"` only when the previous slider appearance is needed.
115
115
  - Always set explicit `min`, `max`, and `step` (and `units` where applicable) to keep behavior predictable.
116
+ - Set `default` on PropsKit value controls when reset behavior should differ from the initial value.
117
+ - PropsKit value controls support `resetToDefault()` and a right-click **Reset** menu; `propskit-slider` also supports double-click reset.
116
118
 
117
119
  ### Control Selection Heuristics
118
120
 
package/README.md CHANGED
@@ -306,15 +306,22 @@ Composes a `<fig-field>` and `<fig-input-number>` into a full-surface property c
306
306
 
307
307
  PropsKit controls that support `size` default to the large layout when the attribute is omitted.
308
308
 
309
+ All PropsKit inputs expose `defaultValue`, `isDefault`, and `resetToDefault()`. A
310
+ `propskit-group` uses this shared contract to track its `dirty` state and reset
311
+ each nested input to its own current `default`.
312
+
309
313
  | Attribute | Type | Default | Description |
310
314
  |---|---|---|---|
311
315
  | `label` | string | `"Label"` | Field label text; use an empty value to hide it |
312
316
  | `direction` | string | `"horizontal"` | Field layout direction |
313
317
  | `size` | string | `"large"` | Control layout size |
318
+ | `default` | number/string | initial `value` | Right-click reset target |
314
319
  | *number attrs* | — | — | All `<fig-input-number>` attributes are forwarded |
315
320
 
316
321
  **Events:** `input`, `change` — forwarded from the inner number input.
317
322
 
323
+ Right-click and choose **Reset**, or call `resetToDefault()`, to restore `default` (falling back to the initial value).
324
+
318
325
  ```html
319
326
  <propskit-number label="Width" value="24" min="0" max="100" units="px"></propskit-number>
320
327
  ```
@@ -327,10 +334,12 @@ PropsKit controls that support `size` default to the large layout when the attri
327
334
 
328
335
  Composes a `<fig-field>` and `<fig-input-color>` into a full-surface property control. Color attributes are forwarded to the inner input and text editing remains enabled.
329
336
 
330
- **Attributes:** `label`, `value`, `alpha`, `disabled`, `size`
337
+ **Attributes:** `label`, `value`, `default`, `alpha`, `disabled`, `size`
331
338
 
332
339
  **Events:** `input`, `change` — forwarded from the inner color input.
333
340
 
341
+ Right-click and choose **Reset**, or call `resetToDefault()`, to restore `default` or the initial color.
342
+
334
343
  ```html
335
344
  <propskit-color label="Fill" value="#0D99FF" alpha="true"></propskit-color>
336
345
  ```
@@ -343,10 +352,12 @@ Composes a `<fig-field>` and `<fig-input-color>` into a full-surface property co
343
352
 
344
353
  Composes a `<fig-field>` and an Off/On `<fig-segmented-control>` into a full-surface boolean property control.
345
354
 
346
- **Attributes:** `label`, `checked`, `disabled`, `name`, `value`, `size`
355
+ **Attributes:** `label`, `checked`, `default`, `disabled`, `name`, `value`, `size`
347
356
 
348
357
  **Events:** `input`, `change` — forwarded from the inner switch.
349
358
 
359
+ Right-click and choose **Reset**, or call `resetToDefault()`, to restore the default checked state.
360
+
350
361
  ```html
351
362
  <propskit-switch label="Visible" checked></propskit-switch>
352
363
  ```
@@ -359,10 +370,12 @@ Composes a `<fig-field>` and an Off/On `<fig-segmented-control>` into a full-sur
359
370
 
360
371
  Composes a `<fig-field>` and `<fig-select>` into a full-surface property control. Options come from the `options` attribute (same formats as `fig-options`: comma-separated, newline-delimited, or a JSON array).
361
372
 
362
- **Attributes:** `label`, `value`, `options`, `disabled`, `size`
373
+ **Attributes:** `label`, `value`, `default`, `options`, `disabled`, `size`
363
374
 
364
375
  **Events:** `input`, `change` — forwarded from the inner select.
365
376
 
377
+ Right-click and choose **Reset**, or call `resetToDefault()`, to restore `default` or the initial selection.
378
+
366
379
  ```html
367
380
  <propskit-select label="Alignment" value="Center" options="Left,Center,Right"></propskit-select>
368
381
  ```
@@ -375,10 +388,12 @@ Composes a `<fig-field>` and `<fig-select>` into a full-surface property control
375
388
 
376
389
  Composes a `<fig-field>` and `<fig-input-text>` into a full-surface, single-line property control. Text input attributes and adornment slots are forwarded to the inner control.
377
390
 
378
- **Attributes:** `label`, `value`, `placeholder`, `type`, `disabled`, `readonly`, `autoresize`, `size`
391
+ **Attributes:** `label`, `value`, `default`, `placeholder`, `type`, `disabled`, `readonly`, `autoresize`, `size`
379
392
 
380
393
  **Events:** `input`, `change` — forwarded from the inner text input.
381
394
 
395
+ Right-click and choose **Reset**, or call `resetToDefault()`, to restore `default` or the initial text.
396
+
382
397
  ```html
383
398
  <propskit-text label="Name" value="Layer 1" placeholder="Enter a name"></propskit-text>
384
399
  ```
@@ -396,11 +411,12 @@ Wraps a `<fig-field>` and `<fig-slider>` into a single labeled control. All slid
396
411
  | `label` | string | — | Field label text |
397
412
  | `direction` | string | `"column"` | Layout direction |
398
413
  | `size` | string | `"large"` | Control layout size |
414
+ | `default` | number/string | initial `value` | Double-click and right-click reset target |
399
415
  | *slider attrs* | — | — | All `<fig-slider>` attributes except host-only PropsKit attributes are forwarded |
400
416
 
401
417
  **Events:** `input`, `change` — forwarded from the inner slider.
402
418
 
403
- Double-click or right-click and choose "Reset to default" to reset to the `default` value, falling back to the slider minimum.
419
+ Double-click or right-click and choose **Reset** to restore `default`, falling back to the initial value.
404
420
 
405
421
  **Methods:** `resetToDefault()` triggers the same reset behavior.
406
422
 
@@ -494,6 +510,7 @@ Waveform oscillator input with composable wave functions, live SVG waveform prev
494
510
  | Attribute | Type | Default | Description |
495
511
  |---|---|---|---|
496
512
  | `value` | JSON string | — | `{"waves":[{"type":"sine","frequency":1,"amplitude":1,"phase":0,"offset":0}]}` |
513
+ | `default` | JSON string | initial `value` | Right-click reset target |
497
514
  | `precision` | number | `2` | Decimal places |
498
515
  | `aspect-ratio` | string | `"2 / 1"` | Editor aspect ratio |
499
516
  | `edit` | boolean | `true` | Show editor and number fields; set to `"false"` for preview only |
@@ -503,6 +520,8 @@ Supported `type` values: `"sine"`, `"square"`, `"sawtooth"`, `"triangle"`.
503
520
 
504
521
  **Properties:** `value` returns a normalized JSON string. `data` returns `{ waves }`. Single-wave JSON values are still accepted and normalized into `waves`.
505
522
 
523
+ Right-click and choose **Reset**, or call `resetToDefault()`, to restore the oscillator value.
524
+
506
525
  **Events:**
507
526
 
508
527
  | Event | Detail |
@@ -559,8 +578,8 @@ A compact solid-color swatch. Uses `<fig-fill-picker>` when the optional picker
559
578
 
560
579
  | Event | Detail |
561
580
  |---|---|
562
- | `input` | `{ color, opacity? }` — while editing |
563
- | `change` | `{ color, opacity? }` — on commit |
581
+ | `input` | `{ color, alpha, opacity }` — while editing |
582
+ | `change` | `{ color, alpha, opacity }` — on commit |
564
583
  | `add` | — (when `control="add"` is clicked) |
565
584
  | `remove` | — (when `control="remove"` is clicked) |
566
585
 
@@ -588,8 +607,10 @@ A compact solid-color swatch. Uses `<fig-fill-picker>` when the optional picker
588
607
 
589
608
  | Event | Detail |
590
609
  |---|---|
591
- | `input` | `{ color, alpha, hsv: { h, s, v, a } }` |
592
- | `change` | `{ color, alpha, hsv: { h, s, v, a } }` |
610
+ | `input` | `{ value, hex, rgba, color, alpha, opacity }` |
611
+ | `change` | `{ value, hex, rgba, color, alpha, opacity }` |
612
+
613
+ `value`, `hex`, and `rgba` retain their legacy values. The additive aliases use opaque `#RRGGBB` for `color`, `0–1` for `alpha`, and `0–100` for `opacity`.
593
614
 
594
615
  ```html
595
616
  <fig-input-color value="#FF5733" text="true"></fig-input-color>
@@ -959,8 +980,8 @@ A draggable handle element. Positioned on a `drag-surface` container with axis c
959
980
  |---|---|
960
981
  | `input` | `{ x, y, px, py, shiftKey }` — while dragging |
961
982
  | `change` | `{ x, y, px, py }` — on release |
962
- | `input` | `{ color, opacity }` — while editing a `type="color"` handle |
963
- | `change` | `{ color, opacity }` — when committing a `type="color"` handle |
983
+ | `input` | `{ color, alpha, opacity }` — while editing a `type="color"` handle |
984
+ | `change` | `{ color, alpha, opacity }` — when committing a `type="color"` handle |
964
985
  | `add` | — (when `tip="add"`) |
965
986
  | `remove` | — (when `tip="remove"`) |
966
987
  | `hitareadown` | `{ originalEvent }` — when `hit-area-mode="delegate"` and the hit area is clicked |
@@ -1008,6 +1029,8 @@ A composite point control with optional radius circle, angle handle, or second p
1008
1029
  | `input` | Value object (shape depends on type) — while dragging |
1009
1030
  | `change` | Value object (shape depends on type) — on release |
1010
1031
 
1032
+ For `type="color"`, color edits add `{ color, alpha, opacity }` to the positional value object.
1033
+
1011
1034
  For `point-point`, both handles support direct drag (with a dynamic directional resize cursor) and rotation via their hit area (dragging from the hit area rotates around the opposite handle at fixed distance, with a rotate cursor).
1012
1035
 
1013
1036
  ```html
@@ -1255,6 +1278,8 @@ A thin styled layer for arbitrary visual content. Use it for generated previews,
1255
1278
  | `full` | boolean | `false` | Stretch to the available width |
1256
1279
  | `checkerboard` | boolean | `false` | Show checkerboard behind transparent content |
1257
1280
 
1281
+ Set `--fig-preview-background` to customize the surface color, including `transparent` to remove it.
1282
+
1258
1283
  ```html
1259
1284
  <fig-preview full style="height: 96px">
1260
1285
  <canvas width="320" height="180"></canvas>
@@ -1345,7 +1370,7 @@ Use `slot="overlay"` for custom overlay controls. Slotted overlays stay as direc
1345
1370
 
1346
1371
  `<fig-card>` — [demo](https://rog.ie/figui3/#card)
1347
1372
 
1348
- A media card with a truncated label, optional link, and attribute-only selection chrome. Composes a generated `fig-image` (or an authored `fig-image` / `fig-media` / `fig-preview` child).
1373
+ A media card with a truncated label, optional link, and attribute-only selection chrome. With `src`, it composes a generated `fig-image`. Without `src`, authored children stay in place and only generated label content is added.
1349
1374
 
1350
1375
  | Attribute | Type | Default | Description |
1351
1376
  |---|---|---|---|
@@ -1368,8 +1393,13 @@ A media card with a truncated label, optional link, and attribute-only selection
1368
1393
  <fig-card src="photo.jpg" label="Shader pill" sublabel="Generative tools/effects" selected></fig-card>
1369
1394
  <fig-card src="photo.jpg" label="Open asset" href="#asset"></fig-card>
1370
1395
  <fig-card src="photo.jpg" label="Wide card" aspect-ratio="16/9" full></fig-card>
1396
+ <fig-card label="Custom preview">
1397
+ <fig-preview>...</fig-preview>
1398
+ </fig-card>
1371
1399
  ```
1372
1400
 
1401
+ When `src` is omitted, authored direct children such as `fig-image`, `fig-media`, or `fig-preview` remain direct children of the card.
1402
+
1373
1403
  Place cards in a CSS grid for multi-column layouts — there is no built-in columns attribute. Prefer `full` in fluid layouts for consistency with other FigUI controls.
1374
1404
 
1375
1405
  ---
package/components.css CHANGED
@@ -1153,7 +1153,8 @@ fig-tabs,
1153
1153
  .tabs {
1154
1154
  --fig-overflow-size: calc(var(--spacer-4) + var(--spacer-2));
1155
1155
  --fig-tabs-inline-padding: 0;
1156
- --fig-tabs-inline-margin: var(--spacer-2-5);
1156
+ --fig-tabs-margin-left: var(--spacer-1);
1157
+ --fig-tabs-margin-right: var(--spacer-2);
1157
1158
  box-sizing: border-box;
1158
1159
  display: flex;
1159
1160
  flex-direction: row;
@@ -1161,11 +1162,14 @@ fig-tabs,
1161
1162
  gap: var(--spacer-1);
1162
1163
  min-width: 0;
1163
1164
  overflow: auto hidden;
1164
- padding: var(--spacer-2) var(--fig-tabs-inline-padding);
1165
- margin: 0 var(--fig-tabs-inline-margin);
1165
+ padding: var(--spacer-2) 0;
1166
+ padding-inline: var(--fig-tabs-inline-padding);
1167
+ margin: 0;
1168
+ margin-left: var(--fig-tabs-margin-left);
1169
+ margin-right: var(--fig-tabs-margin-right);
1166
1170
  position: relative;
1167
1171
  scrollbar-width: none;
1168
- width: calc(100% - var(--fig-tabs-inline-margin) * 2);
1172
+ width: calc(100% - var(--fig-tabs-margin-left) - var(--fig-tabs-margin-right));
1169
1173
 
1170
1174
  > fig-tab {
1171
1175
  flex-shrink: 0;
@@ -1562,6 +1566,10 @@ fig-video {
1562
1566
  width: 100%;
1563
1567
  }
1564
1568
 
1569
+ &[aspect-ratio] {
1570
+ width: 100%;
1571
+ }
1572
+
1565
1573
  > fig-preview {
1566
1574
  --fig-preview-fit: var(--fig-media-fit);
1567
1575
  width: 100%;
@@ -3165,12 +3173,13 @@ fig-tooltip {
3165
3173
 
3166
3174
  fig-preview {
3167
3175
  --fig-preview-fit: contain;
3176
+ --fig-preview-background: var(--figma-color-bg-secondary);
3168
3177
 
3169
3178
  display: block;
3170
3179
  place-items: center;
3171
3180
  min-width: 0;
3172
3181
  max-width: 100%;
3173
- background-color: var(--figma-color-bg-secondary);
3182
+ background-color: var(--fig-preview-background);
3174
3183
  border-radius: var(--radius-medium);
3175
3184
  position: relative;
3176
3185
  overflow: hidden;
@@ -4992,7 +5001,9 @@ fig-chooser {
4992
5001
  fig-card {
4993
5002
  --fig-card-label-line-clamp: 1;
4994
5003
  --fig-card-media-radius: var(--radius-medium);
4995
- display: block;
5004
+ display: flex;
5005
+ flex-direction: column;
5006
+ gap: var(--spacer-1);
4996
5007
  width: 100%;
4997
5008
  min-width: 0;
4998
5009
 
@@ -5000,6 +5011,12 @@ fig-card {
5000
5011
  width: 100%;
5001
5012
  }
5002
5013
 
5014
+ &:is(:not([src]), [src=""]) {
5015
+ padding: var(--spacer-1);
5016
+ border-radius: 0.5625rem;
5017
+ box-sizing: border-box;
5018
+ }
5019
+
5003
5020
  .fig-card-link {
5004
5021
  display: flex;
5005
5022
  flex-direction: column;
@@ -5019,17 +5036,12 @@ fig-card {
5019
5036
  outline-offset: var(--figma-focus-outline-offset);
5020
5037
  }
5021
5038
 
5022
- .fig-card-media {
5023
- position: relative;
5024
- border-radius: var(--fig-card-media-radius);
5025
- background: var(--figma-color-bg-secondary);
5026
- overflow: hidden;
5027
- min-width: 0;
5028
- }
5029
-
5030
- .fig-card-media > fig-image,
5031
- .fig-card-media > fig-media,
5032
- .fig-card-media > fig-preview {
5039
+ .fig-card-link > fig-image,
5040
+ .fig-card-link > fig-media,
5041
+ .fig-card-link > fig-preview,
5042
+ &:is(:not([src]), [src=""]) > fig-image,
5043
+ &:is(:not([src]), [src=""]) > fig-media,
5044
+ &:is(:not([src]), [src=""]) > fig-preview {
5033
5045
  display: block;
5034
5046
  width: 100%;
5035
5047
  max-width: none;
@@ -5037,9 +5049,12 @@ fig-card {
5037
5049
  border-radius: var(--fig-card-media-radius);
5038
5050
  }
5039
5051
 
5040
- .fig-card-media > fig-image > fig-preview,
5041
- .fig-card-media > fig-media > fig-preview,
5042
- .fig-card-media > fig-preview {
5052
+ .fig-card-link > fig-image > fig-preview,
5053
+ .fig-card-link > fig-media > fig-preview,
5054
+ .fig-card-link > fig-preview,
5055
+ &:is(:not([src]), [src=""]) > fig-image > fig-preview,
5056
+ &:is(:not([src]), [src=""]) > fig-media > fig-preview,
5057
+ &:is(:not([src]), [src=""]) > fig-preview {
5043
5058
  width: 100%;
5044
5059
  max-width: none;
5045
5060
  border-radius: var(--fig-card-media-radius);
@@ -5092,24 +5107,39 @@ fig-card {
5092
5107
  &:hover:not([selected]:not([selected="false"])):not(
5093
5108
  [disabled]:not([disabled="false"])
5094
5109
  ):not([aria-disabled="true"])
5095
- .fig-card-link {
5110
+ .fig-card-link,
5111
+ &:is(:not([src]), [src=""]):hover:not(
5112
+ [selected]:not([selected="false"])
5113
+ ):not([disabled]:not([disabled="false"])):not([aria-disabled="true"]) {
5096
5114
  background: var(--figma-color-bg-secondary);
5097
5115
  }
5098
5116
 
5099
- &[selected]:not([selected="false"]) .fig-card-link {
5117
+ &[selected]:not([selected="false"]) .fig-card-link,
5118
+ &:is(:not([src]), [src=""])[selected]:not([selected="false"]) {
5100
5119
  background: var(--figma-color-bg-selected);
5101
5120
  }
5102
5121
 
5103
- &[selected]:not([selected="false"]) .fig-card-media {
5122
+ &[selected]:not([selected="false"])
5123
+ .fig-card-link
5124
+ > :is(fig-image, fig-media, fig-preview),
5125
+ &:is(:not([src]), [src=""])[selected]:not([selected="false"])
5126
+ > :is(fig-image, fig-media, fig-preview) {
5104
5127
  outline: var(--figma-focus-outline);
5105
5128
  outline-offset: var(--figma-focus-outline-offset);
5106
5129
  }
5107
5130
 
5108
5131
  &[selected]:not([selected="false"])
5109
- .fig-card-media
5132
+ .fig-card-link
5133
+ > :is(fig-image, fig-media)
5134
+ > fig-preview::after,
5135
+ &[selected]:not([selected="false"])
5136
+ .fig-card-link
5137
+ > fig-preview::after,
5138
+ &:is(:not([src]), [src=""])[selected]:not([selected="false"])
5110
5139
  > :is(fig-image, fig-media)
5111
5140
  > fig-preview::after,
5112
- &[selected]:not([selected="false"]) .fig-card-media > fig-preview::after {
5141
+ &:is(:not([src]), [src=""])[selected]:not([selected="false"])
5142
+ > fig-preview::after {
5113
5143
  box-shadow: inset 0 0 0 1px var(--figma-color-border-selected);
5114
5144
  }
5115
5145