@rogieking/figui3 10.0.1 → 10.0.3

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.
@@ -447,6 +447,7 @@ Handlers below assume `onInput` / `onChange` / `onClick` from [react.md](react.m
447
447
 
448
448
  - Attrs (chooser): `value`, `layout` (`vertical` | `horizontal` | `grid`), `columns`, `drag`, `loop`, `auto-scroll`, `scroll-behavior`
449
449
  - Choice: always set `value`. Omit chooser `value` to select first; `value=""` means none.
450
+ - A selected choice reflects `selected` to its direct element children.
450
451
  - Children stay in light DOM.
451
452
 
452
453
  ### `fig-separator` / `fig-menu-separator`
@@ -610,7 +611,7 @@ toastRef.current?.showToast();
610
611
  />
611
612
  ```
612
613
 
613
- - Attrs: `type`, `src`, `caption`, `aspect-ratio`, `fit`, `upload`, `loading-indicator`, `checkerboard`, `controls`, `autoplay`, `loop`, `muted`, `poster`
614
+ - Attrs: `type`, `src`, read-only `loaded` (image mode), `caption`, `aspect-ratio`, `fit`, `upload`, `loading-indicator`, `checkerboard`, `controls`, `autoplay`, `loop`, `muted`, `poster`
614
615
  - Events: `loaded` (`detail.src`), `input` / `change`
615
616
 
616
617
  ### `fig-image`
@@ -619,7 +620,8 @@ toastRef.current?.showToast();
619
620
  <fig-image src={src} fit="cover" upload checkerboard="true" onChange={onChange} />
620
621
  ```
621
622
 
622
- - Attrs: same media surface attrs. `loaded` → `detail.src`
623
+ - Attrs: same media surface attrs. The read-only `loaded` attribute supports CSS loaded-state selectors.
624
+ - Events: `load`, `error`, and `loaded` (`detail.src`)
623
625
 
624
626
  ### `fig-video`
625
627
 
@@ -641,7 +643,10 @@ toastRef.current?.showToast();
641
643
  />
642
644
  ```
643
645
 
644
- - Attrs: `src`, `label`, `sublabel`, `selected`, `disabled`, `full`, `size`, `aspect-ratio`, `fit`, `label-line-clamp`
646
+ - Attrs: `src`, `label`, `sublabel`, `selected`, `disabled`, `full`, `size`, `direction`, `aspect-ratio`, `fit`, `label-line-clamp`, `label-overflow`
647
+ - `direction="horizontal"` uses a 40px media preview on the left (`56px` with `size="large"`) and stacks label/sublabel content on the right.
648
+ - `label-overflow="fade"` adds `fig-overflow-fade fig-overflow-fade-horizontal` to both labels; the default is `ellipsis`.
649
+ - Thumbnail vars: `--fig-card-thumbnail-width` (`2.5rem`, or `3.5rem` when large), `--fig-card-thumbnail-height` (`auto`).
645
650
 
646
651
  ### `fig-media-controls`
647
652
 
package/CHANGELOG.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [10.0.3]
6
+
7
+ ### Added
8
+
9
+ - Added a read-only `loaded` state to `fig-image` and image-mode `fig-media` for CSS styling.
10
+ - Selected `fig-choice` elements now reflect `selected` to their direct element children.
11
+
12
+ ### Changed
13
+
14
+ - Arrow keys now move chooser focus without selecting; Enter, Space, and clicks commit selection.
15
+ - Chooser card and color examples now use `fig-card` and `fig-swatch` directly as custom choices.
16
+ - Refined chooser and card focus and selection outlines.
17
+
18
+ ### Fixed
19
+
20
+ - Replaced fragile grid first-row markers with native row gaps and kept overflow controls out of chooser grid flow.
21
+
22
+ ## [10.0.2]
23
+
24
+ ### Added
25
+
26
+ - Added `direction="horizontal"` list layouts to `fig-card` with size-aware thumbnails and configurable thumbnail dimensions.
27
+ - Added `label-overflow="fade"` for horizontal label and sublabel fades.
28
+
29
+ ### Changed
30
+
31
+ - Horizontal cards use 40px thumbnails by default, 56px thumbnails when large, and extra trailing padding at the default size.
32
+
5
33
  ## [10.0.1]
6
34
 
7
35
  ### Changed
package/README.md CHANGED
@@ -881,7 +881,7 @@ When app code rebuilds a chooser by setting `fig-chooser.innerHTML`, the chooser
881
881
  | Attribute | Type | Default | Description |
882
882
  |---|---|---|---|
883
883
  | `value` | string | — | Choice identifier |
884
- | `selected` | boolean | `false` | Selected state |
884
+ | `selected` | boolean | `false` | Selected state; reflected to direct element children |
885
885
  | `disabled` | boolean | `false` | Disabled state |
886
886
 
887
887
  **Events (on fig-chooser):** `input`, `change` — detail is the selected value string.
@@ -1535,6 +1535,7 @@ Unified media component that supports image/video modes and shared sizing/upload
1535
1535
  | `type` | string | `"image"` | `"image"` or `"video"` |
1536
1536
  | `src` | string | — | Media URL |
1537
1537
  | `alt` | string | `""` | Alt text forwarded to the generated `<img>` (image mode) |
1538
+ | `loaded` | boolean | `false` | Read-only reflected state after an image loads successfully |
1538
1539
  | `upload` | boolean | `false` | Show upload overlay (`fig-input-file`) |
1539
1540
  | `loading-indicator` | boolean | `true` | Set to `"false"` to disable the delayed loading spinner for generated images |
1540
1541
  | `label` | string | `"Upload"` | Upload button label |
@@ -1579,6 +1580,7 @@ An image display component with optional upload, aspect ratio, and object-fit co
1579
1580
  |---|---|---|---|
1580
1581
  | `src` | string | — | Image URL |
1581
1582
  | `alt` | string | `""` | Alt text forwarded to the generated `<img>` |
1583
+ | `loaded` | boolean | `false` | Read-only reflected state after the image loads successfully |
1582
1584
  | `upload` | boolean | `false` | Show upload overlay (`fig-input-file`) |
1583
1585
  | `loading-indicator` | boolean | `true` | Set to `"false"` to disable the delayed loading spinner |
1584
1586
  | `label` | string | `"Upload"` | Upload button label |
@@ -1591,6 +1593,7 @@ An image display component with optional upload, aspect ratio, and object-fit co
1591
1593
  Use meaningful `alt` text for informative images. Use `alt=""` for decorative previews, thumbnails with visible labels, or upload placeholders.
1592
1594
 
1593
1595
  The generated image's native `load` and `error` events are re-emitted from `fig-image` as bubbling, composed `CustomEvent`s with `src`, `media`, and `originalEvent` in `event.detail`.
1596
+ The `loaded` attribute is removed when `src` changes or loading fails, so it can be used as a CSS state selector such as `fig-image[loaded]`.
1594
1597
 
1595
1598
  ```html
1596
1599
  <fig-image src="photo.jpg" alt="Selected image"></fig-image>
@@ -1623,12 +1626,18 @@ A media card with a truncated label and attribute-only selection chrome. With `s
1623
1626
  | `disabled` | boolean | `false` | Dim + non-interactive |
1624
1627
  | `full` | boolean | `false` | Stretch to the available width (cards are already `width: 100%` by default) |
1625
1628
  | `size` | string | default | Set to `"large"` for `--spacer-2` card padding and `--spacer-1` label spacing |
1629
+ | `direction` | string | `"vertical"` | Set to `"horizontal"` for a list layout with 40px media on the left (56px with `size="large"`) and stacked labels on the right |
1626
1630
  | `aspect-ratio` | string | `"1/1"` | Forwarded to generated `fig-image` |
1627
1631
  | `fit` | string | `"contain"` | Forwarded to generated `fig-image` |
1628
1632
  | `label-line-clamp` | string | `"1"` | `"1"` or `"2"` line clamp for the label |
1633
+ | `label-overflow` | string | `"ellipsis"` | Set to `"fade"` to apply horizontal overflow fades to the label and sublabel |
1634
+
1635
+ Horizontal thumbnail sizing uses `--fig-card-thumbnail-width` (`2.5rem` by default, `3.5rem` with `size="large"`) and `--fig-card-thumbnail-height` (`auto` by default). Leaving height at `auto` preserves the media `aspect-ratio`.
1629
1636
 
1630
1637
  ```html
1631
1638
  <fig-card src="photo.jpg" label="Autumn field"></fig-card>
1639
+ <fig-card src="photo.jpg" label="Autumn field" sublabel="Landscape photography" direction="horizontal"></fig-card>
1640
+ <fig-card src="photo.jpg" label="A long card label" sublabel="A long card sublabel" direction="horizontal" label-overflow="fade"></fig-card>
1632
1641
  <fig-card src="photo.jpg" label="Large card" size="large"></fig-card>
1633
1642
  <fig-card src="photo.jpg" label="Shader pill" sublabel="Generative tools/effects" selected></fig-card>
1634
1643
  <fig-card src="photo.jpg" label="Wide card" aspect-ratio="16/9" full></fig-card>
@@ -1641,6 +1650,8 @@ A media card with a truncated label and attribute-only selection chrome. With `s
1641
1650
  </fig-card>
1642
1651
  ```
1643
1652
 
1653
+ `label-overflow="fade"` adds both `fig-overflow-fade` and `fig-overflow-fade-horizontal` to generated or authored `.fig-card-label` and `.fig-card-sublabel` elements. Any other value uses the default ellipsis behavior.
1654
+
1644
1655
  When `src` is omitted, authored direct children such as `fig-image`, `fig-media`, or `fig-preview` remain direct children of the card. An authored `fig-footer` is preserved; when no `label`, `text`, or `sublabel` attributes are present, the card does not generate another footer.
1645
1656
 
1646
1657
  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.
package/components.css CHANGED
@@ -89,8 +89,10 @@
89
89
  --figma-color-border-slot: light-dark(#ff24bd, #f316b0);
90
90
 
91
91
  /* Focus */
92
- --figma-focus-outline: 1px solid var(--figma-color-border-selected);
93
- --figma-focus-outline-offset: -1px;
92
+ --figma-focus-outline-width: 1.5px;
93
+ --figma-focus-outline-color: var(--figma-color-border-selected);
94
+ --figma-focus-outline: var(--figma-focus-outline-width) solid var(--figma-focus-outline-color);
95
+ --figma-focus-outline-offset: var(--figma-focus-outline-width);
94
96
  --figma-focus-outline-radius: inherit;
95
97
 
96
98
  /* Icon Colors */
@@ -6241,7 +6243,7 @@ fig-chooser {
6241
6243
  &[layout="grid"] {
6242
6244
  display: grid;
6243
6245
  column-gap: var(--fig-chooser-gap);
6244
- row-gap: 0;
6246
+ row-gap: var(--fig-chooser-gap);
6245
6247
  grid-template-columns: repeat(
6246
6248
  var(--fig-chooser-grid-columns, 2),
6247
6249
  minmax(0, 1fr)
@@ -6251,11 +6253,6 @@ fig-chooser {
6251
6253
 
6252
6254
  > :not(.fig-overflow) {
6253
6255
  margin: 0;
6254
- margin-block-start: var(--fig-chooser-gap);
6255
- }
6256
-
6257
- > [data-fig-chooser-first-row] {
6258
- margin-block-start: 0;
6259
6256
  }
6260
6257
 
6261
6258
  > .fig-overflow {
@@ -6295,6 +6292,8 @@ fig-card {
6295
6292
  --fig-card-label-line-clamp: 1;
6296
6293
  --fig-card-media-radius: var(--radius-medium);
6297
6294
  --fig-card-padding: var(--spacer-1);
6295
+ --fig-card-thumbnail-width: 2.5rem;
6296
+ --fig-card-thumbnail-height: auto;
6298
6297
  display: flex;
6299
6298
  flex-direction: column;
6300
6299
  gap: var(--spacer-1);
@@ -6304,12 +6303,18 @@ fig-card {
6304
6303
  border-radius: 0.5625rem;
6305
6304
  box-sizing: border-box;
6306
6305
 
6306
+ &:focus-visible{
6307
+ outline: var(--figma-focus-outline);
6308
+ outline-offset: var(--figma-focus-outline-offset);
6309
+ }
6310
+
6307
6311
  &[full]:not([full="false"]) {
6308
6312
  width: 100%;
6309
6313
  }
6310
6314
 
6311
6315
  &[size="large"] {
6312
6316
  --fig-card-padding: var(--spacer-2);
6317
+ --fig-card-thumbnail-width: 3.5rem;
6313
6318
 
6314
6319
  > fig-footer {
6315
6320
  margin-top: var(--spacer-1);
@@ -6321,6 +6326,40 @@ fig-card {
6321
6326
  }
6322
6327
  }
6323
6328
 
6329
+ &[direction="horizontal"] {
6330
+ flex-direction: row;
6331
+ align-items: center;
6332
+ gap: var(--spacer-2);
6333
+
6334
+ > :is(fig-image, fig-media, fig-preview) {
6335
+ flex: 0 0 var(--fig-card-thumbnail-width);
6336
+ width: var(--fig-card-thumbnail-width);
6337
+ max-width: var(--fig-card-thumbnail-width);
6338
+ height: var(--fig-card-thumbnail-height);
6339
+ }
6340
+
6341
+ > :is(fig-image, fig-media) > fig-preview,
6342
+ > fig-preview {
6343
+ height: var(--fig-card-thumbnail-height);
6344
+ }
6345
+
6346
+ > fig-footer {
6347
+ flex: 1 1 0;
6348
+ width: auto;
6349
+ margin-top: 0;
6350
+ }
6351
+
6352
+ .fig-card-label,
6353
+ .fig-card-sublabel {
6354
+ padding-inline: 0;
6355
+ }
6356
+ }
6357
+
6358
+ &[direction="horizontal"]:not([size]),
6359
+ &[direction="horizontal"][size=""] {
6360
+ padding-right: var(--spacer-2);
6361
+ }
6362
+
6324
6363
  > fig-image,
6325
6364
  > fig-media,
6326
6365
  > fig-preview {
@@ -6341,6 +6380,9 @@ fig-card {
6341
6380
  max-width: none;
6342
6381
  border-radius: var(--fig-card-media-radius);
6343
6382
  min-height: auto;
6383
+ &::after{
6384
+ visibility: hidden;
6385
+ }
6344
6386
  }
6345
6387
 
6346
6388
  > fig-footer {
@@ -6401,6 +6443,17 @@ fig-card {
6401
6443
  display: none;
6402
6444
  }
6403
6445
 
6446
+ :is(.fig-card-label, .fig-card-sublabel).fig-overflow-fade-horizontal {
6447
+ display: block;
6448
+ -webkit-line-clamp: unset;
6449
+ line-clamp: unset;
6450
+ white-space: nowrap;
6451
+ overflow-x: auto;
6452
+ overflow-y: hidden;
6453
+ text-overflow: clip;
6454
+ word-break: normal;
6455
+ }
6456
+
6404
6457
  &:hover:not(
6405
6458
  [selected]:not([selected="false"])
6406
6459
  ):not([disabled]:not([disabled="false"])):not([aria-disabled="true"]) {
@@ -6413,14 +6466,7 @@ fig-card {
6413
6466
 
6414
6467
  &[selected]:not([selected="false"]) > :is(fig-image, fig-media, fig-preview) {
6415
6468
  outline: var(--figma-focus-outline);
6416
- outline-offset: var(--figma-focus-outline-offset);
6417
- }
6418
-
6419
- &[selected]:not([selected="false"])
6420
- > :is(fig-image, fig-media)
6421
- > fig-preview::after,
6422
- &[selected]:not([selected="false"]) > fig-preview::after {
6423
- box-shadow: inset 0 0 0 1px var(--figma-color-border-selected);
6469
+ outline-offset: 0;
6424
6470
  }
6425
6471
 
6426
6472
  &[disabled]:not([disabled="false"]),
@@ -6436,6 +6482,7 @@ fig-choice {
6436
6482
  --fig-choice-border-radius: var(--radius-medium);
6437
6483
  --fig-choice-min-height: 1.5rem;
6438
6484
  --fig-choice-min-width: 1.5rem;
6485
+ --fig-choice-selection-ring-width: 1.5px;
6439
6486
 
6440
6487
  min-height: var(--fig-choice-min-height);
6441
6488
  min-width: var(--fig-choice-min-width);
@@ -6457,32 +6504,24 @@ fig-choice {
6457
6504
  background-color: var(--figma-color-bg-secondary);
6458
6505
  }
6459
6506
 
6460
- &:focus-visible {
6461
- outline: var(--figma-focus-outline);
6462
- outline-offset: var(--figma-focus-outline-offset);
6463
- }
6464
-
6465
6507
  &[padding]:not([padding="false"]) {
6466
6508
  --fig-choice-padding: var(--spacer-1);
6467
6509
  --fig-choice-border-radius: var(--radius-medium-large);
6468
6510
  }
6469
6511
 
6470
- &[selected]:not([selected="false"]) {
6471
- position: relative;
6472
- &::after {
6473
- content: "";
6474
- position: absolute;
6475
- inset: 0;
6476
- border-radius: calc(
6477
- var(--fig-choice-border-radius)
6478
- );
6479
- pointer-events: none;
6480
- z-index: 1;
6481
- outline: var(--fig-choice-selection-ring-width, 2px) solid
6512
+ &:focus-visible,
6513
+ &[selected]:not([selected="false"]){
6514
+ outline: var(--fig-choice-selection-ring-width) solid
6482
6515
  var(--figma-color-border-selected);
6483
- outline-offset: var(--fig-choice-selection-ring-width, 2px);
6516
+ outline-offset: var(--fig-choice-selection-ring-width);
6517
+ }
6518
+
6519
+ &[selected]:not([selected="false"]) {
6520
+ background-color: var(--figma-color-bg-selected);
6521
+ &>*,
6522
+ &:hover{
6523
+ background-color: transparent;
6484
6524
  }
6485
- background-color: var(--figma-color-bg-secondary);
6486
6525
  }
6487
6526
 
6488
6527
  &[disabled]:not([disabled="false"]),