@astryxdesign/core 0.7.7-canary.f9ac85f → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts +13 -0
  3. package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts.map +1 -0
  4. package/dist/AppShell/useCloseMobileDrawerOnActivate.js +45 -0
  5. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  6. package/dist/BottomSheet/BottomSheet.js +79 -9
  7. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  8. package/dist/BottomSheet/BottomSheetPanel.js +1 -1
  9. package/dist/BottomSheet/bottomSheetStack.d.ts.map +1 -1
  10. package/dist/BottomSheet/bottomSheetStack.js +5 -4
  11. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +2 -1
  12. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  13. package/dist/Breadcrumbs/BreadcrumbItem.js +7 -3
  14. package/dist/Button/Button.d.ts.map +1 -1
  15. package/dist/Button/Button.js +30 -2
  16. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  17. package/dist/CheckboxList/CheckboxList.js +7 -1
  18. package/dist/CheckboxList/CheckboxListContext.d.ts +6 -0
  19. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  20. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  21. package/dist/CheckboxList/CheckboxListItem.js +10 -2
  22. package/dist/DateInput/DateInput.d.ts.map +1 -1
  23. package/dist/DateInput/DateInput.js +5 -2
  24. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  25. package/dist/DateInput/MonthScroller.js +13 -27
  26. package/dist/DateInput/Wheel.d.ts.map +1 -1
  27. package/dist/DateInput/Wheel.js +22 -41
  28. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  29. package/dist/DateTimeInput/DateTimeInput.js +5 -2
  30. package/dist/Dialog/Dialog.d.ts.map +1 -1
  31. package/dist/Dialog/Dialog.js +4 -3
  32. package/dist/Drawer/Drawer.js +0 -1
  33. package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -1
  34. package/dist/Drawer/useDrawerDialogPresence.js +5 -1
  35. package/dist/Field/PanelSearchInput.d.ts +9 -1
  36. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  37. package/dist/Field/PanelSearchInput.js +3 -1
  38. package/dist/InfoTip/InfoTip.d.ts +2 -2
  39. package/dist/InfoTip/InfoTip.js +4 -4
  40. package/dist/Layer/layerAnimations.stylex.d.ts +7 -8
  41. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  42. package/dist/Layer/layerAnimations.stylex.js +25 -20
  43. package/dist/Layer/usePressPin.d.ts +3 -5
  44. package/dist/Layer/usePressPin.d.ts.map +1 -1
  45. package/dist/Layer/usePressPin.js +4 -6
  46. package/dist/MobileNav/MobileNav.js +0 -1
  47. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  48. package/dist/MultiSelector/hooks.js +2 -4
  49. package/dist/Outline/Outline.d.ts +2 -4
  50. package/dist/Outline/Outline.d.ts.map +1 -1
  51. package/dist/Outline/Outline.js +4 -52
  52. package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
  53. package/dist/Outline/useOutlineFromMarkdown.js +1 -0
  54. package/dist/Outline/useScrollSpy.js +3 -3
  55. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  56. package/dist/PowerSearch/PowerSearch.js +134 -179
  57. package/dist/RadioList/RadioList.d.ts.map +1 -1
  58. package/dist/RadioList/RadioList.js +5 -0
  59. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  60. package/dist/Resizable/ResizeHandle.js +118 -260
  61. package/dist/Resizable/resizeGesture.d.ts +91 -0
  62. package/dist/Resizable/resizeGesture.d.ts.map +1 -0
  63. package/dist/Resizable/resizeGesture.js +251 -0
  64. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  65. package/dist/SideNav/SideNavItem.js +3 -9
  66. package/dist/Token/Token.d.ts +2 -2
  67. package/dist/Token/Token.d.ts.map +1 -1
  68. package/dist/Token/Token.js +12 -4
  69. package/dist/Token/TokenNamingContext.d.ts +14 -0
  70. package/dist/Token/TokenNamingContext.d.ts.map +1 -0
  71. package/dist/Token/TokenNamingContext.js +23 -0
  72. package/dist/Tokenizer/Tokenizer.d.ts +46 -59
  73. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  74. package/dist/Tokenizer/Tokenizer.js +35 -58
  75. package/dist/Tokenizer/TokenizerBottomSheet.d.ts +25 -28
  76. package/dist/Tokenizer/TokenizerBottomSheet.d.ts.map +1 -1
  77. package/dist/Tokenizer/TokenizerBottomSheet.js +157 -54
  78. package/dist/Tokenizer/TokenizerItemRemovableContext.d.ts.map +1 -1
  79. package/dist/Tokenizer/TokenizerItemRemovableContext.js +5 -3
  80. package/dist/Tooltip/Tooltip.d.ts +25 -30
  81. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  82. package/dist/Tooltip/Tooltip.js +24 -5
  83. package/dist/Tooltip/index.d.ts +1 -1
  84. package/dist/Tooltip/index.d.ts.map +1 -1
  85. package/dist/Tooltip/useTooltip.d.ts +28 -29
  86. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  87. package/dist/Tooltip/useTooltip.js +20 -8
  88. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  89. package/dist/TopNav/TopNavItem.js +4 -7
  90. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  91. package/dist/TopNav/TopNavMegaMenuItem.js +4 -7
  92. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  93. package/dist/TopNav/TopNavMenu.js +4 -7
  94. package/dist/astryx.css +5 -5
  95. package/dist/gestures/GestureHost.d.ts +27 -1
  96. package/dist/gestures/GestureHost.d.ts.map +1 -1
  97. package/dist/gestures/GestureHost.js +90 -8
  98. package/dist/hooks/useHighlightedOptionScroll.d.ts +11 -1
  99. package/dist/hooks/useHighlightedOptionScroll.d.ts.map +1 -1
  100. package/dist/hooks/useHighlightedOptionScroll.js +103 -5
  101. package/dist/hooks/useMenuPress.d.ts.map +1 -1
  102. package/dist/hooks/useMenuPress.js +4 -2
  103. package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.d.ts.map +1 -1
  104. package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.js +1 -0
  105. package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.d.ts.map +1 -1
  106. package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.js +7 -1
  107. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  108. package/dist/i18n/generated-locales/en.generated.js +1 -0
  109. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  110. package/dist/i18n/generated-locales/pseudo.generated.js +1 -0
  111. package/dist/internal/SheetConfirmation.d.ts +11 -0
  112. package/dist/internal/SheetConfirmation.d.ts.map +1 -0
  113. package/dist/internal/SheetConfirmation.js +92 -0
  114. package/dist/internal/sheetDraft.d.ts +35 -0
  115. package/dist/internal/sheetDraft.d.ts.map +1 -0
  116. package/dist/internal/sheetDraft.js +65 -0
  117. package/dist/internal/touch-sheet.d.ts +21 -0
  118. package/dist/internal/touch-sheet.d.ts.map +1 -0
  119. package/dist/internal/touch-sheet.js +19 -0
  120. package/dist/internal/useDismissSearchOnTouchDrag.d.ts +24 -0
  121. package/dist/internal/useDismissSearchOnTouchDrag.d.ts.map +1 -0
  122. package/dist/internal/useDismissSearchOnTouchDrag.js +56 -0
  123. package/dist/motion/reducedMotion.d.ts +24 -0
  124. package/dist/motion/reducedMotion.d.ts.map +1 -1
  125. package/dist/motion/reducedMotion.js +78 -1
  126. package/dist/utils/clickGuard.d.ts +8 -1
  127. package/dist/utils/clickGuard.d.ts.map +1 -1
  128. package/dist/utils/clickGuard.js +24 -1
  129. package/locales/en.json +4 -0
  130. package/locales/pseudo.json +3 -0
  131. package/package.json +9 -9
  132. package/src/AppShell/useCloseMobileDrawerOnActivate.ts +52 -0
  133. package/src/BottomSheet/BottomSheet.tsx +95 -10
  134. package/src/BottomSheet/BottomSheetPanel.tsx +7 -2
  135. package/src/BottomSheet/bottomSheetStack.ts +8 -4
  136. package/src/Breadcrumbs/BreadcrumbItem.tsx +13 -3
  137. package/src/Button/Button.tsx +37 -1
  138. package/src/CheckboxList/CheckboxList.doc.mjs +4 -2
  139. package/src/CheckboxList/CheckboxList.tsx +6 -0
  140. package/src/CheckboxList/CheckboxListContext.tsx +6 -0
  141. package/src/CheckboxList/CheckboxListItem.tsx +11 -2
  142. package/src/DateInput/DateInput.tsx +8 -2
  143. package/src/DateInput/MonthScroller.tsx +18 -22
  144. package/src/DateInput/Wheel.tsx +30 -32
  145. package/src/DateTimeInput/DateTimeInput.tsx +8 -2
  146. package/src/Dialog/Dialog.tsx +19 -4
  147. package/src/Drawer/Drawer.tsx +1 -1
  148. package/src/Drawer/useDrawerDialogPresence.ts +5 -1
  149. package/src/Field/PanelSearchInput.tsx +11 -0
  150. package/src/InfoTip/InfoTip.doc.mjs +3 -3
  151. package/src/InfoTip/InfoTip.tsx +5 -5
  152. package/src/Layer/layerAnimations.stylex.ts +24 -16
  153. package/src/Layer/usePressPin.ts +5 -11
  154. package/src/MobileNav/MobileNav.tsx +1 -1
  155. package/src/MultiSelector/hooks.ts +2 -4
  156. package/src/Outline/Outline.doc.mjs +177 -51
  157. package/src/Outline/Outline.tsx +5 -57
  158. package/src/Outline/useOutlineFromMarkdown.doc.mjs +157 -0
  159. package/src/Outline/useOutlineFromMarkdown.ts +1 -0
  160. package/src/Outline/useScrollSpy.ts +3 -3
  161. package/src/PowerSearch/PowerSearch.doc.mjs +3 -3
  162. package/src/PowerSearch/PowerSearch.tsx +196 -183
  163. package/src/RadioList/RadioList.doc.mjs +4 -2
  164. package/src/RadioList/RadioList.tsx +4 -0
  165. package/src/Resizable/ResizeHandle.tsx +133 -328
  166. package/src/Resizable/resizeGesture.ts +368 -0
  167. package/src/SideNav/SideNavItem.tsx +3 -7
  168. package/src/Token/Token.tsx +13 -4
  169. package/src/Token/TokenNamingContext.ts +28 -0
  170. package/src/Tokenizer/Tokenizer.doc.mjs +16 -0
  171. package/src/Tokenizer/Tokenizer.tsx +81 -86
  172. package/src/Tokenizer/TokenizerBottomSheet.tsx +219 -93
  173. package/src/Tokenizer/TokenizerItemRemovableContext.ts +5 -3
  174. package/src/Tooltip/Tooltip.doc.mjs +123 -54
  175. package/src/Tooltip/Tooltip.tsx +36 -39
  176. package/src/Tooltip/index.ts +0 -1
  177. package/src/Tooltip/useTooltip.doc.mjs +147 -33
  178. package/src/Tooltip/useTooltip.tsx +39 -32
  179. package/src/TopNav/TopNavItem.tsx +6 -5
  180. package/src/TopNav/TopNavMegaMenuItem.tsx +6 -5
  181. package/src/TopNav/TopNavMenu.tsx +6 -5
  182. package/src/gestures/GestureHost.ts +105 -10
  183. package/src/hooks/useHighlightedOptionScroll.ts +113 -9
  184. package/src/hooks/useMenuPress.ts +8 -2
  185. package/src/i18n/generated-english/PowerSearch/PowerSearch.generated.ts +1 -0
  186. package/src/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.ts +7 -1
  187. package/src/i18n/generated-locales/en.generated.ts +1 -0
  188. package/src/i18n/generated-locales/pseudo.generated.ts +1 -0
  189. package/src/internal/SheetConfirmation.tsx +101 -0
  190. package/src/internal/sheetDraft.ts +80 -0
  191. package/src/internal/touch-sheet.ts +21 -0
  192. package/src/internal/useDismissSearchOnTouchDrag.ts +69 -0
  193. package/src/motion/reducedMotion.ts +79 -1
  194. package/src/utils/clickGuard.ts +37 -1
  195. package/dist/internal/tokenizer-presentation.d.ts +0 -21
  196. package/dist/internal/tokenizer-presentation.d.ts.map +0 -1
  197. package/dist/internal/tokenizer-presentation.js +0 -21
  198. package/src/internal/tokenizer-presentation.ts +0 -25
