@astryxdesign/core 0.6.2-canary.9a80beb → 0.6.2-canary.9cfb2b5

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.
@@ -1,14 +1,16 @@
1
1
  /**
2
2
  * @file Selector.tsx
3
- * @input Uses React, StyleX, usePopover, useTooltip, Icon, InputGroupContext,
4
- * and Selector positioning hooks
5
- * @output Exports Selector component
3
+ * @input Uses React, StyleX, adaptive selection surfaces, theme-resolved
4
+ * indicators, Field, and InputGroup context
5
+ * @output Exports Selector with content-derived option-mark layout
6
6
  * @position Core implementation; consumed by index.ts
7
7
  *
8
8
  * SYNC: When modified, update:
9
+ * - /packages/core/src/Selector/Selector.spec.md
9
10
  * - /packages/core/src/Selector/Selector.doc.mjs
10
11
  * - /packages/core/src/Selector/Selector.test.tsx
11
12
  * - /packages/core/src/Selector/index.ts
13
+ * - /apps/storybook/stories/Selector.stories.tsx
12
14
  * - /apps/storybook/stories/InputGroup.stories.tsx
13
15
  * - /packages/cli/assets/templates/blocks/components/Selector/ (showcase blocks)
14
16
  */
@@ -183,10 +185,10 @@ interface SelectorPropsBase<T extends SelectorOptionType = SelectorOptionType> e
183
185
  */
184
186
  renderValue?: (option: SelectorOptionData) => ReactNode;
185
187
  /**
186
- * Which edge of the option row carries the selected mark. `start` reserves a
187
- * mark column ahead of every label so they stay aligned, the way a native
188
- * menu does; `end` is the house convention shared with Typeahead and
189
- * CommandPalette.
188
+ * Which logical edge of the option row carries a rendered selection mark. An
189
+ * empty mark consumes no space, so selected and unselected labels may shift or
190
+ * have different available width. `end` is the house convention shared with
191
+ * Typeahead and CommandPalette.
190
192
  *
191
193
  * @default 'end'
192
194
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Selector.d.ts","sourceRoot":"","sources":["../../src/Selector/Selector.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Selector.d.ts","sourceRoot":"","sources":["../../src/Selector/Selector.tsx"],"names":[],"mappings":"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"}
@@ -4,15 +4,17 @@
4
4
 
5
5
  /**
6
6
  * @file Selector.tsx
7
- * @input Uses React, StyleX, usePopover, useTooltip, Icon, InputGroupContext,
8
- * and Selector positioning hooks
9
- * @output Exports Selector component
7
+ * @input Uses React, StyleX, adaptive selection surfaces, theme-resolved
8
+ * indicators, Field, and InputGroup context
9
+ * @output Exports Selector with content-derived option-mark layout
10
10
  * @position Core implementation; consumed by index.ts
11
11
  *
12
12
  * SYNC: When modified, update:
13
+ * - /packages/core/src/Selector/Selector.spec.md
13
14
  * - /packages/core/src/Selector/Selector.doc.mjs
14
15
  * - /packages/core/src/Selector/Selector.test.tsx
15
16
  * - /packages/core/src/Selector/index.ts
17
+ * - /apps/storybook/stories/Selector.stories.tsx
16
18
  * - /apps/storybook/stories/InputGroup.stories.tsx
17
19
  * - /packages/cli/assets/templates/blocks/components/Selector/ (showcase blocks)
18
20
  */
