@keenmate/web-multiselect 1.8.6 → 1.9.0

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.
package/README.md CHANGED
@@ -327,11 +327,15 @@ multiselect.addNewCallback = async (value) => {
327
327
 
328
328
  - **↑ ↓** - Navigate up/down through options
329
329
  - **Ctrl+↑ Ctrl+↓** - Jump between matched items (navigate mode only)
330
- - **Enter** - Select focused option
330
+ - **Page Up / Page Down** - Move focus by 10 options at a time
331
+ - **Home / End** - Jump to first / last option
332
+ - **Enter** - Select focused option (or add new entry when `allow-add-new="true"` and the search has text)
331
333
  - **Escape** - Close popover → Clear search → Close dropdown (priority order)
332
334
  - **Tab** - Close dropdown and move to next field
333
335
  - **Type** - Filter options by search term
334
336
 
337
+ > 💡 To surface these shortcuts to your users, set the `search-hint` attribute — the hint floats above the input when focused. Example: `<web-multiselect search-mode="navigate" search-hint="Ctrl/Cmd + ↓ / ↑ to jump between matches">`.
338
+
335
339
  ## Advanced Features
336
340
 
337
341
  ### Rich Content with Icons
package/dist/index.d.ts CHANGED
@@ -321,8 +321,19 @@ export declare class MultiSelectElement<T = any> extends BaseElement {
321
321
  */
322
322
  private parseDeclarativeOptions;
323
323
  private _declarativeSelectedValues?;
324
+ /** Parse all observed attributes via ATTRIBUTE_TABLE into a partial config object. */
325
+ private parseAttributesFromTable;
324
326
  private initializePicker;
325
327
  private reinitialize;
328
+ /**
329
+ * Apply a partial config update to the live picker. Falls back to a full reinit if the
330
+ * picker can't apply the change in place (e.g. adding/removing the `searchHint` element).
331
+ * No-op if the picker hasn't been initialized yet — the next `initializePicker` will pick
332
+ * up the new programmatic state.
333
+ */
334
+ private updatePicker;
335
+ /** Normalize the picker's getValue() return into the array form expected by event detail. */
336
+ private collectSelectedValues;
326
337
  get options(): T[] | undefined;
327
338
  set options(value: T[] | undefined);
328
339
  set valueMember(value: string | null);
@@ -492,9 +503,10 @@ export declare type SearchInputMode = 'normal' | 'readonly' | 'hidden';
492
503
  export declare type SearchMode = 'filter' | 'navigate';
493
504
 
494
505
  /**
495
- * Set log level for a specific category
496
- * @param category Category logger to configure (e.g., 'MULTISELECT:UI')
497
- * @param level Log level to set ('trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent')
506
+ * Set log level for a specific category. Accepts either the full prefixed name
507
+ * (e.g. `MULTISELECT:UI`) or the bare suffix (`UI`) for convenience.
508
+ * @param category Category logger name; bare names (UI/DATA/INIT/INTERACTION) are normalized to the prefixed form.
509
+ * @param level Log level ('trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent')
498
510
  */
499
511
  export declare function setCategoryLevel(category: string, level: string): void;
500
512
 
@@ -534,12 +546,7 @@ export declare class WebMultiSelect<T = any> {
534
546
  private dropdownCleanup;
535
547
  private hintCleanup;
536
548
  private selectedPopoverCleanup;
537
- private badgeTooltips;
538
- private badgeTooltipCleanups;
539
- private badgeTooltipShowTimeouts;
540
- private badgeTooltipHideTimeouts;
541
- private actionButtonTooltips;
542
- private actionButtonTooltipCleanups;
549
+ private tooltips;
543
550
  private virtualScroll;
544
551
  private optionsContainer;
545
552
  private selectedPopoverVirtualScroll;
@@ -554,41 +561,31 @@ export declare class WebMultiSelect<T = any> {
554
561
  private documentKeydownHandler;
555
562
  private documentClickHandler;
556
563
  /**
557
- * Extract value/ID from item
558
- * Precedence: tuple[0] -> valueMember -> getValueCallback -> '[N/A]'
564
+ * Generic field extractor with the precedence:
565
+ * tuple short-circuit -> member property -> callback -> fallback
566
+ *
567
+ * Tuple handling:
568
+ * - `tupleIndex` (0 | 1): for `[key, value]` items, return that slot.
569
+ * - `tupleSkip: true`: for any tuple, skip directly to fallback (used for icon/subtitle/group/disabled —
570
+ * fields that don't make sense on a 2-element array).
571
+ * - neither: tuples flow through the member/callback/fallback chain as if they were objects.
572
+ *
573
+ * `transform` is applied to tuple-slot and member-property reads (not to callback returns or the fallback),
574
+ * so e.g. you can pass `String` to coerce numeric members to strings while letting a typed callback return its
575
+ * own type unchanged.
559
576
  */
577
+ private extractField;
560
578
  private getItemValue;
561
- /**
562
- * Extract display value from item
563
- * Precedence: tuple[1] -> displayValueMember -> getDisplayValueCallback -> '[N/A]'
564
- */
565
579
  private getItemDisplayValue;
566
580
  /**
567
- * Extract badge display value from item
568
- * Precedence: getBadgeDisplayCallback -> getItemDisplayValue()
569
- * This allows customizing badge text separately from dropdown display text
581
+ * Badge display falls back to the regular display value rather than '[N/A]', so consumers can override badge
582
+ * text independently. Doesn't fit the extractField shape (no tuple/member layer of its own).
570
583
  */
571
584
  private getItemBadgeDisplayValue;
572
- /**
573
- * Extract search value from item
574
- * Precedence: searchValueMember -> getSearchValueCallback -> displayValue
575
- */
576
585
  private getItemSearchValue;
577
- /**
578
- * Extract icon from item
579
- */
580
586
  private getItemIcon;
581
- /**
582
- * Extract subtitle from item
583
- */
584
587
  private getItemSubtitle;
585
- /**
586
- * Extract group from item
587
- */
588
588
  private getItemGroup;
589
- /**
590
- * Extract disabled state from item
591
- */
592
589
  private getItemDisabled;
593
590
  constructor(element: HTMLElement, options?: Partial<MultiSelectConfig<T>>);
594
591
  private init;
@@ -607,6 +604,11 @@ export declare class WebMultiSelect<T = any> {
607
604
  * Render dropdown with virtual scrolling
608
605
  */
609
606
  private renderDropdownVirtual;
607
+ /**
608
+ * Render the Select All / Clear All / custom action buttons row.
609
+ * Returns the empty string if multiple-select is off or no buttons are configured.
610
+ */
611
+ private renderActionsHTML;
610
612
  private renderOption;
611
613
  private highlightMatch;
612
614
  private groupOptions;
@@ -617,14 +619,19 @@ export declare class WebMultiSelect<T = any> {
617
619
  private handleDropdownClick;
618
620
  private handleBadgeClick;
619
621
  private handleClickOutside;
622
+ /**
623
+ * Move focus by computing a new index from (current, total).
624
+ * Returning -1 from `compute` is a no-op (used for empty list / no match).
625
+ */
626
+ private focusBy;
620
627
  private focusNext;
621
628
  private focusPrevious;
622
629
  private focusFirst;
623
630
  private focusLast;
624
- private focusNextMatch;
625
- private focusPreviousMatch;
626
631
  private focusPageUp;
627
632
  private focusPageDown;
633
+ private focusNextMatch;
634
+ private focusPreviousMatch;
628
635
  private scrollToFocused;
629
636
  private toggleOption;
630
637
  private handleAddNew;
@@ -632,8 +639,22 @@ export declare class WebMultiSelect<T = any> {
632
639
  private deselectOption;
633
640
  private selectAll;
634
641
  private clearAll;
642
+ /**
643
+ * Re-render and fire callbacks after a selection state change.
644
+ * `added` / `removed` drive per-item select/deselect callbacks.
645
+ * `changeCallback` fires once if anything actually changed.
646
+ */
647
+ private commit;
635
648
  private open;
636
649
  private close;
650
+ /**
651
+ * Anchor a floating panel (dropdown or selected-items popover) below/above the input with
652
+ * placement-locking and width-syncing. Returns the `autoUpdate` cleanup.
653
+ *
654
+ * Both panels share: anchor on input, sync width, default to 'bottom-start', flip on first
655
+ * compute then lock the resulting placement, optionally clamp by dropdownMin/MaxWidth.
656
+ */
657
+ private anchorFloatingPanel;
637
658
  private positionDropdown;
638
659
  private positionHint;
639
660
  private parseInitialSelection;
@@ -642,27 +663,60 @@ export declare class WebMultiSelect<T = any> {
642
663
  private hideSelectedPopover;
643
664
  private renderSelectedPopover;
644
665
  private renderSelectedPopoverVirtual;
645
- private renderBadgeForPopover;
666
+ /**
667
+ * Render a removable badge for a selected option (used by the badges/partial display modes
668
+ * and by the selected-items popover).
669
+ *
670
+ * - In the popover, `renderSelectedItemContentCallback` and `getSelectedItemClassCallback` win
671
+ * over the regular badge callbacks; that's how consumers customize popover items independently.
672
+ * - The `data-value` and aria-label both go through `getItemBadgeDisplayValue` so badge text and
673
+ * accessible name stay in sync.
674
+ */
675
+ private renderBadgeHTML;
646
676
  private handleSelectedPopoverClick;
647
677
  private positionSelectedPopover;
648
678
  private updateHiddenInput;
649
679
  private getFormValue;
650
680
  getSelected(): T[];
651
681
  setSelected(values: (string | number)[]): void;
682
+ /**
683
+ * Merge a partial config update into the live picker without tearing down the DOM.
684
+ *
685
+ * Handles the cheap structural toggles inline (no-checkboxes class, badges-position class,
686
+ * input placeholder, search-input mode) and re-renders dropdown + badges + hidden inputs.
687
+ *
688
+ * Returns `true` if the change could be applied in place. Returns `false` for changes that
689
+ * truly require rebuilding the DOM scaffolding (currently: adding/removing the `searchHint`
690
+ * element, since it's only created in `buildHTML` if a hint string was provided). The caller
691
+ * should fall back to destroy + re-init in that case.
692
+ */
693
+ updateOptions(partial: Partial<MultiSelectConfig<T>>): boolean;
652
694
  get selectedItem(): T | null;
653
695
  get selectedValue(): string | number | (string | number)[] | null;
654
696
  getValue(): string | number | (string | number)[] | null;
697
+ /**
698
+ * Create or replace a tracked tooltip with the given id. Replacing destroys the old one,
699
+ * which is the normal flow when re-rendering badges/actions.
700
+ */
701
+ private spawnTooltip;
702
+ private destroyAllTooltips;
703
+ /** Build the badge-text tooltip content (callback overrides; default = displayValue + optional subtitle on next line). */
704
+ private buildBadgeTooltipContent;
705
+ /** Build the remove-button tooltip text (callback > format string with {0} > "Remove {name}"). */
706
+ private buildRemoveButtonTooltipText;
655
707
  private attachBadgeTooltips;
656
- private createTooltipForElement;
657
- private createRemoveButtonTooltip;
658
- private positionBadgeTooltip;
659
- private cleanupBadgeTooltip;
660
- private destroyAllBadgeTooltips;
661
708
  private attachActionButtonTooltips;
662
- private createActionButtonTooltip;
663
- private positionActionButtonTooltip;
664
- private cleanupActionButtonTooltip;
709
+ /**
710
+ * Destroy only the action-button tooltips. Called from `renderDropdown`/`renderDropdownVirtual`
711
+ * before rebuilding the actions row, so per-button tooltip state doesn't leak.
712
+ */
665
713
  private destroyAllActionButtonTooltips;
714
+ /**
715
+ * Destroy main-badges-container tooltips. Called before re-rendering the badges container.
716
+ * Popover tooltips (prefixed `popover-`) survive — they're owned by the popover lifecycle and
717
+ * cleaned up in `hideSelectedPopover`. Action-button tooltips (prefixed `action-`) survive too.
718
+ */
719
+ private destroyAllBadgeTooltips;
666
720
  destroy(): void;
667
721
  }
668
722