@@ -6,16 +6,10 @@
6
6
  * @file Tokenizer.tsx
7
7
  * @input Uses React, BaseTypeahead, Field, Token, useAnnounce,
8
8
  * useAdaptivePresentation, and TokenizerBottomSheet (lazy)
9
- * @output Exports Tokenizer multi-select typeahead component, and the
10
- * internal TokenizerWithPresentation signature the `internal/tokenizer-presentation`
11
- * entry re-exports
9
+ * @output Exports Tokenizer multi-select typeahead component
12
10
  * @position Composed component; forwards DOM ref and exposes focus control via
13
- * handleRef. The sheet presentation — a trigger that opens
14
- * TokenizerBottomSheet — is implemented but not exposed: `TokenizerProps`
15
- * offers no `experimental.presentation` while its user experience is being
16
- * finished (`component:Tokenizer` DEC-3, implementation gap), and only
17
- * Astryx's own packages reach it through the internal entry
18
- * (`architecture:react-component-runtime` INV13).
11
+ * handleRef. The experimental `presentation` option swaps the inline
12
+ * combobox for a trigger that opens TokenizerBottomSheet.
19
13
  *
20
14
  * SYNC: When modified, update:
21
15
  * - /packages/core/src/Tokenizer/index.ts
@@ -96,6 +90,7 @@ import {composeEventHandlers, mergeProps} from '../utils';
96
90
  import {joinAriaIDs} from '../utils/inputAria';