@@ -772,7 +774,7 @@ export function Selector(props) {
772
774
  */
773
775
  const mark = /*#__PURE__*/_jsx("span", {
774
776
  ...{
775
- className: "x3nfvp2 x6s0dn4 xl56j7k x2lah0s xgomdpo"
777
+ className: "x3nfvp2 x6s0dn4 xl56j7k x2lah0s xgomdpo xjl7jj"
776
778
  },
777
779
  children: /*#__PURE__*/_jsx(SelectionMark, {
778
780
  state: isSelected ? 'checked' : 'unchecked',
package/dist/astryx.css CHANGED
@@ -1424,6 +1424,7 @@
1424
1424
  .xpilulx:last-child:not(#\#):not(#\#):not(#\#){padding-inline-end:max(var(--container-padding-inline-end,var(--spacing-3)),var(--spacing-2))}
1425
1425
  .x1wzlfcn:last-child:not(#\#):not(#\#):not(#\#){padding-inline-end:max(var(--container-padding-inline-end,var(--spacing-4)),var(--spacing-2))}
1426
1426
  .x1dnou9g:nth-child(even):not(#\#):not(#\#):not(#\#){background-color:var(--color-background-muted)}
1427
+ .xjl7jj:empty:not(#\#):not(#\#):not(#\#){display:none}
1427
1428
  .x1c52tdz:focus:not(#\#):not(#\#):not(#\#){background-color:var(--color-overlay-hover)}
1428
1429
  .x1rsz1da:focus:not(#\#):not(#\#):not(#\#){clip-path:none}
1429
1430
  .xodanix:focus:not(#\#):not(#\#):not(#\#){inset-inline-start:var(--spacing-2)}
@@ -341,7 +341,7 @@
341
341
  },
342
342
  "@astryx.checkboxList.item.checkbox": {
343
343
  "defaultMessage": "Merkblokkie",
344
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
344
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
345
345
  },
346
346
  "@astryx.commandPalette.emptySearch": {
347
347
  "defaultMessage": "Geen resultate",
@@ -952,3 +952,4 @@
952
952
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
953
953
  }
954
954
  }
955
+
@@ -365,7 +365,7 @@
365
365
  },
366
366
  "@astryx.checkboxList.item.checkbox": {
367
367
  "defaultMessage": "مربع اختيار",
368
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
368
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
369
369
  },
370
370
  "@astryx.commandPalette.emptySearch": {
371
371
  "defaultMessage": "لا توجد نتائج",
@@ -992,3 +992,4 @@
992
992
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
993
993
  }
994
994
  }
995
+
@@ -357,7 +357,7 @@
357
357
  },
358
358
  "@astryx.checkboxList.item.checkbox": {
359
359
  "defaultMessage": "Casella de selecció",
360
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
360
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
361
361
  },
362
362
  "@astryx.commandPalette.emptySearch": {
363
363
  "defaultMessage": "Cap resultat",
@@ -968,3 +968,4 @@
968
968
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
969
969
  }
970
970
  }
971
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "Zaškrtávací políčko",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "Žádné výsledky",
@@ -984,3 +984,4 @@
984
984
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
985
985
  }
986
986
  }
987
+
@@ -357,7 +357,7 @@
357
357
  },
358
358
  "@astryx.checkboxList.item.checkbox": {
359
359
  "defaultMessage": "Afkrydsningsfelt",
360
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
360
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
361
361
  },
362
362
  "@astryx.commandPalette.emptySearch": {
363
363
  "defaultMessage": "Ingen resultater",
@@ -960,3 +960,4 @@
960
960
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
961
961
  }
962
962
  }
963
+
@@ -357,7 +357,7 @@
357
357
  },
358
358
  "@astryx.checkboxList.item.checkbox": {
359
359
  "defaultMessage": "Kontrollkästchen",
360
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
360
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
361
361
  },
362
362
  "@astryx.commandPalette.emptySearch": {
363
363
  "defaultMessage": "Keine Ergebnisse",
@@ -960,3 +960,4 @@
960
960
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
961
961
  }
962
962
  }
963
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "Πλαίσιο ελέγχου",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "Κανένα αποτέλεσμα",
@@ -984,3 +984,4 @@
984
984
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
985
985
  }
986
986
  }
987
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "Casilla",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "Sin resultados",
@@ -972,3 +972,4 @@
972
972
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
973
973
  }
974
974
  }
975
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "Valintaruutu",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "Ei tuloksia",
@@ -984,3 +984,4 @@
984
984
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
985
985
  }
986
986
  }
987
+
@@ -1,8 +1,4 @@
1
1
  {
2
- "@astryx.pagination.label": {
3
- "defaultMessage": "Pagination",
4
- "description": "Aria label for the pagination navigation region."
5
- },
6
2
  "@astryx.pagination.previous": {
7
3
  "defaultMessage": "Aller à la page précédente",
8
4
  "description": "Aria label for the previous-page button."
@@ -35,10 +31,6 @@
35
31
  "defaultMessage": "Aller à la page",
36
32
  "description": "Aria label for the editable page/row number box in the input pagination variant. No number — the box holds the value itself."
37
33
  },
38
- "@astryx.pagination.pageLabel": {
39
- "defaultMessage": "Page",
40
- "description": "Visible label before the editable box in the input pagination variant. Example: \"Page [ 1 ] / 10\"."
41
- },
42
34
  "@astryx.pagination.pageIndicators": {
43
35
  "defaultMessage": "Indicateurs de page",
44
36
  "description": "Aria label for the dots-variant page-indicator group."
@@ -345,7 +337,7 @@
345
337
  },
346
338
  "@astryx.checkboxList.item.checkbox": {
347
339
  "defaultMessage": "Case à cocher",
348
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
340
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
349
341
  },
350
342
  "@astryx.commandPalette.emptySearch": {
351
343
  "defaultMessage": "Aucun résultat",
@@ -948,3 +940,4 @@
948
940
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
949
941
  }
950
942
  }
943
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "תיבת סימון",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "אין תוצאות",
@@ -984,3 +984,4 @@
984
984
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
985
985
  }
986
986
  }
987
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "Jelölőnégyzet",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "Nincs találat",
@@ -984,3 +984,4 @@
984
984
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
985
985
  }
986
986
  }
987
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "Casella di controllo",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "Nessun risultato",
@@ -972,3 +972,4 @@
972
972
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
973
973
  }
974
974
  }
975
+
@@ -365,7 +365,7 @@
365
365
  },
366
366
  "@astryx.checkboxList.item.checkbox": {
367
367
  "defaultMessage": "チェックボックス",
368
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
368
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
369
369
  },
370
370
  "@astryx.commandPalette.emptySearch": {
371
371
  "defaultMessage": "結果がありません",
@@ -992,3 +992,4 @@
992
992
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
993
993
  }
994
994
  }
995
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "체크박스",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "결과 없음",
@@ -984,3 +984,4 @@
984
984
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
985
985
  }
986
986
  }
987
+
@@ -337,7 +337,7 @@
337
337
  },
