@astryxdesign/core 0.3.0-canary.98ea85c → 0.3.0-canary.b26c2b2

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.
@@ -10,6 +10,11 @@
10
10
  *
11
11
  * Both modes use useListFocus for DOM-based keyboard navigation.
12
12
  *
13
+ * Initial focus on open follows the input modality: a keyboard open
14
+ * (Enter / Space / ArrowDown on the trigger) focuses the first enabled item
15
+ * (APG menu-button); a pointer open focuses the menu container itself so no
16
+ * item reads as pre-selected, and the first ArrowDown then moves to item 1.
17
+ *
13
18
  * SYNC: When modified, update these files to stay in sync:
14
19
  * - /packages/core/src/DropdownMenu/DropdownMenu.doc.mjs
15
20
  * - /packages/core/src/DropdownMenu/DropdownMenu.test.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"DropdownMenu.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenu.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,EAOZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,EAAS,KAAK,WAAW,EAAC,MAAM,WAAW,CAAC;AAInD,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,oBAAoB,CAAC;AAa9D,OAAO,KAAK,EAAC,cAAc,EAAE,cAAc,EAAC,MAAM,mBAAmB,CAAC;AAQtE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAyC5C;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAChD,qBAAqB,EACrB,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,CAC9C;IACC;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,oBAAoB,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,kBAAkB,GAC5B,oBAAoB,GAAG,mBAAmB,GAAG,mBAAmB,CAAC;AAMnE,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AAEnE,UAAU,qBAAsB,SAAQ,SAAS;IAC/C,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,qBAAsB,SAAQ,qBAAqB;IAC3D,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,UAAU,yBAA0B,SAAQ,qBAAqB;IAC/D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GAC3B,qBAAqB,GAAG,yBAAyB,CAAC;AA+BpD,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EAAE,eAAe,EACvB,UAAU,EAAE,gBAAgB,EAC5B,YAAY,EACZ,SAAS,EACT,OAAO,EACP,UAAiB,EACjB,SAAmB,EACnB,SAAmB,EACnB,SAAS,EACT,KAAK,EACL,MAAM,EACN,aAAa,EAAE,MAAM,EACrB,GAAG,KAAK,EACT,EAAE,iBAAiB,qBAwRnB;yBAtSe,YAAY"}
1
+ {"version":3,"file":"DropdownMenu.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenu.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,EAOZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,EAAS,KAAK,WAAW,EAAC,MAAM,WAAW,CAAC;AAInD,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,oBAAoB,CAAC;AAa9D,OAAO,KAAK,EAAC,cAAc,EAAE,cAAc,EAAC,MAAM,mBAAmB,CAAC;AAQtE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAyC5C;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAChD,qBAAqB,EACrB,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,CAC9C;IACC;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,oBAAoB,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,kBAAkB,GAC5B,oBAAoB,GAAG,mBAAmB,GAAG,mBAAmB,CAAC;AAMnE,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AAEnE,UAAU,qBAAsB,SAAQ,SAAS;IAC/C,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,qBAAsB,SAAQ,qBAAqB;IAC3D,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,UAAU,yBAA0B,SAAQ,qBAAqB;IAC/D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GAC3B,qBAAqB,GAAG,yBAAyB,CAAC;AA+BpD,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EAAE,eAAe,EACvB,UAAU,EAAE,gBAAgB,EAC5B,YAAY,EACZ,SAAS,EACT,OAAO,EACP,UAAiB,EACjB,SAAmB,EACnB,SAAmB,EACnB,SAAS,EACT,KAAK,EACL,MAAM,EACN,aAAa,EAAE,MAAM,EACrB,GAAG,KAAK,EACT,EAAE,iBAAiB,qBA+TnB;yBA7Ue,YAAY"}
@@ -14,6 +14,11 @@
14
14
  *
15
15
  * Both modes use useListFocus for DOM-based keyboard navigation.
16
16
  *