97
91
  import {isFocusDetached} from '../utils/focusReturn';
98
92
  import {themeProps} from '../utils/themeProps';
93
+ import type {SheetDraftChange} from '../internal/sheetDraft';
99
94
  import type {TokenizerBottomSheetProps} from './TokenizerBottomSheet';
100
95
  import {useResolvedRequired} from '../hooks/useResolvedRequired';
101
96
  import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement';
@@ -148,36 +143,6 @@ export interface TokenizerHandle {
148
143
  blur(): void;
149
144
  }
150
145
 
151
- /**
152
- * The presentation options the sheet-capable signature admits. Not public:
153
- * `TokenizerProps` has no `experimental` while the sheet's user experience is
154
- * being finished; Astryx's own packages reach the sheet through
155
- * `@astryxdesign/core/internal/tokenizer-presentation`.
156
- */
157
- export interface TokenizerPresentationOptions {
158
- /**
159
- * How the field presents its choices.
160
- * - `'popover'`: the inline combobox with its anchored suggestion menu —
161
- * the stable behavior.
162
- * - `'bottom-sheet'`: a trigger showing the selected tokens that opens one
163
- * tall modal sheet: a search row on top, full-width checkbox rows that
164
- * keep their place while the sheet is open, a create row with
165
- * `hasCreate`, and a footer with a confirmed Clear all and Done. Suited
166
- * to compact touch screens, where the inline combobox competes with the
167
- * software keyboard.
168
- * - `'adaptive'`: the bottom sheet on compact coarse-pointer screens,
169
- * otherwise the popover — the same resolution Selector uses.
170
- *
171
- * In the sheet presentation the trigger is the control: `ref`, `id`,
172
- * `aria-*`, and `data-*` reach it. `renderItem`, `renderToken`,
173
- * `endContent`, `hasClear`, `hasEntriesOnFocus`, `menuWidth`,
174
- * `tokenOverflowBehavior`, and `hasAutoFocus` have no surface to act on
175
- * and warn once in development.
176
- * @default 'popover'
177
- */
178
- presentation?: 'popover' | 'bottom-sheet' | 'adaptive';
179
- }
180
-
181
146
  export interface TokenizerProps<T extends SearchableItem> extends Omit<