338
338
  "@astryx.checkboxList.item.checkbox": {
339
339
  "defaultMessage": "Selectievakje",
340
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
340
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
341
341
  },
342
342
  "@astryx.commandPalette.emptySearch": {
343
343
  "defaultMessage": "Geen resultaten",
@@ -948,3 +948,4 @@
948
948
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
949
949
  }
950
950
  }
951
+
@@ -357,7 +357,7 @@
357
357
  },
358
358
  "@astryx.checkboxList.item.checkbox": {
359
359
  "defaultMessage": "Avmerkingsboks",
360
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
360
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
361
361
  },
362
362
  "@astryx.commandPalette.emptySearch": {
363
363
  "defaultMessage": "Ingen resultater",
@@ -972,3 +972,4 @@
972
972
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
973
973
  }
974
974
  }
975
+
@@ -357,7 +357,7 @@
357
357
  },
358
358
  "@astryx.checkboxList.item.checkbox": {
359
359
  "defaultMessage": "Pole wyboru",
360
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
360
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
361
361
  },
362
362
  "@astryx.commandPalette.emptySearch": {
363
363
  "defaultMessage": "Brak wyników",
@@ -976,3 +976,4 @@
976
976
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
977
977
  }
978
978
  }
979
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "Caixa de seleção",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "Nenhum resultado",
@@ -976,3 +976,4 @@
976
976
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
977
977
  }
978
978
  }
979
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "Caixa de verificação",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "Sem resultados",
@@ -976,3 +976,4 @@
976
976
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
977
977
  }
978
978
  }
979
+
@@ -357,7 +357,7 @@
357
357
  },
358
358
  "@astryx.checkboxList.item.checkbox": {
359
359
  "defaultMessage": "Casetă de selectare",
360
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
360
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
361
361
  },
362
362
  "@astryx.commandPalette.emptySearch": {
363
363
  "defaultMessage": "Niciun rezultat",
@@ -976,3 +976,4 @@
976
976
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
977
977
  }
978
978
  }
979
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "Флажок",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "Ничего не найдено",
@@ -984,3 +984,4 @@
984
984
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
985
985
  }
986
986
  }
987
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "Поље за потврду",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "Нема резултата",
@@ -984,3 +984,4 @@
984
984
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
985
985
  }
986
986
  }
987
+
@@ -357,7 +357,7 @@
357
357
  },
358
358
  "@astryx.checkboxList.item.checkbox": {
359
359
  "defaultMessage": "Kryssruta",
360
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
360
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
361
361
  },
362
362
  "@astryx.commandPalette.emptySearch": {
363
363
  "defaultMessage": "Inga resultat",
@@ -980,3 +980,4 @@
980
980
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
981
981
  }
982
982
  }
983
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "Onay kutusu",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "Sonuç yok",
@@ -984,3 +984,4 @@
984
984
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
985
985
  }
986
986
  }
987
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "Прапорець",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "Немає результатів",
@@ -984,3 +984,4 @@
984
984
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
985
985
  }
986
986
  }
987
+
@@ -361,7 +361,7 @@
361
361
  },
362
362
  "@astryx.checkboxList.item.checkbox": {
363
363
  "defaultMessage": "Hộp kiểm",
364
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
364
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
365
365
  },
366
366
  "@astryx.commandPalette.emptySearch": {
367
367
  "defaultMessage": "Không có kết quả",
@@ -980,3 +980,4 @@
980
980
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
981
981
  }
982
982
  }
983
+
@@ -365,7 +365,7 @@
365
365
  },
366
366
  "@astryx.checkboxList.item.checkbox": {
367
367
  "defaultMessage": "复选框",
368
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
368
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
369
369
  },
370
370
  "@astryx.commandPalette.emptySearch": {
371
371
  "defaultMessage": "无结果",
@@ -992,3 +992,4 @@
992
992
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
993
993
  }
994
994
  }
995
+
@@ -365,7 +365,7 @@
365
365
  },
366
366
  "@astryx.checkboxList.item.checkbox": {
367
367
  "defaultMessage": "核取方塊",
368
- "description": "Screen-reader-only last-ditch fallback name for a checkbox inside a list item when no label is provided. Should almost never render — consumers should supply a real label."
368
+ "description": "Localized hidden text used internally when a rich visible label names the checkbox through aria-labelledby. It does not become the computed name; callers must provide aria-label when rich content has no text."
369
369
  },
370
370
  "@astryx.commandPalette.emptySearch": {
371
371
  "defaultMessage": "無結果",
@@ -992,3 +992,4 @@
992
992
  "description": "Screen-reader-only label on the \"×\" on an individual Token/chip. Example: `Remove John Smith`, `Remove Active`. Imperative verb."
993
993
  }
994
994
  }
995
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/core",
3
- "version": "0.6.2-canary.9a80beb",
3
+ "version": "0.6.2-canary.9cfb2b5",
4
4
  "displayName": "Astryx Core",
5
5
  "description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
6
6
  "author": "Meta Open Source",
@@ -687,8 +687,8 @@
687
687
  "react-dom": ">=19.0.0"
688
688
  },
