@lumx/react 4.12.1-next.0 → 4.12.1-next.1

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 (3) hide show
  1. package/index.js +29 -20
  2. package/index.js.map +1 -1
  3. package/package.json +3 -3
package/index.js CHANGED
@@ -2519,23 +2519,29 @@ const isChipDisabled = chip => chip?.getAttribute('aria-disabled') === 'true';
2519
2519
  */
2520
2520
 
2521
2521
  /**
2522
- * Finds the previous enabled chip before the given chip element using a TreeWalker.
2523
- * If no currentChip is provided, returns the last enabled chip in the container.
2522
+ * Find the nearest enabled chip in the given direction relative to `anchor`.
2523
+ *
2524
+ * - When `anchor` is provided, walks one step in `direction` from it.
2525
+ * - When `anchor` is omitted, returns the first enabled chip from the matching
2526
+ * end of the container (last chip for 'previous', first for 'next').
2524
2527
  */
2525
- function findPreviousChip(container, currentChip) {
2528
+ function findSiblingChip(container, direction, anchor) {
2526
2529
  const walker = createSelectorTreeWalker(container, ENABLED_CHIP_SELECTOR);
2527
- if (!currentChip) {
2528
- // Find the last enabled chip by walking to the end.
2530
+ if (anchor) {
2531
+ walker.currentNode = anchor;
2532
+ const node = direction === 'next' ? walker.nextNode() : walker.previousNode();
2533
+ return node || undefined;
2534
+ }
2535
+
2536
+ // No anchor: walk from the matching end of the container.
2537
+ if (direction === 'previous') {
2529
2538
  let last;
2530
2539
  while (walker.nextNode()) {
2531
2540
  last = walker.currentNode;
2532
2541
  }
2533
2542
  return last;
2534
2543
  }
2535
-
2536
- // Position the walker at the current chip and walk backward.
2537
- walker.currentNode = currentChip;
2538
- return walker.previousNode() || undefined;
2544
+ return walker.nextNode() || undefined;
2539
2545
  }
2540
2546
 
2541
2547
  /** Remove an option by its id and call onChange. */
@@ -2574,9 +2580,6 @@ function setupSelectionChipGroupEvents(options) {
2574
2580
  };
2575
2581
 
2576
2582
  // Delegated keydown handler on the chip group container.
2577
- // Enter/Space trigger removal (like click).
2578
- // Backspace also removes but explicitly moves focus to previous chip (overriding the
2579
- // roving tabindex MutationObserver which defaults to moving focus forward).
2580
2583
  const handleKeyDown = evt => {
2581
2584
  const chip = getChip(evt.target);
2582
2585
  const optionId = chip?.dataset.optionId;
@@ -2584,14 +2587,20 @@ function setupSelectionChipGroupEvents(options) {
2584
2587
  if (optionId == null || !activatingKey || isChipDisabled(chip)) {
2585
2588
  return;
2586
2589
  }
2590
+
2591
+ // Compute focus fallback target before removing the chip.
2592
+ let focusTarget;
2587
2593
  if (evt.key === 'Backspace') {
2588
- // Move focus to previous option (instead of next in listbox)
2589
- const previousChip = findPreviousChip(container, chip);
2590
- const focusTarget = previousChip || options.getInput?.();
2591
- if (focusTarget) {
2592
- focusTarget.focus();
2593
- focusTarget.setAttribute('tabindex', '0');
2594
- }
2594
+ // Custom behavior (not WAI-ARIA recommendation) => focus the previous chip, fallback on input (no more chips)
2595
+ focusTarget = findSiblingChip(container, 'previous', chip) || options.getInput?.() || undefined;
2596
+ } else {
2597
+ // WAI-ARIA recommendation when removing an option in a listbox => focus the next chip, fallback on previous chip
2598
+ // (bonus: we fallback on input when there is no more chips)
2599
+ focusTarget = findSiblingChip(container, 'next', chip) || findSiblingChip(container, 'previous', chip) || options.getInput?.() || undefined;
2600
+ }
2601
+ if (focusTarget) {
2602
+ focusTarget.focus();
2603
+ focusTarget.setAttribute('tabindex', '0');
2595
2604
  }
2596
2605
  evt.preventDefault();
2597
2606
  removeOption(options, optionId);
@@ -2609,7 +2618,7 @@ function setupSelectionChipGroupEvents(options) {
2609
2618
  if (!backspacePressed || !cursorAtStart) return;
2610
2619
  evt.stopPropagation();
2611
2620
  evt.preventDefault();
2612
- const lastChip = findPreviousChip(container);
2621
+ const lastChip = findSiblingChip(container, 'previous');
2613
2622
  lastChip?.focus();
2614
2623
  };
2615
2624
  input.addEventListener('keydown', inputHandler);