182
147
  FieldInputBaseProps<HTMLInputElement>,
183
148
  'onChange' | 'onFocus' | 'onBlur' | 'onKeyDown'
@@ -335,26 +300,58 @@ export interface TokenizerProps<T extends SearchableItem> extends Omit<
335
300
  ref?: React.Ref<HTMLInputElement>;
336
301
  /** Imperative handle ref for focus/blur control. */
337
302
  handleRef?: React.Ref<TokenizerHandle>;
303
+ /**
304
+ * Experimental options, outside the stable compatibility promise: anything
305
+ * in this object may change or be removed in a patch release. Omit it for
306
+ * the stable behavior.
307
+ * @experimental
308
+ */
309
+ experimental?: {
310
+ /**
311
+ * How the field presents its choices.
312
+ * - `'popover'`: the inline combobox with its anchored suggestion menu —
313
+ * the stable behavior.
314
+ * - `'bottom-sheet'`: a trigger showing the selected tokens that opens one
315
+ * tall modal sheet: a search row on top, full-width checkbox rows that
316
+ * keep their place while the sheet is open, a create row with
317
+ * `hasCreate`, and a footer pinned in every state with a confirmed Clear
318
+ * all and Save. The sheet edits a draft: `onChange` fires once, when Save
319
+ * commits it, and a swipe, scrim or Escape close of a changed draft asks
320
+ * before discarding. Suited to compact touch screens, where the inline
321
+ * combobox competes with the software keyboard.
322
+ * - `'adaptive'`: the bottom sheet on compact coarse-pointer screens,
323
+ * otherwise the popover — the same resolution Selector uses.
324
+ *
325
+ * In the sheet presentation the trigger is the control: `ref`, `id`,
326
+ * `aria-*`, and `data-*` reach it. `renderItem`, `renderToken`,
327
+ * `endContent`, `hasClear`, `hasEntriesOnFocus`, `menuWidth`,
328
+ * `tokenOverflowBehavior`, and `hasAutoFocus` have no surface to act on
329
+ * and warn once in development.
330
+ * @experimental
331
+ * @default 'popover'
332
+ */
333
+ presentation?: 'popover' | 'bottom-sheet' | 'adaptive';
334
+ };
338
335
  }