689
689
  "devDependencies": {
690
- "@astryxdesign/a11y-spec": "0.6.2-canary.9a80beb",
691
- "@astryxdesign/cli": "0.6.2-canary.9a80beb",
690
+ "@astryxdesign/a11y-spec": "0.6.2-canary.9cfb2b5",
691
+ "@astryxdesign/cli": "0.6.2-canary.9cfb2b5",
692
692
  "@babel/cli": "^7.29.7",
693
693
  "@babel/core": "^7.29.7",
694
694
  "@babel/preset-react": "^7.29.7",
@@ -83,7 +83,7 @@ const anatomy = [
83
83
  name: 'Option selection indicator',
84
84
  required: false,
85
85
  description:
86
- 'Resolved selection mark rendered for each option in its checked or unchecked state.',
86
+ 'Resolved selection mark rendered for each option in its checked or unchecked state. Its layout space collapses when the resolved indicator draws nothing.',
87
87
  },
88
88
  {
89
89
  name: 'Option divider',
@@ -330,7 +330,7 @@ export const docs = {
330
330
  name: 'indicatorPosition',
331
331
  type: "'start' | 'end'",
332
332
  description:
333
- 'Which edge of the option row carries the selected mark. start reserves a mark column ahead of every label so they stay aligned, the way a native menu does; end is the house convention shared with Typeahead and CommandPalette.',
333
+ 'Which logical edge of the option row carries a rendered selection mark. An empty mark consumes no space, so selected and unselected labels may shift or have different available width. end is the house convention shared with Typeahead and CommandPalette.',
334
334
  default: "'end'",
335
335
  },
336
336
  {
@@ -16,6 +16,8 @@ verified_by:
16
16
  packages/core/src/Selector/Selector.source-build.test.mjs,
17
17
  packages/core/src/Selector/__tests__/Selector.listbox.a11y.test.tsx,
18
18
  packages/core/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts,
19
+ apps/storybook/stories/Selector.stories.tsx,
20
+ .github/scripts/story-play-guard.js,
19
21
  ]
20
22
  modules: []
21
23
  families: [family:input-fields]
@@ -44,8 +46,8 @@ connection between the closed trigger and its selection surface.
44
46
  ## Compatibility and migration
45
47
 
46
48
  - Released defaults and behavior remain unchanged by this record.
47
- - `indicatorPosition` defaults to `end`; every option row currently reserves the
48
- indicator column at the configured logical edge.
49
+ - `indicatorPosition` defaults to `end`; a rendered selection mark occupies
50
+ space at that logical edge, while an empty resolved indicator occupies none.
49
51
  - `presentation` defaults to `popover`. `bottom-sheet` is an explicit modal
50
52
  presentation, and `adaptive` selects it on compact coarse-pointer screens.
51
53
  - `hasClear` changes the value contract to include `null`; that distinction is
@@ -53,9 +55,7 @@ connection between the closed trigger and its selection surface.
53
55
  - `isReadOnly` is additive and defaults to `false`. It preserves the selected
54
56
  value, focus, and form participation while removing selection-surface and
55
57
  editing affordances. `isDisabled` takes precedence when both are set.
56
- - `spec:AST-004/DEC-1` accepts a future change that collapses an empty indicator
57
- column. It is not shipped behavior and does not replace FR3 until implementation,
58
- visual verification, and an update to this current contract land together.
58
+ - `spec:AST-004/DEC-1` governs the state-derived indicator-space behavior in FR3.
59
59
 
60
60
  ## Ownership boundary
61
61
 
@@ -86,7 +86,7 @@ and examples remain in `Selector.doc.mjs`.
86
86
  | ---------------------- | -------------------------------------- | -------------------------------------------------------- | ----------------------------------------- | --------- | -------- | --------- | ------------------------------- |
87
87
  | trigger variant | `input`, `ghost` | Form-field or toolbar presentation | All trigger states | `input` | Selector | released | TypeScript rejects other values |
88
88
  | size | `sm`, `md`, `lg` | Trigger and option-row density | All presentations | `md` | Selector | released | TypeScript rejects other values |
89
- | selected-mark position | `start`, `end` | Logical edge containing the reserved selection column | Every option row | `end` | Selector | released | TypeScript rejects other values |
89
+ | selected-mark position | `start`, `end` | Logical edge containing a rendered selection mark | Every option row | `end` | Selector | released | TypeScript rejects other values |
90
90
  | presentation | `popover`, `bottom-sheet`, `adaptive` | Anchored pointer surface or modal compact-touch surface | All trigger variants | `popover` | Selector | released | TypeScript rejects other values |
91
91
  | popup semantics | `listbox`; modal dialog containing one | Semantics follow the active presentation | Popover; bottom sheet | `listbox` | Selector | released | No separate role prop is public |
92
92
  | option-row state | `selected`, `disabled` | Stable theming state on each option row | Every rendered option | neither | Selector | released | Unknown states are not emitted |
@@ -96,20 +96,20 @@ and examples remain in `Selector.doc.mjs`.
96
96
 
97
97
  These requirements describe shipped behavior on current `main`.
98
98
 
99
- | ID | Shipped invariant | Evidence |
100
- | --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
101
- | FR1 | Selecting an enabled option updates the one selected value, closes the active presentation, and returns the trigger to its stable closed state. | Consumer docs and selection interaction tests |
102
- | FR2 | Without explicit placement, a non-search popover aligns the selected row over the trigger and clamps it to the viewport. Search popovers and explicit placement use normal Layer positioning. | Consumer docs, implementation, and geometry tests |
103
- | FR3 | Every option row reserves one selection-mark column at `indicatorPosition`, even when the default unchecked indicator draws nothing. The column has a minimum width and can grow for a larger themed replacement. | `itemMarkColumn`, indicator-position tests, and theme tests |
104
- | FR4 | `popover` uses an anchored Popover. `bottom-sheet` uses a modal BottomSheet. `adaptive` resolves to the modal bottom sheet on compact coarse-pointer screens and Popover otherwise. | Presentation controller and adaptive-presentation tests |
105
- | FR5 | While `isLoading` is true, the trigger exposes busy state and the listbox suppresses empty and no-results output. | Loading, empty-state, and announcement tests |
106
- | FR6 | While `isReadOnly` is true, the selected value remains focusable and form-submittable, while the selection surface, clear action, disclosure indicator, and every value-change path are unavailable. | Read-only interaction, form, and accessibility tests |
99
+ | ID | Shipped invariant | Evidence |
100
+ | --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
101
+ | FR1 | Selecting an enabled option updates the one selected value, closes the active presentation, and returns the trigger to its stable closed state. | Consumer docs and selection interaction tests |
102
+ | FR2 | Without explicit placement, a non-search popover aligns the selected row over the trigger and clamps it to the viewport. Search popovers and explicit placement use normal Layer positioning. | Consumer docs, implementation, and geometry tests |
103
+ | FR3 | An option whose resolved selection indicator draws no content reserves no mark-column space. Visible selected or themed replacement indicators remain in layout at `indicatorPosition`; the resulting state-dependent label position or available width is intentional, and row content keeps its existing overflow behavior. | `itemMarkColumn`, focused indicator tests, and Chromium evidence stories |
104
+ | FR4 | `popover` uses an anchored Popover. `bottom-sheet` uses a modal BottomSheet. `adaptive` resolves to the modal bottom sheet on compact coarse-pointer screens and Popover otherwise. | Presentation controller and adaptive-presentation tests |
105
+ | FR5 | While `isLoading` is true, the trigger exposes busy state and the listbox suppresses empty and no-results output. | Loading, empty-state, and announcement tests |
106
+ | FR6 | While `isReadOnly` is true, the selected value remains focusable and form-submittable, while the selection surface, clear action, disclosure indicator, and every value-change path are unavailable. | Read-only interaction, form, and accessibility tests |
107
107
 
108
108
  ### Allowed variation
109
109
 
110
- - **AV1 — Indicator rendering.** A theme may replace the check indicator. The
111
- replacement may draw in both selected and unselected states; the current row
112
- still reserves its indicator column.
110
+ - **AV1 — Indicator rendering.** A theme may replace the check indicator. A
111
+ replacement that draws in both selected and unselected states keeps its mark
112
+ space in both states; only resolved empty output collapses.
113
113
  - **AV2 — Option content.** `renderOption` may replace visible option content,
114
114
  while Selector keeps row role, selection, disabled state, navigation, and
115
115
  theming state.
@@ -118,17 +118,17 @@ These requirements describe shipped behavior on current `main`.
118
118
 
119
119
  ### Representative states
120
120
 
121
- | State | Required invariant | Allowed variation |
122
- | ------------------------ | ----------------------------------------------------------------------------------------- | ------------------------------------------------------ |
123
- | closed with no value | Label and placeholder identify the field | Consumer placeholder text |
124
- | closed with a value | Selected option is represented in the trigger | Custom `renderValue` content |
125
- | pointer / popover | Anchored surface exposes the listbox without modal-dialog semantics | Default or explicit placement |
126
- | compact coarse pointer | BottomSheet exposes a modal dialog containing the listbox | Explicit `bottom-sheet` or resolved `adaptive` |
127
- | searching | Visible options, keyboard navigation, and announced result count use one filter | Consumer search and empty text |
128
- | loading | Trigger is busy; empty and no-results output is suppressed | Consumer loading duration |
129
- | disabled with reason | Trigger remains focusable enough to expose the reason while activation stays blocked | Consumer reason text |
130
- | read-only | Value stays focusable and submittable; menu, clear, and disclosure affordances are absent | Value rendering, status, and busy presentation |
131
- | selected/unselected rows | Row semantics and theming state are correct; both reserve the indicator column | Start/end position and themed indicator representation |
121
+ | State | Required invariant | Allowed variation |
122
+ | ------------------------ | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
123
+ | closed with no value | Label and placeholder identify the field | Consumer placeholder text |
124
+ | closed with a value | Selected option is represented in the trigger | Custom `renderValue` content |
125
+ | pointer / popover | Anchored surface exposes the listbox without modal-dialog semantics | Default or explicit placement |
126
+ | compact coarse pointer | BottomSheet exposes a modal dialog containing the listbox | Explicit `bottom-sheet` or resolved `adaptive` |
127
+ | searching | Visible options, keyboard navigation, and announced result count use one filter | Consumer search and empty text |
128
+ | loading | Trigger is busy; empty and no-results output is suppressed | Consumer loading duration |
129
+ | disabled with reason | Trigger remains focusable enough to expose the reason while activation stays blocked | Consumer reason text |
130
+ | read-only | Value stays focusable and submittable; menu, clear, and disclosure affordances are absent | Value rendering, status, and busy presentation |
131
+ | selected/unselected rows | Empty marks consume no width; visible marks retain space at the logical edge | State-dependent label position/width and themed indicators |
132
132
 
133
133
  ### Transformation and precedence order
134
134
 
@@ -176,12 +176,12 @@ These requirements describe shipped behavior on current `main`.
176
176
 
177
177
  No current design spec is linked.
178
178
 
179
- | Anatomy or state | Current representation requirement | Representation authority | Hierarchy role | Component contract |
180
- | -------------------------------- | --------------------------------------------------------------------------------------- | -------------------------- | ----------------------- | ------------------ |
181
- | selected mark and option spacing | Every row reserves the indicator column, preserving label alignment and available width | existing shipped behavior | supporting | FR3 |
182
- | input versus ghost trigger | none recorded | existing released behavior | form or toolbar control | Public concepts |
179
+ | Anatomy or state | Current representation requirement | Representation authority | Hierarchy role | Component contract |
180
+ | -------------------------------- | -------------------------------------------------------------------------------------------------- | -------------------------- | ----------------------- | ------------------ |
181
+ | selected mark and option spacing | Empty resolved marks collapse; visible marks retain space, so row label geometry may vary by state | `spec:AST-004/DEC-1` | supporting | FR3 |
182
+ | input versus ghost trigger | none recorded | existing released behavior | form or toolbar control | Public concepts |
183
183
 
184
- The approved but unimplemented replacement for the first row is owned by
184
+ The first row implements the state-derived spacing decision in
185
185
  `spec:AST-004/DEC-1`.
186
186
 
187
187
  ### Theming anatomy
@@ -257,16 +257,15 @@ content stays outside that target, while the targeted option row remains.
257
257
  current anatomy row.
258
258
 
259
259
  This map records only shipped reachability. It does not treat the accepted,
260
- unimplemented option-source behavior in `spec:AST-001` or indicator-space change
261
- in `spec:AST-004` as current runtime behavior.
260
+ unimplemented option-source behavior in `spec:AST-001` as current runtime behavior.
261
+ `spec:AST-004` governs FR3's shipped indicator-space behavior without adding a
262
+ new theming target.
262
263
 
263
264
  ## Family and system relationships
264
265
 
265
266
  - The current architecture links in frontmatter own public API, theming, icon
266
267
  resolution, interaction modality, and Layer behavior used by Selector.
267
- - `spec:AST-004/DEC-1` owns the accepted future indicator-space change. Its
268
- `accepted` phase is direction for implementation, not evidence that the runtime
269
- has changed.
268
+ - `spec:AST-004/DEC-1` owns FR3's shipped indicator-space behavior.
270
269
  - `spec:AST-011/DEC-1` owns the additive read-only state: caller-owned policy can
271
270
  preserve a focusable, submittable value without exposing selection controls.
272
271
  - `family:input-fields` owns family-wide state display, behavior, appearance,
@@ -276,21 +275,21 @@ in `spec:AST-004` as current runtime behavior.
276
275
 
277
276
  ## Verification map
278
277
 
279
- | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
280
- | --------------------- | -------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
281
- | FR1, AR2, AR3 | `Selector.test.tsx` selection, focus, and keyboard suites | closed/open, search/non-search, disabled option | Removing selection wiring, focus movement, or keyboard behavior fails the named interaction tests | `audit:Selector/behavior` |
282
- | FR2 | placement and selected-item geometry tests | default, explicit, RTL, transformed entry | Using the wrong positioning model fails the expected position-area or margin | `audit:Selector/behavior` |
283
- | FR3, AV1 | `itemMarkColumn` source inspection plus indicator-position and replacement-indicator tests | start/end, selected/unselected, check/radio | Removing the wrapper fails row-structure tests; changing its reserved width requires source/layout review | `audit:Selector/design-rendered` |
284
- | FR4, AR1, AR2 | presentation tests plus `aria-haspopup` source review | pointer, compact coarse pointer, search/non-search | Wrong Popover/dialog roles or focus destinations fail tests; `aria-haspopup` values require source/a11y review | `audit:Selector/accessibility` |
285
- | AR1, AV2 | `Selector.listbox.a11y.test.tsx` and `Listbox.a11y.chromium.spec.ts` against the shared Listbox contract | popover/sheet, flat/grouped, selected/unselected, disabled, search, loading, custom RTL | A new or wider loss of role, listbox/option name, state, or ownership gates; the exact unnamed no-search sheet state remains known debt | `audit:Selector/accessibility` |
286
- | FR5 | loading, empty-state, and live-region tests | empty options, unmatched search, loading | Empty/no-results output appears or is announced while loading | `audit:Selector/behavior` |
287
- | FR6, AR4 | read-only interaction, form, ARIA, and theme-state tests | search/non-search, clearable, open→read-only, disabled precedence | A value changes, popup or edit affordance remains, form value disappears, or read-only semantics are absent | `audit:Selector/accessibility` |
288
- | source-build contract | `Selector.source-build.test.mjs` | package source compiled by consumer Babel | Moving evaluated StyleX values outside the supported source form fails compilation | `audit:Selector/code-health` |
278
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
279
+ | --------------------- | -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
280
+ | FR1, AR2, AR3 | `Selector.test.tsx` selection, focus, and keyboard suites | closed/open, search/non-search, disabled option | Removing selection wiring, focus movement, or keyboard behavior fails the named interaction tests | `audit:Selector/behavior` |
281
+ | FR2 | placement and selected-item geometry tests | default, explicit, RTL, transformed entry | Using the wrong positioning model fails the expected position-area or margin | `audit:Selector/behavior` |
282
+ | FR3, AV1 | Focused `Selector.test.tsx` computed-display checks plus guarded Chromium Storybook matrix and stable visual stories | popover/bottom sheet, narrow/wide, start/end, LTR/RTL, selected/unselected, check/radio | Empty output reserves width, visible output collapses, logical placement flips incorrectly, or row content overflows | `audit:Selector/design-rendered` |
283
+ | FR4, AR1, AR2 | presentation tests plus `aria-haspopup` source review | pointer, compact coarse pointer, search/non-search | Wrong Popover/dialog roles or focus destinations fail tests; `aria-haspopup` values require source/a11y review | `audit:Selector/accessibility` |
284
+ | AR1, AV2 | `Selector.listbox.a11y.test.tsx` and `Listbox.a11y.chromium.spec.ts` against the shared Listbox contract | popover/sheet, flat/grouped, selected/unselected, disabled, search, loading, custom RTL | A new or wider loss of role, listbox/option name, state, or ownership gates; the exact unnamed no-search sheet state remains known debt | `audit:Selector/accessibility` |
285
+ | FR5 | loading, empty-state, and live-region tests | empty options, unmatched search, loading | Empty/no-results output appears or is announced while loading | `audit:Selector/behavior` |
286
+ | FR6, AR4 | read-only interaction, form, ARIA, and theme-state tests | search/non-search, clearable, open→read-only, disabled precedence | A value changes, popup or edit affordance remains, form value disappears, or read-only semantics are absent | `audit:Selector/accessibility` |
287
+ | source-build contract | `Selector.source-build.test.mjs` | package source compiled by consumer Babel | Moving evaluated StyleX values outside the supported source form fails compilation | `audit:Selector/code-health` |
289
288
 
290
289
  ## Decision log
291
290
 
292
- No component-local future decision is recorded here. Accepted unimplemented work
293
- is owned by `spec:AST-004`.
291
+ No component-local future decision is recorded here. FR3 implements the
292
+ system decision owned by `spec:AST-004/DEC-1`.
294
293
 
295
294
  ## Open questions
296
295
 
@@ -3728,67 +3728,73 @@ describe('Selector indicatorPosition', () => {
3728
3728
  ).toBeTruthy();
3729
3729
  });
3730
3730
 
3731
- it('reserves the mark column on every row, at either position', () => {
3732
- // The default check draws nothing when unchecked, so without a reserved
3733
- // column the chosen row would be laid out differently from the rest —
3734
- // indented at the start, truncating earlier at the end. Every row is two
3735
- // children wide either way, so a row's geometry does not depend on whether
3736
- // it happens to be the chosen one.
3737
- const {unmount} = render(
3738
- <Selector
3739
- label="Fruit"
3740
- options={OPTIONS}
3741
- value="Banana"
3742
- onChange={() => {}}
3743
- indicatorPosition="start"
3744
- isDefaultOpen
3745
- />,
3746
- );
3747
- for (const row of openRows()) {
3748
- expect(row.children).toHaveLength(2);
3749
- }
3750
- unmount();
3751
-
3752
- render(
3753
- <Selector
3754
- label="Fruit"
3755
- options={OPTIONS}
3756
- value="Banana"
3757
- onChange={() => {}}
3758
- isDefaultOpen
3759
- />,
3760
- );
3761
- for (const row of openRows()) {
3762
- expect(row.children).toHaveLength(2);
3763
- }
3764
- });
3765
-
3766
- it('positions a themed replacement indicator the same way', () => {
3767
- const theme = defineTheme({
3768
- name: 'selector-start-radio-mark-test',
3769
- indicators: {check: RadioIndicator},
3770
- });
3771
- render(
3772
- <Theme theme={theme}>
3731
+ it.each(['start', 'end'] as const)(
3732
+ 'collapses an empty default mark at the logical %s edge',
3733
+ indicatorPosition => {
3734
+ render(
3773
3735
  <Selector
3774
3736
  label="Fruit"
3775
3737
  options={OPTIONS}
3776
3738
  value="Banana"
3777
3739
  onChange={() => {}}
3778
- indicatorPosition="start"
3740
+ indicatorPosition={indicatorPosition}
3779
3741
  isDefaultOpen
3780
- />
3781
- </Theme>,
3782
- );
3783
- for (const row of openRows()) {
3784
- const radio = row.querySelector('.astryx-radio')!;
3785
- const content = row.querySelector('.astryx-selector-option')!;
3786
- expect(
3787
- content.compareDocumentPosition(radio) &
3788
- Node.DOCUMENT_POSITION_PRECEDING,
3789
- ).toBeTruthy();
3790
- }
3791
- });
3742
+ />,
3743
+ );
3744
+
3745
+ for (const row of openRows()) {
3746
+ const isSelected = row.getAttribute('aria-selected') === 'true';
3747
+ const markColumn =
3748
+ indicatorPosition === 'start'
3749
+ ? row.firstElementChild
3750
+ : row.lastElementChild;
3751
+ expect(markColumn).not.toBeNull();
3752
+ expect(markColumn).toHaveStyle({
3753
+ display: isSelected ? 'inline-flex' : 'none',
3754
+ });
3755
+ }
3756
+ },
3757
+ );
3758
+
3759
+ it.each(['start', 'end'] as const)(
3760
+ 'keeps a themed indicator in layout at the logical %s edge',
3761
+ indicatorPosition => {
3762
+ const theme = defineTheme({
3763
+ name: `selector-${indicatorPosition}-radio-mark-test`,
3764
+ indicators: {check: RadioIndicator},
3765
+ });
3766
+ render(
3767
+ <Theme theme={theme}>
3768
+ <Selector
3769
+ label="Fruit"
3770
+ options={OPTIONS}
3771
+ value="Banana"
3772
+ onChange={() => {}}
3773
+ indicatorPosition={indicatorPosition}
3774
+ isDefaultOpen
3775
+ />
3776
+ </Theme>,
3777
+ );
3778
+
3779
+ for (const row of openRows()) {
3780
+ const markColumn =
3781
+ indicatorPosition === 'start'
3782
+ ? row.firstElementChild
3783
+ : row.lastElementChild;
3784
+ const radio = row.querySelector('.astryx-radio');
3785
+ const content = row.querySelector('.astryx-selector-option');
3786
+ expect(markColumn).toHaveStyle({display: 'inline-flex'});
3787
+ expect(radio).not.toBeNull();
3788
+ expect(content).not.toBeNull();
3789
+ expect(
3790
+ content!.compareDocumentPosition(radio!) &
3791
+ (indicatorPosition === 'start'
3792
+ ? Node.DOCUMENT_POSITION_PRECEDING
3793
+ : Node.DOCUMENT_POSITION_FOLLOWING),
3794
+ ).toBeTruthy();
3795
+ }
3796
+ },
3797
+ );
3792
3798
  });
