stimeo-ui 0.2.1 → 0.3.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/CHANGELOG.md +104 -0
- package/dist/cable/index.d.ts +16 -16
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.js +10 -0
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.d.ts +97 -10
- package/dist/controllers/breadcrumb_controller.js +225 -13
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +11 -1
- package/dist/controllers/calendar_controller.js +89 -22
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +16 -1
- package/dist/controllers/carousel_controller.js +47 -6
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.js +2 -2
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +12 -1
- package/dist/controllers/color_picker_controller.js +46 -7
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +14 -1
- package/dist/controllers/combobox_controller.js +162 -23
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +2 -0
- package/dist/controllers/command_palette_controller.js +194 -17
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +7 -6
- package/dist/controllers/context_menu_controller.js +32 -10
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/count_up_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +19 -1
- package/dist/controllers/data_grid_controller.js +82 -4
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +1 -0
- package/dist/controllers/date_range_picker_controller.js +27 -3
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +1 -2
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.js +1 -0
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +1 -1
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +1 -1
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +1 -1
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.js +36 -11
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.js +31 -10
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +9 -2
- package/dist/controllers/listbox_controller.js +257 -53
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.js +2 -2
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +29 -11
- package/dist/controllers/menu_controller.js +104 -17
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +91 -11
- package/dist/controllers/menubar_controller.js +415 -63
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +31 -5
- package/dist/controllers/multi_select_controller.js +312 -29
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +65 -18
- package/dist/controllers/navigation_menu_controller.js +154 -27
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.d.ts +4 -5
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +1 -1
- package/dist/controllers/number_input_controller.js +7 -0
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/otp_controller.js +18 -1
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +3 -2
- package/dist/controllers/overflow_indicator_controller.js +81 -13
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +78 -19
- package/dist/controllers/overflow_menu_controller.js +381 -57
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +36 -5
- package/dist/controllers/pagination_controller.js +163 -32
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/persist_controller.js +6 -6
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +2 -3
- package/dist/controllers/pointer_drag_controller.js +9 -1
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +1 -2
- package/dist/controllers/popover_controller.js +2 -2
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +4 -4
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.js +22 -3
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +14 -4
- package/dist/controllers/range_slider_controller.js +32 -6
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.js +16 -2
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.js +63 -19
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/resizable_controller.d.ts +3 -1
- package/dist/controllers/resizable_controller.js +65 -1
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.js +17 -2
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.js +86 -12
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.js +1 -1
- package/dist/controllers/scroll_restore_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.d.ts +6 -3
- package/dist/controllers/scroll_visibility_controller.js +33 -3
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +64 -15
- package/dist/controllers/scrollspy_controller.js +346 -73
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +20 -5
- package/dist/controllers/separator_controller.js +9 -0
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +1 -1
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.js +1 -1
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +11 -1
- package/dist/controllers/slider_controller.js +32 -6
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.d.ts +3 -1
- package/dist/controllers/sortable_controller.js +34 -3
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.js +1 -1
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +5 -6
- package/dist/controllers/stick_to_bottom_controller.js +2 -1
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/sticky_observer_controller.js +32 -11
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +2 -2
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.js +1 -0
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +11 -3
- package/dist/controllers/tabs_controller.js +26 -3
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.js +22 -2
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +1 -1
- package/dist/controllers/theme_controller.js +22 -3
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.js +20 -1
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +4 -5
- package/dist/controllers/toast_controller.js +4 -5
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.js +23 -2
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.d.ts +53 -13
- package/dist/controllers/toolbar_controller.js +230 -31
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +1 -1
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +87 -6
- package/dist/controllers/tree_view_controller.js +467 -51
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.js +3514 -689
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +375 -39
- package/dist/inspector/cli.js +228 -14
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +419 -48
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +24 -24
- package/dist/inspector/manifest.json +1040 -125
- package/package.json +5 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/aria_ids.ts","../../src/utils/composition_tracker.ts","../../src/utils/option_scroll.ts","../../src/utils/roving_tabindex.ts","../../src/controllers/multi_select_controller.ts"],"names":[],"mappings":"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","file":"multi_select_controller.js","sourcesContent":["/**\n * Minimal id primitives for wiring ARIA relationships.\n *\n * Accessible relationships such as `aria-describedby`, `aria-errormessage`,\n * `aria-labelledby`, and `aria-activedescendant` reference their targets by DOM\n * `id`. When a controller must establish such a relationship but the consumer's\n * markup left the target without an `id`, it needs to mint one that is stable for\n * the page's lifetime and guaranteed not to collide with another generated id.\n *\n * These helpers own *only* id generation/assignment. The linking policy (which\n * attribute, what order, when to add or remove it) stays in each controller —\n * this deliberately avoids a premature generic \"ARIA linker\" abstraction.\n */\n\n/**\n * Monotonic counter backing {@link uniqueId}. Module-scoped so every generated\n * id is unique across all controller instances sharing this module in a document.\n */\nlet counter = 0;\n\n/**\n * Returns a unique, DOM-id-safe string of the form `` `${prefix}-${n}` `` where\n * `n` increases on each call.\n *\n * The counter alone guarantees uniqueness against other *generated* ids, but a\n * consumer-authored element could already own `` `${prefix}-${n}` ``. When a\n * `document` is available the candidate is therefore advanced until no element\n * with that id exists, so a generated id never collides with author markup\n * either — the minimal \"id registry\" guarantee these helpers exist to provide.\n *\n * @param prefix - Leading segment of the id (e.g. a controller's identifier).\n * Defaults to `\"stimeo\"`.\n */\nexport function uniqueId(prefix = \"stimeo\"): string {\n let candidate: string;\n do {\n counter += 1;\n candidate = `${prefix}-${counter}`;\n } while (typeof document !== \"undefined\" && document.getElementById(candidate) !== null);\n return candidate;\n}\n\n/**\n * Returns `element`'s existing `id`, or assigns it a freshly generated\n * {@link uniqueId} (with the given `prefix`) and returns that. Idempotent: an\n * element that already has an id keeps it, so consumer-authored ids are never\n * overwritten.\n *\n * @param element - The element to read or stamp an `id` onto.\n * @param prefix - Prefix forwarded to {@link uniqueId} when one must be generated.\n * @returns The element's id (existing or newly assigned).\n */\nexport function ensureId(element: Element, prefix = \"stimeo\"): string {\n if (element.id) return element.id;\n const id = uniqueId(prefix);\n element.id = id;\n return id;\n}\n","/** Minimal event shape carrying the platform's per-event composition signal. */\nexport interface CompositionSignal {\n readonly isComposing?: boolean;\n}\n\n/** Hooks that keep component-specific work outside {@link CompositionTracker}. */\nexport interface CompositionTrackerOptions {\n /** Runs after lifecycle state is set for a `compositionstart` event. */\n readonly onStart?: (event: Event) => void;\n /** Runs after lifecycle state is cleared for a `compositionend` event. */\n readonly onEnd?: (event: Event) => void;\n}\n\n/**\n * Owns IME composition listeners and transient state for one or more event targets.\n *\n * Some browsers omit `KeyboardEvent.isComposing` on the keydown that confirms a\n * conversion. Tracking `compositionstart` through `compositionend` supplies the\n * missing lifecycle signal without relying on deprecated numeric key codes.\n * Component policy stays with the consumer: filtering, validation, and submission\n * belong in the optional {@link CompositionTrackerOptions.onEnd} callback.\n *\n * @example\n * ```ts\n * #composition = new CompositionTracker({ onEnd: () => this.filter() });\n *\n * connect(): void {\n * this.#composition.observe(this.inputTarget);\n * }\n *\n * disconnect(): void {\n * this.#composition.disconnect();\n * }\n *\n * onKeydown(event: KeyboardEvent): void {\n * if (this.#composition.isComposing(event)) return;\n * }\n * ```\n */\nexport class CompositionTracker {\n readonly #observedTargets = new Set<EventTarget>();\n readonly #activeTargets = new Set<EventTarget>();\n readonly #onStart: ((event: Event) => void) | undefined;\n readonly #onEnd: ((event: Event) => void) | undefined;\n\n constructor(options: CompositionTrackerOptions = {}) {\n this.#onStart = options.onStart;\n this.#onEnd = options.onEnd;\n }\n\n /** Starts lifecycle tracking for `target`; repeated calls are idempotent. */\n observe(target: EventTarget): void {\n if (this.#observedTargets.has(target)) return;\n target.addEventListener(\"compositionstart\", this.#handleStart);\n target.addEventListener(\"compositionend\", this.#handleEnd);\n this.#observedTargets.add(target);\n }\n\n /** Stops tracking one target and clears any active composition it owned. */\n unobserve(target: EventTarget): void {\n if (!this.#observedTargets.delete(target)) return;\n target.removeEventListener(\"compositionstart\", this.#handleStart);\n target.removeEventListener(\"compositionend\", this.#handleEnd);\n this.#activeTargets.delete(target);\n }\n\n /** Releases every listener and clears state so reconnect starts cleanly. */\n disconnect(): void {\n for (const target of this.#observedTargets) {\n target.removeEventListener(\"compositionstart\", this.#handleStart);\n target.removeEventListener(\"compositionend\", this.#handleEnd);\n }\n this.#observedTargets.clear();\n this.#activeTargets.clear();\n }\n\n /** True when lifecycle tracking or the current event reports composition. */\n isComposing(event?: CompositionSignal): boolean {\n return this.#activeTargets.size > 0 || event?.isComposing === true;\n }\n\n readonly #handleStart = (event: Event): void => {\n if (event.currentTarget) this.#activeTargets.add(event.currentTarget);\n this.#onStart?.(event);\n };\n\n readonly #handleEnd = (event: Event): void => {\n if (event.currentTarget) this.#activeTargets.delete(event.currentTarget);\n this.#onEnd?.(event);\n };\n}\n","/**\n * Scroll-follow for virtually-focused listbox options.\n *\n * Listbox-style widgets that track their active option with\n * `aria-activedescendant` keep DOM focus on the input/trigger, so the\n * browser's native \"scroll the focused element into view\" never runs — in a\n * scrollable list (`max-height` + `overflow`) the active option can walk right\n * out of sight on ArrowDown/ArrowUp. This helper keeps it visible.\n *\n * It adjusts the LIST's own `scrollTop` only — deliberately not\n * `scrollIntoView({ block: \"nearest\" })`, which may also scroll ancestor\n * scrolling boxes (the page) when a floating popup pokes past a viewport\n * edge, desyncing anchored/marker-composed placements. (Command-palette keeps\n * its `scrollIntoView` because its page is scroll-locked behind the modal;\n * page-floating popups must use this helper instead.)\n */\n\n/**\n * Scrolls `option` into view within `list` by minimally adjusting\n * `list.scrollTop`. No-ops when the list does not actually scroll. Never\n * touches any other scrolling box.\n *\n * @param list - The scrollable listbox container.\n * @param option - The (virtually focused) option to keep visible.\n */\nexport function scrollOptionIntoView(list: HTMLElement, option: HTMLElement): void {\n if (list.scrollHeight <= list.clientHeight) return;\n const listRect = list.getBoundingClientRect();\n const optionRect = option.getBoundingClientRect();\n if (optionRect.top < listRect.top) {\n list.scrollTop -= listRect.top - optionRect.top;\n } else if (optionRect.bottom > listRect.bottom) {\n list.scrollTop += optionRect.bottom - listRect.bottom;\n }\n}\n","/**\n * Low-level roving-tabindex primitive shared by composite-widget controllers.\n *\n * The APG roving-tabindex pattern keeps a composite widget a single Tab stop:\n * exactly one item is in the Tab sequence (`tabindex=\"0\"`) while the rest are\n * removed from it (`tabindex=\"-1\"`), and the arrow keys move both DOM focus and\n * that single tabbable position together. {@link RovingTabindex} owns *only* that\n * mechanical bookkeeping — \"which one item is tabbable, and move focus there\".\n *\n * It is intentionally **policy-free**. Orientation, wrapping vs. clamping,\n * selection-follows-focus, typeahead, and `Home`/`End` semantics differ per APG\n * pattern (Radio Group, Toolbar, Rating, …); folding them into one helper would\n * flatten those widgets to a lowest common denominator and lose each pattern's\n * correctness. Those decisions therefore stay in each controller, which calls\n * {@link RovingTabindex.setActive} with an index it computed itself (optionally\n * via the pure {@link rovingMove} helper).\n *\n * @remarks\n * Items are read lazily through a getter so a controller can add or remove\n * targets (Stimulus re-scans the DOM) without re-wiring this helper.\n */\nexport class RovingTabindex {\n /** Returns the current ordered item elements; called on every operation. */\n readonly #getItems: () => HTMLElement[];\n\n /**\n * @param getItems - Returns the current ordered item elements. Called on every\n * operation so the live target list is always used.\n */\n constructor(getItems: () => HTMLElement[]) {\n this.#getItems = getItems;\n }\n\n /** Index of the currently tabbable item (`tabindex=\"0\"`), or `-1` if none. */\n get activeIndex(): number {\n return this.#getItems().findIndex((item) => item.tabIndex === 0);\n }\n\n /**\n * Makes exactly the item at `index` tabbable (`tabindex=\"0\"`) and removes every\n * other item from the Tab sequence (`tabindex=\"-1\"`). An out-of-range `index`\n * (e.g. `-1`) leaves all items at `-1`, which a controller can use to express\n * \"nothing is currently tabbable\".\n *\n * @param index - Position of the item to make tabbable.\n * @param options - Pass `{ focus: true }` to also move DOM focus to that item.\n */\n setActive(index: number, { focus = false }: { focus?: boolean } = {}): void {\n const items = this.#getItems();\n items.forEach((item, i) => {\n item.tabIndex = i === index ? 0 : -1;\n });\n if (focus) items[index]?.focus();\n }\n}\n\n/** Edge behavior for {@link rovingMove}: cycle past the ends, or stop at them. */\nexport type RovingWrap = \"wrap\" | \"clamp\";\n\n/**\n * Pure helper that resolves the target index for a one-step directional move.\n *\n * Keyboard/orientation mapping stays in the caller: it decides that a key means\n * `delta` `+1` (next) or `-1` (previous) and whether the widget should `\"wrap\"`\n * (Radio Group, Toolbar) or `\"clamp\"` at the ends.\n *\n * @param current - The index focus is moving from.\n * @param length - Number of items in the set.\n * @param delta - `+1` to move to the next item, `-1` for the previous.\n * @param wrap - `\"wrap\"` cycles around the ends; `\"clamp\"` stops at them.\n * @returns The resolved index, or `-1` when there are no items.\n */\nexport function rovingMove(\n current: number,\n length: number,\n delta: number,\n wrap: RovingWrap,\n): number {\n if (length === 0) return -1;\n const next = current + delta;\n if (wrap === \"wrap\") return (next + length) % length;\n return Math.min(length - 1, Math.max(0, next));\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { ensureId } from \"../utils/aria_ids\";\nimport { CompositionTracker } from \"../utils/composition_tracker\";\nimport { scrollOptionIntoView } from \"../utils/option_scroll\";\nimport { RovingTabindex } from \"../utils/roving_tabindex\";\n\n/**\n * Headless, accessible multi-select combobox with chips.\n *\n * Markup contract (identifier: `stimeo--multi-select`):\n * <div data-controller=\"stimeo--multi-select\">\n * <ul data-stimeo--multi-select-target=\"tags\" aria-label=\"Selected\"></ul>\n * <input type=\"text\" role=\"combobox\" aria-expanded=\"false\"\n * aria-autocomplete=\"list\" aria-controls=\"ms-list\"\n * data-stimeo--multi-select-target=\"input\"\n * data-action=\"input->stimeo--multi-select#filter\n * keydown->stimeo--multi-select#onKeydown\n * focus->stimeo--multi-select#open\" />\n * <ul id=\"ms-list\" role=\"listbox\" aria-multiselectable=\"true\" hidden\n * data-stimeo--multi-select-target=\"list\">\n * <li id=\"ms-opt-1\" role=\"option\" aria-selected=\"false\" data-value=\"apple\"\n * data-stimeo--multi-select-target=\"option\"\n * data-action=\"click->stimeo--multi-select#toggleOption\">Apple</li>\n * </ul>\n * <span role=\"status\" aria-live=\"polite\" class=\"visually-hidden\"\n * data-stimeo--multi-select-target=\"status\"></span>\n * <!-- Optional: submit the selection as name=\"fruits[]\" hidden inputs. -->\n * <div data-stimeo--multi-select-target=\"fields\"></div>\n * <template data-stimeo--multi-select-target=\"tagTemplate\">…</template>\n * </div>\n *\n * Implements the WAI-ARIA APG **Combobox** pattern in its list-autocomplete,\n * multi-select form. Focus stays on the input; the active option is tracked with\n * `aria-activedescendant` and selection with `aria-selected`. Selected options are\n * mirrored as removable chips. For single selection use {@link ListboxController}\n * or Combobox; for free-text tags use {@link TagsInputController}.\n *\n * Behavior provided:\n * - Typing filters options by substring and opens the list; `ArrowDown`/`ArrowUp`\n * (wrapping), `Home`/`End` move the active option; `Enter` toggles it (the list\n * stays open); `Escape`/`Tab`/outside click close.\n * - Toggling syncs `aria-selected`, adds/removes a `Remove {label}` chip, mirrors\n * the live region, and dispatches `stimeo--multi-select:change` with `values`;\n * filtering dispatches `stimeo--multi-select:filter` for async candidates.\n * - The chips are one roving Tab stop: `ArrowLeft`/`ArrowRight` move between them,\n * `Delete`/`Backspace` remove the focused chip, and `Backspace` on an empty\n * input removes the last; removal re-homes focus to a neighbor or the input.\n * - `max` caps the selection (`0` = unlimited).\n * - With a `fields` target the selected values are mirrored into named hidden\n * inputs (default name `options[]`), so the selection submits with a normal\n * form and no consumer JS — parity with {@link TagsInputController}. An optional\n * `form` value sets the hidden inputs' `form` attribute, associating them with a\n * `<form>` by id even when the picker lives outside it. Purely additive: with no\n * `fields` target the behavior is unchanged.\n */\nexport class MultiSelectController extends Controller<HTMLElement> {\n static override targets = [\n \"input\",\n \"list\",\n \"option\",\n \"tags\",\n \"tag\",\n \"tagTemplate\",\n \"status\",\n \"fields\",\n ];\n static override values = {\n max: { type: Number, default: 0 },\n name: { type: String, default: \"options[]\" },\n form: { type: String, default: \"\" },\n };\n static actions = [\"close\", \"filter\", \"onKeydown\", \"open\", \"toggleOption\"] as const;\n static events = [\"change\", \"filter\"] as const;\n\n declare readonly inputTarget: HTMLInputElement;\n declare readonly listTarget: HTMLElement;\n declare readonly optionTargets: HTMLElement[];\n declare readonly tagsTarget: HTMLElement;\n declare readonly tagTargets: HTMLElement[];\n declare readonly tagTemplateTarget: HTMLTemplateElement;\n declare readonly statusTarget: HTMLElement;\n declare readonly fieldsTarget: HTMLElement;\n declare readonly hasListTarget: boolean;\n declare readonly hasInputTarget: boolean;\n declare readonly hasTagsTarget: boolean;\n declare readonly hasTagTemplateTarget: boolean;\n declare readonly hasStatusTarget: boolean;\n declare readonly hasFieldsTarget: boolean;\n\n declare maxValue: number;\n declare nameValue: string;\n declare formValue: string;\n declare readonly hasFormValue: boolean;\n\n /** The active option (tracked via `aria-activedescendant`), or null. */\n #activeOption: HTMLElement | null = null;\n /** Absorbs the browser's redundant final input after compositionend. */\n #ignorePostCompositionInput = false;\n /** Owns IME lifecycle state; confirmed text emits one filter result. */\n readonly #composition = new CompositionTracker({\n onStart: () => {\n this.#ignorePostCompositionInput = false;\n },\n onEnd: () => {\n // Apply the confirmed query even in browsers that omit a final input event.\n // When the usual final input follows synchronously, absorb it so async\n // consumers receive one filter event for the confirmed text, not two.\n this.#ignorePostCompositionInput = true;\n queueMicrotask(() => {\n this.#ignorePostCompositionInput = false;\n });\n this.filter();\n },\n });\n readonly #roving = new RovingTabindex(() => this.#removeButtons);\n\n /** Starts closed, syncs chips for any pre-selected options, and listens out. */\n override connect(): void {\n if (this.hasInputTarget) this.#composition.observe(this.inputTarget);\n this.close();\n if (this.hasTagsTarget) {\n this.tagsTarget.addEventListener(\"keydown\", this.#onTagKeydown);\n this.tagsTarget.addEventListener(\"click\", this.#onTagClick);\n // Rebuild chips idempotently: a Turbo Drive cache restore or morph can\n // re-connect with chips already in the DOM, so clear them before deriving\n // afresh from the selected options to avoid duplicate chips.\n for (const tag of this.tagTargets) tag.remove();\n for (const option of this.#selectedOptions) this.#appendTag(option);\n if (this.#removeButtons.length > 0) this.#roving.setActive(0);\n }\n // Seed the hidden fields from any pre-selected options so the form submits\n // the initial selection without an interaction.\n this.#syncFields();\n document.addEventListener(\"click\", this.#onOutsideClick);\n }\n\n /** Tears down document and chip listeners on disconnect (Turbo included). */\n override disconnect(): void {\n this.#composition.disconnect();\n this.#ignorePostCompositionInput = false;\n if (this.hasTagsTarget) {\n this.tagsTarget.removeEventListener(\"keydown\", this.#onTagKeydown);\n this.tagsTarget.removeEventListener(\"click\", this.#onTagClick);\n }\n document.removeEventListener(\"click\", this.#onOutsideClick);\n }\n\n /** Tracks an input added initially or after connect. */\n inputTargetConnected(input: HTMLInputElement): void {\n this.#composition.observe(input);\n }\n\n /** Removes composition listeners when the active input is replaced or removed. */\n inputTargetDisconnected(input: HTMLInputElement): void {\n this.#composition.unobserve(input);\n this.#ignorePostCompositionInput = false;\n }\n\n /** Filters confirmed input text, opens, and re-seeds the active option. */\n filter(event?: InputEvent): void {\n if (event && this.#ignorePostCompositionInput) {\n this.#ignorePostCompositionInput = false;\n return;\n }\n if (this.#composition.isComposing(event)) return;\n const query = this.inputTarget.value.trim().toLowerCase();\n for (const option of this.optionTargets) {\n const label = (option.textContent ?? \"\").trim().toLowerCase();\n option.hidden = query !== \"\" && !label.includes(query);\n }\n this.open();\n const visible = this.#visibleOptions;\n this.element.toggleAttribute(\"data-stimeo--multi-select-empty\", visible.length === 0);\n this.#setActive(visible[0] ?? null);\n this.dispatch(\"filter\", { detail: { query } });\n }\n\n /** Opens the list and activates the first visible option when none is active. */\n open(): void {\n if (!this.hasListTarget) return;\n this.listTarget.hidden = false;\n this.inputTarget.setAttribute(\"aria-expanded\", \"true\");\n if (!this.#activeOption) this.#setActive(this.#visibleOptions[0] ?? null);\n }\n\n /** Closes the list and clears the active option. */\n close(): void {\n if (!this.hasListTarget) return;\n this.listTarget.hidden = true;\n this.inputTarget.setAttribute(\"aria-expanded\", \"false\");\n this.#setActive(null);\n }\n\n /** Routes input keyboard interaction per the multi-select combobox model. */\n onKeydown(event: KeyboardEvent): void {\n // Ignore keys fired during IME composition: the `Enter` that confirms a\n // candidate (and arrows that move within it) must not select an option or\n // navigate the chip list. Controller-owned lifecycle state covers confirming\n // events that omit the standard per-event signal.\n if (this.#composition.isComposing(event)) return;\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n if (this.#isClosed) this.open();\n else this.#moveActive(1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n if (this.#isClosed) this.open();\n else this.#moveActive(-1);\n break;\n case \"Home\":\n if (!this.#isClosed) {\n event.preventDefault();\n this.#setActive(this.#visibleOptions[0] ?? null);\n }\n break;\n case \"End\": {\n if (!this.#isClosed) {\n event.preventDefault();\n const visible = this.#visibleOptions;\n this.#setActive(visible[visible.length - 1] ?? null);\n }\n break;\n }\n case \"Enter\":\n if (!this.#isClosed && this.#activeOption) {\n event.preventDefault();\n this.#toggleSelection(this.#activeOption);\n }\n break;\n case \"Escape\":\n // Layered-Escape contract: leave a press an inner handler already\n // owned (rule 1), and consume only while owning a dismissable state —\n // with the list already closed the press stays free for the shared\n // resolver, so an enclosing dialog still closes from a focused input.\n if (event.defaultPrevented || this.#isClosed) break;\n event.preventDefault();\n this.close();\n break;\n case \"Backspace\":\n if (this.inputTarget.value === \"\") {\n const buttons = this.#removeButtons;\n if (buttons.length > 0) {\n event.preventDefault();\n this.#removeTagAt(buttons.length - 1);\n }\n }\n break;\n case \"ArrowLeft\":\n if (this.inputTarget.value === \"\" && this.#removeButtons.length > 0) {\n event.preventDefault();\n this.#roving.setActive(this.#removeButtons.length - 1, { focus: true });\n }\n break;\n case \"Tab\":\n this.close();\n break;\n default:\n break;\n }\n }\n\n /**\n * Toggles the clicked option's selection. Bound via `data-action`. Focus is\n * re-homed to the input afterwards: options are non-focusable, so the click blurs\n * the input to `body` — and with the list deliberately staying open, every\n * keyboard affordance (Escape, arrows, typing) is bound to the input and would\n * otherwise go dead until the user clicks back in (\"focus stays on the input\").\n */\n toggleOption(event: Event): void {\n const option = (event.currentTarget as HTMLElement).closest<HTMLElement>('[role=\"option\"]');\n if (!option) return;\n this.#toggleSelection(option);\n this.inputTarget.focus();\n }\n\n /**\n * Removes the chip whose remove button was clicked, deselecting its option.\n * Delegated on the tags container (like `#onTagKeydown`) rather than bound\n * per chip via `data-action`, so it works the instant a chip is appended without\n * waiting on Stimulus to wire a freshly created element.\n */\n readonly #onTagClick = (event: MouseEvent): void => {\n const button = (event.target as HTMLElement).closest(\"button\");\n if (!button || !this.tagsTarget.contains(button)) return;\n const index = this.#removeButtons.indexOf(button as HTMLButtonElement);\n if (index !== -1) this.#removeTagAt(index);\n };\n\n /** Moves the active option by `delta` among visible options, wrapping. */\n #moveActive(delta: number): void {\n const visible = this.#visibleOptions;\n if (visible.length === 0) return;\n const current = this.#activeOption ? visible.indexOf(this.#activeOption) : -1;\n const next = (current + delta + visible.length) % visible.length;\n this.#setActive(visible[next] ?? null);\n }\n\n /** Selects/deselects `option`, honoring `max`, and syncs chip + live region. */\n #toggleSelection(option: HTMLElement): void {\n const selected = option.getAttribute(\"aria-selected\") === \"true\";\n if (!selected && this.maxValue > 0 && this.#selectedOptions.length >= this.maxValue) {\n return;\n }\n option.setAttribute(\"aria-selected\", String(!selected));\n if (selected) {\n this.#removeTagFor(option);\n } else {\n this.#appendTag(option);\n }\n this.#announce(this.#optionLabel(option));\n this.#refreshRoving();\n this.#syncFields();\n this.dispatch(\"change\", { detail: { values: this.#values } });\n }\n\n /** Builds one chip from the template for `option`. */\n #appendTag(option: HTMLElement): void {\n if (!this.hasTagTemplateTarget || !this.hasTagsTarget) return;\n const fragment = this.tagTemplateTarget.content.cloneNode(true) as DocumentFragment;\n const tag = fragment.querySelector<HTMLElement>('[data-stimeo--multi-select-target=\"tag\"]');\n const label = fragment.querySelector<HTMLElement>('[data-multi-select-slot=\"label\"]');\n const button = fragment.querySelector<HTMLButtonElement>(\"button\");\n if (!tag || !button) return;\n const text = this.#optionLabel(option);\n tag.dataset.value = this.#optionValue(option);\n if (label) label.textContent = text;\n button.setAttribute(\"aria-label\", `Remove ${text}`);\n button.tabIndex = -1;\n this.tagsTarget.appendChild(fragment);\n }\n\n /** Removes the chip mirroring `option`, if present. */\n #removeTagFor(option: HTMLElement): void {\n const value = this.#optionValue(option);\n const tag = this.tagTargets.find((candidate) => candidate.dataset.value === value);\n tag?.remove();\n }\n\n /** Removes chip `index` and deselects its option, re-homing focus. */\n #removeTagAt(index: number): void {\n const tag = this.tagTargets[index];\n if (!tag) return;\n const value = tag.dataset.value ?? \"\";\n // Match by the option's stable value, which falls back to its label when no\n // data-value is present, so a label-keyed chip still finds its option.\n const option = this.optionTargets.find((candidate) => this.#optionValue(candidate) === value);\n if (option) option.setAttribute(\"aria-selected\", \"false\");\n tag.remove();\n // Prefer the option's display label for the announcement (e.g. \"Apple\"),\n // which can differ from its data-value (e.g. \"apple\").\n this.#announce(option ? this.#optionLabel(option) : value);\n this.#refreshRoving();\n this.#syncFields();\n this.dispatch(\"change\", { detail: { values: this.#values } });\n\n const remaining = this.#removeButtons;\n if (remaining.length === 0) {\n this.inputTarget.focus();\n } else {\n this.#roving.setActive(Math.min(index, remaining.length - 1), { focus: true });\n }\n }\n\n /** Arrow navigation and deletion within the chip list (delegated). */\n readonly #onTagKeydown = (event: KeyboardEvent): void => {\n const button = (event.target as HTMLElement).closest(\"button\");\n if (!button) return;\n const buttons = this.#removeButtons;\n const index = buttons.indexOf(button);\n if (index === -1) return;\n switch (event.key) {\n case \"ArrowLeft\":\n if (index > 0) {\n event.preventDefault();\n this.#roving.setActive(index - 1, { focus: true });\n }\n break;\n case \"ArrowRight\":\n event.preventDefault();\n if (index < buttons.length - 1) this.#roving.setActive(index + 1, { focus: true });\n else this.inputTarget.focus();\n break;\n case \"Delete\":\n case \"Backspace\":\n event.preventDefault();\n this.#removeTagAt(index);\n break;\n default:\n break;\n }\n };\n\n /**\n * Marks `option` active via `data-active` and the input's\n * `aria-activedescendant` (the attribute is removed, not emptied, when null).\n */\n #setActive(option: HTMLElement | null): void {\n this.#activeOption = option;\n for (const candidate of this.optionTargets) {\n candidate.toggleAttribute(\"data-active\", candidate === option);\n }\n if (option) {\n this.inputTarget.setAttribute(\"aria-activedescendant\", ensureId(option, \"stimeo-ms-opt\"));\n // Virtual focus never triggers the browser's native focus-scrolling, so a\n // scrollable list must follow the active option itself (list-only scroll).\n if (this.hasListTarget) scrollOptionIntoView(this.listTarget, option);\n } else {\n this.inputTarget.removeAttribute(\"aria-activedescendant\");\n }\n }\n\n /**\n * Mirrors the selected values into named hidden inputs under the `fields`\n * target so the selection submits with a normal form (parity with tags-input).\n * No-ops without a `fields` target, keeping the control back-compat. When the\n * `form` value is set, each input gets a matching `form` attribute so the picker\n * can submit with a `<form>` it lives outside of.\n */\n #syncFields(): void {\n if (!this.hasFieldsTarget) return;\n this.fieldsTarget.replaceChildren(\n ...this.#values.map((value) => {\n const input = document.createElement(\"input\");\n input.type = \"hidden\";\n input.name = this.nameValue;\n input.value = value;\n if (this.hasFormValue && this.formValue !== \"\") input.setAttribute(\"form\", this.formValue);\n return input;\n }),\n );\n }\n\n /** Keeps exactly one chip remove button tabbable after the set changes. */\n #refreshRoving(): void {\n if (this.#removeButtons.length > 0 && this.#roving.activeIndex === -1)\n this.#roving.setActive(0);\n }\n\n /** Mirrors the changed option label into the live region. */\n #announce(text: string): void {\n if (this.hasStatusTarget) this.statusTarget.textContent = text;\n }\n\n /** Closes the list on a click outside the controller element. */\n readonly #onOutsideClick = (event: MouseEvent): void => {\n if (!this.#isClosed && !this.element.contains(event.target as Node)) this.close();\n };\n\n /** Trimmed visible label of an option. */\n #optionLabel(option: HTMLElement): string {\n return (option.textContent ?? \"\").trim();\n }\n\n /** An option's stable value: its `data-value`, else its display label. */\n #optionValue(option: HTMLElement): string {\n return option.dataset.value ?? this.#optionLabel(option);\n }\n\n /** Options not hidden by the current filter. */\n get #visibleOptions(): HTMLElement[] {\n return this.optionTargets.filter((option) => !option.hidden);\n }\n\n /** Options currently selected. */\n get #selectedOptions(): HTMLElement[] {\n return this.optionTargets.filter((option) => option.getAttribute(\"aria-selected\") === \"true\");\n }\n\n /** Selected values in option order. */\n get #values(): string[] {\n return this.#selectedOptions.map((option) => this.#optionValue(option));\n }\n\n /** The chip remove buttons in order (the roving navigation set). */\n get #removeButtons(): HTMLButtonElement[] {\n return this.hasTagsTarget\n ? Array.from(this.tagsTarget.querySelectorAll<HTMLButtonElement>(\"button\"))\n : [];\n }\n\n /** Whether the list is currently hidden. */\n get #isClosed(): boolean {\n return !this.hasListTarget || this.listTarget.hidden !== false;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/aria_ids.ts","../../src/utils/logical_scroll.ts","../../src/utils/arrow_step.ts","../../src/utils/composition_tracker.ts","../../src/utils/microtask_coalescer.ts","../../src/utils/option_scroll.ts","../../src/utils/roving_tabindex.ts","../../src/utils/tabindex_loan.ts","../../src/controllers/multi_select_controller.ts"],"names":[],"mappings":"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","file":"multi_select_controller.js","sourcesContent":["/**\n * Minimal id primitives for wiring ARIA relationships.\n *\n * Accessible relationships such as `aria-describedby`, `aria-errormessage`,\n * `aria-labelledby`, and `aria-activedescendant` reference their targets by DOM\n * `id`. When a controller must establish such a relationship but the consumer's\n * markup left the target without an `id`, it needs to mint one that is stable for\n * the page's lifetime and guaranteed not to collide with another generated id.\n *\n * These helpers own *only* id generation/assignment. The linking policy (which\n * attribute, what order, when to add or remove it) stays in each controller —\n * this deliberately avoids a premature generic \"ARIA linker\" abstraction.\n */\n\n/**\n * Monotonic counter backing {@link uniqueId}. Module-scoped so every generated\n * id is unique across all controller instances sharing this module in a document.\n */\nlet counter = 0;\n\n/**\n * Returns a unique, DOM-id-safe string of the form `` `${prefix}-${n}` `` where\n * `n` increases on each call.\n *\n * The counter alone guarantees uniqueness against other *generated* ids, but a\n * consumer-authored element could already own `` `${prefix}-${n}` ``. When a\n * `document` is available the candidate is therefore advanced until no element\n * with that id exists, so a generated id never collides with author markup\n * either — the minimal \"id registry\" guarantee these helpers exist to provide.\n *\n * @param prefix - Leading segment of the id (e.g. a controller's identifier).\n * Defaults to `\"stimeo\"`.\n */\nexport function uniqueId(prefix = \"stimeo\"): string {\n let candidate: string;\n do {\n counter += 1;\n candidate = `${prefix}-${counter}`;\n } while (typeof document !== \"undefined\" && document.getElementById(candidate) !== null);\n return candidate;\n}\n\n/**\n * Returns `element`'s existing `id`, or assigns it a freshly generated\n * {@link uniqueId} (with the given `prefix`) and returns that. Idempotent: an\n * element that already has an id keeps it, so consumer-authored ids are never\n * overwritten.\n *\n * @param element - The element to read or stamp an `id` onto.\n * @param prefix - Prefix forwarded to {@link uniqueId} when one must be generated.\n * @returns The element's id (existing or newly assigned).\n */\nexport function ensureId(element: Element, prefix = \"stimeo\"): string {\n if (element.id) return element.id;\n const id = uniqueId(prefix);\n element.id = id;\n return id;\n}\n","/** Normalized scroll position and maximum distance on one logical axis. */\nexport interface LogicalScrollMetrics {\n position: number;\n max: number;\n}\n\n/**\n * Whether horizontal scrolling on `element` follows right-to-left inline flow.\n *\n * Resolved from the **computed** `direction`, so the authoring contract is the\n * usual `dir=\"rtl\"` (or a stylesheet) on the element or any ancestor.\n *\n * Scope: horizontal writing modes. A vertical writing mode (`writing-mode:\n * vertical-rl`) also inverts the horizontal axis, which this check does not\n * model — vertical writing modes are out of scope for the scroll utilities\n * (their consumers describe axes as horizontal/vertical, not inline/block).\n */\nexport function isRtl(element: Element): boolean {\n return window.getComputedStyle(element).direction === \"rtl\";\n}\n\n/**\n * Returns scroll distance from the logical start edge.\n *\n * CSSOM View exposes standards-mode RTL horizontal offsets as `0` at the inline\n * start (right) and increasingly negative values toward the inline end (left).\n * The normalized position is always clamped to `[0, max]`, which also absorbs\n * Safari's elastic overscroll values.\n */\nexport function logicalScrollMetrics(\n element: HTMLElement,\n horizontal: boolean,\n): LogicalScrollMetrics {\n const max = Math.max(\n 0,\n horizontal\n ? element.scrollWidth - element.clientWidth\n : element.scrollHeight - element.clientHeight,\n );\n const raw = horizontal ? element.scrollLeft : element.scrollTop;\n const position = horizontal && isRtl(element) ? -raw : raw;\n return { position: Math.min(max, Math.max(0, position)), max };\n}\n\n/**\n * Converts a logical start/end delta to the physical value accepted by\n * `Element.scrollBy`.\n */\nexport function physicalScrollDelta(\n element: HTMLElement,\n horizontal: boolean,\n logicalDelta: number,\n): number {\n return horizontal && isRtl(element) ? -logicalDelta : logicalDelta;\n}\n","import { isRtl } from \"./logical_scroll\";\n\n/**\n * Turns an arrow key into a **logical** step: `+1` for \"next\", `-1` for\n * \"previous\", `0` when the key names neither.\n *\n * APG defines the horizontal pair as *next / previous* and says a vertical\n * arrangement swaps in Down/Up for the same meaning — so the pair is one axis's\n * spelling of an order, and the order reverses with the writing direction. Only\n * the horizontal pair reverses. Down/Up name an axis the writing direction does\n * not mirror, and returning them unchanged is the point: many controllers fold\n * both pairs into one branch, where swapping the branches under RTL would flip\n * the vertical axis too — a bug that reads as \"the arrows work\" until someone\n * presses Down.\n *\n * **Direction is read from the element the caller passes, which should be the\n * container that lays the items out** — not the focused child. A child may carry\n * its own `dir` (an LTR input inside an RTL form is ordinary authoring), and\n * probing per handler makes two handlers disagree at the boundary between them.\n *\n * This decides direction only. Whether the axis is even active (an\n * `orientation=\"horizontal\"` widget ignoring Down/Up), how far the step lands,\n * and what wrapping does all stay with the caller.\n *\n * **It encodes the list-order convention: `ArrowDown` is *next*.** Widgets that\n * pair the arrows by *value* instead — `ArrowUp` meaning \"more\", as a rating or a\n * slider does — must not use this, or their vertical axis inverts. Reverse the\n * horizontal pair on its own there.\n *\n * @example\n * ```ts\n * const step = logicalArrowStep(event.key, this.element);\n * if (step === 0) return;\n * this.#roving.setActive(rovingMove(current, length, step, \"wrap\"), { focus: true });\n * ```\n */\nexport function logicalArrowStep(key: string, element: Element): 1 | -1 | 0 {\n if (key === \"ArrowDown\") return 1;\n if (key === \"ArrowUp\") return -1;\n if (key !== \"ArrowRight\" && key !== \"ArrowLeft\") return 0;\n const forward = isRtl(element) ? \"ArrowLeft\" : \"ArrowRight\";\n return key === forward ? 1 : -1;\n}\n\n/**\n * Rewrites `key` so an existing LTR-shaped branch keeps working under RTL:\n * `ArrowRight` and `ArrowLeft` trade places, everything else passes through.\n *\n * The alternative — negating a delta — silently breaks handlers whose two\n * horizontal branches are **not mirror images**. A grid that clamps one edge but\n * not the other, or a segmented field guarding `index > 0` on one side and\n * `index < length - 1` on the other, ends up applying the wrong guard to the\n * wrong direction. Swapping the key leaves each branch, guards and all, exactly\n * where its author put it.\n *\n * Same rule as {@link logicalArrowStep} about which element to read: pass the\n * container that lays the items out, not the focused child.\n *\n * @example\n * ```ts\n * switch (logicalArrowKey(event.key, this.element)) {\n * case \"ArrowLeft\": // \"previous\" — whatever direction that is on screen\n * ```\n */\nexport function logicalArrowKey(key: string, element: Element): string {\n if (key !== \"ArrowRight\" && key !== \"ArrowLeft\") return key;\n if (!isRtl(element)) return key;\n return key === \"ArrowRight\" ? \"ArrowLeft\" : \"ArrowRight\";\n}\n\n/** Modifiers a widget may claim on an arrow key, named for the `allow` list. */\nexport type ArrowModifier = \"alt\" | \"ctrl\" | \"meta\" | \"shift\";\n\n/**\n * True when an arrow key arrived carrying a modifier the widget must leave to\n * the browser: return without calling `preventDefault()` and without moving any\n * state.\n *\n * A bare arrow belongs to the widget; a chorded one usually does not.\n * `Alt`/`Meta` plus a horizontal arrow is history back/forward on every desktop\n * browser, and a widget that swallows it makes the shortcut work or not\n * depending on where focus happens to sit — a coin-flip the user cannot see.\n *\n * `allow` is for the combinations APG assigns to a pattern **and the widget\n * actually implements** — today only Combobox's optional `Alt+Down`/`Alt+Up`.\n * Listing one the widget does not implement defeats the point: the chord then\n * runs the plain-arrow branch, which is exactly what this guard exists to stop.\n * Non-arrow keys return `false`, so chorded letters and\n * `Control+Home`/`Control+End` are untouched.\n *\n * @example\n * ```ts\n * if (isReservedArrowChord(event)) return;\n * ```\n */\nexport function isReservedArrowChord(\n event: KeyboardEvent,\n allow: readonly ArrowModifier[] = [],\n): boolean {\n if (!event.key.startsWith(\"Arrow\")) return false;\n return (\n (event.altKey && !allow.includes(\"alt\")) ||\n (event.ctrlKey && !allow.includes(\"ctrl\")) ||\n (event.metaKey && !allow.includes(\"meta\")) ||\n (event.shiftKey && !allow.includes(\"shift\"))\n );\n}\n","/** Minimal event shape carrying the platform's per-event composition signal. */\nexport interface CompositionSignal {\n readonly isComposing?: boolean;\n}\n\n/** Hooks that keep component-specific work outside {@link CompositionTracker}. */\nexport interface CompositionTrackerOptions {\n /** Runs after lifecycle state is set for a `compositionstart` event. */\n readonly onStart?: (event: Event) => void;\n /** Runs after lifecycle state is cleared for a `compositionend` event. */\n readonly onEnd?: (event: Event) => void;\n}\n\n/**\n * Owns IME composition listeners and transient state for one or more event targets.\n *\n * Some browsers omit `KeyboardEvent.isComposing` on the keydown that confirms a\n * conversion. Tracking `compositionstart` through `compositionend` supplies the\n * missing lifecycle signal without relying on deprecated numeric key codes.\n * Component policy stays with the consumer: filtering, validation, and submission\n * belong in the optional {@link CompositionTrackerOptions.onEnd} callback.\n *\n * @example\n * ```ts\n * #composition = new CompositionTracker({ onEnd: () => this.filter() });\n *\n * connect(): void {\n * this.#composition.observe(this.inputTarget);\n * }\n *\n * disconnect(): void {\n * this.#composition.disconnect();\n * }\n *\n * onKeydown(event: KeyboardEvent): void {\n * if (this.#composition.isComposing(event)) return;\n * }\n * ```\n */\nexport class CompositionTracker {\n readonly #observedTargets = new Set<EventTarget>();\n readonly #activeTargets = new Set<EventTarget>();\n readonly #onStart: ((event: Event) => void) | undefined;\n readonly #onEnd: ((event: Event) => void) | undefined;\n\n constructor(options: CompositionTrackerOptions = {}) {\n this.#onStart = options.onStart;\n this.#onEnd = options.onEnd;\n }\n\n /** Starts lifecycle tracking for `target`; repeated calls are idempotent. */\n observe(target: EventTarget): void {\n if (this.#observedTargets.has(target)) return;\n target.addEventListener(\"compositionstart\", this.#handleStart);\n target.addEventListener(\"compositionend\", this.#handleEnd);\n this.#observedTargets.add(target);\n }\n\n /** Stops tracking one target and clears any active composition it owned. */\n unobserve(target: EventTarget): void {\n if (!this.#observedTargets.delete(target)) return;\n target.removeEventListener(\"compositionstart\", this.#handleStart);\n target.removeEventListener(\"compositionend\", this.#handleEnd);\n this.#activeTargets.delete(target);\n }\n\n /** Releases every listener and clears state so reconnect starts cleanly. */\n disconnect(): void {\n for (const target of this.#observedTargets) {\n target.removeEventListener(\"compositionstart\", this.#handleStart);\n target.removeEventListener(\"compositionend\", this.#handleEnd);\n }\n this.#observedTargets.clear();\n this.#activeTargets.clear();\n }\n\n /** True when lifecycle tracking or the current event reports composition. */\n isComposing(event?: CompositionSignal): boolean {\n return this.#activeTargets.size > 0 || event?.isComposing === true;\n }\n\n readonly #handleStart = (event: Event): void => {\n if (event.currentTarget) this.#activeTargets.add(event.currentTarget);\n this.#onStart?.(event);\n };\n\n readonly #handleEnd = (event: Event): void => {\n if (event.currentTarget) this.#activeTargets.delete(event.currentTarget);\n this.#onEnd?.(event);\n };\n}\n","/**\n * Collapses many target callbacks from one DOM mutation into a single pass.\n *\n * Stimulus fires `<name>TargetConnected` / `Disconnected` once per element, so\n * replacing a list of N options delivers N callbacks — but the useful unit of\n * work is \"reconcile against the DOM that resulted\", once, after the batch has\n * settled. Every controller that owns a reconcilable target set needs the same\n * shape: a `queued` flag plus `queueMicrotask`.\n *\n * **A microtask is the right horizon, and the reason is specific.** Stimulus\n * drives these callbacks from a `MutationObserver`, whose own callback already\n * runs as a microtask with the whole batch in hand; scheduling one more lands\n * after the last sibling callback of that batch and still before paint or any\n * event handler. A timer would be later than it needs to be, and reconciling\n * synchronously would run once per element against a half-applied DOM.\n *\n * **The two guards are not the same guard.** Scheduling is refused before the\n * controller connects, and running is refused after it disconnects:\n *\n * - **Before `connect()`** — Stimulus delivers the *initial* target callbacks\n * ahead of `connect()`. Reconciling there would compute a fallback against a\n * controller whose own state has not been initialised, and `connect()` is\n * about to do a full pass anyway.\n * - **After `disconnect()`** — Stimulus fires a callback for **every** target\n * during teardown, and a microtask queued just before it would otherwise run\n * against a detached tree. {@link MicrotaskCoalescer.cancel} exists for the\n * teardown path to drop the pending pass outright.\n *\n * Both guards are part of one contract here rather than something each consumer\n * has to remember separately.\n *\n * Scope is the scheduling only. *What* to reconcile — keep the surviving active\n * option, fall back to the next / previous / first visible one, rebuild derived\n * chips or hidden fields — stays in the controller, because no two consumers\n * answer it the same way.\n *\n * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and only the contract belongs on the members.\n *\n * @example\n * ```ts\n * readonly #reconcile = new MicrotaskCoalescer(() => this.#reconcileOptions());\n *\n * connect() { this.#reconcile.activate(); }\n * disconnect() { this.#reconcile.cancel(); }\n *\n * optionTargetConnected() { this.#reconcile.schedule(); }\n * optionTargetDisconnected() { this.#reconcile.schedule(); }\n * ```\n */\nexport class MicrotaskCoalescer {\n readonly #run: () => void;\n #queued = false;\n #active = false;\n #generation = 0;\n\n /** @param run - the single reconciliation pass, invoked at most once per batch. */\n constructor(run: () => void) {\n this.#run = run;\n }\n\n /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */\n activate(): void {\n this.#active = true;\n }\n\n /** Closes the window and drops any pending pass; call from `disconnect()`. */\n cancel(): void {\n this.#active = false;\n this.#queued = false;\n this.#generation += 1;\n }\n\n /** Requests one pass after the batch settles. Idempotent; inert outside the window. */\n schedule(): void {\n if (!this.#active || this.#queued) return;\n this.#queued = true;\n const generation = this.#generation;\n queueMicrotask(() => {\n // A cancelled callback must not consume a pass queued after reconnect.\n if (generation !== this.#generation || !this.#queued || !this.#active) return;\n this.#queued = false;\n this.#run();\n });\n }\n}\n","/**\n * Scroll-follow for virtually-focused listbox options.\n *\n * Listbox-style widgets that track their active option with\n * `aria-activedescendant` keep DOM focus on the input/trigger, so the\n * browser's native \"scroll the focused element into view\" never runs — in a\n * scrollable list (`max-height` + `overflow`) the active option can walk right\n * out of sight on ArrowDown/ArrowUp. This helper keeps it visible.\n *\n * It adjusts the LIST's own `scrollTop` only — deliberately not\n * `scrollIntoView({ block: \"nearest\" })`, which may also scroll ancestor\n * scrolling boxes (the page) when a floating popup pokes past a viewport\n * edge, desyncing anchored/marker-composed placements. (Command-palette uses\n * `scrollIntoView` because its page is scroll-locked behind the modal;\n * page-floating popups must use this helper instead.)\n */\n\n/**\n * Scrolls `option` into view within `list` by minimally adjusting\n * `list.scrollTop`. No-ops when the list does not actually scroll. Never\n * touches any other scrolling box.\n *\n * @param list - The scrollable listbox container.\n * @param option - The (virtually focused) option to keep visible.\n */\nexport function scrollOptionIntoView(list: HTMLElement, option: HTMLElement): void {\n if (list.scrollHeight <= list.clientHeight) return;\n const listRect = list.getBoundingClientRect();\n const optionRect = option.getBoundingClientRect();\n if (optionRect.top < listRect.top) {\n list.scrollTop -= listRect.top - optionRect.top;\n } else if (optionRect.bottom > listRect.bottom) {\n list.scrollTop += optionRect.bottom - listRect.bottom;\n }\n}\n","/**\n * Low-level roving-tabindex primitive shared by composite-widget controllers.\n *\n * The APG roving-tabindex pattern keeps a composite widget a single Tab stop:\n * exactly one item is in the Tab sequence (`tabindex=\"0\"`) while the rest are\n * removed from it (`tabindex=\"-1\"`), and the arrow keys move both DOM focus and\n * that single tabbable position together. {@link RovingTabindex} owns *only* that\n * mechanical bookkeeping — \"which one item is tabbable, and move focus there\".\n *\n * It is intentionally **policy-free**. Orientation, wrapping vs. clamping,\n * selection-follows-focus, typeahead, and `Home`/`End` semantics differ per APG\n * pattern (Radio Group, Toolbar, Rating, …); folding them into one helper would\n * flatten those widgets to a lowest common denominator and lose each pattern's\n * correctness. Those decisions therefore stay in each controller, which calls\n * {@link RovingTabindex.setActive} with an index it computed itself (optionally\n * via the pure {@link rovingMove} helper).\n *\n * @remarks\n * Items are read lazily through a getter so a controller can add or remove\n * targets (Stimulus re-scans the DOM) without re-wiring this helper.\n */\nexport class RovingTabindex {\n /** Returns the current ordered item elements; called on every operation. */\n readonly #getItems: () => HTMLElement[];\n\n /**\n * @param getItems - Returns the current ordered item elements. Called on every\n * operation so the live target list is always used.\n */\n constructor(getItems: () => HTMLElement[]) {\n this.#getItems = getItems;\n }\n\n /** Index of the currently tabbable item (`tabindex=\"0\"`), or `-1` if none. */\n get activeIndex(): number {\n return this.#getItems().findIndex((item) => item.tabIndex === 0);\n }\n\n /**\n * Makes exactly the item at `index` tabbable (`tabindex=\"0\"`) and removes every\n * other item from the Tab sequence (`tabindex=\"-1\"`). An out-of-range `index`\n * (e.g. `-1`) leaves all items at `-1`, which a controller can use to express\n * \"nothing is currently tabbable\".\n *\n * @param index - Position of the item to make tabbable.\n * @param options - Pass `{ focus: true }` to also move DOM focus to that item.\n */\n setActive(index: number, { focus = false }: { focus?: boolean } = {}): void {\n const items = this.#getItems();\n items.forEach((item, i) => {\n item.tabIndex = i === index ? 0 : -1;\n });\n if (focus) items[index]?.focus();\n }\n}\n\n/** Edge behavior for {@link rovingMove}: cycle past the ends, or stop at them. */\nexport type RovingWrap = \"wrap\" | \"clamp\";\n\n/**\n * Pure helper that resolves the target index for a one-step directional move.\n *\n * Keyboard/orientation mapping stays in the caller: it decides that a key means\n * `delta` `+1` (next) or `-1` (previous) and whether the widget should `\"wrap\"`\n * (Radio Group, Toolbar) or `\"clamp\"` at the ends.\n *\n * @param current - The index focus is moving from.\n * @param length - Number of items in the set.\n * @param delta - `+1` to move to the next item, `-1` for the previous.\n * @param wrap - `\"wrap\"` cycles around the ends; `\"clamp\"` stops at them.\n * @returns The resolved index, or `-1` when there are no items.\n */\nexport function rovingMove(\n current: number,\n length: number,\n delta: number,\n wrap: RovingWrap,\n): number {\n if (length === 0) return -1;\n const next = current + delta;\n if (wrap === \"wrap\") return (next + length) % length;\n return Math.min(length - 1, Math.max(0, next));\n}\n","/**\n * Shared bookkeeping for a `tabindex` a controller lends an element temporarily.\n *\n * A controller that must move focus somewhere the author never made focusable\n * (a landmark root, a scroll destination) reaches for the same trick: add a\n * `tabindex` just-in-time and hand it back once it is no longer needed. The\n * borrow is the easy half; the return is what the two conditions below are for.\n *\n * **Returning needs two conditions, not one.** Owning the borrow is not enough:\n * the attribute must also still hold the value this instance wrote. A consumer\n * that changed it afterwards — `tabindex=\"0\"` to make the root its own Tab stop\n * — owns it now, and removing it there silently discards authored markup. The\n * bookkeeping is dropped either way, since the loan is over regardless of who\n * ends up owning the value.\n *\n * **Never borrow over an existing value.** An element that already carries a\n * `tabindex` is the author's to control, so there is nothing to lend and nothing\n * to return.\n *\n * The registry is keyed by element, so a controller borrowing on a single\n * element (`this.element`) and one borrowing across a changing set of targets\n * use the same API — the single-element case is a set of one. It holds no\n * opinion about *when* to borrow or where focus goes next; that stays in the\n * controller.\n *\n * **The API is deliberately two methods.** This file's own doc block is dropped\n * from `dist`, but every member comment is inlined into **each** consumer entry\n * (`tsup` builds with `splitting: false`), so rationale belongs here, only the\n * contract belongs on the members, and every method no consumer calls is still\n * paid for once per consumer entry.\n *\n * @example\n * ```ts\n * readonly #tabindex = new TabindexLoan();\n *\n * #rescueFocus() {\n * this.#tabindex.lend(this.element);\n * this.element.focus();\n * }\n *\n * disconnect() {\n * this.#tabindex.returnAll();\n * }\n * ```\n */\nexport class TabindexLoan<T extends HTMLElement = HTMLElement> {\n readonly #value: string;\n readonly #lent = new Set<T>();\n\n /**\n * @param value - the `tabindex` to lend. `\"-1\"` (the default) is\n * programmatically focusable but not a Tab stop; `\"0\"` is a real Tab stop,\n * which a scroll region with no focusable content of its own needs.\n */\n constructor(value: string = \"-1\") {\n this.#value = value;\n }\n\n /** Lends `element` the value; no-ops when it already carries a `tabindex`. */\n lend(element: T): void {\n if (element.hasAttribute(\"tabindex\")) return;\n element.setAttribute(\"tabindex\", this.#value);\n this.#lent.add(element);\n }\n\n /** Takes back every loan whose value is still the one that was lent. */\n returnAll(): void {\n for (const element of this.#lent) {\n if (element.getAttribute(\"tabindex\") === this.#value) element.removeAttribute(\"tabindex\");\n }\n this.#lent.clear();\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { ensureId } from \"../utils/aria_ids\";\nimport { isReservedArrowChord, logicalArrowKey } from \"../utils/arrow_step\";\nimport { CompositionTracker } from \"../utils/composition_tracker\";\nimport { MicrotaskCoalescer } from \"../utils/microtask_coalescer\";\nimport { scrollOptionIntoView } from \"../utils/option_scroll\";\nimport { RovingTabindex } from \"../utils/roving_tabindex\";\nimport { TabindexLoan } from \"../utils/tabindex_loan\";\n\n/**\n * Headless, accessible multi-select combobox with chips.\n *\n * Markup contract (identifier: `stimeo--multi-select`):\n * <div data-controller=\"stimeo--multi-select\">\n * <ul data-stimeo--multi-select-target=\"tags\" aria-label=\"Selected\"></ul>\n * <input type=\"text\" role=\"combobox\" aria-expanded=\"false\"\n * aria-autocomplete=\"list\" aria-controls=\"ms-list\"\n * data-stimeo--multi-select-target=\"input\"\n * data-action=\"input->stimeo--multi-select#filter\n * keydown->stimeo--multi-select#onKeydown\n * focus->stimeo--multi-select#open\" />\n * <ul id=\"ms-list\" role=\"listbox\" aria-multiselectable=\"true\" hidden\n * data-stimeo--multi-select-target=\"list\">\n * <li id=\"ms-opt-1\" role=\"option\" aria-selected=\"false\" data-value=\"apple\"\n * data-stimeo--multi-select-target=\"option\"\n * data-action=\"click->stimeo--multi-select#toggleOption\">Apple</li>\n * </ul>\n * <span role=\"status\" aria-live=\"polite\" class=\"visually-hidden\"\n * data-stimeo--multi-select-target=\"status\"></span>\n * <!-- Optional: submit the selection as name=\"fruits[]\" hidden inputs. -->\n * <div data-stimeo--multi-select-target=\"fields\"></div>\n * <template data-stimeo--multi-select-target=\"tagTemplate\">…</template>\n * </div>\n *\n * Implements the WAI-ARIA APG **Combobox** pattern in its list-autocomplete,\n * multi-select form. Focus stays on the input; the active option is tracked with\n * `aria-activedescendant` and selection with `aria-selected`. Selected options are\n * mirrored as removable chips. For single selection use {@link ListboxController}\n * or Combobox; for free-text tags use {@link TagsInputController}.\n *\n * Behavior provided:\n * - Typing filters options by substring and opens the list; `ArrowDown`/`ArrowUp`\n * (wrapping), `Home`/`End` move the active option; `Enter` toggles it (the list\n * stays open); `Escape`/`Tab`/outside click close.\n * - Toggling syncs `aria-selected`, adds/removes a `Remove {label}` chip, mirrors\n * the live region, and dispatches `stimeo--multi-select:change` with `values`;\n * filtering dispatches `stimeo--multi-select:filter` for async candidates.\n * - The chips are one roving Tab stop: `ArrowLeft`/`ArrowRight` move between them,\n * `Delete`/`Backspace` remove the focused chip, and `Backspace` on an empty\n * input removes the last; removal re-homes focus to a neighbor or the input.\n * - `max` caps the selection (`0` = unlimited).\n * - With a `fields` target the selected values are mirrored into named hidden\n * inputs (default name `options[]`), so the selection submits with a normal\n * form and no consumer JS — parity with {@link TagsInputController}. An optional\n * `form` value sets the hidden inputs' `form` attribute, associating them with a\n * `<form>` by id even when the picker lives outside it. Without a `fields`\n * target the hidden inputs are simply not written.\n */\nexport class MultiSelectController extends Controller<HTMLElement> {\n static override targets = [\n \"input\",\n \"list\",\n \"option\",\n \"tags\",\n \"tag\",\n \"tagTemplate\",\n \"status\",\n \"fields\",\n ];\n static override values = {\n max: { type: Number, default: 0 },\n name: { type: String, default: \"options[]\" },\n form: { type: String, default: \"\" },\n };\n static actions = [\"close\", \"filter\", \"onKeydown\", \"open\", \"toggleOption\"] as const;\n static events = [\"change\", \"filter\"] as const;\n\n declare readonly inputTarget: HTMLInputElement;\n declare readonly listTarget: HTMLElement;\n declare readonly optionTargets: HTMLElement[];\n declare readonly tagsTarget: HTMLElement;\n declare readonly tagTargets: HTMLElement[];\n declare readonly tagTemplateTarget: HTMLTemplateElement;\n declare readonly statusTarget: HTMLElement;\n declare readonly fieldsTarget: HTMLElement;\n declare readonly hasListTarget: boolean;\n declare readonly hasInputTarget: boolean;\n declare readonly hasTagsTarget: boolean;\n declare readonly hasTagTemplateTarget: boolean;\n declare readonly hasStatusTarget: boolean;\n declare readonly hasFieldsTarget: boolean;\n\n declare maxValue: number;\n declare nameValue: string;\n declare formValue: string;\n declare readonly hasFormValue: boolean;\n\n /** Stable id of the active option; the current target is resolved from the DOM. */\n #activeOptionId: string | null = null;\n /** Whether the root borrowed a tab stop to catch focus, so teardown can undo it. */\n readonly #tabindex = new TabindexLoan();\n /** Prevents initial/teardown target callbacks from mutating authored DOM. */\n #connected = false;\n /** Collapses one batch of target callbacks into a single final-DOM reconciliation. */\n readonly #reconcile = new MicrotaskCoalescer(() => this.#reconcileOptions());\n /** Absorbs the browser's redundant final input after compositionend. */\n #ignorePostCompositionInput = false;\n /** Owns IME lifecycle state; confirmed text emits one filter result. */\n readonly #composition = new CompositionTracker({\n onStart: () => {\n this.#ignorePostCompositionInput = false;\n },\n onEnd: () => {\n // Apply the confirmed query even in browsers that omit a final input event.\n // When the usual final input follows synchronously, absorb it so async\n // consumers receive one filter event for the confirmed text, not two.\n this.#ignorePostCompositionInput = true;\n queueMicrotask(() => {\n this.#ignorePostCompositionInput = false;\n });\n this.filter();\n },\n });\n readonly #roving = new RovingTabindex(() => this.#removeButtons);\n\n /** Starts closed, syncs chips for any pre-selected options, and listens out. */\n override connect(): void {\n if (this.hasInputTarget) this.#composition.observe(this.inputTarget);\n this.#normalizeSelection();\n this.close();\n if (this.hasTagsTarget) {\n this.tagsTarget.addEventListener(\"keydown\", this.#onTagKeydown);\n this.tagsTarget.addEventListener(\"click\", this.#onTagClick);\n this.#rebuildTags();\n }\n // Seed the hidden fields from any pre-selected options so the form submits\n // the initial selection without an interaction.\n this.#syncFields();\n document.addEventListener(\"click\", this.#onOutsideClick, true);\n this.#connected = true;\n this.#reconcile.activate();\n }\n\n /**\n * Derives the chips from the selected options, idempotently: a Turbo Drive cache\n * restore or morph can re-connect with chips already in the DOM, so they are\n * cleared before deriving afresh to avoid duplicates.\n */\n #rebuildTags(): void {\n if (!this.hasTagsTarget) return;\n for (const tag of this.tagTargets) tag.remove();\n for (const option of this.#selectedOptions) this.#appendTag(option);\n if (this.#removeButtons.length > 0) this.#roving.setActive(0);\n }\n\n /** Tears down document and chip listeners on disconnect (Turbo included). */\n override disconnect(): void {\n this.#connected = false;\n this.#reconcile.cancel();\n this.#composition.disconnect();\n this.#ignorePostCompositionInput = false;\n if (this.hasTagsTarget) {\n this.tagsTarget.removeEventListener(\"keydown\", this.#onTagKeydown);\n this.tagsTarget.removeEventListener(\"click\", this.#onTagClick);\n }\n document.removeEventListener(\"click\", this.#onOutsideClick, true);\n this.#releaseTabindex();\n }\n\n /** Reconciles active state after an option target is added at runtime. */\n optionTargetConnected(): void {\n this.#scheduleOptionReconcile();\n }\n\n /** Cleans a removed target and reconciles active state against the surviving DOM. */\n optionTargetDisconnected(option: HTMLElement): void {\n if (!this.#connected) return;\n // `#setActive` can only scan current targets; clean the old node explicitly in\n // case a Turbo morph reuses it elsewhere.\n option.removeAttribute(\"data-active\");\n this.#scheduleOptionReconcile();\n }\n\n /** Schedules one reconciliation after all callbacks in the mutation batch. */\n #scheduleOptionReconcile(): void {\n this.#reconcile.schedule();\n }\n\n /**\n * Keeps a surviving/same-id active target, otherwise falls back to the first\n * visible one — and brings the derived state back in line with the new option set.\n *\n * The baseline pass fills in any missing `aria-selected`, and the chips and\n * hidden fields are re-derived from it, because the options are the truth source\n * for the selection. The chips are rebuilt **only when the selected value set\n * actually moved**: the rebuild removes and recreates every chip, so running it\n * for an unrelated option would drop focus from a chip's remove button to\n * `<body>`, losing the keyboard user's place for something that did not concern\n * them.\n */\n #reconcileOptions(): void {\n const visible = this.#visibleOptions;\n const active = this.#activeOption;\n const next = this.#isClosed ? null : active && !active.hidden ? active : (visible[0] ?? null);\n this.#setActive(next);\n this.#reflectEmpty();\n\n this.#normalizeSelection();\n const selected = this.#selectedOptions;\n // Compared as a set, not a sequence: the chips are in selection order while\n // the options are in DOM order, so an index-wise comparison reports a change\n // for every selection the user made out of DOM order and rebuilds forever.\n const nextValues = selected.map((option) => this.#optionValue(option)).sort();\n const tagValues = this.tagTargets.map((tag) => tag.dataset.value ?? \"\").sort();\n const unchanged =\n nextValues.length === tagValues.length &&\n nextValues.every((value, index) => value === tagValues[index]);\n if (unchanged) this.#refreshTagLabels(selected);\n else this.#rebuildTags();\n this.#syncFields();\n }\n\n /**\n * Gives every option an explicit `aria-selected`, without changing which ones\n * the author chose. An absent value means \"not selectable\" in ARIA, so a\n * forgotten attribute hides a selectable option. Several `true` is the normal\n * case here — the list is `aria-multiselectable` — so nothing is dropped.\n */\n #normalizeSelection(): void {\n for (const option of this.optionTargets) {\n if (option.getAttribute(\"aria-selected\") !== \"true\") {\n option.setAttribute(\"aria-selected\", \"false\");\n }\n }\n }\n\n /**\n * Tracks an input added initially or after connect, and makes it describe the\n * widget that is actually on screen: a swapped-in input arrives with the\n * authored ARIA of a fresh node while this controller still holds the popup\n * state, and the open path cannot repair that (it seeds an active option only\n * when there is none), so a live list would go unannounced.\n */\n inputTargetConnected(input: HTMLInputElement): void {\n this.#composition.observe(input);\n input.setAttribute(\"aria-expanded\", String(!this.#isClosed));\n const active = this.#activeOption;\n if (active) input.setAttribute(\"aria-activedescendant\", ensureId(active, \"stimeo-ms-opt\"));\n else input.removeAttribute(\"aria-activedescendant\");\n }\n\n /** Removes composition listeners when the active input is replaced or removed. */\n inputTargetDisconnected(input: HTMLInputElement): void {\n this.#composition.unobserve(input);\n this.#ignorePostCompositionInput = false;\n }\n\n /** Filters confirmed input text, opens, and re-seeds the active option. */\n filter(event?: InputEvent): void {\n if (event && this.#ignorePostCompositionInput) {\n this.#ignorePostCompositionInput = false;\n return;\n }\n if (this.#composition.isComposing(event)) return;\n const query = this.inputTarget.value.trim().toLowerCase();\n for (const option of this.optionTargets) {\n const label = (option.textContent ?? \"\").trim().toLowerCase();\n option.hidden = query !== \"\" && !label.includes(query);\n }\n this.open();\n const visible = this.#visibleOptions;\n this.#reflectEmpty();\n this.#setActive(visible[0] ?? null);\n this.dispatch(\"filter\", { detail: { query } });\n }\n\n /**\n * Opens the list and activates the first visible option when none is active.\n *\n * Needs the input, which owns `aria-expanded` and `aria-activedescendant`: a\n * list shown without one is a popup no assistive technology is told about. So\n * opening is skipped entirely, where {@link close} still closes.\n */\n open(): void {\n if (!this.hasListTarget || !this.hasInputTarget) return;\n this.listTarget.hidden = false;\n this.inputTarget.setAttribute(\"aria-expanded\", \"true\");\n if (!this.#activeOption) this.#setActive(this.#visibleOptions[0] ?? null);\n }\n\n /**\n * Closes the list and clears the active option.\n *\n * Survives a missing input in both directions. `connect()` calls this second,\n * so dereferencing the input here would throw before the chips, the roving\n * seed, the chip listeners, the hidden fields and the outside-click listener —\n * and Stimulus keeps the controller alive after that throw, so none of them\n * would ever run and the selection would silently stop submitting. An input\n * removed while the list is open must still let it come down *and* forget its\n * active option, so only the `aria-expanded` write is guarded.\n */\n close(): void {\n if (!this.hasListTarget) return;\n this.listTarget.hidden = true;\n this.#setActive(null);\n if (!this.hasInputTarget) return;\n this.inputTarget.setAttribute(\"aria-expanded\", \"false\");\n }\n\n /** Routes input keyboard interaction per the multi-select combobox model. */\n onKeydown(event: KeyboardEvent): void {\n // A descendant widget that already claimed the key must not ALSO open the\n // list or move the active option — composition depends on this yield.\n if (event.defaultPrevented) return;\n if (isReservedArrowChord(event)) return;\n // Ignore keys fired during IME composition: the `Enter` that confirms a\n // candidate (and arrows that move within it) must not select an option or\n // navigate the chip list. Controller-owned lifecycle state covers confirming\n // events that omit the standard per-event signal.\n if (this.#composition.isComposing(event)) return;\n // Target callbacks are MutationObserver-driven. Resolve a replacement now, or\n // clear a removed id now, so a synchronously dispatched key cannot commit a\n // detached node before the callback gets its turn.\n this.#reconcileActiveForInteraction();\n // Logical, not physical. The key is normalised rather than a new case added:\n // the two horizontal branches are not mirror images — only one guards its\n // edge, and the other hands focus back to the input — so swapping the key\n // keeps each branch, guards and all, with its own direction. Both handlers\n // read the same element on purpose; probing the focused child would let the\n // input and the chips disagree at the boundary between them.\n switch (logicalArrowKey(event.key, this.element)) {\n case \"ArrowDown\":\n event.preventDefault();\n if (this.#isClosed) this.open();\n else this.#moveActive(1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n if (this.#isClosed) this.open();\n else this.#moveActive(-1);\n break;\n case \"Home\":\n if (!this.#isClosed) {\n event.preventDefault();\n this.#setActive(this.#visibleOptions[0] ?? null);\n }\n break;\n case \"End\": {\n if (!this.#isClosed) {\n event.preventDefault();\n const visible = this.#visibleOptions;\n this.#setActive(visible[visible.length - 1] ?? null);\n }\n break;\n }\n case \"Enter\": {\n const active = this.#activeOption;\n if (!this.#isClosed && active) {\n event.preventDefault();\n this.#toggleSelection(active);\n }\n break;\n }\n case \"Escape\":\n // A press an inner handler already owned is yielded at the top of this\n // handler. Escape is consumed only while the list is open: with it closed\n // the press stays free for the shared resolver, so an enclosing dialog\n // still closes from a focused input.\n if (this.#isClosed) break;\n event.preventDefault();\n this.close();\n break;\n case \"Backspace\":\n if (this.inputTarget.value === \"\") {\n const buttons = this.#removeButtons;\n if (buttons.length > 0) {\n event.preventDefault();\n this.#removeTagAt(buttons.length - 1);\n }\n }\n break;\n case \"ArrowLeft\":\n if (this.inputTarget.value === \"\" && this.#removeButtons.length > 0) {\n event.preventDefault();\n this.#roving.setActive(this.#removeButtons.length - 1, { focus: true });\n }\n break;\n case \"Tab\":\n this.close();\n break;\n default:\n break;\n }\n }\n\n /**\n * Toggles the clicked option's selection. Bound via `data-action`. Focus is\n * re-homed to the input afterwards: options are non-focusable, so the click blurs\n * the input to `body` — and with the list deliberately staying open, every\n * keyboard affordance (Escape, arrows, typing) is bound to the input and would\n * otherwise go dead until the user clicks back in (\"focus stays on the input\").\n */\n toggleOption(event: Event): void {\n const option = (event.currentTarget as HTMLElement).closest<HTMLElement>('[role=\"option\"]');\n if (!option || !this.optionTargets.includes(option)) return;\n this.#toggleSelection(option);\n this.#focusInput();\n }\n\n /**\n * Re-homes focus to the input, or leaves it where it is when there is none.\n *\n * All three callers run *after* an option or a chip already took focus, and all\n * three are reachable without an input — options and chips carry their own\n * `data-action`. Throwing here would leave the chip removed but focus stranded\n * on a detached button.\n */\n #focusInput(): void {\n if (this.hasInputTarget) this.inputTarget.focus();\n }\n\n /**\n * Re-homes focus after the last chip was removed: to the input, else the root.\n *\n * Unlike the other {@link #focusInput} callers, the element that held focus has\n * just left the DOM, so \"leave it alone\" is not an option — the browser already\n * dropped it to `<body>`. The root borrows a `tabindex=\"-1\"` just-in-time (not a\n * Tab stop, handed back on teardown). Focus that landed on a real element is\n * left alone, so a chip removed out of band never steals it.\n */\n #focusAfterLastTag(): void {\n if (this.hasInputTarget) {\n this.inputTarget.focus();\n return;\n }\n const doc = this.element.ownerDocument;\n const active = doc.activeElement;\n // Anything else still holding focus keeps it; only `<body>`, the root element\n // and a detached node mean the browser had nowhere to put it.\n if (active && active !== doc.body && active !== doc.documentElement && active.isConnected) {\n return;\n }\n this.#tabindex.lend(this.element);\n this.element.focus();\n }\n\n /**\n * Returns the borrowed tab stop. Owning the borrow is not enough — the value\n * has to still be the one this instance wrote, since a consumer that changed it\n * afterwards owns it now.\n */\n #releaseTabindex(): void {\n this.#tabindex.returnAll();\n }\n\n /**\n * Removes the chip whose remove button was clicked, deselecting its option.\n * Delegated on the tags container (like `#onTagKeydown`) rather than bound\n * per chip via `data-action`, so it works the instant a chip is appended without\n * waiting on Stimulus to wire a freshly created element.\n */\n readonly #onTagClick = (event: MouseEvent): void => {\n const button = (event.target as HTMLElement).closest(\"button\");\n if (!button || !this.tagsTarget.contains(button)) return;\n const index = this.#removeButtons.indexOf(button as HTMLButtonElement);\n if (index !== -1) this.#removeTagAt(index);\n };\n\n /** Moves the active option by `delta` among visible options, wrapping. */\n #moveActive(delta: number): void {\n const visible = this.#visibleOptions;\n if (visible.length === 0) return;\n const current = this.#activeOption ? visible.indexOf(this.#activeOption) : -1;\n const candidate = current === -1 ? (delta > 0 ? 0 : visible.length - 1) : current + delta;\n const next = (candidate + visible.length) % visible.length;\n this.#setActive(visible[next] ?? null);\n }\n\n /** Selects/deselects `option`, honoring `max`, and syncs chip + live region. */\n #toggleSelection(option: HTMLElement): void {\n const selected = option.getAttribute(\"aria-selected\") === \"true\";\n if (!selected && this.maxValue > 0 && this.#selectedOptions.length >= this.maxValue) {\n return;\n }\n option.setAttribute(\"aria-selected\", String(!selected));\n if (selected) {\n this.#removeTagFor(option);\n } else {\n this.#appendTag(option);\n }\n this.#announce(this.#optionLabel(option));\n this.#refreshRoving();\n this.#syncFields();\n this.dispatch(\"change\", { detail: { values: this.#values } });\n }\n\n /**\n * Re-reads each chip's label from its option, in place.\n *\n * The value order alone does not say the chips are still correct: a server can\n * re-render the same candidate with a new label (\"Apple\" → \"Green Apple\"), and\n * the chip text and its `Remove {label}` name are both derived from the option.\n * Updating them here keeps the rebuild — which would drop focus — for the case\n * that actually needs it, a changed selection.\n */\n #refreshTagLabels(selected: readonly HTMLElement[]): void {\n // Paired by value rather than by position: the chips are in selection order\n // and the options in DOM order, so the two lists hold the same values in\n // different places and an index-wise pairing would relabel the wrong chip.\n const options = new Map(selected.map((option) => [this.#optionValue(option), option]));\n for (const tag of this.tagTargets) {\n const option = options.get(tag.dataset.value ?? \"\");\n if (!option) continue;\n const text = this.#optionLabel(option);\n const label = tag.querySelector<HTMLElement>('[data-multi-select-slot=\"label\"]');\n if (label && label.textContent !== text) label.textContent = text;\n const button = tag.querySelector(\"button\");\n const name = `Remove ${text}`;\n if (button && button.getAttribute(\"aria-label\") !== name) {\n button.setAttribute(\"aria-label\", name);\n }\n }\n }\n\n /** Builds one chip from the template for `option`. */\n #appendTag(option: HTMLElement): void {\n if (!this.hasTagTemplateTarget || !this.hasTagsTarget) return;\n const fragment = this.tagTemplateTarget.content.cloneNode(true) as DocumentFragment;\n const tag = fragment.querySelector<HTMLElement>('[data-stimeo--multi-select-target=\"tag\"]');\n const label = fragment.querySelector<HTMLElement>('[data-multi-select-slot=\"label\"]');\n const button = fragment.querySelector<HTMLButtonElement>(\"button\");\n if (!tag || !button) return;\n const text = this.#optionLabel(option);\n tag.dataset.value = this.#optionValue(option);\n if (label) label.textContent = text;\n button.setAttribute(\"aria-label\", `Remove ${text}`);\n button.tabIndex = -1;\n this.tagsTarget.appendChild(fragment);\n }\n\n /** Removes the chip mirroring `option`, if present. */\n #removeTagFor(option: HTMLElement): void {\n const value = this.#optionValue(option);\n const tag = this.tagTargets.find((candidate) => candidate.dataset.value === value);\n tag?.remove();\n }\n\n /** Removes chip `index` and deselects its option, re-homing focus. */\n #removeTagAt(index: number): void {\n const tag = this.tagTargets[index];\n if (!tag) return;\n const value = tag.dataset.value ?? \"\";\n // Match by the option's stable value, which falls back to its label when no\n // data-value is present, so a label-keyed chip still finds its option.\n const option = this.optionTargets.find((candidate) => this.#optionValue(candidate) === value);\n if (option) option.setAttribute(\"aria-selected\", \"false\");\n tag.remove();\n // Prefer the option's display label for the announcement (e.g. \"Apple\"),\n // which can differ from its data-value (e.g. \"apple\").\n this.#announce(option ? this.#optionLabel(option) : value);\n this.#refreshRoving();\n this.#syncFields();\n this.dispatch(\"change\", { detail: { values: this.#values } });\n\n const remaining = this.#removeButtons;\n if (remaining.length === 0) {\n this.#focusAfterLastTag();\n } else {\n this.#roving.setActive(Math.min(index, remaining.length - 1), { focus: true });\n }\n }\n\n /** Arrow navigation and deletion within the chip list (delegated). */\n readonly #onTagKeydown = (event: KeyboardEvent): void => {\n // A descendant widget that already claimed the key must not ALSO move the\n // chip focus — composition depends on this yield. Chips render from the\n // consumer's template, so an arbitrary widget can live inside one.\n if (event.defaultPrevented) return;\n if (isReservedArrowChord(event)) return;\n const button = (event.target as HTMLElement).closest(\"button\");\n if (!button) return;\n const buttons = this.#removeButtons;\n const index = buttons.indexOf(button);\n if (index === -1) return;\n // Logical, not physical. The key is normalised rather than a new case added:\n // the two horizontal branches are not mirror images — only one guards its\n // edge, and the other hands focus back to the input — so swapping the key\n // keeps each branch, guards and all, with its own direction. Both handlers\n // read the same element on purpose; probing the focused child would let the\n // input and the chips disagree at the boundary between them.\n switch (logicalArrowKey(event.key, this.element)) {\n case \"ArrowLeft\":\n if (index > 0) {\n event.preventDefault();\n this.#roving.setActive(index - 1, { focus: true });\n }\n break;\n case \"ArrowRight\":\n event.preventDefault();\n if (index < buttons.length - 1) this.#roving.setActive(index + 1, { focus: true });\n else this.#focusInput();\n break;\n case \"Delete\":\n case \"Backspace\":\n event.preventDefault();\n this.#removeTagAt(index);\n break;\n default:\n break;\n }\n };\n\n /**\n * Marks `option` active via `data-active` and the input's\n * `aria-activedescendant` (the attribute is removed, not emptied, when null).\n *\n * The state half runs even with no input, so a `close()` that cannot touch ARIA\n * still clears it: {@link open} seeds an active option only when there is none,\n * so a stale one makes the next open skip the seeding and a replacement input\n * gets no `aria-activedescendant` at all.\n */\n #setActive(option: HTMLElement | null): void {\n const activeId = option ? ensureId(option, \"stimeo-ms-opt\") : null;\n this.#activeOptionId = activeId;\n for (const candidate of this.optionTargets) {\n candidate.toggleAttribute(\"data-active\", candidate === option);\n }\n // Virtual focus never triggers the browser's native focus-scrolling, so a\n // scrollable list must follow the active option itself (list-only scroll).\n if (option && this.hasListTarget) scrollOptionIntoView(this.listTarget, option);\n if (!this.hasInputTarget) return;\n if (activeId !== null) {\n this.inputTarget.setAttribute(\"aria-activedescendant\", activeId);\n } else {\n this.inputTarget.removeAttribute(\"aria-activedescendant\");\n }\n }\n\n /** Repairs only active identity before a key; the fallback waits for the target callback. */\n #reconcileActiveForInteraction(): void {\n const activeId = this.#activeOptionId;\n if (activeId === null) return;\n const resolved = this.#activeOption;\n const active = resolved && !resolved.hidden ? resolved : null;\n const marked = this.optionTargets.filter((candidate) => candidate.hasAttribute(\"data-active\"));\n const idref = this.hasInputTarget\n ? this.inputTarget.getAttribute(\"aria-activedescendant\")\n : null;\n if (!active || marked.length !== 1 || marked[0] !== active || idref !== activeId) {\n this.#setActive(active);\n }\n }\n\n /** Reflects whether the open list currently has no visible option targets. */\n #reflectEmpty(): void {\n this.element.toggleAttribute(\n \"data-stimeo--multi-select-empty\",\n !this.#isClosed && this.#visibleOptions.length === 0,\n );\n }\n\n /**\n * Mirrors the selected values into named hidden inputs under the `fields`\n * target so the selection submits with a normal form (parity with tags-input).\n * No-ops without a `fields` target. When the\n * `form` value is set, each input gets a matching `form` attribute so the picker\n * can submit with a `<form>` it lives outside of.\n */\n #syncFields(): void {\n if (!this.hasFieldsTarget) return;\n this.fieldsTarget.replaceChildren(\n ...this.#values.map((value) => {\n const input = document.createElement(\"input\");\n input.type = \"hidden\";\n input.name = this.nameValue;\n input.value = value;\n if (this.hasFormValue && this.formValue !== \"\") input.setAttribute(\"form\", this.formValue);\n return input;\n }),\n );\n }\n\n /** Keeps exactly one chip remove button tabbable after the set changes. */\n #refreshRoving(): void {\n if (this.#removeButtons.length > 0 && this.#roving.activeIndex === -1)\n this.#roving.setActive(0);\n }\n\n /** Mirrors the changed option label into the live region. */\n #announce(text: string): void {\n if (this.hasStatusTarget) this.statusTarget.textContent = text;\n }\n\n /** Closes the list on a click outside the controller element. */\n readonly #onOutsideClick = (event: MouseEvent): void => {\n if (!this.#isClosed && !this.element.contains(event.target as Node)) this.close();\n };\n\n /** Trimmed visible label of an option. */\n #optionLabel(option: HTMLElement): string {\n return (option.textContent ?? \"\").trim();\n }\n\n /** An option's stable value: its `data-value`, else its display label. */\n #optionValue(option: HTMLElement): string {\n return option.dataset.value ?? this.#optionLabel(option);\n }\n\n /** Options not hidden by the current filter. */\n get #visibleOptions(): HTMLElement[] {\n return this.optionTargets.filter((option) => !option.hidden);\n }\n\n /** Current active target resolved by stable id, never a detached node reference. */\n get #activeOption(): HTMLElement | null {\n const activeId = this.#activeOptionId;\n if (activeId === null) return null;\n return this.optionTargets.find((option) => option.id === activeId) ?? null;\n }\n\n /** Options currently selected. */\n get #selectedOptions(): HTMLElement[] {\n return this.optionTargets.filter((option) => option.getAttribute(\"aria-selected\") === \"true\");\n }\n\n /** Selected values in option order. */\n get #values(): string[] {\n return this.#selectedOptions.map((option) => this.#optionValue(option));\n }\n\n /** The chip remove buttons in order (the roving navigation set). */\n get #removeButtons(): HTMLButtonElement[] {\n return this.hasTagsTarget\n ? Array.from(this.tagsTarget.querySelectorAll<HTMLButtonElement>(\"button\"))\n : [];\n }\n\n /** Whether the list is currently hidden. */\n get #isClosed(): boolean {\n return !this.hasListTarget || this.listTarget.hidden !== false;\n }\n}\n"]}
|
|
@@ -28,26 +28,45 @@ import { Controller } from '@hotwired/stimulus';
|
|
|
28
28
|
* While a panel is open the nav is a layer on the shared {@link EscapeLayer}
|
|
29
29
|
* stack; it claims a press only while focus is inside the nav or fell to the
|
|
30
30
|
* body (a click on non-focusable panel content), so one keypress closes exactly
|
|
31
|
-
* one layer
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
31
|
+
* one layer. An outside click, or focus leaving the nav for a known external
|
|
32
|
+
* destination, closes it. `ArrowLeft`/`ArrowRight` move focus between the
|
|
33
|
+
* *navigable* triggers without rewriting `tabindex` (they keep their natural Tab
|
|
34
|
+
* order); a modified press (`Alt`/`Ctrl`/`Meta`/`Shift`, e.g. the browser's
|
|
35
|
+
* `Alt+←` history back) is left to the browser. Hover open/close is opt-in via
|
|
36
|
+
* `openOnHover`.
|
|
37
|
+
*
|
|
38
|
+
* Every `panel` must be a **descendant of the nav element**: Stimulus resolves
|
|
39
|
+
* targets within the controller's own scope, so a panel moved outside it (a
|
|
40
|
+
* portal) never becomes a `panel` target and its trigger then opens nothing.
|
|
35
41
|
*
|
|
36
42
|
* By default the hover region is each trigger and its panel. An optional
|
|
37
|
-
* `hoverArea` target widens it: mark a wrapper that contains
|
|
38
|
-
* `<li>` holding a top-level *link*, its disclosure button, and
|
|
39
|
-
* APG "Disclosure Navigation with Top-Level Links" arrangement)
|
|
40
|
-
* anywhere over that wrapper opens the contained trigger's panel.
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
43
|
+
* `hoverArea` target widens it: mark a wrapper that contains **exactly one**
|
|
44
|
+
* trigger (e.g. the `<li>` holding a top-level *link*, its disclosure button, and
|
|
45
|
+
* its panel — the APG "Disclosure Navigation with Top-Level Links" arrangement)
|
|
46
|
+
* and hovering anywhere over that wrapper opens the contained trigger's panel.
|
|
47
|
+
* Wrapping several triggers at once (marking the whole `<ul>`, say) is not
|
|
48
|
+
* supported: every hover over the area then resolves to the *first* contained
|
|
49
|
+
* trigger. A trigger or panel inside a `hoverArea` defers to the wrapper (its own
|
|
50
|
+
* edges stop scheduling open/close), so pointer movement within the area never
|
|
51
|
+
* flickers the panel. Keep a trigger's panel inside its `hoverArea`: a panel left
|
|
52
|
+
* outside simply falls back to the default two-region behavior — moving straight
|
|
53
|
+
* across a shared edge never schedules a close (the leave handler checks
|
|
46
54
|
* `relatedTarget`), and `hoverDelay` bridges the pointer's travel across an
|
|
47
|
-
* actual gap. Targets added or removed while connected (e.g. a Turbo Stream
|
|
48
|
-
* append) re-wire the hover listeners via Stimulus target callbacks
|
|
55
|
+
* actual gap. Targets added or removed **while connected** (e.g. a Turbo Stream
|
|
56
|
+
* append) re-wire the hover listeners via Stimulus target callbacks; the same
|
|
57
|
+
* callbacks also run during Stimulus teardown, so they no-op once disconnected.
|
|
58
|
+
* Flipping `openOnHover` at runtime wires or unwires the listeners in place.
|
|
49
59
|
* `hoverArea` has no effect unless `openOnHover` is enabled.
|
|
50
60
|
*
|
|
61
|
+
* Pointer-driven closing never hides focused content — hiding the panel would
|
|
62
|
+
* take the focused link with it and drop focus to the body. So a hover *leave*
|
|
63
|
+
* keeps the panel open while focus is inside it, and a hover *switch* to another
|
|
64
|
+
* trigger returns focus to the outgoing trigger before hiding its panel. For the
|
|
65
|
+
* same reason, while `openOnHover` is on, a click on a trigger whose hover region
|
|
66
|
+
* the pointer currently occupies is a no-op: closing it would leave it unopenable
|
|
67
|
+
* until the pointer left and re-entered (`mouseenter` does not re-fire in place).
|
|
68
|
+
* `Escape` and outside clicks dismiss regardless.
|
|
69
|
+
*
|
|
51
70
|
* @remarks
|
|
52
71
|
* Behavior only. Panel layout, mega-menu styling, and animation are the
|
|
53
72
|
* consumer's CSS. Static placement is CSS; viewport-edge collision avoidance is
|
|
@@ -76,8 +95,15 @@ declare class NavigationMenuController extends Controller<HTMLElement> {
|
|
|
76
95
|
hoverDelayValue: number;
|
|
77
96
|
/** Establishes the closed baseline and the dismissal listeners. */
|
|
78
97
|
connect(): void;
|
|
79
|
-
/** Removes every listener, pending hover timer, and stack membership
|
|
98
|
+
/** Removes every listener, pending hover timer, and stack membership taken while connected. */
|
|
80
99
|
disconnect(): void;
|
|
100
|
+
/**
|
|
101
|
+
* Follows a runtime flip of `openOnHover` (a morph or a scripted attribute
|
|
102
|
+
* change) by wiring or unwiring the hover listeners in place. Stimulus also
|
|
103
|
+
* fires this once before `connect()`, which the `#connected` guard skips —
|
|
104
|
+
* `connect()` owns the initial wiring.
|
|
105
|
+
*/
|
|
106
|
+
openOnHoverValueChanged(): void;
|
|
81
107
|
/** Re-wires hover listeners when a target is added after connect (Turbo Streams etc.). */
|
|
82
108
|
triggerTargetConnected(): void;
|
|
83
109
|
/** Re-wires hover listeners when a target is removed while connected. */
|
|
@@ -90,9 +116,30 @@ declare class NavigationMenuController extends Controller<HTMLElement> {
|
|
|
90
116
|
hoverAreaTargetConnected(): void;
|
|
91
117
|
/** See {@link NavigationMenuController.triggerTargetDisconnected}. */
|
|
92
118
|
hoverAreaTargetDisconnected(): void;
|
|
93
|
-
/**
|
|
119
|
+
/**
|
|
120
|
+
* Toggles a trigger's panel (single-open). Bound via `data-action` (click).
|
|
121
|
+
*
|
|
122
|
+
* An explicit activation always wins over a scheduled hover open/close, so any
|
|
123
|
+
* pending hover timer is dropped first — otherwise a close scheduled just
|
|
124
|
+
* before the click would fire into the freshly opened panel.
|
|
125
|
+
*
|
|
126
|
+
* Under `openOnHover`, closing a panel the pointer itself is holding open is
|
|
127
|
+
* refused: the pointer stays put, `mouseenter` does not re-fire, and the panel
|
|
128
|
+
* would be unopenable until the pointer left and came back. Keyboard
|
|
129
|
+
* activation (pointer elsewhere) still toggles it closed, and `Escape` and
|
|
130
|
+
* outside clicks dismiss either way.
|
|
131
|
+
*/
|
|
94
132
|
toggle(event: Event): void;
|
|
95
|
-
/**
|
|
133
|
+
/**
|
|
134
|
+
* `ArrowLeft`/`ArrowRight` move focus between triggers (keeping Tab order).
|
|
135
|
+
*
|
|
136
|
+
* Only navigable triggers are destinations, so a `hidden` or disabled item
|
|
137
|
+
* never swallows the press. A modified arrow belongs to the browser or OS
|
|
138
|
+
* (`Alt+←`/`Alt+→` is history back/forward), and with no destination at all
|
|
139
|
+
* (a single-trigger nav) the press is left to the page — `preventDefault()` is
|
|
140
|
+
* called only when focus actually moves, keeping the scroll-suppression
|
|
141
|
+
* contract honest.
|
|
142
|
+
*/
|
|
96
143
|
onTriggerKeydown(event: KeyboardEvent): void;
|
|
97
144
|
}
|
|
98
145
|
|