stimeo-ui 0.17.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +136 -2
- package/README.md +19 -10
- package/dist/cable/index.d.ts +11 -9
- package/dist/cable/index.js +22 -14
- package/dist/cable/index.js.map +1 -1
- package/dist/coerce-JtZVNG5h.d.ts +20 -0
- package/dist/controllers/accordion_controller.js +14 -6
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.js +145 -118
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +5 -3
- package/dist/controllers/announcer_controller.js +113 -17
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.js +185 -60
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +55 -43
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +3 -3
- package/dist/controllers/bulk_select_controller.js +64 -50
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +15 -12
- package/dist/controllers/calendar_controller.js +55 -53
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +9 -5
- package/dist/controllers/carousel_controller.js +124 -86
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +27 -6
- package/dist/controllers/character_counter_controller.js +291 -95
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.js +6 -0
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.js +118 -27
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +1 -1
- package/dist/controllers/collapsible_controller.js +52 -49
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.js +29 -7
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +1 -1
- package/dist/controllers/combobox_controller.js +210 -71
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +9 -9
- package/dist/controllers/command_palette_controller.js +290 -140
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.js +145 -58
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.js +145 -118
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +49 -14
- package/dist/controllers/context_menu_controller.js +138 -73
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/count_up_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +2 -2
- package/dist/controllers/countdown_controller.js +109 -21
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +75 -11
- package/dist/controllers/currency_input_controller.js +446 -72
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +10 -8
- package/dist/controllers/data_grid_controller.js +134 -28
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +12 -9
- package/dist/controllers/date_range_picker_controller.js +93 -72
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +4 -5
- package/dist/controllers/dialog_controller.js +145 -118
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +1 -1
- package/dist/controllers/direct_upload_controller.js +24 -14
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.d.ts +6 -1
- package/dist/controllers/dirty_form_controller.js +68 -31
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.js +66 -46
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.js +145 -118
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.js +76 -63
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.js +200 -62
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.js +13 -6
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +3 -0
- package/dist/controllers/file_dropzone_controller.js +55 -48
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.js +9 -2
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +4 -4
- package/dist/controllers/flash_controller.js +66 -33
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +3 -3
- package/dist/controllers/focus_controller.js +39 -14
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.d.ts +2 -1
- package/dist/controllers/form_field_controller.js +52 -43
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.d.ts +3 -3
- package/dist/controllers/form_validation_controller.js +18 -11
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +13 -12
- package/dist/controllers/frame_loading_controller.js +55 -48
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +5 -4
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +4 -1
- package/dist/controllers/hover_card_controller.js +67 -61
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +6 -5
- package/dist/controllers/idle_controller.js +10 -6
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +31 -13
- package/dist/controllers/input_mask_controller.js +434 -24
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.js +3 -0
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.d.ts +3 -2
- package/dist/controllers/lazy_frame_controller.js +15 -3
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +10 -7
- package/dist/controllers/listbox_controller.js +260 -79
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.d.ts +8 -6
- package/dist/controllers/local_time_controller.js +48 -13
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.js +11 -4
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +24 -5
- package/dist/controllers/menu_controller.js +138 -82
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +49 -7
- package/dist/controllers/menubar_controller.js +65 -19
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.d.ts +13 -20
- package/dist/controllers/meter_controller.js +270 -24
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +19 -16
- package/dist/controllers/multi_select_controller.js +272 -95
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +19 -5
- package/dist/controllers/navigation_menu_controller.js +67 -19
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.js +98 -64
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.js +70 -52
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +24 -6
- package/dist/controllers/number_input_controller.js +344 -94
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.d.ts +8 -5
- package/dist/controllers/optimistic_controller.js +29 -11
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +35 -4
- package/dist/controllers/otp_controller.js +502 -104
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.js +6 -0
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +10 -9
- package/dist/controllers/overflow_menu_controller.js +136 -21
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +3 -2
- package/dist/controllers/pagination_controller.js +47 -14
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +5 -4
- package/dist/controllers/password_reveal_controller.js +52 -49
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +1 -1
- package/dist/controllers/password_strength_controller.js +13 -6
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.js +48 -128
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +20 -8
- package/dist/controllers/pointer_drag_controller.js +255 -24
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +9 -5
- package/dist/controllers/popover_controller.js +91 -65
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.js +6 -0
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.js +14 -3
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +9 -19
- package/dist/controllers/progress_controller.js +271 -27
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +11 -8
- package/dist/controllers/radio_group_controller.js +167 -80
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +4 -3
- package/dist/controllers/range_slider_controller.js +27 -7
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.js +93 -57
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.js +10 -6
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/reading_progress_controller.js +22 -5
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.d.ts +4 -4
- package/dist/controllers/relative_time_controller.js +75 -11
- package/dist/controllers/relative_time_controller.js.map +1 -1
- package/dist/controllers/reset_on_restore_controller.d.ts +3 -3
- package/dist/controllers/reset_on_restore_controller.js +6 -0
- package/dist/controllers/reset_on_restore_controller.js.map +1 -1
- package/dist/controllers/resizable_controller.d.ts +3 -0
- package/dist/controllers/resizable_controller.js +55 -22
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.d.ts +3 -3
- package/dist/controllers/roving_controller.js +10 -4
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.js +129 -88
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.d.ts +3 -3
- package/dist/controllers/scroll_restore_controller.js +14 -2
- package/dist/controllers/scroll_restore_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.js +93 -60
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.js +11 -5
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +1 -1
- package/dist/controllers/separator_controller.js +59 -43
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +1 -1
- package/dist/controllers/sidebar_controller.js +150 -122
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.js +55 -48
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +2 -1
- package/dist/controllers/slider_controller.js +27 -6
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.d.ts +1 -1
- package/dist/controllers/smart_sticky_header_controller.js +13 -5
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.d.ts +4 -4
- package/dist/controllers/sortable_controller.js +27 -14
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.js +55 -48
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +4 -4
- package/dist/controllers/step_indicator_controller.js +6 -0
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +2 -3
- package/dist/controllers/stepper_controller.js +6 -0
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +3 -1
- package/dist/controllers/stick_to_bottom_controller.js +19 -8
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/sticky_observer_controller.js +49 -43
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +2 -1
- package/dist/controllers/submit_once_controller.js +145 -85
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +7 -4
- package/dist/controllers/switch_controller.js +172 -28
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +7 -4
- package/dist/controllers/tabs_controller.js +6 -1
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +4 -3
- package/dist/controllers/tags_input_controller.js +157 -28
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.d.ts +1 -1
- package/dist/controllers/textarea_autosize_controller.js +40 -19
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +3 -2
- package/dist/controllers/theme_controller.js +10 -4
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +38 -5
- package/dist/controllers/time_picker_controller.js +192 -54
- 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 +58 -27
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +2 -3
- package/dist/controllers/toggle_group_controller.js +158 -56
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.d.ts +8 -6
- package/dist/controllers/toolbar_controller.js +150 -21
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +4 -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 +1 -1
- package/dist/controllers/tree_view_controller.js +134 -25
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2515 -1315
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +25 -12
- package/dist/inspector/cli.js +21 -3
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.d.ts +1 -1
- package/dist/inspector/cli_bin.js +62 -8
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +13 -13
- package/dist/inspector/manifest.json +19 -5
- package/dist/positioning/index.d.ts +9 -8
- package/dist/positioning/index.js +6 -0
- package/dist/positioning/index.js.map +1 -1
- package/package.json +11 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/state_reason.ts","../../src/utils/action_source.ts","../../src/utils/logical_scroll.ts","../../src/utils/arrow_step.ts","../../src/utils/field_mirror.ts","../../src/utils/interactive_host.ts","../../src/utils/listener_set.ts","../../src/utils/microtask_coalescer.ts","../../src/utils/morph_render_watcher.ts","../../src/utils/move_counter.ts","../../src/utils/target_selector.ts","../../src/controllers/data_grid_controller.ts"],"names":[],"mappings":"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","file":"data_grid_controller.js","sourcesContent":["/**\n * Why a component's public state moved, carried as `detail.reason`\n * on the state event that reports the move.\n *\n * The attribute a component publishes (`aria-expanded`, `hidden`, `data-state`)\n * only says *what* the state is now. A subscriber that mirrors the state\n * elsewhere, or reports it, needs *why*: closing on `Escape` and closing because\n * the consumer called the action are the same attribute write and different\n * events to the page around it.\n *\n * `\"api\"` is the one every subscriber has to look at. A close the consumer asked\n * for arrives back at their own listener, so a listener that closes something\n * else on `close` loops unless it ignores its own calls.\n *\n * | Reason | The state moved because |\n * | --- | --- |\n * | `\"user\"` | a control of this component was operated |\n * | `\"select\"` | an item inside it was activated |\n * | `\"escape\"` | `Escape` was pressed and this layer owned it |\n * | `\"outside\"` | a pointer landed outside it, or on its backdrop |\n * | `\"focus\"` | focus entered or left it |\n * | `\"pointer\"` | the pointer entered or left it |\n * | `\"scroll\"` | a tracked scroll container scrolled |\n * | `\"api\"` | a public action was called with no DOM event |\n * | `\"timeout\"` | an automatic timer advanced or dismissed the state |\n * | `\"limit\"` | a capacity limit dismissed an item |\n * | `\"media\"` | an operating-system media preference changed |\n */\nexport type StateReason =\n | \"user\"\n | \"select\"\n | \"escape\"\n | \"outside\"\n | \"focus\"\n | \"pointer\"\n | \"scroll\"\n | \"api\"\n | \"timeout\"\n | \"limit\"\n | \"media\";\n\n/** Focus modality: the events a focus move delivers to an action. */\nconst FOCUS_EVENTS = new Set([\"blur\", \"focus\", \"focusin\", \"focusout\"]);\n\n/** Pointer modality: the crossing events an action is bound to for hover. */\nconst POINTER_EVENTS = new Set([\"mouseenter\", \"mouseleave\", \"pointerenter\", \"pointerleave\"]);\n\n/**\n * Reads the interaction behind a **public action** from the DOM event it was\n * handed.\n *\n * Stimulus always passes the event to an action it invoked from `data-action`,\n * and a consumer calling the method themselves passes nothing — which is what\n * separates `\"api\"` from the rest.\n *\n * Only an action entry may resolve a reason this way. Inside the component the\n * event type no longer identifies the interaction: one `click` is the trigger\n * being pressed, the page outside being pressed, and an item being activated,\n * and those paths pass their own reason instead.\n *\n * @param event - The event the action received, if any.\n * @returns `\"api\"` with no event, else the modality the event type names, else\n * `\"user\"`.\n *\n * @example\n * ```ts\n * toggle(event?: Event): void {\n * this.#apply(!this.#isOpen, stateReasonFor(event));\n * }\n * ```\n */\nexport function stateReasonFor(event?: Event | null): \"user\" | \"focus\" | \"pointer\" | \"api\" {\n if (!event) return \"api\";\n if (FOCUS_EVENTS.has(event.type)) return \"focus\";\n if (POINTER_EVENTS.has(event.type)) return \"pointer\";\n return \"user\";\n}\n","import { stateReasonFor } from \"./state_reason\";\n\n/** The DOM binding and origin of a public action, or its explicit API element. */\nexport interface ActionSource {\n readonly event: Event | null;\n readonly host: Element | null;\n readonly origin: Element | null;\n readonly reason: ReturnType<typeof stateReasonFor>;\n}\n\n/**\n * Normalizes an action's DOM event or explicit element without choosing an owned target.\n *\n * Event hosts and origins stay separate so consumers can yield to nested controls.\n * An explicit element supplies both, with no DOM event and an `\"api\"` reason.\n * Consumers resolve current targets and enforce their own scope and interaction rules.\n *\n * @param source - The DOM event, explicit element, or absent optional argument.\n * @returns The source elements, original event and public interaction reason.\n */\nexport function actionSource(source?: Event | HTMLElement): ActionSource {\n if (source instanceof HTMLElement) {\n return { event: null, host: source, origin: source, reason: \"api\" };\n }\n const event = source ?? null;\n return {\n event,\n host: event?.currentTarget instanceof Element ? event.currentTarget : null,\n origin: event?.target instanceof Element ? event.target : null,\n reason: stateReasonFor(event),\n };\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\n/**\n * True when a press arrived carrying any modifier, for the keys the browser and\n * the OS own outright: return without calling `preventDefault()` and without\n * moving any state.\n *\n * `Control+Home` and `Control+End` jump the document to its ends, and a widget\n * that swallows them makes the shortcut work or not depending on where focus\n * happens to sit. {@link isReservedArrowChord} answers the same question for the\n * arrows, but returns `false` for every other key so that this one can decide.\n *\n * There is no `allow` list here on purpose. APG assigns no modifier chord to\n * `Home`/`End`, so a widget that wanted one would be claiming a combination the\n * pattern never gave it.\n *\n * @example\n * ```ts\n * case \"Home\":\n * case \"End\":\n * if (hasModifierChord(event)) return;\n * ```\n */\nexport function hasModifierChord(event: KeyboardEvent): boolean {\n return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;\n}\n","/**\n * The hidden form fields a widget keeps in step with the state it owns.\n *\n * A widget whose value lives in ARIA attributes submits nothing on its own, so\n * it mirrors that value into `<input type=\"hidden\">` — one field for a single\n * value, a container of generated fields for a set. The mirror is one-way: the\n * widget's own state stays the source of truth and the field is what the form\n * reads.\n *\n * The write and the report are separate calls because they answer to different\n * rules. A mirror is refreshed on connect, after a morph, and whenever the\n * widget repairs itself — none of which a form should treat as an edit. Only\n * the user's own commit reports one, which is why {@link commitField} is never\n * folded into the writes.\n */\n\n/** How a widget's generated fields identify themselves to the form. */\nexport interface GeneratedFieldOptions {\n /** The `name` every generated input submits under. */\n readonly name: string;\n /** The `form` id, for a widget that sits outside the form it submits to. */\n readonly form?: string;\n}\n\n/**\n * Mirrors one value into a hidden field.\n *\n * @returns Whether the field's value moved.\n *\n * @example\n * ```ts\n * if (writeField(this.fieldTarget, String(value)) && userDriven) {\n * commitField(this.fieldTarget);\n * }\n * ```\n */\nexport function writeField(field: HTMLInputElement, value: string): boolean {\n if (field.value === value) return false;\n field.value = value;\n return true;\n}\n\n/**\n * Mirrors a set of values into `container` as generated hidden fields, in the\n * order given.\n *\n * A container that already submits exactly this set is left untouched, children\n * and all — the mirror is refreshed far more often than it moves (every connect,\n * morph and repair), and a rebuild that changes nothing is still a DOM mutation\n * a consumer's observer and a `:empty` rule can see.\n *\n * @returns Whether the submitted set moved — its values, their order, the\n * `name`, or the `form`.\n */\nexport function writeFields(\n container: HTMLElement,\n values: readonly string[],\n { name, form = \"\" }: GeneratedFieldOptions,\n): boolean {\n const current = [...container.children];\n const submits =\n current.length === values.length &&\n current.every(\n (child, index) =>\n child instanceof HTMLInputElement &&\n child.type === \"hidden\" &&\n child.value === values[index] &&\n child.name === name &&\n (child.getAttribute(\"form\") ?? \"\") === form,\n );\n if (submits) return false;\n\n container.replaceChildren(\n ...values.map((value) => {\n const input = document.createElement(\"input\");\n input.type = \"hidden\";\n input.name = name;\n input.value = value;\n if (form !== \"\") input.setAttribute(\"form\", form);\n return input;\n }),\n );\n return true;\n}\n\n/**\n * Reports a committed value from `target` the way a native form control does:\n * one bubbling, non-cancelable `change`.\n *\n * Call it only for a move the user made. Form-level behaviors listen for this —\n * validation re-checks, auto-submit — and a mirror refreshed by a morph or a\n * repair is not something the user submitted.\n *\n * `target` is the element that carries the value: the hidden field for a single\n * mirrored value, the container for a generated set (whose own inputs are\n * replaced on every write), or a widget's own native control where that is what\n * the form reads.\n */\nexport function commitField(target: HTMLElement): void {\n target.dispatchEvent(new Event(\"change\", { bubbles: true }));\n}\n","/**\n * Native interactive elements whose built-in activation must not be replaced\n * wholesale by a controller. Deliberately excludes `[tabindex]`: a generic\n * element made focusable is the supported host for several headless patterns.\n * Contenteditable is handled by {@link isInteractiveHost} because its state is\n * inherited and its keywords are ASCII-case-insensitive.\n */\nexport const INTERACTIVE_HOST_SELECTOR =\n \"button, input, select, textarea, label, a[href], area[href], summary, details, audio[controls], video[controls], iframe, object, embed\";\n\n/**\n * Whether an element owns native activation or an inherited editing surface.\n *\n * The `contenteditable` missing and invalid states inherit. Walking explicitly\n * also keeps `contenteditable=\"false\"` as a real boundary inside an editable\n * ancestor instead of treating any ancestor attribute as decisive.\n */\nexport function isInteractiveHost(element: HTMLElement): boolean {\n if (element.matches(INTERACTIVE_HOST_SELECTOR)) return true;\n\n let current: HTMLElement | null = element;\n while (current) {\n const raw = current.getAttribute(\"contenteditable\");\n if (raw !== null) {\n const value = raw.trim().toLowerCase();\n if (value === \"false\") return false;\n if (value === \"\" || value === \"true\" || value === \"plaintext-only\") return true;\n }\n current = current.parentElement;\n }\n return false;\n}\n","/**\n * Owns every listener a controller attaches for one connected lifetime and\n * releases them together, so an event name is spelled once and the release\n * cannot drift from the registration.\n *\n * Detaching by hand repeats three things at the release site — the type, the\n * handler reference and the capture flag — and any one of them drifting leaves\n * a listener alive on an element the controller no longer drives. Aborting the\n * signal a registration was created with releases it whatever it was made of,\n * so the release site names nothing.\n *\n * One set per lifetime, held as a field and never rebuilt. {@link\n * ListenerSet.dispose} is the only thing that closes a generation, and it opens\n * the next one in the same step, so the lifecycle hooks can call `add` and\n * `dispose` in any order any number of times, and no `add` ever meets an\n * already aborted signal — a DOM that registers a listener on such a signal\n * would keep it forever. Rebuilding the set instead would leak:\n * `addEventListener` matches an existing registration on target, type, callback\n * and capture alone, so re-adding the same handler while the first registration\n * is live is discarded, and that live registration keeps the signal it was\n * attached with — a set built by a second `connect()` can never release what it\n * appears to own.\n *\n * Scope is the registration only. Timers, observers, leases and the order they\n * are unwound in stay with the controller, which places `dispose()` in its\n * cleanup sequence. A receiver that changes at runtime (a\n * swapped target) is rebound by calling `dispose()` and then `add()` again from\n * the rebind, not by a second set.\n *\n * The set must own every registration of a tuple it holds: a listener the same\n * target already carries for the same type, callback and capture flag keeps the\n * right to remove it, and this set's `add` for that tuple is discarded. A\n * subscription shared between instances behind a participant count, or one that\n * lives for less than the connection, keeps its own pair of calls.\n *\n * @example\n * ```ts\n * readonly #listeners = new ListenerSet();\n *\n * connect(): void {\n * this.#listeners.add(this.element, \"submit\", this.#onNativeSubmit, { capture: true });\n * this.#listeners.add(this.element, \"turbo:submit-start\", this.#onSubmitStart);\n * this.#listeners.add(this.element, \"turbo:submit-end\", this.#onSubmitEnd);\n * }\n *\n * disconnect(): void {\n * this.#listeners.dispose();\n * }\n * ```\n */\nexport class ListenerSet {\n /** The generation every `add` joins until the next `dispose()`. */\n #abort = new AbortController();\n\n /**\n * Attaches `handler` to the open generation, exactly as the caller spelled it.\n *\n * The set supplies the signal, so `options` carries everything else the DOM\n * accepts — `capture` included, which has to match at release time and no\n * longer has a second place to drift from.\n */\n add(\n target: EventTarget,\n type: string,\n handler: EventListener,\n options?: Omit<AddEventListenerOptions, \"signal\">,\n ): void {\n target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });\n }\n\n /**\n * Releases every listener of the open generation, synchronously, and opens the\n * next one. Idempotent, and safe before anything has been added.\n */\n dispose(): void {\n this.#abort.abort();\n this.#abort = new AbortController();\n }\n}\n","/**\n * Collapses many Stimulus lifecycle callbacks from one DOM mutation into a\n * single pass.\n *\n * Stimulus fires `<name>TargetConnected` / `Disconnected` once per element and\n * `<name>ValueChanged` once per changed attribute. Replacing a list of N options\n * or morphing several render Values therefore delivers N callbacks — but the\n * useful unit of work is \"reconcile against the resulting declarative input\",\n * once, after the batch has settled. Every controller with reconcilable targets\n * or render Values needs the same 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 initial target and Value callbacks\n * ahead of `connect()`. Reconciling there would compute output 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 * @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","import { ListenerSet } from \"./listener_set\";\nimport { MicrotaskCoalescer } from \"./microtask_coalescer\";\n\n/**\n * Coalesces retained-element morphs and explicit lifecycle requests into one pass.\n *\n * Observe the controller's element after its initial state is settled. Morphs on\n * that element and bubbling morphs on its descendants share the same microtask\n * as {@link schedule}. The callback repairs derived output; the controller owns\n * any logical transition reporting. {@link disconnect} releases the listener and\n * cancels pending work, including work from an earlier connected lifetime.\n */\nexport class MorphRenderWatcher {\n readonly #listeners = new ListenerSet();\n readonly #pass: MicrotaskCoalescer;\n #element: Element | null = null;\n readonly #onMorph = (): void => this.#pass.schedule();\n\n /** @param run - the single pass that repairs the settled controller's output. */\n constructor(run: () => void) {\n this.#pass = new MicrotaskCoalescer(run);\n }\n\n /** Opens observation; observing the same element again preserves queued work. */\n observe(element: Element): void {\n if (this.#element === element) return;\n this.disconnect();\n this.#element = element;\n this.#pass.activate();\n this.#listeners.add(element, \"turbo:morph-element\", this.#onMorph);\n }\n\n /** Requests one pass; requests outside an observed lifetime are ignored. */\n schedule(): void {\n this.#pass.schedule();\n }\n\n /** Releases observation and pending work synchronously; safe to repeat. */\n disconnect(): void {\n this.#listeners.dispose();\n this.#pass.cancel();\n this.#element = null;\n }\n}\n","/**\n * Identifies the latest reported state transition of one owner.\n *\n * Record a transition after its state is written and before invoking external\n * code. Check the returned token before each later report: a synchronous\n * transition made by a listener supersedes reports that have not been sent.\n * Reading state or committing an unchanged state must not advance the counter.\n * Reports stay in the caller so their ordering remains explicit.\n */\nexport class MoveCounter {\n #sequence = 0;\n\n /** Records a state transition and returns its report token. */\n record(): number {\n this.#sequence += 1;\n return this.#sequence;\n }\n\n /** Whether no later state transition has superseded this report token. */\n isLatest(token: number): boolean {\n return token === this.#sequence;\n }\n}\n","/**\n * The attribute selector for one declared target, in a controller's own namespace.\n *\n * Stimulus reads `data-<identifier>-target` as a **space-separated token list**, so a\n * single element can carry several target names at once. Matching the attribute value\n * whole would reach only the elements that declare exactly one name, which is why the\n * name is paired with `~=`.\n *\n * `identifier` is the name the controller is registered under, so the selector reaches\n * the markup that registration renders rather than one fixed spelling.\n */\nexport function targetSelector(identifier: string, name: string): string {\n return `[data-${identifier}-target~=\"${name}\"]`;\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { actionSource } from \"../utils/action_source\";\nimport { isReservedArrowChord, logicalArrowKey } from \"../utils/arrow_step\";\nimport { commitField, writeFields } from \"../utils/field_mirror\";\nimport { INTERACTIVE_HOST_SELECTOR, isInteractiveHost } from \"../utils/interactive_host\";\nimport { MorphRenderWatcher } from \"../utils/morph_render_watcher\";\nimport { MoveCounter } from \"../utils/move_counter\";\nimport type { StateReason } from \"../utils/state_reason\";\nimport { targetSelector } from \"../utils/target_selector\";\n\n/** Cycle order for a sortable column header's `aria-sort`. */\nconst SORT_CYCLE = [\"none\", \"ascending\", \"descending\"] as const;\ntype SortDirection = (typeof SORT_CYCLE)[number];\n\n/** Row attributes a page can rewrite in place that move the published selection. */\nconst OBSERVED_ATTRIBUTES = [\"aria-selected\", \"data-value\"];\n\n/** The published selection: each selected row with the value it submits. */\ntype Selection = ReadonlyMap<HTMLElement, string | undefined>;\n\n/**\n * Sets `name` on `element` only when its value differs. A same-value write still\n * queues a mutation record for every observer on the page, so an unchanged state\n * writes nothing.\n */\nfunction setAttributeIfChanged(element: Element, name: string, value: string): void {\n if (element.getAttribute(name) !== value) element.setAttribute(name, value);\n}\n\n/** Whether two selections hold the same rows submitting the same values, in any order. */\nfunction sameSelection(a: Selection, b: Selection): boolean {\n if (a.size !== b.size) return false;\n for (const [row, value] of a) {\n if (!b.has(row) || b.get(row) !== value) return false;\n }\n return true;\n}\n\n/**\n * Returns the next `aria-sort` direction in the cycle after `current`. Unknown or\n * ARIA-only values (e.g. `\"other\"`, or an empty/missing attribute) are treated as\n * `\"none\"`, so the first activation always advances to `\"ascending\"` instead of\n * stalling.\n */\nfunction nextSortDirection(current: string): SortDirection {\n const index = SORT_CYCLE.indexOf(current as SortDirection);\n const from = index < 0 ? 0 : index;\n return SORT_CYCLE[(from + 1) % SORT_CYCLE.length] ?? \"ascending\";\n}\n\n/**\n * Headless, accessible **Data Grid** behavior: column sorting, row selection, and\n * roving keyboard navigation over an interactive table.\n *\n * Markup contract (identifier: `stimeo--data-grid`):\n * <table data-controller=\"stimeo--data-grid\" role=\"grid\" aria-label=\"Users\"\n * data-stimeo--data-grid-selection-value=\"single\"\n * data-stimeo--data-grid-name-value=\"user_id\">\n * <caption><div data-stimeo--data-grid-target=\"fields\"></div></caption>\n * <thead><tr role=\"row\">\n * <th role=\"columnheader\" aria-sort=\"none\" tabindex=\"-1\"\n * data-stimeo--data-grid-target=\"columnHeader\"\n * data-action=\"click->stimeo--data-grid#sort\n * keydown->stimeo--data-grid#onKeydown\">Name</th>\n * </tr></thead>\n * <tbody><tr role=\"row\" aria-selected=\"false\" data-value=\"7\"\n * data-stimeo--data-grid-target=\"row\">\n * <td role=\"gridcell\" tabindex=\"0\" data-stimeo--data-grid-target=\"cell\"\n * data-action=\"keydown->stimeo--data-grid#onKeydown\">Jane</td>\n * </tr></tbody>\n * </table>\n *\n * Implements the WAI-ARIA APG **Grid** pattern plus `aria-sort`. The whole grid is\n * a single Tab stop (roving `tabindex`: exactly one cell/header is `0`, the rest\n * `-1`); arrow keys move both DOM focus and that tabbable position. Sort state is\n * exposed via `aria-sort` on headers, selection via `aria-selected` on rows.\n *\n * `selectionchange` dispatches `{ rows: HTMLElement[] }` when the user changes the\n * selection; `sort` dispatches\n * `{ column: HTMLElement, direction: \"ascending\" | \"descending\" | \"none\" }`.\n *\n * `reconcile` dispatches `{ rows: HTMLElement[] }` — the selected rows, as\n * `selectionchange` carries them — when the page moves the selection instead: a\n * selected row removed or dropped from the targets, a morph or a script writing\n * a row's `aria-selected` or a selected row's `data-value`, a selected row that\n * arrives ahead of the selection in `single` mode, or a runtime `selection` that\n * collapses it. One batch of such changes reports once, and only when the\n * selected rows or the values they submit differ from the selection last settled\n * — on connect, by the user, or by the previous `reconcile` — so connecting\n * reports nothing.\n *\n * With a `fields` target the selected rows are mirrored into `name`d hidden\n * inputs so the selection can be submitted and read server-side — one per\n * selected row that carries a `data-value`, in DOM order; a row without one\n * submits nothing. `form` points them at a `<form>` by id when the container\n * sits outside it. A selection the user made emits a native bubbling `change`\n * from the container, the way a form control does, so `stimeo--auto-submit` and\n * form-level validation hear it. The mirror is refreshed silently on connect,\n * on a replacement container, on row churn, and whenever `selection`, `name` or\n * `form` changes; a selection the page moved is reported by `reconcile` alone.\n *\n * User `selectionchange` reports compare the resulting state with the last published\n * state. A pending page write handled in the same script joins that confirmation;\n * a browser-delivered listener may settle it first as `reconcile`. Confirming\n * the last published state reports nothing.\n *\n * A synchronous subscriber that confirms another state replaces reports still\n * pending for the outer confirmation. Reading state or confirming it unchanged\n * does not replace them. An event already being dispatched cannot be recalled.\n *\n * @remarks\n * Behavior only — the consumer performs the actual data sort/render in response to\n * the `sort` event and owns all styling. While connected a `MutationObserver`\n * watches the rows' `aria-selected` and `data-value`, which a morph can rewrite\n * without adding or removing a target; `disconnect()` releases it and drops a\n * pending pass. `connect()` rebuilds the single tab stop idempotently from the\n * DOM, and the target callbacks rebuild it again after rows or cells are added or\n * removed at runtime.\n *\n * Behavior provided:\n * - `Arrow*` move between cells (clamped at edges); `Home`/`End` to the row's\n * first/last cell; `Ctrl+Home`/`Ctrl+End` to the grid's first/last cell.\n * - `Enter`/`Space` cycles a header's sort (`none→ascending→descending`) or toggles\n * the focused row's selection when selection is enabled.\n *\n * Consumer contract — controls nested inside a cell or header:\n * - APG's grid hosts working controls in its cells, so a keystroke or click that\n * reached one — a nested button, link or form control, an editable host, or any\n * widget that already called `preventDefault()` — is left to it entirely: no\n * move, no sort, no selection. The one control that hands the event on is a\n * sortable header's own `<button>`, whose activation is what the click carries.\n * - A control keeps its own Tab behavior, so give it `tabindex=\"-1\"` to preserve\n * the grid's single Tab stop. A sortable `columnheader` hosting a `<button>` is\n * the common case: the roving position stays on the header, and the button's\n * own activation reaches `sort` through the click that bubbles to it.\n * - Host `role=\"gridcell\"` / `role=\"columnheader\"` on a non-interactive element\n * (`td`, `th`); an interactive host makes the grid stand down on that cell.\n */\nexport class DataGridController extends Controller<HTMLElement> {\n readonly #moves = new MoveCounter();\n /** The cell this controller last gave the Tab stop. */\n #stopHolder: HTMLElement | null = null;\n static override targets = [\"columnHeader\", \"row\", \"cell\", \"fields\"];\n static override values = {\n selection: { type: String, default: \"none\" },\n name: { type: String, default: \"rows[]\" },\n form: { type: String, default: \"\" },\n };\n static actions = [\"onKeydown\", \"sort\", \"toggleSelect\"] as const;\n static events = [\"selectionchange\", \"sort\", \"reconcile\"] as const;\n\n declare readonly columnHeaderTargets: HTMLElement[];\n declare readonly rowTargets: HTMLElement[];\n declare readonly cellTargets: HTMLElement[];\n declare readonly fieldsTarget: HTMLElement;\n declare readonly hasFieldsTarget: boolean;\n declare selectionValue: string;\n declare nameValue: string;\n declare formValue: string;\n\n /**\n * Collapses the target callbacks, Value changes and observed row writes of one\n * DOM mutation into a single pass, and refuses to run before `connect()` or\n * after `disconnect()`.\n *\n * Stimulus reports every target one at a time, so an ungated pass would re-walk\n * the whole grid once per authored cell on mount and once per streamed cell\n * afterwards — quadratic in the cell count both times.\n */\n readonly #reconcile = new MorphRenderWatcher(() => this.#reconcileRows());\n /** Watches the row attributes a page can rewrite in place; set while connected. */\n #observer: MutationObserver | null = null;\n /** The selection last settled: on connect, by the user, or by a reported pass. */\n #settled: Selection = new Map();\n\n /**\n * Establishes a single tab stop across all navigable cells/headers, brings the\n * rows to their baseline, and settles the selection without reporting it.\n *\n * The whole baseline runs here rather than in the Value callbacks: Stimulus\n * delivers those before `connect()`, where the pass is refused, and a\n * re-attached element reuses its cached context, whose value observer does not\n * fire again.\n */\n override connect(): void {\n this.#restoreBaseline();\n this.#settled = this.#selection();\n this.#reconcile.observe(this.element);\n this.#observeRows();\n }\n\n /** Releases the row observer and drops a queued pass, so neither outlives the element. */\n override disconnect(): void {\n this.#reconcile.disconnect();\n this.#observer?.disconnect();\n this.#observer = null;\n }\n\n /**\n * Rebuilds the DOM-owned baselines from the live grid: exactly one navigable\n * cell is in the Tab sequence, `aria-multiselectable` follows `selection`, every\n * selectable row carries an explicit `aria-selected`, and the fields mirror the\n * selected rows.\n *\n * The tab stop keeps whichever cell already holds it, so a rebuild triggered by\n * an unrelated row arriving does not throw the user's position away — the cell\n * this controller gave it keeps it while it still carries it, even when an\n * arriving cell brings its own `tabindex=\"0\"` ahead of it in the grid; only when\n * no cell holds it — the grid is fresh, or the holder was removed — does the\n * first navigable cell take over. Without that fallback a grid whose active row\n * is removed keeps every cell at `-1` and drops out of the Tab sequence\n * entirely.\n */\n #restoreBaseline(): void {\n const cells = this.#navigableCells();\n const holder = this.#stopHolder;\n const kept = holder !== null && holder.tabIndex === 0 && cells.includes(holder);\n const active = (kept ? holder : cells.find((cell) => cell.tabIndex === 0)) ?? cells[0];\n if (active) this.#setActiveCell(active, { focus: false }, cells);\n this.#syncSelectable();\n const pageWrote = this.#takePageRecords();\n this.#normalizeSelection();\n this.#dropOwnRecords(pageWrote);\n this.#mirrorFields(false);\n }\n\n /**\n * The pass the page's changes run: the baselines are rebuilt, then a selection\n * that moved is reported. The report comes last, so rows a subscriber rewrites\n * are the next pass's to settle.\n */\n #reconcileRows(): void {\n this.#restoreBaseline();\n this.#reportMove();\n }\n\n /**\n * Reports the selection as `reconcile` when it differs from the one last\n * settled. The settled selection is replaced before dispatching, so a move a\n * subscriber makes is measured against what it was told.\n */\n #reportMove(): void {\n const selection = this.#selection();\n if (sameSelection(selection, this.#settled)) return;\n this.#settled = selection;\n this.dispatch(\"reconcile\", { detail: { rows: [...selection.keys()] } });\n }\n\n /**\n * Re-renders `aria-multiselectable`, the rows and the fields when application\n * code (or a Turbo morph) changes `selection` at runtime; a selection the new\n * mode collapses is reported as `reconcile`.\n */\n selectionValueChanged(): void {\n this.#reconcile.schedule();\n }\n\n /** Rebuilds the submitted fields when the public name changes at runtime. */\n nameValueChanged(): void {\n this.#reconcile.schedule();\n }\n\n /** Repoints the submitted fields when the owning form changes at runtime. */\n formValueChanged(): void {\n this.#reconcile.schedule();\n }\n\n /** Seeds a fields container inserted after connect from the current selection. */\n fieldsTargetConnected(): void {\n this.#reconcile.schedule();\n }\n\n /** Brings the fields container that stays to the current selection when an earlier one leaves. */\n fieldsTargetDisconnected(): void {\n this.#reconcile.schedule();\n }\n\n /** Re-establishes the baselines for a row added after connect. */\n rowTargetConnected(): void {\n this.#reconcile.schedule();\n }\n\n /** Settles the selection again when a row leaves the grid or drops its target token. */\n rowTargetDisconnected(): void {\n this.#reconcile.schedule();\n }\n\n /** Re-establishes the tab stop when a cell joins the grid after connect. */\n cellTargetConnected(): void {\n this.#reconcile.schedule();\n }\n\n /** Re-establishes the tab stop when a cell leaves the grid. */\n cellTargetDisconnected(): void {\n this.#reconcile.schedule();\n }\n\n /** Re-establishes the tab stop when a header joins the grid after connect. */\n columnHeaderTargetConnected(): void {\n this.#reconcile.schedule();\n }\n\n /** Re-establishes the tab stop when a header leaves the grid. */\n columnHeaderTargetDisconnected(): void {\n this.#reconcile.schedule();\n }\n\n /**\n * Brings the authored rows to the shape the APG requires, without changing\n * which rows the author chose.\n *\n * Every selectable row gets an explicit value — an absent `aria-selected` means\n * \"not selectable\" in ARIA, so a forgotten attribute hides a selectable row —\n * and a single-select grid keeps at most one `true`, first in DOM order.\n * A grid that declares `selection=\"none\"` has no selectable rows, so the\n * attribute is removed rather than written: in ARIA its absence is what \"not\n * selectable\" looks like.\n *\n * @stimeoRenderRoot\n */\n #normalizeSelection(): void {\n const rows = this.rowTargets;\n if (this.selectionValue === \"none\") {\n // Reclaim, do not merely skip: a grid that *became* unselectable would\n // otherwise keep announcing rows as selected while the logic refuses to\n // change them. In ARIA the absence of the attribute is what \"not\n // selectable\" looks like, so the rows have to lose it outright.\n for (const row of rows) row.removeAttribute(\"aria-selected\");\n return;\n }\n const single = this.selectionValue === \"single\";\n const first = rows.find((row) => row.getAttribute(\"aria-selected\") === \"true\");\n for (const row of rows) {\n if (single) {\n setAttributeIfChanged(row, \"aria-selected\", row === first ? \"true\" : \"false\");\n } else if (row.getAttribute(\"aria-selected\") !== \"true\") {\n setAttributeIfChanged(row, \"aria-selected\", \"false\");\n }\n }\n }\n\n /**\n * Mirrors `selection=\"multiple\"` onto `aria-multiselectable` (APG Grid) so SRs\n * announce that more than one row can be selected; cleared for single/none so a\n * grid never carries a misleading attribute.\n *\n * @stimeoRenderRoot\n */\n #syncSelectable(): void {\n if (this.selectionValue === \"multiple\") {\n setAttributeIfChanged(this.element, \"aria-multiselectable\", \"true\");\n } else {\n this.element.removeAttribute(\"aria-multiselectable\");\n }\n }\n\n /** Cycles an owned column header's sort from an event, the header, or its descendant. */\n sort(source: Event | HTMLElement): void {\n const { event, host, origin, reason } = actionSource(source);\n if (origin && origin.closest(`[data-controller~=\"${this.identifier}\"]`) !== this.element)\n return;\n const header = host?.closest<HTMLElement>(targetSelector(this.identifier, \"columnHeader\"));\n if (!header || !this.columnHeaderTargets.includes(header)) return;\n if (event?.defaultPrevented) return;\n // A sortable header hosts a `<button>`, and that button's activation is\n // exactly what this click carries, so it is the one control that does not\n // take the event away. A link or a field inside the header is its own\n // destination, and sorting on its click would act in parallel.\n const control = event ? this.#claimingControl(event, header) : null;\n if (control && !(control instanceof HTMLButtonElement)) return;\n\n const direction = nextSortDirection(header.getAttribute(\"aria-sort\") ?? \"none\");\n\n // Only one column is sorted at a time: reset the others to `none`.\n for (const other of this.columnHeaderTargets) {\n other.setAttribute(\"aria-sort\", other === header ? direction : \"none\");\n }\n\n this.#setActiveCell(header, { focus: false });\n this.dispatch(\"sort\", { detail: { column: header, direction, reason } });\n }\n\n /** Toggles an owned row from an action event, the row, or its descendant. */\n toggleSelect(source: Event | HTMLElement): void {\n const { event, host, origin, reason } = actionSource(source);\n if (origin && origin.closest(`[data-controller~=\"${this.identifier}\"]`) !== this.element)\n return;\n // The pointer path guards on `selection=\"none\"` exactly as the keyboard path\n // does, so a grid that declares itself unselectable never grows selected rows.\n if (this.selectionValue === \"none\") return;\n // A widget that handled the click owns it, exactly as the keyboard path\n // stands down on a keystroke a descendant consumed.\n if (event?.defaultPrevented) return;\n if (event && host instanceof HTMLElement && this.#claimedByDescendant(event, host)) return;\n const row = host?.closest<HTMLElement>(targetSelector(this.identifier, \"row\"));\n if (row && this.rowTargets.includes(row)) this.#toggleRow(row, reason);\n }\n\n /** Grid navigation + sort/select activation. Bound to cells and headers. */\n onKeydown(event: KeyboardEvent): void {\n // A descendant widget that already claimed the key (a grabbed drag handle, a\n // nested menu) must not ALSO act on it — composition depends on this yield.\n if (event.defaultPrevented) return;\n if (isReservedArrowChord(event)) return;\n if (event.isComposing) return;\n const cell = event.currentTarget as HTMLElement;\n // A native control inside the cell never calls `preventDefault()` — its\n // activation IS the default action — so the yield above cannot see it. This\n // recognises those by element shape instead.\n if (this.#claimedByDescendant(event, cell)) return;\n const matrix = this.#matrix();\n const position = this.#locate(matrix, cell);\n if (!position) return;\n const [row, col] = position;\n const rowCells = matrix[row] ?? [];\n\n let target: HTMLElement | undefined;\n // Logical, not physical. The key is normalised rather than the\n // delta negated: these two branches are not mirror images — their guards\n // differ — so swapping the key keeps each guard with its own direction.\n switch (logicalArrowKey(event.key, this.element)) {\n case \"ArrowRight\":\n target = this.#cellInRow(matrix, row, col + 1);\n break;\n case \"ArrowLeft\":\n target = this.#cellInRow(matrix, row, Math.max(col - 1, 0));\n break;\n case \"ArrowDown\":\n target = this.#cellInRow(matrix, Math.min(row + 1, matrix.length - 1), col);\n break;\n case \"ArrowUp\":\n target = this.#cellInRow(matrix, Math.max(row - 1, 0), col);\n break;\n case \"Home\":\n target = event.ctrlKey ? this.#cellInRow(matrix, 0, 0) : rowCells[0];\n break;\n case \"End\":\n target = event.ctrlKey\n ? this.#cellInRow(matrix, matrix.length - 1, Number.POSITIVE_INFINITY)\n : rowCells[rowCells.length - 1];\n break;\n case \"Enter\":\n case \" \":\n this.#activate(cell);\n event.preventDefault();\n return;\n default:\n return;\n }\n\n if (target) {\n event.preventDefault();\n this.#setActiveCell(target, { focus: true }, matrix.flat());\n }\n }\n\n /**\n * Whether the event was addressed to a control inside `host` rather than to the\n * grid.\n *\n * Cells and headers hold consumer markup, and APG's grid pattern expects that\n * markup to include working controls — a row action button, an inline editor.\n * Those own their own keystrokes and clicks, so the grid stands down entirely\n * rather than acting in parallel. An editable host (its `contenteditable` state\n * is inherited, so the walk is explicit) counts the same way.\n */\n #claimedByDescendant(event: Event, host: HTMLElement): boolean {\n return this.#claimingControl(event, host) !== null;\n }\n\n /**\n * The nested control this event belongs to, or `null` when the host owns it.\n *\n * Naming the control, rather than answering yes or no, is what lets the click\n * path treat a sortable header's `<button>` as the activation it is while every\n * other control still takes the event away.\n */\n #claimingControl(event: Event, host: HTMLElement): HTMLElement | null {\n const source = event.target as HTMLElement;\n const control = source.closest<HTMLElement>(INTERACTIVE_HOST_SELECTOR);\n if (control && host.contains(control)) return control;\n return isInteractiveHost(source) ? source : null;\n }\n\n /**\n * Performs a header's sort or a cell row's selection toggle on activation.\n *\n * @stimeoRuntimeOnly `selection` decides whether this one activation toggles the row; the sort it\n * cycles does not depend on it.\n */\n #activate(cell: HTMLElement): void {\n if (this.columnHeaderTargets.includes(cell)) {\n this.#cycleSort(cell);\n return;\n }\n if (this.selectionValue === \"none\") return;\n const row = cell.closest<HTMLElement>(\"[role='row']\");\n if (row && this.rowTargets.includes(row)) this.#toggleRow(row);\n }\n\n /** Cycles a header's sort on keyboard activation and emits `sort`. */\n #cycleSort(header: HTMLElement): void {\n const direction = nextSortDirection(header.getAttribute(\"aria-sort\") ?? \"none\");\n for (const other of this.columnHeaderTargets) {\n other.setAttribute(\"aria-sort\", other === header ? direction : \"none\");\n }\n this.dispatch(\"sort\", { detail: { column: header, direction, reason: \"user\" } });\n }\n\n /**\n * Toggles a row's `aria-selected`, honoring single vs. multiple selection.\n *\n * @stimeoRuntimeOnly `selection` decides how this one toggle treats the other rows.\n */\n #toggleRow(row: HTMLElement, reason: StateReason = \"user\"): void {\n const selected = row.getAttribute(\"aria-selected\") === \"true\";\n const pageWrote = this.#takePageRecords();\n // Enforce single-ness on every toggle, not only when turning a row on:\n // switching one *off* would otherwise leave a second `true` behind until\n // the next baseline pass.\n if (this.selectionValue === \"single\") {\n for (const other of this.rowTargets) {\n if (other !== row) setAttributeIfChanged(other, \"aria-selected\", \"false\");\n }\n }\n row.setAttribute(\"aria-selected\", selected ? \"false\" : \"true\");\n this.#dropOwnRecords(pageWrote);\n\n // Settled before anything is reported, so a listener that moves the rows\n // again is measured against this selection.\n const selection = this.#selection();\n const changed = !sameSelection(selection, this.#settled);\n this.#settled = selection;\n if (!changed) {\n this.#mirrorFields(false);\n return;\n }\n const token = this.#moves.record();\n this.#mirrorFields(true);\n if (!this.#moves.isLatest(token)) return;\n this.dispatch(\"selectionchange\", { detail: { rows: [...selection.keys()], reason } });\n }\n\n /** The selected rows in DOM order, each with the `data-value` it submits, if any. */\n #selection(): Selection {\n return new Map(\n this.rowTargets\n .filter((row) => row.getAttribute(\"aria-selected\") === \"true\")\n .map((row) => [row, row.dataset.value] as const),\n );\n }\n\n /**\n * Watches the rows' `aria-selected` and `data-value`. Every write the grid makes\n * to them sits between a take and a drop of the observer's queue, so each record\n * the callback receives is the page's, and only a record on one of the grid's own\n * rows schedules a pass.\n */\n #observeRows(): void {\n const observer = new MutationObserver((records) => {\n if (this.#concernsRows(records)) this.#reconcile.schedule();\n });\n observer.observe(this.element, {\n subtree: true,\n attributes: true,\n attributeFilter: OBSERVED_ATTRIBUTES,\n });\n this.#observer = observer;\n }\n\n /**\n * Empties the observer's queue before the grid writes to the rows, and says\n * whether the page had queued anything about them. Those records still owe a\n * pass: a toggle in `multiple` mode rewrites only its own row, so it does not fold\n * in what the page wrote to the others.\n */\n #takePageRecords(): boolean {\n const records = this.#observer?.takeRecords() ?? [];\n return records.length > 0 && this.#concernsRows(records);\n }\n\n /**\n * Whether any record is about one of this grid's own rows. A widget nested in a\n * cell writes the same attributes on its own elements, and those are no reason\n * to reconcile the grid.\n */\n #concernsRows(records: readonly MutationRecord[]): boolean {\n const rows = new Set<Node>(this.rowTargets);\n return records.some((record) => rows.has(record.target));\n }\n\n /**\n * Drops the records the grid's own writes just queued, so the observer never\n * takes them for the page's, then schedules the pass the page's records owe.\n */\n #dropOwnRecords(pageWrote: boolean): void {\n this.#observer?.takeRecords();\n if (pageWrote) this.#reconcile.schedule();\n }\n\n /**\n * Mirrors the selected rows' `data-value` into the optional fields container.\n * A selected row without one submits nothing, so a grid whose rows carry no\n * value submits an empty set rather than a row of blanks.\n *\n * @stimeoRenderRoot\n */\n #mirrorFields(notify: boolean): void {\n if (!this.hasFieldsTarget) return;\n const values = this.rowTargets\n .filter((row) => row.getAttribute(\"aria-selected\") === \"true\")\n .map((row) => row.dataset.value)\n .filter((value): value is string => value !== undefined);\n const options = { name: this.nameValue, form: this.formValue };\n if (writeFields(this.fieldsTarget, values, options) && notify) {\n commitField(this.fieldsTarget);\n }\n }\n\n /**\n * Makes `cell` the single tabbable cell (roving) and optionally focuses it.\n *\n * `cells` lets a caller that already walked the grid hand its collection over,\n * so one keystroke rebuilds the matrix once instead of twice. The write is\n * skipped where the attribute already holds the wanted value — comparing the\n * attribute rather than the IDL property, because a cell with no `tabindex` at\n * all reports `-1` and would then never receive the attribute it needs to be\n * focusable.\n */\n #setActiveCell(\n cell: HTMLElement,\n { focus }: { focus: boolean },\n cells?: readonly HTMLElement[],\n ): void {\n this.#stopHolder = cell;\n for (const candidate of cells ?? this.#navigableCells()) {\n const wanted = candidate === cell ? \"0\" : \"-1\";\n if (candidate.getAttribute(\"tabindex\") !== wanted) {\n candidate.setAttribute(\"tabindex\", wanted);\n }\n }\n if (focus) cell.focus();\n }\n\n /** All navigable elements (headers + cells) in DOM order. */\n #navigableCells(): HTMLElement[] {\n return this.#matrix().flat();\n }\n\n /** The grid as rows of navigable cells, derived from each `role=\"row\"`. */\n #matrix(): HTMLElement[][] {\n const navigable = new Set<HTMLElement>([...this.columnHeaderTargets, ...this.cellTargets]);\n const rows = Array.from(this.element.querySelectorAll<HTMLElement>(\"[role='row']\"));\n return rows\n .map((row) =>\n Array.from(row.children).filter((child): child is HTMLElement =>\n navigable.has(child as HTMLElement),\n ),\n )\n .filter((cells) => cells.length > 0);\n }\n\n /** Finds `[rowIndex, colIndex]` of `cell` within `matrix`, or null. */\n #locate(matrix: HTMLElement[][], cell: HTMLElement): [number, number] | null {\n for (let row = 0; row < matrix.length; row++) {\n const col = (matrix[row] ?? []).indexOf(cell);\n if (col !== -1) return [row, col];\n }\n return null;\n }\n\n /** The cell at `[row, col]`, clamping `col` to that row's last cell. */\n #cellInRow(matrix: HTMLElement[][], row: number, col: number): HTMLElement | undefined {\n const cells = matrix[row];\n if (!cells || cells.length === 0) return undefined;\n return cells[Math.min(col, cells.length - 1)];\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/state_reason.ts","../../src/utils/action_source.ts","../../src/utils/logical_scroll.ts","../../src/utils/arrow_step.ts","../../src/utils/attribute_write.ts","../../src/utils/authored_mutation_watcher.ts","../../src/utils/field_mirror.ts","../../src/utils/interactive_host.ts","../../src/utils/listener_set.ts","../../src/utils/microtask_coalescer.ts","../../src/utils/morph_render_watcher.ts","../../src/utils/move_counter.ts","../../src/utils/target_selector.ts","../../src/controllers/data_grid_controller.ts"],"names":["pending"],"mappings":"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","file":"data_grid_controller.js","sourcesContent":["/**\n * Why a component's public state moved, carried as `detail.reason`\n * on the state event that reports the move.\n *\n * The attribute a component publishes (`aria-expanded`, `hidden`, `data-state`)\n * only says *what* the state is now. A subscriber that mirrors the state\n * elsewhere, or reports it, needs *why*: closing on `Escape` and closing because\n * the consumer called the action are the same attribute write and different\n * events to the page around it.\n *\n * `\"api\"` is the one every subscriber has to look at. A close the consumer asked\n * for arrives back at their own listener, so a listener that closes something\n * else on `close` loops unless it ignores its own calls.\n *\n * | Reason | The state moved because |\n * | --- | --- |\n * | `\"user\"` | a control of this component was operated |\n * | `\"select\"` | an item inside it was activated |\n * | `\"escape\"` | `Escape` was pressed and this layer owned it |\n * | `\"outside\"` | a pointer landed outside it, or on its backdrop |\n * | `\"focus\"` | focus entered or left it |\n * | `\"pointer\"` | the pointer entered or left it |\n * | `\"scroll\"` | a tracked scroll container scrolled |\n * | `\"api\"` | a public action was called with no DOM event |\n * | `\"timeout\"` | an automatic timer advanced or dismissed the state |\n * | `\"limit\"` | a capacity limit dismissed an item |\n * | `\"media\"` | an operating-system media preference changed |\n */\nexport type StateReason =\n | \"user\"\n | \"select\"\n | \"escape\"\n | \"outside\"\n | \"focus\"\n | \"pointer\"\n | \"scroll\"\n | \"api\"\n | \"timeout\"\n | \"limit\"\n | \"media\";\n\n/** Focus modality: the events a focus move delivers to an action. */\nconst FOCUS_EVENTS = new Set([\"blur\", \"focus\", \"focusin\", \"focusout\"]);\n\n/** Pointer modality: the crossing events an action is bound to for hover. */\nconst POINTER_EVENTS = new Set([\"mouseenter\", \"mouseleave\", \"pointerenter\", \"pointerleave\"]);\n\n/**\n * Reads the interaction behind a **public action** from the DOM event it was\n * handed.\n *\n * Stimulus always passes the event to an action it invoked from `data-action`,\n * and a consumer calling the method themselves passes nothing — which is what\n * separates `\"api\"` from the rest.\n *\n * Only an action entry may resolve a reason this way. Inside the component the\n * event type no longer identifies the interaction: one `click` is the trigger\n * being pressed, the page outside being pressed, and an item being activated,\n * and those paths pass their own reason instead.\n *\n * @param event - The event the action received, if any.\n * @returns `\"api\"` with no event, else the modality the event type names, else\n * `\"user\"`.\n *\n * @example\n * ```ts\n * toggle(event?: Event): void {\n * this.#apply(!this.#isOpen, stateReasonFor(event));\n * }\n * ```\n */\nexport function stateReasonFor(event?: Event | null): \"user\" | \"focus\" | \"pointer\" | \"api\" {\n if (!event) return \"api\";\n if (FOCUS_EVENTS.has(event.type)) return \"focus\";\n if (POINTER_EVENTS.has(event.type)) return \"pointer\";\n return \"user\";\n}\n","import { stateReasonFor } from \"./state_reason\";\n\n/** The DOM binding and origin of a public action, or its explicit API element. */\nexport interface ActionSource {\n readonly event: Event | null;\n readonly host: Element | null;\n readonly origin: Element | null;\n readonly reason: ReturnType<typeof stateReasonFor>;\n}\n\n/**\n * Normalizes an action's DOM event or explicit element without choosing an owned target.\n *\n * Event hosts and origins stay separate so consumers can yield to nested controls.\n * An explicit element supplies both, with no DOM event and an `\"api\"` reason.\n * Consumers resolve current targets and enforce their own scope and interaction rules.\n *\n * @param source - The DOM event, explicit element, or absent optional argument.\n * @returns The source elements, original event and public interaction reason.\n */\nexport function actionSource(source?: Event | HTMLElement): ActionSource {\n if (source instanceof HTMLElement) {\n return { event: null, host: source, origin: source, reason: \"api\" };\n }\n const event = source ?? null;\n return {\n event,\n host: event?.currentTarget instanceof Element ? event.currentTarget : null,\n origin: event?.target instanceof Element ? event.target : null,\n reason: stateReasonFor(event),\n };\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/**\n * The element whose writing direction orders a grid's columns: the nearest\n * `<table>` holding `grid` inside `root`, else `grid` itself. Table columns follow\n * the table's direction, so a direction declared only on a row group such as\n * `<tbody>` does not reorder them and must not reverse the arrows either.\n */\nexport function gridDirectionElement(grid: Element, root: Element): Element {\n const table = grid.closest(\"table\");\n return table !== null && root.contains(table) ? table : grid;\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** — for example `Alt+Down`/`Alt+Up`, which the APG Combobox\n * pattern makes optional and `stimeo--combobox` implements.\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/**\n * True when a press carries Alt, Control, Meta, or Shift, regardless of its key.\n *\n * This predicate reports modifier presence only; key ownership stays with the\n * caller. Check the widget's implemented key/modifier bindings and their state\n * conditions before using this result to yield a chord. An unimplemented chord\n * must not fall through to the bare-key behavior.\n *\n * Dedicated bindings, such as a grid's Control+Home/End navigation, belong in\n * the caller. Unlike {@link isReservedArrowChord}, this function examines every\n * key and has no arrow-specific reservation or `allow` list.\n *\n * @example\n * ```ts\n * // A Home/End handler with no modifier bindings yields every modified press.\n * case \"Home\":\n * case \"End\":\n * if (hasModifierChord(event)) return;\n * ```\n */\nexport function hasModifierChord(event: KeyboardEvent): boolean {\n return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;\n}\n","/**\n * Reflects a serialized attribute value only when the current value differs.\n *\n * A same-value `setAttribute` still queues a mutation record for every observer, so an\n * unchanged value writes nothing.\n *\n * @param element - The element whose attribute is reflected.\n * @param name - The attribute name, using the non-namespaced DOM API.\n * @param value - The serialized value, or null to remove the attribute.\n * @param beforeWrite - Called only for a change, before the DOM mutation, so the\n * caller can record ownership or identify its own observer records.\n * @returns Whether an attribute was written or removed.\n */\nexport function setAttributeIfChanged(\n element: Element,\n name: string,\n value: string | null,\n beforeWrite?: () => void,\n): boolean {\n if (element.getAttribute(name) === value) return false;\n beforeWrite?.();\n if (value === null) element.removeAttribute(name);\n else element.setAttribute(name, value);\n return true;\n}\n","/** The retained attributes, descendants, and inherited state one connection watches. */\nexport interface AuthoredMutationOptions {\n readonly attributes: readonly string[];\n readonly subtree?: boolean;\n readonly childList?: boolean;\n readonly ancestorAttributes?: readonly string[];\n}\n\n/** A final attribute value and the number of records its writes produce. */\ninterface AttributeClaim {\n value: string | null;\n count: number;\n}\n\n/** Added and removed nodes still represented by controller-owned child-list records. */\ninterface ChildClaim {\n readonly added: Map<Node, number>;\n readonly removed: Map<Node, number>;\n}\n\n/** Pending claims belong only to the active observed lifetime. */\ninterface MutationClaims {\n readonly attributes: Map<Element, Map<string, AttributeClaim>>;\n readonly children: Map<Node, ChildClaim>;\n}\n\n/**\n * Watches authored mutations without suspending observation during DOM repair.\n *\n * The consumer claims only actual writes, before an attribute write or immediately\n * after a synchronous helper writes. Matching uses the final value and one claim\n * per record, so an extra same-value page write remains authored. Child-list claims\n * identify the nodes a replacement adds and removes, independently of how the DOM\n * implementation splits those nodes into records. Every delivery or synchronous\n * take expires all claims before the consumer can move focus or notify subscribers.\n * Ownership, reconciliation, and DOM writes remain the consumer's responsibility.\n */\nexport class AuthoredMutationWatcher {\n readonly #onAuthored: (records: MutationRecord[]) => void;\n #claims: MutationClaims | null = null;\n #observer: MutationObserver | null = null;\n\n /** @param onAuthored - Receives a nonempty batch after controller records are excluded. */\n constructor(onAuthored: (records: MutationRecord[]) => void) {\n this.#onAuthored = onAuthored;\n }\n\n /** Starts a new connected lifetime, including the root's current ancestor chain. */\n observe(root: Element, options: AuthoredMutationOptions): void {\n this.disconnect();\n const observer = new MutationObserver((records) => {\n if (this.#observer !== observer) return;\n const authored = this.#filter(records);\n if (authored.length > 0) this.#onAuthored(authored);\n });\n this.#observer = observer;\n this.#claims = { attributes: new Map(), children: new Map() };\n observer.observe(root, {\n attributes: true,\n attributeFilter: [...options.attributes],\n subtree: options.subtree ?? false,\n childList: options.childList ?? false,\n });\n if (options.ancestorAttributes) {\n let ancestor = root.parentElement;\n while (ancestor) {\n observer.observe(ancestor, {\n attributes: true,\n attributeFilter: [...options.ancestorAttributes],\n });\n ancestor = ancestor.parentElement;\n }\n }\n }\n\n /** Claims one actual attribute write; calls outside observation own no records. */\n claim(target: Element, name: string, value: string | null): void {\n const claims = this.#claims?.attributes;\n let attributes = claims?.get(target);\n if (!attributes) {\n attributes = new Map();\n claims?.set(target, attributes);\n }\n const pending = attributes.get(name);\n attributes.set(name, { value, count: (pending?.count ?? 0) + 1 });\n }\n\n /** Claims a synchronous helper's actual attribute write before any external callback runs. */\n claimCurrent(target: Element, name: string): void {\n this.claim(target, name, target.getAttribute(name));\n }\n\n /** Claims the actual nodes added and removed by one synchronous child replacement. */\n claimChildList(target: Node, added: readonly Node[], removed: readonly Node[]): void {\n const claims = this.#claims?.children;\n const pending = claims?.get(target) ?? { added: new Map(), removed: new Map() };\n for (const node of added) pending.added.set(node, (pending.added.get(node) ?? 0) + 1);\n for (const node of removed) pending.removed.set(node, (pending.removed.get(node) ?? 0) + 1);\n claims?.set(target, pending);\n }\n\n /** Returns queued authored records synchronously and expires every pending claim. */\n take(): MutationRecord[] {\n return this.#filter(this.#observer?.takeRecords() ?? []);\n }\n\n /** Releases observation and claims; safe to repeat and reuse on a later connection. */\n disconnect(): void {\n this.#observer?.disconnect();\n this.#observer = null;\n this.#claims = null;\n }\n\n /** Applies one batch's claims before handing control back to the consumer. */\n #filter(records: MutationRecord[]): MutationRecord[] {\n const authored = records.filter((record) => !this.#consume(record));\n this.#claims?.attributes.clear();\n this.#claims?.children.clear();\n return authored;\n }\n\n /**\n * Matches the native attribute or nonempty child-list records this watcher registers.\n * @see https://dom.spec.whatwg.org/#queue-a-tree-mutation-record\n */\n #consume(record: MutationRecord): boolean {\n if (record.type === \"attributes\") {\n const target = record.target as Element;\n const pending = this.#claims?.attributes.get(target)?.get(record.attributeName as string);\n if (\n !pending ||\n pending.count === 0 ||\n target.getAttribute(record.attributeName as string) !== pending.value\n ) {\n return false;\n }\n pending.count -= 1;\n return true;\n }\n const pending = this.#claims?.children.get(record.target);\n if (!pending) return false;\n const added = [...record.addedNodes];\n const removed = [...record.removedNodes];\n if (\n added.some((node) => !pending.added.get(node)) ||\n removed.some((node) => !pending.removed.get(node))\n ) {\n return false;\n }\n for (const node of added) pending.added.set(node, (pending.added.get(node) ?? 0) - 1);\n for (const node of removed) pending.removed.set(node, (pending.removed.get(node) ?? 0) - 1);\n return true;\n }\n}\n","/**\n * The hidden form fields a widget keeps in step with the state it owns.\n *\n * A widget whose value lives in ARIA attributes submits nothing on its own, so\n * it mirrors that value into `<input type=\"hidden\">` — one field for a single\n * value, a container of generated fields for a set. The mirror is one-way: the\n * widget's own state stays the source of truth and the field is what the form\n * reads.\n *\n * The write and the report are separate calls because they answer to different\n * rules. A mirror is refreshed on connect, after a morph, and whenever the\n * widget repairs itself — none of which a form should treat as an edit. Only\n * the user's own commit reports one, which is why {@link commitField} is never\n * folded into the writes.\n */\n\n/** How a widget's generated fields identify themselves to the form. */\nexport interface GeneratedFieldOptions {\n /** The `name` every generated input submits under. */\n readonly name: string;\n /** The `form` id, for a widget that sits outside the form it submits to. */\n readonly form?: string;\n}\n\n/**\n * Mirrors one value into a hidden field.\n *\n * @returns Whether the field's value moved.\n *\n * @example\n * ```ts\n * if (writeField(this.fieldTarget, String(value)) && userDriven) {\n * commitField(this.fieldTarget);\n * }\n * ```\n */\nexport function writeField(field: HTMLInputElement, value: string): boolean {\n if (field.value === value) return false;\n field.value = value;\n return true;\n}\n\n/**\n * Mirrors a set of values into `container` as generated hidden fields, in the\n * order given.\n *\n * A container that already submits exactly this set is left untouched, children\n * and all — the mirror is refreshed far more often than it moves (every connect,\n * morph and repair), and a rebuild that changes nothing is still a DOM mutation\n * a consumer's observer and a `:empty` rule can see.\n *\n * @returns Whether the submitted set moved — its values, their order, the\n * `name`, or the `form`.\n */\nexport function writeFields(\n container: HTMLElement,\n values: readonly string[],\n { name, form = \"\" }: GeneratedFieldOptions,\n): boolean {\n const current = [...container.children];\n const submits =\n current.length === values.length &&\n current.every(\n (child, index) =>\n child instanceof HTMLInputElement &&\n child.type === \"hidden\" &&\n child.value === values[index] &&\n child.name === name &&\n (child.getAttribute(\"form\") ?? \"\") === form,\n );\n if (submits) return false;\n\n container.replaceChildren(\n ...values.map((value) => {\n const input = document.createElement(\"input\");\n input.type = \"hidden\";\n input.name = name;\n input.value = value;\n if (form !== \"\") input.setAttribute(\"form\", form);\n return input;\n }),\n );\n return true;\n}\n\n/**\n * Reports a committed value from `target` the way a native form control does:\n * one bubbling, non-cancelable `change`.\n *\n * Call it only for a move the user made. Form-level behaviors listen for this —\n * validation re-checks, auto-submit — and a mirror refreshed by a morph or a\n * repair is not something the user submitted.\n *\n * `target` is the element that carries the value: the hidden field for a single\n * mirrored value, the container for a generated set (whose own inputs are\n * replaced on every write), or a widget's own native control where that is what\n * the form reads.\n */\nexport function commitField(target: HTMLElement): void {\n target.dispatchEvent(new Event(\"change\", { bubbles: true }));\n}\n","/**\n * Native interactive elements whose built-in activation must not be replaced\n * wholesale by a controller. Deliberately excludes `[tabindex]`: a generic\n * element made focusable is the supported host for several headless patterns.\n * Contenteditable is handled by {@link isInteractiveHost} because its state is\n * inherited and its keywords are ASCII-case-insensitive.\n */\nexport const INTERACTIVE_HOST_SELECTOR =\n \"button, input, select, textarea, label, a[href], area[href], summary, details, audio[controls], video[controls], iframe, object, embed\";\n\n/**\n * Whether an element owns native activation or an inherited editing surface.\n *\n * The `contenteditable` missing and invalid states inherit. Walking explicitly\n * also keeps `contenteditable=\"false\"` as a real boundary inside an editable\n * ancestor instead of treating any ancestor attribute as decisive.\n */\nexport function isInteractiveHost(element: HTMLElement): boolean {\n if (element.matches(INTERACTIVE_HOST_SELECTOR)) return true;\n\n let current: HTMLElement | null = element;\n while (current) {\n const raw = current.getAttribute(\"contenteditable\");\n if (raw !== null) {\n const value = raw.trim().toLowerCase();\n if (value === \"false\") return false;\n if (value === \"\" || value === \"true\" || value === \"plaintext-only\") return true;\n }\n current = current.parentElement;\n }\n return false;\n}\n","/** What {@link ListenerSet.add} passes on besides the signal it supplies itself. */\nexport type ListenerOptions = Omit<AddEventListenerOptions, \"signal\">;\n\n/**\n * The event map a target's own `addEventListener` types its handlers with: the\n * window's, a document's, an HTML or SVG element's, any other element's, or a\n * media query list's. Other targets name none.\n */\ntype EventMapOf<T extends EventTarget> = T extends Window\n ? WindowEventMap\n : T extends Document\n ? DocumentEventMap\n : T extends HTMLElement\n ? HTMLElementEventMap\n : T extends SVGElement\n ? SVGElementEventMap\n : T extends Element\n ? ElementEventMap\n : T extends MediaQueryList\n ? MediaQueryListEventMap\n : Record<never, never>;\n\n/**\n * Owns every listener a controller attaches for one connected lifetime and\n * releases them together, so an event name is spelled once and the release\n * cannot drift from the registration.\n *\n * Detaching by hand repeats three things at the release site — the type, the\n * handler reference and the capture flag — and any one of them drifting leaves\n * a listener alive on an element the controller no longer drives. Aborting the\n * signal a registration was created with releases it whatever it was made of,\n * so the release site names nothing.\n *\n * One set per lifetime, held as a field and never rebuilt.\n * {@link ListenerSet.dispose} is the only thing that closes a generation, and it opens\n * the next one in the same step, so the lifecycle hooks can call `add` and\n * `dispose` in any order any number of times, and no `add` ever meets an\n * already aborted signal — a DOM that registers a listener on such a signal\n * would keep it forever. Rebuilding the set instead would leak:\n * `addEventListener` matches an existing registration on target, type, callback\n * and capture alone, so re-adding the same handler while the first registration\n * is live is discarded, and that live registration keeps the signal it was\n * attached with — a set built by a second `connect()` can never release what it\n * appears to own.\n *\n * Scope is the registration only. Timers, observers, leases and the order they\n * are unwound in stay with the controller, which places `dispose()` in its\n * cleanup sequence. A receiver that changes at runtime (a\n * swapped target) is rebound by calling `dispose()` and then `add()` again from\n * the rebind, not by a second set.\n *\n * A lifetime shorter than the connection — while a Value holds, until a first\n * load, while a subscription is open — gets a set of its own, which the end of\n * that lifetime disposes. A controller therefore holds one set per lifetime: the\n * connection's, and one for each swapped receiver or shorter span.\n *\n * The set must own every registration of a tuple it holds: a listener the same\n * target already carries for the same type, callback and capture flag keeps the\n * right to remove it, and this set's `add` for that tuple is discarded. A\n * subscription shared between instances behind a participant count, listeners of\n * several elements that come and go one at a time, and those one gesture holds\n * keep their own pair of calls or a signal of their own.\n *\n * @example\n * ```ts\n * readonly #listeners = new ListenerSet();\n *\n * connect(): void {\n * this.#listeners.add(this.element, \"submit\", this.#onNativeSubmit, { capture: true });\n * this.#listeners.add(this.element, \"turbo:submit-start\", this.#onSubmitStart);\n * this.#listeners.add(this.element, \"turbo:submit-end\", this.#onSubmitEnd);\n * }\n *\n * disconnect(): void {\n * this.#listeners.dispose();\n * }\n * ```\n */\nexport class ListenerSet {\n /** The generation every `add` joins until the next `dispose()`. */\n #abort = new AbortController();\n\n /**\n * Attaches `handler` to the open generation, exactly as the caller spelled it.\n *\n * The set supplies the signal, so `options` carries everything else the DOM\n * accepts — `capture` included. A removal by hand would have to repeat that\n * flag; aborting the signal releases the listener without it.\n *\n * A `type` the target's own event map names types `handler` with that event,\n * as the target's `addEventListener` does, so a handler written for a\n * `KeyboardEvent` is passed as it is; any other `type` takes an\n * `EventListener`.\n */\n add<T extends EventTarget, K extends keyof EventMapOf<T> & string>(\n target: T,\n type: K,\n handler: (event: EventMapOf<T>[K]) => void,\n options?: ListenerOptions,\n ): void;\n add(target: EventTarget, type: string, handler: EventListener, options?: ListenerOptions): void;\n add(target: EventTarget, type: string, handler: EventListener, options?: ListenerOptions): void {\n target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });\n }\n\n /**\n * Releases every listener of the open generation, synchronously, and opens the\n * next one. Idempotent, and safe before anything has been added.\n */\n dispose(): void {\n this.#abort.abort();\n this.#abort = new AbortController();\n }\n}\n","/**\n * Collapses many Stimulus lifecycle callbacks from one DOM mutation into a\n * single pass.\n *\n * Stimulus fires `<name>TargetConnected` / `Disconnected` once per element and\n * `<name>ValueChanged` once per changed attribute. Replacing a list of N options\n * or morphing several render Values therefore delivers N callbacks — but the\n * useful unit of work is \"reconcile against the resulting declarative input\",\n * once, after the batch has settled. Every controller with reconcilable targets\n * or render Values needs the same 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 initial target and Value callbacks\n * ahead of `connect()`. Reconciling there would compute output 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 * @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 /** Whether a requested pass has yet to run. */\n get pending(): boolean {\n return this.#queued;\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","import { ListenerSet } from \"./listener_set\";\nimport { MicrotaskCoalescer } from \"./microtask_coalescer\";\n\n/**\n * Coalesces retained-element morphs and explicit lifecycle requests into one pass.\n *\n * Observe the controller's element after its initial state is settled. Morphs on\n * that element and bubbling morphs on its descendants share the same microtask\n * as {@link schedule}. The callback repairs derived output; the controller owns\n * any logical transition reporting. {@link disconnect} releases the listener and\n * cancels pending work, including work from an earlier connected lifetime.\n */\nexport class MorphRenderWatcher {\n readonly #listeners = new ListenerSet();\n readonly #pass: MicrotaskCoalescer;\n #element: Element | null = null;\n readonly #onMorph = (): void => this.#pass.schedule();\n\n /** @param run - the single pass that repairs the settled controller's output. */\n constructor(run: () => void) {\n this.#pass = new MicrotaskCoalescer(run);\n }\n\n /** Opens observation; observing the same element again preserves queued work. */\n observe(element: Element): void {\n if (this.#element === element) return;\n this.disconnect();\n this.#element = element;\n this.#pass.activate();\n this.#listeners.add(element, \"turbo:morph-element\", this.#onMorph);\n }\n\n /** Requests one pass; requests outside an observed lifetime are ignored. */\n schedule(): void {\n this.#pass.schedule();\n }\n\n /** Whether a pass requested by a morph or {@link schedule} has yet to run. */\n get pending(): boolean {\n return this.#pass.pending;\n }\n\n /** Releases observation and pending work synchronously; safe to repeat. */\n disconnect(): void {\n this.#listeners.dispose();\n this.#pass.cancel();\n this.#element = null;\n }\n}\n","/**\n * Identifies the latest reported state transition of one owner.\n *\n * Record a transition after its state is written and before invoking external\n * code. Check the returned token before each later report: a synchronous\n * transition made by a listener supersedes reports that have not been sent.\n * Reading state or committing an unchanged state must not advance the counter.\n * Reports stay in the caller so their ordering remains explicit.\n */\nexport class MoveCounter {\n #sequence = 0;\n\n /** Records a state transition and returns its report token. */\n record(): number {\n this.#sequence += 1;\n return this.#sequence;\n }\n\n /** Whether no later state transition has superseded this report token. */\n isLatest(token: number): boolean {\n return token === this.#sequence;\n }\n}\n","/**\n * The attribute selector for one declared target, in a controller's own namespace.\n *\n * Stimulus reads `data-<identifier>-target` as a **space-separated token list**, so a\n * single element can carry several target names at once. Matching the attribute value\n * whole would reach only the elements that declare exactly one name, which is why the\n * name is paired with `~=`.\n *\n * `identifier` is the name the controller is registered under, so the selector reaches\n * the markup that registration renders rather than one fixed spelling.\n */\nexport function targetSelector(identifier: string, name: string): string {\n return `[data-${identifier}-target~=\"${name}\"]`;\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { actionSource } from \"../utils/action_source\";\nimport { isReservedArrowChord, logicalArrowKey } from \"../utils/arrow_step\";\nimport { setAttributeIfChanged } from \"../utils/attribute_write\";\nimport { AuthoredMutationWatcher } from \"../utils/authored_mutation_watcher\";\nimport { commitField, writeFields } from \"../utils/field_mirror\";\nimport { INTERACTIVE_HOST_SELECTOR, isInteractiveHost } from \"../utils/interactive_host\";\nimport { MorphRenderWatcher } from \"../utils/morph_render_watcher\";\nimport { MoveCounter } from \"../utils/move_counter\";\nimport type { StateReason } from \"../utils/state_reason\";\nimport { targetSelector } from \"../utils/target_selector\";\n\n/** Cycle order for a sortable column header's `aria-sort`. */\nconst SORT_CYCLE = [\"none\", \"ascending\", \"descending\"] as const;\ntype SortDirection = (typeof SORT_CYCLE)[number];\n\n/** Row attributes a page can rewrite in place that move the published selection. */\nconst OBSERVED_ATTRIBUTES = [\"aria-selected\", \"data-value\"];\n\n/** The published selection: each selected row with the value it submits. */\ntype Selection = ReadonlyMap<HTMLElement, string | undefined>;\n\n/** Whether two selections hold the same rows submitting the same values, in any order. */\nfunction sameSelection(a: Selection, b: Selection): boolean {\n if (a.size !== b.size) return false;\n for (const [row, value] of a) {\n if (!b.has(row) || b.get(row) !== value) return false;\n }\n return true;\n}\n\n/**\n * Returns the next `aria-sort` direction in the cycle after `current`. Unknown or\n * ARIA-only values (e.g. `\"other\"`, or an empty/missing attribute) are treated as\n * `\"none\"`, so the first activation always advances to `\"ascending\"` instead of\n * stalling.\n */\nfunction nextSortDirection(current: string): SortDirection {\n const index = SORT_CYCLE.indexOf(current as SortDirection);\n const from = index < 0 ? 0 : index;\n return SORT_CYCLE[(from + 1) % SORT_CYCLE.length] ?? \"ascending\";\n}\n\n/**\n * Headless, accessible **Data Grid** behavior: column sorting, row selection, and\n * roving keyboard navigation over an interactive table.\n *\n * Markup contract (identifier: `stimeo--data-grid`):\n * <table data-controller=\"stimeo--data-grid\" role=\"grid\" aria-label=\"Users\"\n * data-stimeo--data-grid-selection-value=\"single\"\n * data-stimeo--data-grid-name-value=\"user_id\">\n * <caption><div data-stimeo--data-grid-target=\"fields\"></div></caption>\n * <thead><tr role=\"row\">\n * <th role=\"columnheader\" aria-sort=\"none\" tabindex=\"-1\"\n * data-stimeo--data-grid-target=\"columnHeader\"\n * data-action=\"click->stimeo--data-grid#sort\n * keydown->stimeo--data-grid#onKeydown\">Name</th>\n * </tr></thead>\n * <tbody><tr role=\"row\" aria-selected=\"false\" data-value=\"7\"\n * data-stimeo--data-grid-target=\"row\">\n * <td role=\"gridcell\" tabindex=\"0\" data-stimeo--data-grid-target=\"cell\"\n * data-action=\"keydown->stimeo--data-grid#onKeydown\">Jane</td>\n * </tr></tbody>\n * </table>\n *\n * Implements the WAI-ARIA APG **Grid** pattern plus `aria-sort`. The whole grid is\n * a single Tab stop (roving `tabindex`: exactly one cell/header is `0`, the rest\n * `-1`); arrow keys move both DOM focus and that tabbable position. Sort state is\n * exposed via `aria-sort` on headers, selection via `aria-selected` on rows.\n *\n * `selectionchange` dispatches `{ rows: HTMLElement[], reason: StateReason }` whenever\n * the controller changes the selection itself, and `sort` dispatches\n * `{ column: HTMLElement, direction: \"ascending\" | \"descending\" | \"none\", reason: StateReason }`;\n * `reason` tells a user's choice from an `api` call.\n *\n * `reconcile` dispatches `{ rows: HTMLElement[] }` — the selected rows, as\n * `selectionchange` carries them — when the page moves the selection instead: a\n * selected row removed or dropped from the targets, a morph or a script writing\n * a row's `aria-selected` or a selected row's `data-value`, a selected row that\n * arrives ahead of the selection in `single` mode, or a runtime `selection` that\n * collapses it. One batch of such changes reports once, and only when the\n * selected rows or the values they submit differ from the selection last settled\n * — on connect, by a toggle (a user's choice or a `toggleSelect()` call), or by\n * the previous `reconcile` — so connecting reports nothing.\n *\n * With a `fields` target the selected rows are mirrored into `name`d hidden\n * inputs so the selection can be submitted and read server-side — one per\n * selected row that carries a `data-value`, in DOM order; a row without one\n * submits nothing. `form` points them at a `<form>` by id when the container\n * sits outside it. When a selection the controller changes — a user's choice or a\n * `toggleSelect()` call — moves the values the fields submit, the container emits\n * a native bubbling `change`, the way a form control does, so `stimeo--auto-submit`\n * and form-level validation hear it. The mirror is refreshed silently on connect,\n * on a replacement container, on row churn, and whenever `selection`, `name` or\n * `form` changes; a selection the page moved is reported by `reconcile` alone.\n *\n * User `selectionchange` reports compare the resulting state with the last published\n * state. A pending page write handled in the same script joins that confirmation;\n * a browser-delivered listener may settle it first as `reconcile`. Confirming\n * the last published state reports nothing.\n *\n * A synchronous subscriber that confirms another state replaces reports still\n * pending for the outer confirmation. Reading state or confirming it unchanged\n * does not replace them. An event already being dispatched cannot be recalled.\n *\n * @remarks\n * Behavior only — the consumer performs the actual data sort/render in response to\n * the `sort` event and owns all styling. While connected a `MutationObserver`\n * watches the rows' `aria-selected` and `data-value`, which a morph can rewrite\n * without adding or removing a target; `disconnect()` releases it and drops a\n * pending pass. `connect()` rebuilds the single tab stop idempotently from the\n * DOM, and the target callbacks rebuild it again after rows or cells are added or\n * removed at runtime.\n *\n * Behavior provided:\n * - `Arrow*` move between cells (clamped at edges); `Home`/`End` to the row's\n * first/last cell; `Ctrl+Home`/`Ctrl+End` to the grid's first/last cell.\n * - `Enter`/`Space` cycles a header's sort (`none→ascending→descending`) or toggles\n * the focused row's selection when selection is enabled.\n *\n * Consumer contract — controls nested inside a cell or header:\n * - APG's grid hosts working controls in its cells, so a keystroke or click that\n * reached one — a nested button, link or form control, an editable host, or any\n * widget that already called `preventDefault()` — is left to it entirely: no\n * move, no sort, no selection. The one control that hands the event on is a\n * sortable header's own `<button>`, whose activation is what the click carries.\n * - A control keeps its own Tab behavior, so give it `tabindex=\"-1\"` to preserve\n * the grid's single Tab stop. A sortable `columnheader` hosting a `<button>` is\n * the common case: the roving position stays on the header, and the button's\n * own activation reaches `sort` through the click that bubbles to it.\n * - Host `role=\"gridcell\"` / `role=\"columnheader\"` on a non-interactive element\n * (`td`, `th`); an interactive host makes the grid stand down on that cell.\n */\nexport class DataGridController extends Controller<HTMLElement> {\n readonly #moves = new MoveCounter();\n #connectionGeneration = 0;\n /** The cell this controller last gave the Tab stop. */\n #stopHolder: HTMLElement | null = null;\n static override targets = [\"columnHeader\", \"row\", \"cell\", \"fields\"];\n static override values = {\n selection: { type: String, default: \"none\" },\n name: { type: String, default: \"rows[]\" },\n form: { type: String, default: \"\" },\n };\n static actions = [\"onKeydown\", \"sort\", \"toggleSelect\"] as const;\n static events = [\"selectionchange\", \"sort\", \"reconcile\"] as const;\n\n declare readonly columnHeaderTargets: HTMLElement[];\n declare readonly rowTargets: HTMLElement[];\n declare readonly cellTargets: HTMLElement[];\n declare readonly fieldsTarget: HTMLElement;\n declare readonly hasFieldsTarget: boolean;\n declare selectionValue: string;\n declare nameValue: string;\n declare formValue: string;\n\n /**\n * Collapses the target callbacks, Value changes and observed row writes of one\n * DOM mutation into a single pass, and refuses to run before `connect()` or\n * after `disconnect()`.\n *\n * Stimulus reports every target one at a time, so an ungated pass would re-walk\n * the whole grid once per authored cell on mount and once per streamed cell\n * afterwards — quadratic in the cell count both times.\n */\n readonly #reconcile = new MorphRenderWatcher(() => this.#reconcileRows());\n /** Watches the row attributes a page can rewrite in place; set while connected. */\n readonly #observer = new AuthoredMutationWatcher((records) => {\n if (this.#concernsRows(records)) this.#reconcile.schedule();\n });\n /** The selection last settled: on connect, by a toggle, or by a reported pass. */\n #settled: Selection = new Map();\n\n /**\n * Establishes a single tab stop across all navigable cells/headers, brings the\n * rows to their baseline, and settles the selection without reporting it.\n *\n * The whole baseline runs here rather than in the Value callbacks: Stimulus\n * delivers those before `connect()`, where the pass is refused, and a\n * re-attached element reuses its cached context, whose value observer does not\n * fire again.\n */\n override connect(): void {\n this.#restoreBaseline();\n this.#settled = this.#selection();\n this.#reconcile.observe(this.element);\n this.#observeRows();\n }\n\n /** Releases the row observer and drops a queued pass, so neither outlives the element. */\n override disconnect(): void {\n this.#connectionGeneration += 1;\n this.#reconcile.disconnect();\n this.#observer.disconnect();\n }\n\n /**\n * Rebuilds the DOM-owned baselines from the live grid: exactly one navigable\n * cell is in the Tab sequence, `aria-multiselectable` follows `selection`, every\n * selectable row carries an explicit `aria-selected`, and the fields mirror the\n * selected rows.\n *\n * The tab stop keeps whichever cell already holds it, so a rebuild triggered by\n * an unrelated row arriving does not throw the user's position away — the cell\n * this controller gave it keeps it while it still carries it, even when an\n * arriving cell brings its own `tabindex=\"0\"` ahead of it in the grid; only when\n * no cell holds it — the grid is fresh, or the holder was removed — does the\n * first navigable cell take over. Without that fallback a grid whose active row\n * is removed keeps every cell at `-1` and drops out of the Tab sequence\n * entirely.\n */\n #restoreBaseline(): void {\n const cells = this.#navigableCells();\n const holder = this.#stopHolder;\n const kept = holder !== null && holder.tabIndex === 0 && cells.includes(holder);\n const active = (kept ? holder : cells.find((cell) => cell.tabIndex === 0)) ?? cells[0];\n if (active) this.#setActiveCell(active, { focus: false }, cells);\n this.#syncSelectable();\n const pageWrote = this.#takePageRecords();\n this.#normalizeSelection();\n this.#flushMutations(pageWrote);\n this.#mirrorFields(false);\n }\n\n /**\n * The pass the page's changes run: the baselines are rebuilt, then a selection\n * that moved is reported. The report comes last, so rows a subscriber rewrites\n * are the next pass's to settle.\n */\n #reconcileRows(): void {\n this.#restoreBaseline();\n this.#reportMove();\n }\n\n /**\n * Reports the selection as `reconcile` when it differs from the one last\n * settled. The settled selection is replaced before dispatching, so a move a\n * subscriber makes is measured against what it was told.\n */\n #reportMove(): void {\n const selection = this.#selection();\n if (sameSelection(selection, this.#settled)) return;\n this.#settled = selection;\n this.dispatch(\"reconcile\", { detail: { rows: [...selection.keys()] } });\n }\n\n /**\n * Re-renders `aria-multiselectable`, the rows and the fields when application\n * code (or a Turbo morph) changes `selection` at runtime; a selection the new\n * mode collapses is reported as `reconcile`.\n */\n selectionValueChanged(): void {\n this.#reconcile.schedule();\n }\n\n /** Rebuilds the submitted fields when the public name changes at runtime. */\n nameValueChanged(): void {\n this.#reconcile.schedule();\n }\n\n /** Repoints the submitted fields when the owning form changes at runtime. */\n formValueChanged(): void {\n this.#reconcile.schedule();\n }\n\n /** Seeds a fields container inserted after connect from the current selection. */\n fieldsTargetConnected(): void {\n this.#reconcile.schedule();\n }\n\n /** Brings the fields container that stays to the current selection when an earlier one leaves. */\n fieldsTargetDisconnected(): void {\n this.#reconcile.schedule();\n }\n\n /** Re-establishes the baselines for a row added after connect. */\n rowTargetConnected(): void {\n this.#reconcile.schedule();\n }\n\n /** Settles the selection again when a row leaves the grid or drops its target token. */\n rowTargetDisconnected(): void {\n this.#reconcile.schedule();\n }\n\n /** Re-establishes the tab stop when a cell joins the grid after connect. */\n cellTargetConnected(): void {\n this.#reconcile.schedule();\n }\n\n /** Re-establishes the tab stop when a cell leaves the grid. */\n cellTargetDisconnected(): void {\n this.#reconcile.schedule();\n }\n\n /** Re-establishes the tab stop when a header joins the grid after connect. */\n columnHeaderTargetConnected(): void {\n this.#reconcile.schedule();\n }\n\n /** Re-establishes the tab stop when a header leaves the grid. */\n columnHeaderTargetDisconnected(): void {\n this.#reconcile.schedule();\n }\n\n /**\n * Brings the authored rows to the shape the APG requires, without changing\n * which rows the author chose.\n *\n * Every selectable row gets an explicit value — an absent `aria-selected` means\n * \"not selectable\" in ARIA, so a forgotten attribute hides a selectable row —\n * and a single-select grid keeps at most one `true`, first in DOM order.\n * A grid that declares `selection=\"none\"` has no selectable rows, so the\n * attribute is removed rather than written: in ARIA its absence is what \"not\n * selectable\" looks like.\n *\n * @stimeoRenderRoot\n */\n #normalizeSelection(): void {\n const rows = this.rowTargets;\n if (this.selectionValue === \"none\") {\n // Reclaim, do not merely skip: a grid that *became* unselectable would\n // otherwise keep announcing rows as selected while the logic refuses to\n // change them. In ARIA the absence of the attribute is what \"not\n // selectable\" looks like, so the rows have to lose it outright.\n for (const row of rows) this.#writeSelection(row, null);\n return;\n }\n const single = this.selectionValue === \"single\";\n const first = rows.find((row) => row.getAttribute(\"aria-selected\") === \"true\");\n for (const row of rows) {\n if (single) {\n this.#writeSelection(row, row === first ? \"true\" : \"false\");\n } else if (row.getAttribute(\"aria-selected\") !== \"true\") {\n this.#writeSelection(row, \"false\");\n }\n }\n }\n\n /**\n * Mirrors `selection=\"multiple\"` onto `aria-multiselectable` (APG Grid) so SRs\n * announce that more than one row can be selected; cleared for single/none so a\n * grid never carries a misleading attribute.\n *\n * @stimeoRenderRoot\n */\n #syncSelectable(): void {\n if (this.selectionValue === \"multiple\") {\n setAttributeIfChanged(this.element, \"aria-multiselectable\", \"true\");\n } else {\n this.element.removeAttribute(\"aria-multiselectable\");\n }\n }\n\n /** Cycles an owned column header's sort from an event, the header, or its descendant. */\n sort(source: Event | HTMLElement): void {\n const { event, host, origin, reason } = actionSource(source);\n if (origin && origin.closest(`[data-controller~=\"${this.identifier}\"]`) !== this.element)\n return;\n const header = host?.closest<HTMLElement>(targetSelector(this.identifier, \"columnHeader\"));\n if (!header || !this.columnHeaderTargets.includes(header)) return;\n if (event?.defaultPrevented) return;\n // A sortable header hosts a `<button>`, and that button's activation is\n // exactly what this click carries, so it is the one control that does not\n // take the event away. A link or a field inside the header is its own\n // destination, and sorting on its click would act in parallel.\n const control = event ? this.#claimingControl(event, header) : null;\n if (control && !(control instanceof HTMLButtonElement)) return;\n\n const direction = nextSortDirection(header.getAttribute(\"aria-sort\") ?? \"none\");\n\n // Only one column is sorted at a time: reset the others to `none`.\n for (const other of this.columnHeaderTargets) {\n other.setAttribute(\"aria-sort\", other === header ? direction : \"none\");\n }\n\n this.#setActiveCell(header, { focus: false });\n this.dispatch(\"sort\", { detail: { column: header, direction, reason } });\n }\n\n /** Toggles an owned row from an action event, the row, or its descendant. */\n toggleSelect(source: Event | HTMLElement): void {\n const { event, host, origin, reason } = actionSource(source);\n if (origin && origin.closest(`[data-controller~=\"${this.identifier}\"]`) !== this.element)\n return;\n // The pointer path guards on `selection=\"none\"` exactly as the keyboard path\n // does, so a grid that declares itself unselectable never grows selected rows.\n if (this.selectionValue === \"none\") return;\n // A widget that handled the click owns it, exactly as the keyboard path\n // stands down on a keystroke a descendant consumed.\n if (event?.defaultPrevented) return;\n if (event && host instanceof HTMLElement && this.#claimedByDescendant(event, host)) return;\n const row = host?.closest<HTMLElement>(targetSelector(this.identifier, \"row\"));\n if (row && this.rowTargets.includes(row)) this.#toggleRow(row, reason);\n }\n\n /** Grid navigation + sort/select activation. Bound to cells and headers. */\n onKeydown(event: KeyboardEvent): void {\n // A descendant widget that already claimed the key (a grabbed drag handle, a\n // nested menu) must not ALSO act on it — composition depends on this yield.\n if (event.defaultPrevented) return;\n if (isReservedArrowChord(event)) return;\n if (event.isComposing) return;\n if (\n (event.key === \"Home\" || event.key === \"End\") &&\n (event.altKey || event.metaKey || event.shiftKey)\n ) {\n return;\n }\n const cell = event.currentTarget as HTMLElement;\n // A native control inside the cell never calls `preventDefault()` — its\n // activation IS the default action — so the yield above cannot see it. This\n // recognises those by element shape instead.\n if (this.#claimedByDescendant(event, cell)) return;\n const matrix = this.#matrix();\n const position = this.#locate(matrix, cell);\n if (!position) return;\n const [row, col] = position;\n const rowCells = matrix[row] ?? [];\n\n let target: HTMLElement | undefined;\n // Logical, not physical. The key is normalised rather than the\n // delta negated: these two branches are not mirror images — their guards\n // differ — so swapping the key keeps each guard with its own direction.\n switch (logicalArrowKey(event.key, this.element)) {\n case \"ArrowRight\":\n target = this.#cellInRow(matrix, row, col + 1);\n break;\n case \"ArrowLeft\":\n target = this.#cellInRow(matrix, row, Math.max(col - 1, 0));\n break;\n case \"ArrowDown\":\n target = this.#cellInRow(matrix, Math.min(row + 1, matrix.length - 1), col);\n break;\n case \"ArrowUp\":\n target = this.#cellInRow(matrix, Math.max(row - 1, 0), col);\n break;\n case \"Home\":\n target = event.ctrlKey ? this.#cellInRow(matrix, 0, 0) : rowCells[0];\n break;\n case \"End\":\n target = event.ctrlKey\n ? this.#cellInRow(matrix, matrix.length - 1, Number.POSITIVE_INFINITY)\n : rowCells[rowCells.length - 1];\n break;\n case \"Enter\":\n case \" \":\n this.#activate(cell);\n event.preventDefault();\n return;\n default:\n return;\n }\n\n if (target) {\n event.preventDefault();\n this.#setActiveCell(target, { focus: true }, matrix.flat());\n }\n }\n\n /**\n * Whether the event was addressed to a control inside `host` rather than to the\n * grid.\n *\n * Cells and headers hold consumer markup, and APG's grid pattern expects that\n * markup to include working controls — a row action button, an inline editor.\n * Those own their own keystrokes and clicks, so the grid stands down entirely\n * rather than acting in parallel. An editable host (its `contenteditable` state\n * is inherited, so the walk is explicit) counts the same way.\n */\n #claimedByDescendant(event: Event, host: HTMLElement): boolean {\n return this.#claimingControl(event, host) !== null;\n }\n\n /**\n * The nested control this event belongs to, or `null` when the host owns it.\n *\n * Naming the control, rather than answering yes or no, is what lets the click\n * path treat a sortable header's `<button>` as the activation it is while every\n * other control still takes the event away.\n */\n #claimingControl(event: Event, host: HTMLElement): HTMLElement | null {\n const source = event.target as HTMLElement;\n const control = source.closest<HTMLElement>(INTERACTIVE_HOST_SELECTOR);\n if (control && host.contains(control)) return control;\n return isInteractiveHost(source) ? source : null;\n }\n\n /**\n * Performs a header's sort or a cell row's selection toggle on activation.\n *\n * @stimeoRuntimeOnly `selection` decides whether this one activation toggles the row; the sort it\n * cycles does not depend on it.\n */\n #activate(cell: HTMLElement): void {\n if (this.columnHeaderTargets.includes(cell)) {\n this.#cycleSort(cell);\n return;\n }\n if (this.selectionValue === \"none\") return;\n const row = cell.closest<HTMLElement>(\"[role='row']\");\n if (row && this.rowTargets.includes(row)) this.#toggleRow(row);\n }\n\n /** Cycles a header's sort on keyboard activation and emits `sort`. */\n #cycleSort(header: HTMLElement): void {\n const direction = nextSortDirection(header.getAttribute(\"aria-sort\") ?? \"none\");\n for (const other of this.columnHeaderTargets) {\n other.setAttribute(\"aria-sort\", other === header ? direction : \"none\");\n }\n this.dispatch(\"sort\", { detail: { column: header, direction, reason: \"user\" } });\n }\n\n /**\n * Toggles a row's `aria-selected`, honoring single vs. multiple selection.\n *\n * @stimeoRuntimeOnly `selection` decides how this one toggle treats the other rows.\n */\n #toggleRow(row: HTMLElement, reason: StateReason = \"user\"): void {\n const generation = this.#connectionGeneration;\n const selected = row.getAttribute(\"aria-selected\") === \"true\";\n const pageWrote = this.#takePageRecords();\n // Enforce single-ness on every toggle, not only when turning a row on:\n // switching one *off* would otherwise leave a second `true` behind until\n // the next baseline pass.\n if (this.selectionValue === \"single\") {\n for (const other of this.rowTargets) {\n if (other !== row) this.#writeSelection(other, \"false\");\n }\n }\n this.#writeSelection(row, selected ? \"false\" : \"true\");\n this.#flushMutations(pageWrote);\n\n // Settled before anything is reported, so a listener that moves the rows\n // again is measured against this selection.\n const selection = this.#selection();\n const changed = !sameSelection(selection, this.#settled);\n this.#settled = selection;\n if (!changed) {\n this.#mirrorFields(false);\n return;\n }\n const token = this.#moves.record();\n this.#mirrorFields(true);\n if (generation !== this.#connectionGeneration || !this.#moves.isLatest(token)) return;\n this.dispatch(\"selectionchange\", { detail: { rows: [...selection.keys()], reason } });\n }\n\n /** The selected rows in DOM order, each with the `data-value` it submits, if any. */\n #selection(): Selection {\n return new Map(\n this.rowTargets\n .filter((row) => row.getAttribute(\"aria-selected\") === \"true\")\n .map((row) => [row, row.dataset.value] as const),\n );\n }\n\n /** Watches authored state on this component's targets without discarding page records. */\n #observeRows(): void {\n this.#observer.observe(this.element, {\n subtree: true,\n attributes: OBSERVED_ATTRIBUTES,\n });\n }\n\n /**\n * Empties the observer's queue before the grid writes to the rows, and says\n * whether the page had queued anything about them. Those records still owe a\n * pass: a toggle in `multiple` mode rewrites only its own row, so it does not fold\n * in what the page wrote to the others.\n */\n #takePageRecords(): boolean {\n const records = this.#observer.take();\n return records.length > 0 && this.#concernsRows(records);\n }\n\n /**\n * Whether any record is about one of this grid's own rows. A widget nested in a\n * cell writes the same attributes on its own elements, and those are no reason\n * to reconcile the grid.\n */\n #concernsRows(records: readonly MutationRecord[]): boolean {\n const rows = new Set<Node>(this.rowTargets);\n return records.some((record) => rows.has(record.target));\n }\n\n /** Expires own claims and reserves a pass for queued authored row changes. */\n #flushMutations(pageWrote: boolean): void {\n const authored = this.#concernsRows(this.#observer.take());\n if (pageWrote || authored) this.#reconcile.schedule();\n }\n\n /**\n * Mirrors the selected rows' `data-value` into the optional fields container.\n * A selected row without one submits nothing, so a grid whose rows carry no\n * value submits an empty set rather than a row of blanks.\n *\n * @stimeoRenderRoot\n */\n #mirrorFields(notify: boolean): void {\n if (!this.hasFieldsTarget) return;\n const values = this.rowTargets\n .filter((row) => row.getAttribute(\"aria-selected\") === \"true\")\n .map((row) => row.dataset.value)\n .filter((value): value is string => value !== undefined);\n const options = { name: this.nameValue, form: this.formValue };\n if (writeFields(this.fieldsTarget, values, options) && notify) {\n commitField(this.fieldsTarget);\n }\n }\n\n /**\n * Makes `cell` the single tabbable cell (roving) and optionally focuses it.\n *\n * `cells` lets a caller that already walked the grid hand its collection over,\n * so one keystroke rebuilds the matrix once instead of twice. The write is\n * skipped where the attribute already holds the wanted value — comparing the\n * attribute rather than the IDL property, because a cell with no `tabindex` at\n * all reports `-1` and would then never receive the attribute it needs to be\n * focusable.\n */\n #setActiveCell(\n cell: HTMLElement,\n { focus }: { focus: boolean },\n cells?: readonly HTMLElement[],\n ): void {\n this.#stopHolder = cell;\n for (const candidate of cells ?? this.#navigableCells()) {\n const wanted = candidate === cell ? \"0\" : \"-1\";\n setAttributeIfChanged(candidate, \"tabindex\", wanted);\n }\n if (focus) cell.focus();\n }\n\n /** All navigable elements (headers + cells) in DOM order. */\n #navigableCells(): HTMLElement[] {\n return this.#matrix().flat();\n }\n\n /** The grid as rows of navigable cells, derived from each `role=\"row\"`. */\n #matrix(): HTMLElement[][] {\n const navigable = new Set<HTMLElement>([...this.columnHeaderTargets, ...this.cellTargets]);\n const rows = Array.from(this.element.querySelectorAll<HTMLElement>(\"[role='row']\"));\n return rows\n .map((row) =>\n Array.from(row.children).filter((child): child is HTMLElement =>\n navigable.has(child as HTMLElement),\n ),\n )\n .filter((cells) => cells.length > 0);\n }\n\n /** Finds `[rowIndex, colIndex]` of `cell` within `matrix`, or null. */\n #locate(matrix: HTMLElement[][], cell: HTMLElement): [number, number] | null {\n for (let row = 0; row < matrix.length; row++) {\n const col = (matrix[row] ?? []).indexOf(cell);\n if (col !== -1) return [row, col];\n }\n return null;\n }\n\n /** The cell at `[row, col]`, clamping `col` to that row's last cell. */\n #cellInRow(matrix: HTMLElement[][], row: number, col: number): HTMLElement | undefined {\n const cells = matrix[row];\n if (!cells || cells.length === 0) return undefined;\n return cells[Math.min(col, cells.length - 1)];\n }\n /**\n * Records one changed selection attribute before this controller's mutation watcher\n * receives it.\n */\n #writeSelection(target: HTMLElement, value: string | null): void {\n setAttributeIfChanged(target, \"aria-selected\", value, () => {\n this.#observer.claim(target, \"aria-selected\", value);\n });\n }\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import { Controller } from '@hotwired/stimulus';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Headless, accessible **date range picker** behavior. A derivative of
|
|
5
|
-
*
|
|
5
|
+
* `stimeo--calendar`: it reuses the month-grid navigation model and adds
|
|
6
6
|
* two-point range selection, in-progress range preview, and presets.
|
|
7
7
|
*
|
|
8
8
|
* Markup contract (identifier: `stimeo--date-range-picker`):
|
|
@@ -36,8 +36,8 @@ import { Controller } from '@hotwired/stimulus';
|
|
|
36
36
|
* Behavior only — the consumer styles the grid and renders the range using the
|
|
37
37
|
* `data-range-start` / `data-in-range` / `data-range-end` hooks. Assistive tech
|
|
38
38
|
* is told the two confirmed endpoints via `aria-selected`; inner cells use
|
|
39
|
-
* `data-in-range` (visual only) so the announcement stays to the two ends.
|
|
40
|
-
* range
|
|
39
|
+
* `data-in-range` (visual only) so the announcement stays to the two ends. A
|
|
40
|
+
* confirmed range is also announced in the live `status` region.
|
|
41
41
|
*
|
|
42
42
|
* Selection model: the first click/Enter sets a *pending* start and enters
|
|
43
43
|
* "selecting" mode (preview follows the pointer/focus); the second confirms the
|
|
@@ -49,10 +49,12 @@ import { Controller } from '@hotwired/stimulus';
|
|
|
49
49
|
* before anything reports it. Each hidden field that moved then emits a native
|
|
50
50
|
* bubbling `change`, the way a form control does, so `stimeo--auto-submit` and
|
|
51
51
|
* form-level validation hear it; `monthchange` follows when the paint moved the
|
|
52
|
-
* month, and last `change` dispatches
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
52
|
+
* month, and last `change` dispatches
|
|
53
|
+
* `{ start: string, end: string, reason: StateReason }` — the ISO range the
|
|
54
|
+
* picker holds, with `reason` telling a user's choice from an `api` call. A bound
|
|
55
|
+
* a listener moves meanwhile is applied by the repaint its Value callback runs,
|
|
56
|
+
* so the `reconcile` it causes follows the `change`. A listener that replaces
|
|
57
|
+
* the range before these reports are all out
|
|
56
58
|
* — by confirming another range, or with a repaint that narrows this one — has
|
|
57
59
|
* the replacing range report itself: what is still pending for the replaced
|
|
58
60
|
* range is not sent, and focus stays where the replacing move put it. The seed
|
|
@@ -71,9 +73,10 @@ import { Controller } from '@hotwired/stimulus';
|
|
|
71
73
|
* or `disabledDates` leaves an end unselectable, the paint narrows the range:
|
|
72
74
|
* each end moves inward to the nearest day that can still be chosen, and a
|
|
73
75
|
* range with no such day left is cleared. The hidden fields follow without a
|
|
74
|
-
* native `change`, the status keeps announcing
|
|
76
|
+
* native `change`, the status keeps announcing the range last confirmed, and
|
|
75
77
|
* `reconcile` dispatches `{ start: string, end: string }` — the narrowed range,
|
|
76
|
-
* empty strings once cleared — rather than `change`, which
|
|
78
|
+
* empty strings once cleared — rather than `change`, which reports
|
|
79
|
+
* confirmations only.
|
|
77
80
|
* `connect()` narrows the range it reads from the fields the same way,
|
|
78
81
|
* silently. An in-progress selection whose start becomes unselectable is
|
|
79
82
|
* dropped, as Escape would drop it, and the next pick starts a new one.
|