339
336
 
340
337
  /**
341
- * The props of the sheet-capable signature: `TokenizerProps` plus the
342
- * presentation options and a `ref` that also accepts the sheet presentation's
343
- * trigger button, the control it stands for there. Not public: reached only
344
- * as `TokenizerWithPresentation` through the internal entry, so the released
345
- * `TokenizerProps` — an interface, so `interface X extends TokenizerProps<T>`,
346
- * `Omit<TokenizerProps<T>, …>` and `ComponentProps<typeof Tokenizer>` keep
347
- * their released shapes — names no presentation.
338
+ * The props the experimental opt-in admits. `experimental` is present, and
339
+ * `ref` also accepts the sheet presentation's trigger button, the control it
340
+ * stands for there. Only the overload below reaches this type. `TokenizerProps`
341
+ * stays the component's declared props type: it is an interface, so
342
+ * `interface X extends TokenizerProps<T>`, `Omit<TokenizerProps<T>, …>` and
343
+ * `ComponentProps<typeof Tokenizer>` keep their released shapes.
348
344
  */
349
- export type TokenizerWithPresentationProps<T extends SearchableItem> = Omit<
345
+ type TokenizerExperimentalProps<T extends SearchableItem> = Omit<
350
346
  TokenizerProps<T>,
351
- 'ref'
347
+ 'ref' | 'experimental'
352
348
  > & {
353
- /** How the field presents its choices; see `TokenizerPresentationOptions`. */
354
- experimental?: TokenizerPresentationOptions;
349
+ /** @experimental */
350
+ experimental: NonNullable<TokenizerProps<T>['experimental']>;
355
351
  /**
356
352
  * Ref forwarded to the control: the combobox input in the popover
357
353
  * presentation, the trigger button in the sheet presentation.
354
+ * @experimental
358
355
  */
359
356
  ref?: React.Ref<HTMLInputElement | HTMLButtonElement>;
360
357
  };
@@ -724,10 +721,13 @@ function EndLane({
724
721
  );
725
722
  }
726
723
 
