stimeo-ui 0.2.0 → 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 +163 -0
- package/dist/cable/index.d.ts +16 -16
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.d.ts +3 -2
- package/dist/controllers/accordion_controller.js +10 -0
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +64 -0
- package/dist/controllers/alert_dialog_controller.js +318 -0
- package/dist/controllers/alert_dialog_controller.js.map +1 -0
- 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 +115 -0
- package/dist/controllers/carousel_controller.js +313 -0
- package/dist/controllers/carousel_controller.js.map +1 -0
- package/dist/controllers/clipboard_controller.d.ts +72 -0
- package/dist/controllers/clipboard_controller.js +144 -0
- package/dist/controllers/clipboard_controller.js.map +1 -0
- package/dist/controllers/collapsible_controller.d.ts +70 -0
- package/dist/controllers/collapsible_controller.js +327 -0
- package/dist/controllers/collapsible_controller.js.map +1 -0
- package/dist/controllers/color_picker_controller.d.ts +87 -0
- package/dist/controllers/color_picker_controller.js +252 -0
- package/dist/controllers/color_picker_controller.js.map +1 -0
- 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 +8 -1
- package/dist/controllers/count_up_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +72 -0
- package/dist/controllers/currency_input_controller.js +147 -0
- package/dist/controllers/currency_input_controller.js.map +1 -0
- package/dist/controllers/data_grid_controller.d.ts +89 -0
- package/dist/controllers/data_grid_controller.js +246 -0
- package/dist/controllers/data_grid_controller.js.map +1 -0
- package/dist/controllers/date_range_picker_controller.d.ts +88 -0
- package/dist/controllers/date_range_picker_controller.js +441 -0
- package/dist/controllers/date_range_picker_controller.js.map +1 -0
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.d.ts +57 -0
- package/dist/controllers/dismissible_controller.js +117 -0
- package/dist/controllers/dismissible_controller.js.map +1 -0
- package/dist/controllers/drawer_controller.d.ts +96 -0
- package/dist/controllers/drawer_controller.js +630 -0
- package/dist/controllers/drawer_controller.js.map +1 -0
- package/dist/controllers/dropdown_controller.d.ts +1 -2
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.d.ts +73 -0
- package/dist/controllers/editable_controller.js +169 -0
- package/dist/controllers/editable_controller.js.map +1 -0
- package/dist/controllers/file_dropzone_controller.d.ts +92 -0
- package/dist/controllers/file_dropzone_controller.js +165 -0
- package/dist/controllers/file_dropzone_controller.js.map +1 -0
- package/dist/controllers/filter_controller.d.ts +60 -0
- package/dist/controllers/filter_controller.js +86 -0
- package/dist/controllers/filter_controller.js.map +1 -0
- package/dist/controllers/flash_controller.js +36 -5
- package/dist/controllers/flash_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 +6 -4
- 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 +67 -19
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.js +54 -11
- 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/masonry_controller.d.ts +54 -0
- package/dist/controllers/masonry_controller.js +142 -0
- package/dist/controllers/masonry_controller.js.map +1 -0
- 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 +142 -0
- package/dist/controllers/menubar_controller.js +785 -0
- package/dist/controllers/menubar_controller.js.map +1 -0
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +141 -0
- package/dist/controllers/multi_select_controller.js +755 -0
- package/dist/controllers/multi_select_controller.js.map +1 -0
- package/dist/controllers/navigation_menu_controller.d.ts +146 -0
- package/dist/controllers/navigation_menu_controller.js +511 -0
- package/dist/controllers/navigation_menu_controller.js.map +1 -0
- 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 +15 -6
- package/dist/controllers/overflow_indicator_controller.js +246 -27
- 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/password_reveal_controller.d.ts +49 -0
- package/dist/controllers/password_reveal_controller.js +117 -0
- package/dist/controllers/password_reveal_controller.js.map +1 -0
- 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 +97 -0
- package/dist/controllers/range_slider_controller.js +192 -0
- package/dist/controllers/range_slider_controller.js.map +1 -0
- package/dist/controllers/rating_controller.js +16 -2
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +52 -0
- package/dist/controllers/read_more_controller.js +238 -0
- package/dist/controllers/read_more_controller.js.map +1 -0
- 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 +101 -14
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.d.ts +54 -0
- package/dist/controllers/scroll_restore_controller.js +93 -0
- package/dist/controllers/scroll_restore_controller.js.map +1 -0
- package/dist/controllers/scroll_visibility_controller.d.ts +6 -3
- package/dist/controllers/scroll_visibility_controller.js +40 -6
- 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 +369 -74
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +69 -0
- package/dist/controllers/separator_controller.js +96 -0
- package/dist/controllers/separator_controller.js.map +1 -0
- package/dist/controllers/sidebar_controller.d.ts +107 -0
- package/dist/controllers/sidebar_controller.js +761 -0
- package/dist/controllers/sidebar_controller.js.map +1 -0
- 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/stepper_controller.d.ts +4 -0
- package/dist/controllers/stepper_controller.js +28 -12
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +5 -6
- package/dist/controllers/stick_to_bottom_controller.js +9 -4
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/sticky_observer_controller.d.ts +17 -4
- package/dist/controllers/sticky_observer_controller.js +109 -20
- 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 +13 -4
- package/dist/controllers/tabs_controller.js +26 -3
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +91 -0
- package/dist/controllers/tags_input_controller.js +295 -0
- package/dist/controllers/tags_input_controller.js.map +1 -0
- package/dist/controllers/theme_controller.d.ts +1 -1
- package/dist/controllers/theme_controller.js +42 -13
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +67 -0
- package/dist/controllers/time_picker_controller.js +231 -0
- package/dist/controllers/time_picker_controller.js.map +1 -0
- package/dist/controllers/toast_controller.d.ts +5 -14
- package/dist/controllers/toast_controller.js +40 -14
- 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.d.ts +14 -8
- package/dist/controllers/transition_controller.js +153 -38
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +135 -0
- package/dist/controllers/tree_view_controller.js +691 -0
- package/dist/controllers/tree_view_controller.js.map +1 -0
- package/dist/index.d.ts +27 -1818
- package/dist/index.js +4256 -915
- 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 +26 -26
- package/dist/inspector/manifest.json +1054 -127
- package/package.json +5 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/logical_scroll.ts","../../src/utils/arrow_step.ts","../../src/utils/focus_candidate.ts","../../src/utils/roving_tabindex.ts","../../src/utils/safe_timeout.ts","../../src/utils/typeahead.ts","../../src/controllers/tree_view_controller.ts"],"names":[],"mappings":"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","file":"tree_view_controller.js","sourcesContent":["/** 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","/**\n * Whether a disabled `<fieldset>` ancestor actually reaches `control`.\n *\n * HTML exempts the contents of a fieldset's **first direct-child `<legend>`**, and\n * the exemption is per fieldset — a control legal in one legend can still be\n * disabled by a second, outer fieldset, so the walk continues upward.\n *\n * Exported for the consumer that needs this rule *without* the rest of\n * {@link canTakeFocus}: `toolbar` bounds its own `hidden` walk at the toolbar\n * root, which is a different rule, so it composes the two itself.\n */\nexport function inheritsFieldsetDisabled(control: HTMLElement): boolean {\n let fieldset: Element | null = control.closest(\"fieldset[disabled]\");\n while (fieldset) {\n const legend = Array.from(fieldset.children).find((child) => child.tagName === \"LEGEND\");\n if (!legend?.contains(control)) return true;\n fieldset = fieldset.parentElement?.closest(\"fieldset[disabled]\") ?? null;\n }\n return false;\n}\n\n/**\n * Whether an element can actually take focus, checked **before** `focus()` runs.\n *\n * A controller that must move focus off a control it is about to disable or hide\n * picks a destination and calls `focus()` on it. If that destination cannot take\n * focus, the call **fails silently**: `hidden` and natively `disabled` elements\n * swallow it, so the caret stays in the subtree that is disappearing and lands on\n * `<body>` a frame later — the exact outcome the rescue exists to prevent, minus\n * any signal that it happened.\n *\n * Testing after the fact is the obvious alternative and is deliberately not the\n * rule. Reading `document.activeElement` back only works in a real browser, and\n * looping over candidates that way performs a real focus move per failure —\n * observable to assistive technology. Checking first costs nothing and catches\n * the cases that actually occur.\n *\n * **`aria-disabled` is not disqualifying.** It is the attribute an author uses\n * for a control that must stay *discoverable*, and the roving contract keeps\n * such items reachable. Only the three conditions that make the platform refuse\n * focus are checked: `hidden` (an ancestor's counts), the native `disabled`\n * property, and `disabled` inherited from an ancestor `fieldset`. CSS-only\n * invisibility is not detectable here and stays the consumer's problem.\n *\n * Reading `:disabled` instead of walking the fieldset chain would be shorter, but\n * that pseudo-class is not evaluated consistently outside real browsers and this\n * has to be right headlessly too. happy-dom in particular focuses a `<button>`\n * inside a disabled fieldset where a real engine refuses, so the inheritance is\n * spelled out rather than delegated.\n *\n * What a consumer does when nothing survives is its own call: some fall back to\n * their landmark, while a widget whose caret already sits somewhere legitimate\n * refuses the move outright rather than relocating it.\n *\n * @example\n * ```ts\n * const target = candidates.find(canTakeFocus);\n * if (target) target.focus();\n * else {\n * this.#tabindex.lend(this.element); // nothing left: fall back to the landmark\n * this.element.focus();\n * }\n * ```\n *\n * @param element - the candidate destination\n */\nexport function canTakeFocus(element: HTMLElement): boolean {\n if (element.closest(\"[hidden]\")) return false;\n if (!(\"disabled\" in element)) return true;\n if ((element as HTMLElement & { disabled: boolean }).disabled) return false;\n return !inheritsFieldsetDisabled(element);\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 * Self-cleaning timer registries shared by Stimeo controllers.\n *\n * Stimulus controllers frequently schedule `setTimeout` / `setInterval` work\n * (auto-dismiss, debouncing, polling). When the element leaves the DOM — a\n * Turbo Drive navigation, a Turbo Stream replacement, or any `disconnect()` —\n * orphaned timers keep firing against a detached controller, leaking memory and\n * mutating stale state. {@link SafeTimeout} and {@link SafeInterval} track every\n * timer they create so a single {@link TimerRegistry.clearAll | clearAll()} call\n * in `disconnect()` tears them all down.\n *\n * These are intentionally low-level primitives: they own *registration and\n * cleanup only*. Higher-level policy (pause/resume, remaining-time accounting)\n * stays in the individual controllers so per-widget semantics are not flattened\n * into a lowest-common-denominator helper.\n */\n\n/**\n * Shared registry bookkeeping for the timeout/interval variants.\n *\n * Subclasses provide the scheduling primitive ({@link schedule}) and its matching\n * canceller ({@link cancel}); this base owns the set of live ids plus the\n * per-id and bulk teardown shared by both.\n */\nabstract class TimerRegistry {\n /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */\n protected readonly ids = new Set<number>();\n\n /** Schedules the underlying platform timer and returns its id. */\n protected abstract schedule(callback: () => void, delay: number): number;\n\n /** Cancels the underlying platform timer for `id`. */\n protected abstract cancel(id: number): void;\n\n /**\n * Cancels a single tracked timer.\n *\n * No-ops if the id is unknown (already cleared, fired, or never owned by this\n * registry), so callers can clear defensively without guarding.\n */\n clear(id: number): void {\n if (this.ids.delete(id)) {\n this.cancel(id);\n }\n }\n\n /**\n * Cancels every tracked timer. Call this from a controller's `disconnect()`\n * to guarantee no timer outlives the element.\n */\n clearAll(): void {\n for (const id of this.ids) {\n this.cancel(id);\n }\n this.ids.clear();\n }\n\n /** Number of timers currently tracked (pending). */\n get size(): number {\n return this.ids.size;\n }\n}\n\n/**\n * `setTimeout` wrapper that auto-forgets each timer once it fires and supports\n * bulk teardown on disconnect.\n *\n * @example\n * ```ts\n * #timers = new SafeTimeout();\n *\n * connect() {\n * this.#timers.set(() => this.dismiss(), 5000);\n * }\n *\n * disconnect() {\n * this.#timers.clearAll();\n * }\n * ```\n */\nexport class SafeTimeout extends TimerRegistry {\n /**\n * Schedules `callback` after `delay` ms and returns the timer id.\n *\n * The id is removed from the registry automatically when the timeout fires,\n * so {@link TimerRegistry.size | size} reflects only still-pending timers.\n */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(() => {\n this.ids.delete(id);\n callback();\n }, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setTimeout(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearTimeout(id);\n }\n}\n\n/**\n * `setInterval` wrapper that tracks every interval for bulk teardown on\n * disconnect. Unlike {@link SafeTimeout}, intervals are retained until they are\n * explicitly cleared because they fire repeatedly.\n *\n * @example\n * ```ts\n * #intervals = new SafeInterval();\n *\n * connect() {\n * this.#intervals.set(() => this.tick(), 1000);\n * }\n *\n * disconnect() {\n * this.#intervals.clearAll();\n * }\n * ```\n */\nexport class SafeInterval extends TimerRegistry {\n /** Schedules a repeating `callback` every `delay` ms and returns the timer id. */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(callback, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setInterval(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearInterval(id);\n }\n}\n","/**\n * Shared type-ahead (first-letter navigation) primitive for composite widgets.\n *\n * The APG asks list-like widgets to move focus by typed characters: one\n * character jumps to the next item starting with it, and several characters\n * typed in quick succession narrow the match to that prefix. Getting that right\n * needs three pieces of bookkeeping — accumulating the query, dropping it after\n * an idle window, and resolving the label each candidate is matched against —\n * and single-sourcing them here keeps every widget on the same rules instead of\n * letting them drift apart.\n *\n * The helpers here own *only* that mechanical part. They are deliberately\n * **policy-free** in the same sense as `RovingTabindex`: which elements are\n * candidates, where the search starts, and what happens on a match (real DOM\n * focus, a roving tab stop, or a virtual `aria-activedescendant`) differ per APG\n * pattern, so each controller keeps those decisions and calls\n * {@link findTypeaheadMatch} with a candidate list it assembled itself.\n *\n * Three decisions are worth stating up front:\n *\n * - **A repeated character collapses the stored query**, rather than deriving a\n * shorter one at search time. That is what lets a follow-up character resume\n * narrowing: `s`, `s`, `e` searches `\"se\"`, not the dead `\"sse\"` that no label\n * can match. Growing the query instead leaves the widget frozen on exactly\n * that sequence, which is why the choice is made here and not per widget.\n * - **`aria-label` wins over text**, so type-ahead matches what a screen reader\n * announces — but only when it contributes a name. A blank (empty or\n * whitespace-only) one is skipped, because accname skips it too: honoring it\n * would leave an element that AT announces by its text unreachable by that\n * text. This mirrors the accessible-name computation rather than the raw text.\n * - **`Space` and composition input are never type-ahead.** `Space` natively\n * activates a `<button>`-based item, and characters still being composed are\n * not a committed query.\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 contracts belong on the members.\n */\n\nimport { SafeTimeout } from \"./safe_timeout\";\n\n/** Idle window after which an accumulated query is dropped, in milliseconds. */\nexport const TYPEAHEAD_RESET_MS = 500;\n\n/** Construction options for {@link Typeahead}. */\nexport interface TypeaheadOptions {\n /** Idle window before the query resets. Defaults to {@link TYPEAHEAD_RESET_MS}. */\n resetMs?: number;\n}\n\n/**\n * Accumulates typed characters into a search query and drops it when idle.\n *\n * @remarks\n * Owns a {@link SafeTimeout} of its own, so a consumer's `#timers.clearAll()` does\n * **not** reach the pending reset — call {@link Typeahead.reset} from `disconnect()`.\n */\nexport class Typeahead {\n /** Timer registry for the pending idle reset; private so `reset()` is the only exit. */\n readonly #timers = new SafeTimeout();\n /** Idle window before the query resets, in milliseconds. */\n readonly #resetMs: number;\n /** The accumulated lowercase query, empty when idle. */\n #query = \"\";\n /** Id of the pending reset timer, `0` when none is scheduled. */\n #timerId = 0;\n\n /** @param options - Overrides for the idle window. */\n constructor({ resetMs = TYPEAHEAD_RESET_MS }: TypeaheadOptions = {}) {\n this.#resetMs = resetMs;\n }\n\n /** The query a search would currently run with; empty while idle. */\n get query(): string {\n return this.#query;\n }\n\n /**\n * Folds `key` into the query, restarts the idle window, and returns the query to\n * search with. A repeated character collapses the query to that one character.\n */\n push(key: string): string {\n const char = key.toLowerCase();\n const repeated = this.#query.length > 0 && [...this.#query].every((c) => c === char);\n this.#query = repeated ? char : this.#query + char;\n this.#timers.clear(this.#timerId);\n this.#timerId = this.#timers.set(() => this.reset(), this.#resetMs);\n return this.#query;\n }\n\n /** Clears the query and cancels the pending idle reset. */\n reset(): void {\n this.#query = \"\";\n this.#timers.clear(this.#timerId);\n this.#timerId = 0;\n }\n}\n\n/** Whether `event` is a bare printable character usable for type-ahead. */\nexport function isTypeaheadKey(event: KeyboardEvent): boolean {\n return (\n event.key.length === 1 &&\n event.key !== \" \" &&\n !event.ctrlKey &&\n !event.metaKey &&\n !event.altKey &&\n !event.isComposing\n );\n}\n\n/**\n * The name `element` is matched under, normalized for comparison.\n *\n * @param fallbackText - Produces the name when `aria-label` contributes none.\n * Defaults to the element's `textContent`; pass a narrower source when part of\n * the subtree is not part of the name (a tree item's nested child group, say).\n * Called lazily, so an `aria-label` costs no subtree walk.\n */\nexport function typeaheadLabel(element: HTMLElement, fallbackText?: () => string): string {\n // Blank means absent, matching accname: a whitespace-only `aria-label` is\n // skipped there too, so honoring it here would make an element that a screen\n // reader announces by its text unreachable by that text.\n const label = element.getAttribute(\"aria-label\")?.trim();\n if (label) return label.toLowerCase();\n const text = fallbackText ? fallbackText() : (element.textContent ?? \"\");\n return text.trim().toLowerCase();\n}\n\n/**\n * Index of the first item after `from` whose label starts with `query`, wrapping\n * and evaluating `from` itself last, or `-1` when nothing matches.\n *\n * @param items - Candidates in navigation order, already filtered by the caller.\n * @param from - Index the search moves on from; `-1` starts at the first item. Any\n * out-of-range value is folded back in, so `indexOf` results pass straight through.\n * @param query - Lowercase query, normally the return of {@link Typeahead.push}.\n * @param label - Resolves a candidate's name. Defaults to {@link typeaheadLabel}.\n */\nexport function findTypeaheadMatch(\n items: readonly HTMLElement[],\n from: number,\n query: string,\n label: (item: HTMLElement) => string = (item) => typeaheadLabel(item),\n): number {\n // An empty query would prefix-match everything; an empty list never enters the\n // loop, so it needs no guard of its own.\n if (query === \"\") return -1;\n const count = items.length;\n for (let step = 1; step <= count; step += 1) {\n // Folded twice so a negative `from` still lands on a real slot: `%` keeps the\n // sign of its left operand, and a negative index would read `undefined`.\n const index = (((from + step) % count) + count) % count;\n const candidate = items[index];\n if (candidate && label(candidate).startsWith(query)) return index;\n }\n return -1;\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { isReservedArrowChord } from \"../utils/arrow_step\";\nimport { canTakeFocus } from \"../utils/focus_candidate\";\nimport { isRtl } from \"../utils/logical_scroll\";\nimport { RovingTabindex } from \"../utils/roving_tabindex\";\nimport { SafeTimeout } from \"../utils/safe_timeout\";\nimport { findTypeaheadMatch, isTypeaheadKey, Typeahead, typeaheadLabel } from \"../utils/typeahead\";\n\n/**\n * Interactive descendants a `treeitem` may legitimately contain (inline rename\n * field, link, chevron). Keys and clicks raised inside one belong to it, not to\n * the tree, so the tree handlers step aside.\n *\n * The selector is matched with `closest()`, which also matches the item host\n * itself — deliberately. An item host that *is* one of these elements is out of\n * contract (see the class TSDoc), and the tree then stands down completely\n * rather than responding to some keys and not others.\n */\nconst NESTED_INTERACTIVE =\n 'input, textarea, select, button, a[href], [contenteditable]:not([contenteditable=\"false\"])';\n\n/**\n * Headless, accessible single-select tree view.\n *\n * Markup contract (identifier: `stimeo--tree-view`):\n * <ul data-controller=\"stimeo--tree-view\" role=\"tree\" aria-label=\"Files\">\n * <li role=\"treeitem\" aria-expanded=\"false\" aria-selected=\"false\" tabindex=\"0\"\n * data-stimeo--tree-view-target=\"item\"\n * data-action=\"keydown->stimeo--tree-view#onKeydown\n * click->stimeo--tree-view#onClick\">\n * <span>src</span>\n * <button type=\"button\" tabindex=\"-1\" aria-hidden=\"true\"\n * data-action=\"click->stimeo--tree-view#toggle\"></button>\n * <ul role=\"group\" data-stimeo--tree-view-target=\"group\" hidden>\n * <li role=\"treeitem\" aria-selected=\"false\" tabindex=\"-1\"\n * data-stimeo--tree-view-target=\"item\" data-action=\"…\">…</li>\n * </ul>\n * </li>\n * </ul>\n *\n * Implements the WAI-ARIA APG **Tree View** (single-select) pattern. Parent/child\n * structure is read from the DOM nesting (`treeitem` → child `group`). The whole\n * tree is one Tab stop (roving tabindex); arrows navigate visible items, expand /\n * collapse, and move between parent and child.\n *\n * Behavior provided:\n * - `ArrowDown`/`ArrowUp` move between visible items; `Home`/`End` jump to the\n * first / last visible item; printable characters typeahead by label prefix\n * (`aria-label` first, else the item's own text), with a repeated character\n * cycling through the items that start with it.\n * - `ArrowRight` expands a collapsed parent or steps into its first child;\n * `ArrowLeft` collapses an expanded parent or steps to the parent item.\n * - `Enter`/`Space`/click select the item (single selection via `aria-selected`);\n * an `aria-disabled=\"true\"` item stays focusable but cannot be selected.\n * - `aria-expanded` and each child `group`'s `hidden` stay in sync, dispatching\n * `stimeo--tree-view:toggle`; selection dispatches `stimeo--tree-view:select`.\n * - The optional `toggle` action expands / collapses the nearest item, so a\n * chevron gives pointer users the reach that `ArrowRight` gives the keyboard.\n *\n * Contract notes:\n * - A child container is resolved by `role=\"group\"`, not by the target: the\n * `group` target is what `stimeo check`'s accessibility rule anchors on, which\n * is why the contract still declares it.\n * - Nested interactive controls ({@link NESTED_INTERACTIVE}) and IME composition\n * keys are left alone; a click on a child `group`'s own box (its indentation\n * band) selects nothing rather than the parent row.\n * - A key a descendant already consumed (`event.defaultPrevented`) is yielded,\n * so a composed widget inside a row never causes a double move.\n *\n * Consumer contract — the item host:\n * - **Host `role=\"treeitem\"` on a non-interactive element** (`li`, `div`), never\n * on {@link NESTED_INTERACTIVE}. APG's *Navigation Treeview* does host it on a\n * link, but that variant activates on `Enter`, which this pattern has already\n * spent on selection; a link host is therefore out of contract and would need\n * `Enter` / click / `select` redefined first.\n * - An out-of-contract host makes `onKeydown` and `onClick` stand down\n * **entirely** — no movement, no selection, no `select` — rather than respond\n * partially. `stimeo check` cannot flag it: the rule schema states which\n * attributes are required and cannot forbid an element shape.\n *\n * Consumer contract — controls nested inside a row:\n * - Keep them out of the Tab sequence (`tabindex=\"-1\"`); the tree is one Tab\n * stop and writes `tabindex` on nothing but its own item targets.\n * - Moving focus *into* a control (to start an inline rename) is the consumer's\n * call — the tree never focuses one.\n * - When the edit or action ends, focus the `treeitem` itself. The roving\n * position never moved, so that alone restores the keyboard path.\n * - The tree consumes none of the control's events, by two mechanisms:\n * {@link NESTED_INTERACTIVE} names controls by *element shape*, the\n * `defaultPrevented` yield covers widgets no selector can name.\n */\nexport class TreeViewController extends Controller<HTMLElement> {\n static override targets = [\"item\", \"group\"];\n static actions = [\"onClick\", \"onKeydown\", \"toggle\"] as const;\n static events = [\"select\", \"toggle\"] as const;\n\n declare readonly itemTargets: HTMLElement[];\n\n readonly #roving = new RovingTabindex(() => this.itemTargets);\n readonly #typeahead = new Typeahead();\n readonly #timers = new SafeTimeout();\n #connected = false;\n /**\n * Item targets in DOM order as of the last connect / target change. A removed\n * item is gone from `itemTargets` by the time Stimulus reports it, so this is\n * the only record of where in the order the gap opened.\n */\n #order: HTMLElement[] = [];\n /** The `treeitem` that last took DOM focus inside this tree, if any. */\n #focused: HTMLElement | null = null;\n\n /**\n * Records which row owns DOM focus. `focusin` bubbles, so one listener covers\n * rows and anything nested in them; the tree needs this because a removal is\n * only reported *after* the fact, when the browser has already reset\n * `document.activeElement`.\n */\n readonly #onFocusIn = (event: FocusEvent): void => {\n const target = event.target as HTMLElement | null;\n this.#focused = target?.closest<HTMLElement>('[role=\"treeitem\"]') ?? null;\n };\n\n /**\n * Forgets the tracked row when focus genuinely leaves the tree, so a later\n * removal of that row cannot be mistaken for \"focus died with it\" and pull\n * focus back in.\n *\n * A *removed* focused row must not clear the record — that is exactly the case\n * {@link TreeViewController.itemTargetDisconnected} needs it for. The two are\n * indistinguishable while the event is dispatching: Chromium fires `focusout`\n * for a removed row *before* the detach lands — `isConnected` still reads\n * `true` — with `document.activeElement` already on `<body>`, exactly like a\n * plain `blur()`. Deciding on a microtask separates them — a row that is gone\n * by then was removed. Focus moving between two rows needs no handling:\n * `focusin` overwrites the record right after.\n */\n readonly #onFocusOut = (event: FocusEvent): void => {\n const target = event.target as HTMLElement | null;\n const row = target?.closest<HTMLElement>('[role=\"treeitem\"]') ?? null;\n if (!row || row !== this.#focused) return;\n const next = event.relatedTarget;\n if (next instanceof Node && this.element.contains(next)) return;\n queueMicrotask(() => {\n if (this.#focused === row && row.isConnected) this.#focused = null;\n });\n };\n\n /**\n * Reconciles authored expansion state and establishes the single tab stop.\n * Idempotent, so a Turbo cache restore / morph re-runs it safely (re-adding\n * the same listener reference is a no-op per the DOM spec).\n */\n override connect(): void {\n this.element.addEventListener(\"focusin\", this.#onFocusIn);\n this.element.addEventListener(\"focusout\", this.#onFocusOut);\n this.#seedFocused();\n this.#reconcileExpansion();\n this.#normalizeSelection();\n this.#normalizeTabStop();\n this.#order = [...this.itemTargets];\n this.#connected = true;\n }\n\n /** Detaches the focus trackers, drops the typeahead buffer and its timer. */\n override disconnect(): void {\n this.element.removeEventListener(\"focusin\", this.#onFocusIn);\n this.element.removeEventListener(\"focusout\", this.#onFocusOut);\n this.#connected = false;\n this.#typeahead.reset();\n this.#timers.clearAll();\n this.#order = [];\n this.#focused = null;\n }\n\n /**\n * Seeds the focus record from the live document. A row can already hold focus\n * when the controller connects (a Turbo restore, or `data-controller` added to\n * a tree the user was already in), and the `focusin` listener alone would\n * never learn about it.\n */\n #seedFocused(): void {\n const active = this.element.ownerDocument.activeElement;\n this.#focused =\n active instanceof HTMLElement && this.element.contains(active)\n ? active.closest<HTMLElement>('[role=\"treeitem\"]')\n : null;\n }\n\n /**\n * Re-normalizes the tab stop when a `treeitem` is added at runtime, so an\n * appended item carrying `tabindex=\"0\"` cannot turn the tree into two Tab\n * stops. Skipped before `connect()`: Stimulus registers the initial targets\n * first, and `connect()` owns the initial tab-stop policy.\n */\n itemTargetConnected(): void {\n if (!this.#connected) return;\n this.#normalizeSelection();\n this.#normalizeTabStop();\n this.#trackOrder();\n }\n\n /**\n * Brings authored `aria-selected` to the shape the APG requires, without\n * changing which item the author chose.\n *\n * Every item gets an explicit value — an absent `aria-selected` means \"not\n * selectable\" in ARIA, so a forgotten attribute hides a selectable row — and a\n * single-select tree keeps at most one `true`, first in DOM order. `connect()`\n * reconciles authored expansion the same way. The scan is the `item` target\n * set: a `role=\"treeitem\"` without the target is outside the contract and is\n * neither counted nor written.\n */\n #normalizeSelection(): void {\n const items = this.itemTargets;\n const selected = items.find((item) => item.getAttribute(\"aria-selected\") === \"true\");\n for (const item of items) {\n item.setAttribute(\"aria-selected\", item === selected ? \"true\" : \"false\");\n }\n }\n\n /**\n * Restores the roving invariants when a `treeitem` leaves at runtime — a Turbo\n * Stream `remove`, a morph, or plain DOM surgery. The mirror of\n * {@link TreeViewController.itemTargetConnected}: without it, removing the row\n * that held `tabindex=\"0\"` leaves every survivor at `-1`, i.e. a tree with\n * **no** Tab stop, and drops DOM focus to `<body>` when the removed subtree\n * held it.\n *\n * The replacement is the nearest surviving *visible* item in the pre-removal\n * order — the one after the gap, else the one before it — which is the row\n * `ArrowDown` would have reached. Removing a non-active item changes nothing.\n * When no visible item survives, a tree with zero Tab stops is the correct end\n * state.\n *\n * Focus is only *restored*, never *stolen*: DOM focus moves solely when it was\n * inside the removed subtree and the document has nowhere left to put it.\n */\n itemTargetDisconnected(item: HTMLElement): void {\n // Stimulus drains the target callbacks for a removed *tree* before calling\n // `disconnect()`; recovering a roving position for a widget that is already\n // gone would move focus for nothing.\n if (!this.#connected || !this.element.isConnected) return;\n const stranded = this.#focusDiedWith(item);\n if (stranded) this.#focused = null;\n if (stranded || this.#roving.activeIndex === -1) {\n const next = this.#neighborOf(item);\n if (next) this.#roving.setActive(this.itemTargets.indexOf(next), { focus: stranded });\n }\n // Drop only the item just reported. One DOM mutation batch arrives as\n // several callbacks, so rebuilding from `itemTargets` here would also erase\n // the recorded position of every sibling whose callback has yet to run.\n this.#order = this.#order.filter((tracked) => tracked !== item);\n }\n\n /**\n * Folds newly connected targets into the DOM-order snapshot, keeping the slot\n * of any item whose disconnect callback has not arrived yet — a morph that\n * both adds and removes rows reports one batch as several callbacks, and\n * {@link TreeViewController.#neighborOf} needs those slots.\n */\n #trackOrder(): void {\n const live = this.itemTargets;\n const pending = this.#order.filter((tracked) => !live.includes(tracked));\n if (pending.length === 0) {\n this.#order = live;\n return;\n }\n // `pending` is in ascending recorded order, so re-inserting each at the\n // index it held rebuilds the pre-mutation sequence.\n const merged = [...live];\n for (const tracked of pending) {\n merged.splice(Math.min(this.#order.indexOf(tracked), merged.length), 0, tracked);\n }\n this.#order = merged;\n }\n\n /**\n * Routes tree keyboard interaction. Because `treeitem`s nest, only the handler\n * on the nearest item to the event target acts; the same keydown bubbling to an\n * ancestor item's handler is ignored to avoid double moves / selections. Keys\n * from a nested interactive control (or an IME composition) are left untouched.\n */\n onKeydown(event: KeyboardEvent): void {\n // A descendant widget that already claimed the key (a grabbed drag handle, a\n // nested custom control) must not ALSO move the roving focus — composition\n // depends on this yield. `#ownerItem` covers the controls a selector can\n // name; this covers the ones it cannot.\n if (event.defaultPrevented) return;\n if (isReservedArrowChord(event)) return;\n if (event.isComposing) return;\n const item = this.#ownerItem(event);\n if (!item) return;\n\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n this.#moveBy(item, 1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n this.#moveBy(item, -1);\n break;\n // Logical, not physical: APG describes these as \"to the child level\" and\n // \"to the parent level\" — a spatial move — so they follow the writing\n // direction, exactly as horizontal roving does. `isRtl()` is the shared\n // detector; never introduce a second one.\n //\n // Direction comes from the tree, not from `item`: the tree is what lays\n // the rows out, and a row may carry its own `dir` (an LTR path inside an\n // RTL browser is ordinary bidi authoring). Probing the focused row instead\n // would let one row's arrows mean the opposite of its sibling's.\n case \"ArrowRight\":\n event.preventDefault();\n if (isRtl(this.element)) this.#collapseOrLeave(item);\n else this.#expandOrEnter(item);\n break;\n case \"ArrowLeft\":\n event.preventDefault();\n if (isRtl(this.element)) this.#expandOrEnter(item);\n else this.#collapseOrLeave(item);\n break;\n case \"Home\":\n event.preventDefault();\n this.#focusEdge(0);\n break;\n case \"End\":\n event.preventDefault();\n this.#focusEdge(-1);\n break;\n case \"Enter\":\n case \" \":\n event.preventDefault();\n this.#select(item);\n break;\n default:\n if (isTypeaheadKey(event)) {\n event.preventDefault();\n this.#typeaheadTo(item, event.key);\n }\n break;\n }\n }\n\n /** Selects the clicked item (nearest to the target only). */\n onClick(event: Event): void {\n const item = this.#ownerItem(event);\n if (!item) return;\n // The indentation band of a child group is the group's own box, not the\n // parent row: a click that lands there must select nothing.\n const group = this.#childGroup(item);\n if (group?.contains(event.target as Node)) return;\n this.#focusItem(item);\n this.#select(item);\n }\n\n /**\n * Expands or collapses the `treeitem` nearest to the event target. Optional:\n * wire it on a chevron control so pointer users can reach child nodes, which\n * otherwise only `ArrowRight` / `ArrowLeft` can do. A leaf item is a no-op.\n *\n * The toggled row then takes focus and the tab stop, matching what the keyboard\n * path guarantees. Without it, a browser that focuses buttons on mousedown leaves\n * focus on the clicked chevron: arrows would stop working until focus returns to a\n * row, and a decorative `aria-hidden` chevron would hold focus. The synchronous\n * `stimeo--tree-view:toggle` event is observed before this final focus hand-off.\n */\n toggle(event: Event): void {\n const item = (event.target as HTMLElement).closest<HTMLElement>('[role=\"treeitem\"]');\n if (!item || !this.element.contains(item)) return;\n if (!this.#childGroup(item)) return;\n this.#setExpanded(item, !this.#isExpanded(item));\n this.#focusItem(item);\n }\n\n /**\n * The item a raw event belongs to, or `null` when the tree must not act: the\n * event came from a nested `treeitem` (it bubbled to an ancestor's handler) or\n * from an interactive control inside the item.\n */\n #ownerItem(event: Event): HTMLElement | null {\n const item = event.currentTarget as HTMLElement;\n const target = event.target as HTMLElement;\n if (target.closest('[role=\"treeitem\"]') !== item) return null;\n const control = target.closest<HTMLElement>(NESTED_INTERACTIVE);\n if (control && item.contains(control)) return null;\n return item;\n }\n\n /**\n * The surviving visible item that inherits `removed`'s position: the next one\n * in the pre-removal order, else the previous one. Falls back to the first\n * visible item when `removed` is not in that order — a target that arrived and\n * left between two callbacks — and to `undefined` when nothing visible is left.\n */\n #neighborOf(removed: HTMLElement): HTMLElement | undefined {\n const visible = this.#visibleItems;\n const index = this.#order.indexOf(removed);\n for (let at = index + 1; at < this.#order.length; at += 1) {\n const candidate = this.#order[at];\n if (candidate && visible.includes(candidate)) return candidate;\n }\n for (let at = index - 1; at >= 0; at -= 1) {\n const candidate = this.#order[at];\n if (candidate && visible.includes(candidate)) return candidate;\n }\n return visible[0];\n }\n\n /**\n * Whether DOM focus went down with `removed`: it sat on that row (or inside\n * it) and the document now has nowhere to put focus. A browser falls back to\n * `<body>`; a still-referenced but detached `activeElement` is the same\n * condition seen from the other side. Focus that landed on a real element —\n * inside this tree or anywhere else on the page — is left alone, so a removal\n * can never steal it.\n */\n #focusDiedWith(removed: HTMLElement): boolean {\n const focused = this.#focused;\n if (!focused || (focused !== removed && !removed.contains(focused))) return false;\n const doc = this.element.ownerDocument;\n const active = doc.activeElement;\n if (active === null || active === doc.body || active === doc.documentElement) return true;\n return !active.isConnected;\n }\n\n /** Moves focus to the next (`delta=1`) or previous visible item, if any. */\n #moveBy(item: HTMLElement, delta: number): void {\n const visible = this.#visibleItems;\n const current = visible.indexOf(item);\n // An item outside the visible set has no neighbors; without this guard a\n // `-1 + 1` index would warp focus to the top of the tree.\n if (current === -1) return;\n const next = visible[current + delta];\n if (next) this.#focusItem(next);\n }\n\n /**\n * Expands a collapsed parent, else steps into the first child.\n *\n * Bound to `ArrowRight` under LTR and `ArrowLeft` under RTL — the caller picks;\n * this method only knows \"toward the child level\".\n */\n #expandOrEnter(item: HTMLElement): void {\n const group = this.#childGroup(item);\n if (!group) return;\n if (!this.#isExpanded(item)) {\n this.#setExpanded(item, true);\n return;\n }\n // Never move the only tab stop into a subtree a consumer hid directly.\n if (group.hidden) return;\n // The first child that is not itself hidden. Matched on the element rather\n // than through `#visibleItems`, so a `role=\"treeitem\"` without the item\n // target still receives focus (the fallback in `#focusItem`).\n const firstChild = Array.from(\n group.querySelectorAll<HTMLElement>(':scope > [role=\"treeitem\"]'),\n ).find((child) => !child.hidden);\n if (firstChild) this.#focusItem(firstChild);\n }\n\n /** `ArrowLeft`: collapse an expanded parent, else step out to the parent item. */\n #collapseOrLeave(item: HTMLElement): void {\n if (this.#childGroup(item) && this.#isExpanded(item)) {\n this.#setExpanded(item, false);\n return;\n }\n const parent = this.#parentItem(item);\n if (parent) this.#focusItem(parent);\n }\n\n /** Focuses the first (`0`) or last (`-1`) visible item. */\n #focusEdge(index: number): void {\n const visible = this.#visibleItems;\n const target = index < 0 ? visible[visible.length - 1] : visible[index];\n if (target) this.#focusItem(target);\n }\n\n /**\n * Whether the item is disabled — **its own attribute or an ancestor's**.\n *\n * ARIA is explicit that the state carries down: \"The state of being disabled\n * applies to the current element *and all focusable descendant elements* of the\n * element on which the `aria-disabled` attribute is applied.\" A tree is the only\n * pattern here whose items nest, so it is the only place the inheritance is\n * observable — but the rule is the shared one.\n *\n * The walk stops at the controller element: an `aria-disabled` on the tree root\n * would otherwise disable every row, and a disabled *tree* is the consumer's\n * call to make with `inert`, not something this controller infers.\n */\n #isDisabled(item: HTMLElement): boolean {\n let current: HTMLElement | null = item;\n while (current && current !== this.element) {\n if (current.getAttribute(\"aria-disabled\") === \"true\") return true;\n current = current.parentElement;\n }\n return false;\n }\n\n /**\n * Applies single selection and dispatches `select`. An `aria-disabled=\"true\"`\n * item stays focusable (APG keeps disabled nodes reachable) but is never\n * activated, so consumers see no `select` for it.\n */\n #select(item: HTMLElement): void {\n if (this.#isDisabled(item)) return;\n for (const candidate of this.itemTargets) {\n candidate.setAttribute(\"aria-selected\", candidate === item ? \"true\" : \"false\");\n }\n this.dispatch(\"select\", { detail: { item } });\n }\n\n /** Updates expansion, reconciles a collapsed subtree, then synchronously dispatches `toggle`. */\n #setExpanded(item: HTMLElement, expanded: boolean): void {\n const group = this.#childGroup(item);\n if (!group) return;\n item.setAttribute(\"aria-expanded\", String(expanded));\n group.hidden = !expanded;\n if (!expanded) this.#escapeCollapsedSubtree(item, group);\n this.dispatch(\"toggle\", { detail: { item, expanded } });\n }\n\n /**\n * Reconciles focus and the roving tab stop before a synchronous `toggle` event\n * observes a newly collapsed group. If DOM focus remains inside the group,\n * both move to the parent. Otherwise only a stranded tab stop moves, preserving\n * DOM focus. A native chevron click takes this path when mousedown has already\n * focused the button. {@link TreeViewController.toggle} completes its focus\n * hand-off after this reconciliation and event dispatch.\n */\n #escapeCollapsedSubtree(item: HTMLElement, group: HTMLElement): void {\n const active = document.activeElement;\n if (active instanceof HTMLElement && group.contains(active)) {\n this.#focusItem(item);\n return;\n }\n // Focus is elsewhere on the page: relocate the tab stop without stealing it.\n const activeIndex = this.#roving.activeIndex;\n const tabbable = activeIndex === -1 ? undefined : this.itemTargets[activeIndex];\n if (!tabbable || !group.contains(tabbable)) return;\n const index = this.itemTargets.indexOf(item);\n if (index !== -1) this.#roving.setActive(index);\n }\n\n /** Makes `item` the single tab stop and moves DOM focus to it. */\n #focusItem(item: HTMLElement): void {\n const index = this.itemTargets.indexOf(item);\n if (index !== -1) {\n this.#roving.setActive(index, { focus: true });\n return;\n }\n // A `role=\"treeitem\"` without the item target is outside roving bookkeeping,\n // yet DOM structure still reaches it as a parent / first child. Focus it so\n // the tree never silently stops responding; `tabindex=\"-1\"` keeps the single\n // Tab stop on a real target.\n //\n // Checked first: `hidden` and natively disabled elements swallow `focus()`,\n // and moving toward one would leave the caret where it was with no signal.\n // Refusing is the honest outcome here — there is no better destination to\n // fall back to, since roving already declined this node.\n if (!canTakeFocus(item)) return;\n item.tabIndex = -1;\n item.focus();\n }\n\n /** Advances the typeahead query and focuses the next matching visible item. */\n #typeaheadTo(item: HTMLElement, char: string): void {\n const query = this.#typeahead.push(char);\n // Only visible rows are candidates: an item inside a collapsed group is not\n // reachable by arrow keys either, so typeahead must not tunnel into one.\n const visible = this.#visibleItems;\n const index = findTypeaheadMatch(visible, visible.indexOf(item), query, (candidate) =>\n this.#label(candidate),\n );\n // A miss reads back as `undefined` (`visible[-1]`), so the guard below is the\n // only branch — spelling the `-1` out again would add an unreachable one.\n const match = visible[index];\n if (match) this.#focusItem(match);\n }\n\n /**\n * Aligns each parent's `aria-expanded` with its child `group`'s `hidden`.\n * `aria-expanded` wins when authored (it is what assistive tech reads); a\n * parent without it derives one from `hidden` (establish-only-when-absent, so\n * a restored snapshot is never clobbered). Disagreeing markup would otherwise\n * strand the only tab stop on an invisible item.\n */\n #reconcileExpansion(): void {\n for (const item of this.itemTargets) {\n const group = this.#childGroup(item);\n if (!group) continue;\n if (item.hasAttribute(\"aria-expanded\")) {\n group.hidden = !this.#isExpanded(item);\n } else {\n item.setAttribute(\"aria-expanded\", String(!group.hidden));\n }\n }\n }\n\n /**\n * Leaves exactly one item tabbable. An existing, still-visible tab stop wins\n * (the DOM is the source of truth after a Turbo restore, so the user's roving\n * position survives); otherwise the selected item takes it (APG: a single-select\n * tree puts initial focus on its selected node), else the first visible item.\n */\n #normalizeTabStop(): void {\n const items = this.itemTargets;\n const visible = this.#visibleItems;\n const activeIndex = this.#roving.activeIndex;\n const active = activeIndex === -1 ? undefined : items[activeIndex];\n if (active && visible.includes(active)) {\n this.#roving.setActive(activeIndex);\n return;\n }\n const selected = visible.find((item) => item.getAttribute(\"aria-selected\") === \"true\");\n const next = selected ?? visible[0];\n this.#roving.setActive(next ? items.indexOf(next) : -1);\n }\n\n /**\n * The visible items: those with no `hidden` ancestor up to the tree, and not\n * `hidden` themselves. A hidden row is out of the move set — it can hold\n * neither DOM focus nor the only Tab stop — and neither can anything nested\n * under one, which is why the walk tests every ancestor rather than only the\n * collapsed `role=\"group\"` containers.\n */\n get #visibleItems(): HTMLElement[] {\n return this.itemTargets.filter((item) => {\n let node: HTMLElement | null = item;\n while (node && node !== this.element) {\n if (node.hidden) return false;\n node = node.parentElement;\n }\n return true;\n });\n }\n\n /** The child `group` owned directly by `item`, or `null` for a leaf. */\n #childGroup(item: HTMLElement): HTMLElement | null {\n return item.querySelector<HTMLElement>(':scope > [role=\"group\"]');\n }\n\n /** The nearest ancestor `treeitem`, or `null` at the root level. */\n #parentItem(item: HTMLElement): HTMLElement | null {\n return item.parentElement?.closest<HTMLElement>('[role=\"treeitem\"]') ?? null;\n }\n\n /** Whether a parent item is currently expanded. */\n #isExpanded(item: HTMLElement): boolean {\n return item.getAttribute(\"aria-expanded\") === \"true\";\n }\n\n /**\n * `item`'s name for typeahead. The fallback is the item's *own* text: a parent\n * row's nested child group is rendered inside it, and folding a whole subtree\n * into one name would let \"readme\" match the folder containing it.\n */\n #label(item: HTMLElement): string {\n return typeaheadLabel(item, () => {\n let text = \"\";\n for (const node of Array.from(item.childNodes)) {\n if (node.nodeType === Node.ELEMENT_NODE && (node as Element).matches('[role=\"group\"]')) {\n continue;\n }\n text += node.textContent ?? \"\";\n }\n return text;\n });\n }\n}\n"]}
|