17
+ * Initial focus on open follows the input modality: a keyboard open
18
+ * (Enter / Space / ArrowDown on the trigger) focuses the first enabled item
19
+ * (APG menu-button); a pointer open focuses the menu container itself so no
20
+ * item reads as pre-selected, and the first ArrowDown then moves to item 1.
21
+ *
17
22
  * SYNC: When modified, update these files to stay in sync:
18
23
  * - /packages/core/src/DropdownMenu/DropdownMenu.doc.mjs
19
24
  * - /packages/core/src/DropdownMenu/DropdownMenu.test.tsx
@@ -179,6 +184,13 @@ export function DropdownMenu({
179
184
  // Defer item focus until the layer has committed open, so focus restore
180
185
  // captures the trigger instead of the first menu item.
181
186
  const shouldFocusOnOpenRef = useRef(false);
187
+
188
+ // How the next open was initiated. Keyboard (and programmatic) opens focus
189
+ // the first enabled item per the APG menu-button pattern; pointer opens
190
+ // focus the menu container instead, so no item is visually highlighted as
191
+ // if pre-selected (#4477). Reset to 'keyboard' after every open so
192
+ // programmatic controlled opens keep the item-focus behavior.
193
+ const openModalityRef = useRef('keyboard');
182
194
  const handleLayerShow = useCallback(() => {
183
195
  onOpenChange?.(true);
184
196
  if (!isControlled) {
@@ -238,14 +250,26 @@ export function DropdownMenu({
238
250
  }
239
251
  }, [controlledIsOpen, isControlled, popover]);
240
252
 
241
- // Move focus into the menu only after the layer has committed open.
253
+ // Move focus into the menu only after the layer has committed open,
254
+ // honoring the input modality: keyboard (and programmatic) opens land on
255
+ // the first enabled item per the APG menu-button pattern; pointer opens
256
+ // focus the menu container itself (tabIndex={-1}) so no item is
257
+ // highlighted as if pre-selected (#4477). Container focus keeps arrows,
258
+ // typeahead, Escape and Tab in the menu's onKeyDown, and is also the
259
+ // fallback when no item is focusable (e.g. all disabled), mirroring the
260
+ // submenu flyout fallback.
242
261
  useEffect(() => {
243
262
  if (!popover.isOpen || !shouldFocusOnOpenRef.current) {
244
263
  return;
245
264
  }
246
265
  shouldFocusOnOpenRef.current = false;
247
- requestAnimationFrame(() => focusFirst());
248
- }, [popover.isOpen, focusFirst]);
266
+ requestAnimationFrame(() => {
267
+ if (openModalityRef.current === 'pointer' || !focusFirst()) {
268
+ listRef.current?.focus();
269
+ }
270
+ openModalityRef.current = 'keyboard';
271
+ });
272
+ }, [popover.isOpen, focusFirst, listRef]);
249
273
 
250
274
  // Extend useListFocus with Enter/Space activation + typeahead
251
275
  const listKeyDown = useCallback(e => {
@@ -278,25 +302,34 @@ export function DropdownMenu({
278
302
  }
279
303
  listNavKeyDown(e);
280
304
  }, [listNavKeyDown, closeMenu, typeahead, ownsEvent]);
281
- const openAndFocus = useCallback(() => {
305
+ const openAndFocus = useCallback((modality = 'keyboard') => {
306
+ openModalityRef.current = modality;
282
307
  shouldFocusOnOpenRef.current = true;
283
308
  popover.show();
284
309
  }, [popover]);
285
- const handleButtonClick = useCallback(() => {
310
+ const handleButtonClick = useCallback(e => {
286
311
  // If the menu was just closed by light dismiss (e.g. iOS Safari fires
287
312
  // pointerdown → hide before the trigger's click), the click would
288
- // otherwise immediately re-open it. Short-circuit within the guard window.
313
+ // otherwise immediately re-open it. Short-circuit within the guard
314
+ // window.
289
315
  if (Date.now() - lastHideTimeRef.current < 50) {
290
316
  return;
291
317
  }
292
318
  onClick?.();
319
+ // detail === 0 marks a synthesized click (screen reader / AT
320
+ // activation): treat it as keyboard so those users still land on the
321
+ // first item. Real pointer clicks report detail >= 1.
322
+ const modality = e.detail === 0 ? 'keyboard' : 'pointer';
293
323
  if (isControlled) {
324
+ if (!controlledIsOpen) {
325
+ openModalityRef.current = modality;
326
+ }
294
327
  onOpenChange?.(!controlledIsOpen);
295
328
  } else {
296
329
  if (popover.isOpen) {
297
330
  popover.hide();
298
331
  } else {
299
- openAndFocus();
332
+ openAndFocus(modality);
300
333
  }
301
334
  }
302
335
  }, [onClick, isControlled, onOpenChange, controlledIsOpen, popover, openAndFocus]);
@@ -356,6 +389,12 @@ export function DropdownMenu({
356
389
  ref: listRef,
357
390
  id: menuId,
358
391
  role: "menu"
392
+ // Focus target for pointer opens (not in the Tab order): holding
393
+ // focus on the container keeps key events (arrows, typeahead,
394
+ // Escape, Tab) inside the menu without highlighting any item.
395
+ // Mirrors the DropdownMenuSubMenu flyout container.
396
+ ,
397
+ tabIndex: -1
359
398
  // Give the menu an accessible name from its trigger's label, so
360
399
  // screen readers announce e.g. "Actions menu" rather than an unnamed
361
400
  // menu (menus-13).
@@ -1 +1 @@
1
- {"version":3,"file":"MultiSelector.d.ts","sourceRoot":"","sources":["../../src/MultiSelector/MultiSelector.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MultiSelector.d.ts","sourceRoot":"","sources":["../../src/MultiSelector/MultiSelector.tsx"],"names":[],"mappings":"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"}
@@ -842,7 +842,8 @@ export function MultiSelector({
842
842
  ...mergeProps(themeProps('multi-selector', {
843
843
  variant,
844
844
  size,
845
- status: status?.type ?? null
845
+ status: status?.type ?? null,
846
+ disabled: isDisabled ? 'disabled' : null
846
847
  }), stylex.props(inputWrapperStyles.base, styles.triggerContainer, sizeStyles[size], variant === 'ghost' && styles.triggerGhost, isDisabled && inputWrapperStyles.disabled, variant === 'ghost' && isDisabled && styles.triggerGhostDisabled, optimisticValue.length === 0 && styles.triggerPlaceholder, variant !== 'ghost' && status && inputStatusBorderStyles[status.type], variant !== 'ghost' && status && !isDisabled && inputStatusHoverShadowStyles[status.type], variant !== 'ghost' && inputGroup && groupStyles.inGroup, xstyle), className, style),
847
848
  children: [startIcon && renderIconSlot(startIcon, {
848
849
  size: 'sm',
@@ -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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"}
@@ -31,6 +31,7 @@ import "../theme/tokens.stylex.js";
31
31
  import { colorVars, sizeVars, spacingVars, radiusVars, durationVars, easeVars, typographyVars, fontWeightVars, typeScaleVars, borderVars } from "../theme/tokens.stylex.js";
32
32
  import { isOptionData, isDivider, isSection, normalizeOption, getSelectableOptions } from "./utils.js";
33
33
  import { useCombobox, useSelectedItemOffset } from "./hooks.js";
34
+ import { useTypeahead } from "../hooks/useTypeahead.js";
34
35
  import { SelectorOption } from "./SelectorOption.js";
35
36
  import { getInputARIA, mergeProps } from "../utils/index.js";
36
37
  import { useSize } from "../SizeContext/SizeContext.js";
@@ -304,6 +305,7 @@ export function Selector(props) {
304
305
  const [, startTransition] = useTransition();
305
306
  const [optimisticValue, setOptimisticValue] = useOptimistic(normalizedValue);
306
307
  const isBusy = isLoading || optimisticValue !== normalizedValue;
308
+ const announce = useAnnounce();
307
309
 
308
310
  // Disabled-reason tooltip. Disabled controls swallow pointer events, so the
309
311
  // tooltip listeners attach to the trigger container (which already exists)
@@ -344,15 +346,15 @@ export function Selector(props) {
344
346
  // Ref for listbox to measure selected item position
345
347
  const listboxRef = useRef(null);
346
348
 
347
- // Announce match counts / "No results found" politely as the user types, so
348
- // screen-reader users hear how many options remain. Filtering was previously
349
- // silent (comboboxes-7). Mirrors BaseTypeahead, which announces from its
350
- // query-change callback (not a reactive effect) via the same useAnnounce hook.
351
- const announce = useAnnounce();
349
+ // Typeahead is defined below (it needs the popover), but closing and clearing
350
+ // must drop its pending buffer — otherwise a stale prefix survives the reset
351
+ // window and poisons the next keystroke ("Dog" then "c" would search "dc").
352
+ const resetTypeaheadRef = useRef(() => {});
352
353
 
353
354
  // Layer for dropdown positioning
354
355
  const handleLayerHide = useCallback(() => {
355
356
  setSearchQuery('');
357
+ resetTypeaheadRef.current();
356
358
  // Clear any lingering result count when the popover closes so stale status
357
359
  // text does not linger in the a11y tree.
358
360
  announce('');
@@ -415,6 +417,7 @@ export function Selector(props) {
415
417
  // Clear the current value. Shared by the clear button and the keyboard
416
418
  // Delete/Backspace path so clearing is reachable without a mouse.
417
419
  const clearValue = useCallback(() => {
420
+ resetTypeaheadRef.current();
418
421
  onChange?.(null);
419
422
  if (changeAction) {
420
423
  startTransition(async () => {
@@ -424,10 +427,25 @@ export function Selector(props) {
424
427
  }
425
428
  }, [onChange, changeAction, startTransition, setOptimisticValue]);
426
429
 
427
- // Selector behavior (keyboard nav, typeahead, selection)
430
+ // Type-to-find appends to the query rather than replacing it: characters
431
+ // typed before focus reaches the search input must not be dropped.
432
+ const appendSearchQuery = useCallback(char => {
433
+ setSearchQuery(query => query + char);
434
+ }, []);
435
+ const commitValue = useCallback(newValue => {
436
+ onChange?.(newValue);
437
+ if (changeAction) {
438
+ startTransition(async () => {
439
+ setOptimisticValue(newValue);
440
+ await changeAction(newValue);
441
+ });
442
+ }
443
+ }, [onChange, changeAction, startTransition, setOptimisticValue]);
444
+
445
+ // Selector behavior (keyboard nav, selection)
428
446
  const {
429
447
  highlightedIndex,
430
- setHighlightedIndex: _setHighlightedIndex,
448
+ setHighlightedIndex,
431
449
  getItemId,
432
450
  onTriggerClick,
433
451
  onKeyDown,
@@ -435,7 +453,11 @@ export function Selector(props) {
435
453
  onItemMouseEnter
436
454
  } = useCombobox({
437
455
  selectableItems: filteredItems,
438
- value: normalizedValue,
456
+ // The optimistic value, not the raw prop: with a pending changeAction the
457
+ // prop still holds the old selection, so the popup would open with the
458
+ // highlight on it and Delete/Backspace could clear a value the action has
459
+ // already replaced.
460
+ value: optimisticValue,
439
461
  isDisabled,
440
462
  isOpen: popover.isOpen,
441
463
  hasSearch,
@@ -443,24 +465,56 @@ export function Selector(props) {
443
465
  popover.show();
444
466
  if (hasSearch) {
445
467
  requestAnimationFrame(() => {
446
- searchRef.current?.focus();
468
+ const input = searchRef.current;
469
+ if (input) {
470
+ input.focus();
471
+ // When typing seeded the query, place the caret after it so the
472
+ // user keeps typing where they left off.
473
+ input.setSelectionRange(input.value.length, input.value.length);
474
+ }
447
475
  });
448
476
  }
449
477
  }, [popover, hasSearch]),
450
478
  onClose: popover.hide,
451
- onSelect: useCallback(newValue => {
452
- onChange?.(newValue);
453
- if (changeAction) {
454
- startTransition(async () => {
455
- setOptimisticValue(newValue);
456
- await changeAction(newValue);
457
- });
458
- }
459
- }, [onChange, changeAction, startTransition, setOptimisticValue]),
479
+ onSelect: commitValue,
460
480
  onClear: hasClear ? clearValue : undefined,
481
+ onSearchSeed: appendSearchQuery,
461
482
  listboxId
462
483
  });
463
484
 
485
+ // Type-to-select, shared with the other collections (menus, listboxes).
486
+ // Open, it walks the highlight — aria-activedescendant announces each match.
487
+ // Closed, it commits the match like a native select, which changes the value
488
+ // without opening the popup or moving focus, so nothing else would prompt
489
+ // assistive tech to re-read the trigger: announce it explicitly.
490
+ const typeahead = useTypeahead({
491
+ getItemLabels: () => selectableItems.map(item => item.label ?? item.value),
492
+ isDisabled: index => selectableItems[index]?.disabled === true,
493
+ // Cycle onward from the highlight when open, from the committed selection
494
+ // when closed — the optimistic one, so a pending changeAction cannot strand
495
+ // cycling on the first match. -1 means nothing is selected or highlighted,
496
+ // which the hook reads as "search from the top".
497
+ getCurrentIndex: () => popover.isOpen ? highlightedIndex : selectedItemIndex,
498
+ onMatch: index => {
499
+ const item = selectableItems[index];
500
+ if (popover.isOpen) {
501
+ setHighlightedIndex(index);
502
+ } else if (item.value !== optimisticValue) {
503
+ commitValue(item.value);
504
+ announce(item.label ?? item.value);
505
+ }
506
+ }
507
+ });
508
+ resetTypeaheadRef.current = typeahead.reset;
509
+ const handleTriggerKeyDown = useCallback(e => {
510
+ // With hasSearch the query input owns typing, so type-to-select is off.
511
+ if (!isDisabled && !hasSearch && typeahead.onKeyDown(e)) {
512
+ e.preventDefault();
513
+ return;
514
+ }
515
+ onKeyDown(e);
516
+ }, [isDisabled, hasSearch, typeahead, onKeyDown]);
517
+
464
518
  // Keep the highlighted option visible during keyboard navigation. The
465
519
  // listbox is a fixed-height scroll container, so without this the virtual
466
520
  // cursor walks off-screen once navigation passes the visible window. Mirrors
@@ -674,7 +728,8 @@ export function Selector(props) {
674
728
  ...mergeProps(themeProps('selector', {
675
729
  variant,
676
730
  size,
677
- status: status?.type ?? null
731
+ status: status?.type ?? null,
732
+ disabled: isDisabled ? 'disabled' : null
678
733
  }), stylex.props(inputWrapperStyles.base, styles.triggerContainer, sizeStyles[size], variant === 'ghost' && styles.triggerGhost, isDisabled && inputWrapperStyles.disabled, variant === 'ghost' && isDisabled && styles.triggerGhostDisabled, !selectedItem && styles.triggerPlaceholder, variant !== 'ghost' && status && inputStatusBorderStyles[status.type], variant !== 'ghost' && status && !isDisabled && inputStatusHoverShadowStyles[status.type], variant !== 'ghost' && inputGroup && groupStyles.inGroup, xstyle), className, style),
679
734
  children: [startIcon && renderIconSlot(startIcon, {
680
735
  size: 'sm',
@@ -707,7 +762,7 @@ export function Selector(props) {
707
762
  ,
708
763
  disabled: isDisabled && !showsDisabledMessage,
709
764
  "aria-disabled": showsDisabledMessage ? 'true' : undefined,
710
- onKeyDown: onKeyDown,
765
+ onKeyDown: handleTriggerKeyDown,
711
766
  tabIndex: isDisabled && !showsDisabledMessage ? -1 : 0,
712
767
  ...{
713
768
  className: "x78zum5 x6s0dn4 x1qughib x1txdalj x1iyjqo2 xs83m0k x1r8uery xeuugli x1717udv x1ghz6dp xc342km xng3xce xjbqb8w xjb2p0i x1qlqyl8 x15bjb6t x1heor9g x1ypdohk x1a2a7pz"
@@ -38,6 +38,13 @@ interface UseComboboxOptions {
38
38
  * navigation owns those keys there) or when there is no value.
39
39
  */
40
40
  onClear?: () => void;
41
+ /**
42
+ * With `hasSearch`, printable characters typed on the trigger are appended to
43
+ * the search query (opening the popup if needed), so type-to-find works
44
+ * without a separate open step. Characters keep arriving here until focus
45
+ * lands in the search input, which then owns its own typing.
46
+ */
47
+ onSearchSeed?: (char: string) => void;
41
48
  listboxId: string;
42
49
  }
43
50
  interface UseComboboxResult {
@@ -50,8 +57,13 @@ interface UseComboboxResult {
50
57
  onItemMouseEnter: (item: SelectorOptionData, index: number) => void;
51
58
  }
52
59
  /**
53
- * Handles keyboard navigation, typeahead search, and selection for combobox/listbox patterns.
60
+ * Handles keyboard navigation and selection for combobox/listbox patterns.
61
+ *
62
+ * Type-to-select is not handled here: callers that want it compose the shared
63
+ * `useTypeahead` hook and run it ahead of this handler (see Selector). That
64
+ * keeps matching consistent with the other collections and leaves consumers
65
+ * whose own input already filters the items (CommandPalette) untouched.
54
66
  */
55
- export declare function useCombobox({ selectableItems, value, isDisabled, isOpen, hasSearch, onOpen, onClose, onSelect, onClear, listboxId, }: UseComboboxOptions): UseComboboxResult;
67
+ export declare function useCombobox({ selectableItems, value, isDisabled, isOpen, hasSearch, onOpen, onClose, onSelect, onClear, onSearchSeed, listboxId, }: UseComboboxOptions): UseComboboxResult;
56
68
  export {};
57
69
  //# sourceMappingURL=hooks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/Selector/hooks.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AACrC,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAyBhD,UAAU,4BAA4B;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC7C,SAAS,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAC1C;AAED,UAAU,2BAA2B;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,iBAAiB,EACjB,SAAS,EACT,UAAU,EACV,SAAS,GACV,EAAE,4BAA4B,GAAG,2BAA2B,CAgF5D;AAMD,UAAU,kBAAkB;IAC1B,eAAe,EAAE,kBAAkB,EAAE,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,iBAAiB;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,SAAS,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,KAAK,IAAI,CAAC;IAC5C,YAAY,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACjD,gBAAgB,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACrE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAC1B,eAAe,EACf,KAAK,EACL,UAAkB,EAClB,MAAM,EACN,SAAiB,EACjB,MAAM,EACN,OAAO,EACP,QAAQ,EACR,OAAO,EACP,SAAS,GACV,EAAE,kBAAkB,GAAG,iBAAiB,CAqOxC"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/Selector/hooks.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AACrC,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAyBhD,UAAU,4BAA4B;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC7C,SAAS,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAC1C;AAED,UAAU,2BAA2B;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,iBAAiB,EACjB,SAAS,EACT,UAAU,EACV,SAAS,GACV,EAAE,4BAA4B,GAAG,2BAA2B,CAgF5D;AAMD,UAAU,kBAAkB;IAC1B,eAAe,EAAE,kBAAkB,EAAE,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,iBAAiB;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,SAAS,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,KAAK,IAAI,CAAC;IAC5C,YAAY,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACjD,gBAAgB,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACrE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,EAC1B,eAAe,EACf,KAAK,EACL,UAAkB,EAClB,MAAM,EACN,SAAiB,EACjB,MAAM,EACN,OAAO,EACP,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,SAAS,GACV,EAAE,kBAAkB,GAAG,iBAAiB,CA2NxC"}
@@ -9,7 +9,7 @@
9
9
  * @output Hooks for Selector
10
10
  * @position Internal hooks; used by Selector.tsx
11
11
  */
12
- import { useCallback, useRef, useState } from 'react';
12
+ import { useCallback, useState } from 'react';
13
13
  import { useIsomorphicLayoutEffect } from "../hooks/useIsomorphicLayoutEffect.js";
14
14
  // The selected row renders one optical pixel below the closed trigger label at
15
15
  // the same mathematical center, so compensate before viewport clamping.
@@ -114,11 +114,16 @@ export function useSelectedItemOffset({
114
114
  }
115
115
 
116
116
  // =============================================================================
117
- // useCombobox - Keyboard navigation, typeahead, and selection
117
+ // useCombobox - Keyboard navigation and selection
118
118
  // =============================================================================
119
119
 
120
120
  /**
121
- * Handles keyboard navigation, typeahead search, and selection for combobox/listbox patterns.
121
+ * Handles keyboard navigation and selection for combobox/listbox patterns.
122
+ *
123
+ * Type-to-select is not handled here: callers that want it compose the shared
124
+ * `useTypeahead` hook and run it ahead of this handler (see Selector). That
125
+ * keeps matching consistent with the other collections and leaves consumers
126
+ * whose own input already filters the items (CommandPalette) untouched.
122
127
  */
123
128
  export function useCombobox({
124
129
  selectableItems,
@@ -130,11 +135,10 @@ export function useCombobox({
130
135
  onClose,
131
136
  onSelect,
132
137
  onClear,
138
+ onSearchSeed,
133
139
  listboxId
134
140
  }) {
135
141
  const [highlightedIndex, setHighlightedIndex] = useState(-1);
136
- const [typeahead, setTypeahead] = useState('');
137
- const typeaheadTimeoutRef = useRef(undefined);
138
142
  const getItemId = useCallback(index => `${listboxId}-item-${index}`, [listboxId]);
139
143
  const getEnabledIndices = useCallback(() => {
140
144
  return selectableItems.map((item, i) => !item.disabled ? i : -1).filter(i => i >= 0);
@@ -204,6 +208,8 @@ export function useCombobox({
204
208
  if (hasSearch) {
205
209
  break;
206
210
  }
211
+ // A space mid-typeahead extends the query instead of activating; the
212
+ // caller's useTypeahead claims it before this handler ever runs.
207
213
  // falls through
208
214
  case 'Enter':
209
215
  e.preventDefault();
@@ -272,26 +278,18 @@ export function useCombobox({
272
278
  }
273
279
  break;
274
280
  default:
275
- if (!hasSearch && e.key.length === 1 && !e.ctrlKey && !e.metaKey) {
276
- const newTypeahead = typeahead + e.key.toLowerCase();
277
- setTypeahead(newTypeahead);
278
- if (typeaheadTimeoutRef.current) {
279
- clearTimeout(typeaheadTimeoutRef.current);
280
- }
281
- typeaheadTimeoutRef.current = setTimeout(() => {
282
- setTypeahead('');
283
- }, 500);
284
- const matchIndex = selectableItems.findIndex(item => !item.disabled && item.label?.toLowerCase().startsWith(newTypeahead));
285
- if (matchIndex >= 0) {
286
- if (!isOpen) {
287
- onOpen();
288
- }
289
- setHighlightedIndex(matchIndex);
281
+ // Keys land here only while the trigger holds focus — once the search
282
+ // input takes over it handles its own typing — so they belong in the
283
+ // query, including the ones racing the open.
284
+ if (hasSearch && onSearchSeed && e.key.length === 1 && !e.ctrlKey && !e.metaKey) {
285
+ if (!isOpen) {
286
+ onOpen();
290
287
  }
288
+ onSearchSeed(e.key);
291
289
  }
292
290
  break;
293
291
  }
294
- }, [isDisabled, isOpen, onOpen, closeAndReset, selectableItems, highlightedIndex, selectItem, findSelectedIndex, getEnabledIndices, typeahead, hasSearch, onClear, value]);
292
+ }, [isDisabled, isOpen, onOpen, closeAndReset, selectableItems, highlightedIndex, selectItem, findSelectedIndex, getEnabledIndices, hasSearch, onClear, onSearchSeed, value]);
295
293
  return {
296
294
  highlightedIndex,
297
295
  setHighlightedIndex,