727
- // The public signature names no presentation: `ComponentProps<typeof
728
- // Tokenizer>` and every public call resolve against `TokenizerProps`. The
729
- // implementation also reads the presentation options, reached only through
730
- // `TokenizerWithPresentation` below.
724
+ // Keep this overload first. TypeScript resolves `ComponentProps<typeof
725
+ // Tokenizer>` against the last overload, so that type stays the stable props.
726
+ // A call that passes `experimental` matches this one and may pass the wider
727
+ // `ref`.
728
+ export function Tokenizer<T extends SearchableItem>(
729
+ props: TokenizerExperimentalProps<T>,
730
+ ): React.JSX.Element;
731
731
  export function Tokenizer<T extends SearchableItem>(
732
732
  props: TokenizerProps<T>,
733
733
  ): React.JSX.Element;
@@ -781,7 +781,7 @@ export function Tokenizer<T extends SearchableItem>({
781
781
  'aria-describedby': ariaDescribedByProp,
782
782
  'aria-labelledby': ariaLabelledByProp,
783
783
  ...restProps
784
- }: TokenizerWithPresentationProps<T>): React.JSX.Element {
784
+ }: TokenizerProps<T> | TokenizerExperimentalProps<T>): React.JSX.Element {
785
785
  const isEffectivelyRequired = useResolvedRequired({isRequired, isOptional});
786
786
  const {rest, container} = useFieldInputStyling('Tokenizer', restProps, {
787
787
  containerXstyle,
@@ -844,6 +844,17 @@ export function Tokenizer<T extends SearchableItem>({
844
844
  },
845
845
  [onChangeQuery],
846
846
  );
847
+ // Save's one report. The sheet announced each edit as it happened, so
848
+ // nothing is announced here; a field that is disabled at Save reports
849
+ // nothing.
850
+ const handleSheetCommit = useCallback(
851
+ (items: T[], change: SheetDraftChange<T>) => {
852
+ if (!isDisabled) {
853
+ onChange(items, change);
854
+ }
855
+ },
856
+ [isDisabled, onChange],
857
+ );
847
858
  // Set around a focus move the field makes itself — a presentation swap
848
859
  // handing focus from one control to the other — so the receiving control
849
860
  // does not report it as focus entering from outside.
@@ -1009,9 +1020,8 @@ export function Tokenizer<T extends SearchableItem>({
1009
1020
  // `endContent`, a clear button, or all three — so its width is measured
1010
1021
  // rather than assumed, and the input reserves it.
1011
1022
  const [laneRef, laneReserve] = useEndLaneReserve(END_LANE_INSET);
1012
- // A composing component can lock tokens (PowerSearch's read-only field).
1013
- // Backspace and the token remove controls skip a locked token. Clear all
1014
- // does not check it.
1023
+ // A composing component can lock tokens (PowerSearch's read-only filters
1024
+ // and read-only field). No remove path removes a locked token.
1015
1025
  const isItemRemovable = use(TokenizerItemRemovableContext);
1016
1026
  const canRemoveItem = useCallback(
1017
1027
  (item: T) => isItemRemovable?.(item) ?? true,
@@ -1021,7 +1031,7 @@ export function Tokenizer<T extends SearchableItem>({
1021
1031
  // is the leaf's own business — folding it in here would mean reading the
1022
1032
  // busy state during this render, which is exactly the re-render the store
1023
1033
  // exists to avoid.
1024
- const hasClearButton = hasClear && value.length > 0 && !isDisabled;
1034
+ const hasClearButton = hasClear && !isDisabled && value.some(canRemoveItem);
1025
1035
  const hasStaticEndLane = Boolean(endContent || hasClearButton);
1026
1036
  const [isFocusedWithin, setIsFocusedWithin] = useState(false);
1027
1037
  const isTruncated =
@@ -1287,19 +1297,22 @@ export function Tokenizer<T extends SearchableItem>({
1287
1297
  [value, onChange, isDisabled, canRemoveItem, announce, t],
1288
1298
  );
1289
1299
 
1290
- // Handle clearing all items
1291
1300
  // A composing component that words its own Clear all (PowerSearch) provides
1292
1301
  // its words; otherwise the field's own (component:Tokenizer AR7).
1293
1302
  const clearAllMessage =
1294
1303
  use(TokenizerClearAllMessageContext) ?? t('@astryx.tokenizer.allRemoved');
1295
1304
  const handleClearAll = useCallback(
1296
1305
  (options?: {silent?: boolean}) => {
1297
- if (isDisabled || value.length === 0) {
1306
+ const removedItems = value.filter(canRemoveItem);
1307
+ if (isDisabled || removedItems.length === 0) {
1298
1308
  return;
1299
1309
  }
1300
- // Report the last item as removed (convention)
1301
- const lastItem = value[value.length - 1];
1302
- onChange([], {item: lastItem, type: 'remove'});
1310
+ // onChange names one item. Clear all names the last one it removes.
1311
+ const lastRemovedItem = removedItems[removedItems.length - 1];
1312
+ onChange(
1313
+ value.filter(item => !canRemoveItem(item)),
1314
+ {item: lastRemovedItem, type: 'remove'},
1315
+ );
1303
1316
  // Focus moves to the input, so the emptied selection is announced, in
1304
1317
  // the sheet's words (WCAG 4.1.3). The sheet announces its own Clear all.
1305
1318
  if (!options?.silent) {
@@ -1307,7 +1320,7 @@ export function Tokenizer<T extends SearchableItem>({
1307
1320
  }
1308
1321
  inputRef.current?.focus();
1309
1322
  },
1310
- [value, onChange, isDisabled, announce, clearAllMessage],
1323
+ [value, onChange, isDisabled, canRemoveItem, announce, clearAllMessage],
1311
1324
  );
1312
1325
 
1313
1326
  // Handle backspace on empty input — remove last token
@@ -1370,7 +1383,7 @@ export function Tokenizer<T extends SearchableItem>({
1370
1383
  key={item.id}
1371
1384
  label={item.label}
1372
1385
  size={size}
1373
- onRemove={isDisabled ? undefined : onRemoveItem}
1386
+ onRemove={isDisabled || !canRemoveItem(item) ? undefined : onRemoveItem}
1374
1387
  isDisabled={isDisabled}
1375
1388
  xstyle={styles.token}
1376
1389
  />
@@ -1715,15 +1728,13 @@ export function Tokenizer<T extends SearchableItem>({
1715
1728
  label={label}
1716
1729
  searchSource={searchSource}
1717
1730
  value={value}
1718
- isAtMax={isAtMax}
1731
+ maxEntries={maxEntries}
1719
1732
  hasCreate={hasCreate}
1720
1733
  maxMenuItems={maxMenuItems ?? 10}
1721
1734
  minQueryLength={minQueryLength ?? 1}
1722
1735
  debounceMs={debounceMs ?? 150}
1723
1736
  emptySearchText={emptySearchText}
1724
- onAdd={handleAdd}
1725
- onRemove={handleRemove}
1726
- onClearAll={() => handleClearAll({silent: true})}
1737
+ onCommit={handleSheetCommit}
1727
1738
  onChangeQuery={handleSheetQueryChange}
1728
1739
  />
1729
1740
  )}
@@ -1830,19 +1841,3 @@ export function Tokenizer<T extends SearchableItem>({
1830
1841
  }
1831
1842
 
1832
1843
  Tokenizer.displayName = 'Tokenizer';
1833
-
1834
- /**
1835
- * The same component with the sheet presentation reachable: `experimental`
1836
- * (`presentation: 'popover' | 'bottom-sheet' | 'adaptive'`) and a `ref` that
1837
- * also takes the trigger button. Not public — re-exported only by
1838
- * `@astryxdesign/core/internal/tokenizer-presentation`, the entry reserved
1839
- * for Astryx's own packages (`architecture:react-component-runtime` INV13),
1840
- * while the sheet's user experience is being finished (`component:Tokenizer`
1841
- * DEC-3). The public `Tokenizer` renders the popover presentation for every
1842
- * field.
1843
- */
1844
- export const TokenizerWithPresentation = Tokenizer as <
1845
- T extends SearchableItem,
1846
- >(
1847
- props: TokenizerWithPresentationProps<T>,
1848
- ) => React.JSX.Element;