3793
3799
 
3794
3800
  describe('Selector popup theme target', () => {
@@ -4,15 +4,17 @@
4
4
 
5
5
  /**
6
6
  * @file Selector.tsx
7
- * @input Uses React, StyleX, usePopover, useTooltip, Icon, InputGroupContext,
8
- * and Selector positioning hooks
9
- * @output Exports Selector component
7
+ * @input Uses React, StyleX, adaptive selection surfaces, theme-resolved
8
+ * indicators, Field, and InputGroup context
9
+ * @output Exports Selector with content-derived option-mark layout
10
10
  * @position Core implementation; consumed by index.ts
11
11
  *
12
12
  * SYNC: When modified, update:
13
+ * - /packages/core/src/Selector/Selector.spec.md
13
14
  * - /packages/core/src/Selector/Selector.doc.mjs
14
15
  * - /packages/core/src/Selector/Selector.test.tsx
15
16
  * - /packages/core/src/Selector/index.ts
17
+ * - /apps/storybook/stories/Selector.stories.tsx
16
18
  * - /apps/storybook/stories/InputGroup.stories.tsx
17
19
  * - /packages/cli/assets/templates/blocks/components/Selector/ (showcase blocks)
18
20
  */
@@ -367,18 +369,20 @@ const styles = stylex.create({
367
369
  flex: 1,
368
370
  minWidth: 0,
369
371
  },
370
- // The mark's column, reserved on every row and at either position, so a row
371
- // occupies the same geometry whether or not it is the chosen one — the
372
- // default check draws nothing when unchecked, and without the column a list
373
- // would indent (or truncate) its chosen row differently from the rest.
374
- // `minWidth` rather than `width`: a theme can replace `check` with a larger
375
- // indicator (a radio is 20px at `sm`), and the column has to grow with it.
372
+ // The wrapper keeps a visible mark at either logical position and lets a
373
+ // replacement indicator grow beyond the default 1rem minimum. When the
374
+ // resolved indicator draws nothing, `:empty` removes the wrapper from layout
375
+ // instead of reserving blank space. Selected and unselected labels may
376
+ // therefore shift or have different available width by design (AST-004).
376
377
  itemMarkColumn: {
377
378
  display: 'inline-flex',
378
379
  alignItems: 'center',
379
380
  justifyContent: 'center',
380
381
  flexShrink: 0,
381
382
  minWidth: '1rem',
383
+ ':empty': {
384
+ display: 'none',
385
+ },
382
386
  },
383
387
  itemCheckmark: {
384
388
  flexShrink: 0,
@@ -654,10 +658,10 @@ interface SelectorPropsBase<
654
658
  renderValue?: (option: SelectorOptionData) => ReactNode;
655
659
 
656
660
  /**
657
- * Which edge of the option row carries the selected mark. `start` reserves a
658
- * mark column ahead of every label so they stay aligned, the way a native
659
- * menu does; `end` is the house convention shared with Typeahead and
660
- * CommandPalette.
661
+ * Which logical edge of the option row carries a rendered selection mark. An
662
+ * empty mark consumes no space, so selected and unselected labels may shift or
663
+ * have different available width. `end` is the house convention shared with
664
+ * Typeahead and CommandPalette.
661
665
  *
662
666
  * @default 'end'
663
667
  */