stimeo-ui 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +209 -0
- package/README.md +128 -1
- package/dist/cable/index.d.ts +79 -23
- package/dist/cable/index.js +392 -417
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.d.ts +34 -5
- package/dist/controllers/accordion_controller.js +228 -28
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +20 -9
- package/dist/controllers/alert_dialog_controller.js +862 -221
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +38 -7
- package/dist/controllers/announcer_controller.js +135 -191
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/aspect_ratio_controller.js +0 -10
- package/dist/controllers/aspect_ratio_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.d.ts +30 -6
- package/dist/controllers/auto_submit_controller.js +295 -110
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +172 -106
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.d.ts +10 -7
- package/dist/controllers/breadcrumb_controller.js +0 -129
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +22 -4
- package/dist/controllers/bulk_select_controller.js +264 -53
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +109 -28
- package/dist/controllers/calendar_controller.js +346 -180
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +99 -27
- package/dist/controllers/carousel_controller.js +587 -300
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +18 -2
- package/dist/controllers/character_counter_controller.js +271 -143
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.d.ts +7 -2
- package/dist/controllers/checkbox_controller.js +77 -75
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +44 -7
- package/dist/controllers/clipboard_controller.js +187 -108
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +41 -5
- package/dist/controllers/collapsible_controller.js +374 -108
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +47 -14
- package/dist/controllers/color_picker_controller.js +183 -127
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +33 -6
- package/dist/controllers/combobox_controller.js +426 -109
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +45 -14
- package/dist/controllers/command_palette_controller.js +994 -350
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.d.ts +15 -2
- package/dist/controllers/conditional_fields_controller.js +212 -125
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.d.ts +49 -12
- package/dist/controllers/confirm_controller.js +939 -245
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +36 -3
- package/dist/controllers/context_menu_controller.js +275 -127
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/count_up_controller.d.ts +13 -1
- package/dist/controllers/count_up_controller.js +73 -21
- package/dist/controllers/count_up_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +75 -2
- package/dist/controllers/countdown_controller.js +235 -97
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +93 -15
- package/dist/controllers/currency_input_controller.js +241 -118
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +78 -22
- package/dist/controllers/data_grid_controller.js +229 -135
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +64 -11
- package/dist/controllers/date_range_picker_controller.js +359 -165
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +22 -3
- package/dist/controllers/dialog_controller.js +887 -225
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +24 -7
- package/dist/controllers/direct_upload_controller.js +80 -142
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.d.ts +2 -2
- package/dist/controllers/dirty_form_controller.js +26 -47
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.d.ts +19 -0
- package/dist/controllers/dismissible_controller.js +175 -20
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +52 -16
- package/dist/controllers/drawer_controller.js +963 -336
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +39 -3
- package/dist/controllers/dropdown_controller.js +342 -85
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.d.ts +12 -1
- package/dist/controllers/editable_controller.js +182 -58
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.js +67 -53
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +60 -18
- package/dist/controllers/file_dropzone_controller.js +426 -229
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.d.ts +10 -4
- package/dist/controllers/filter_controller.js +78 -36
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +57 -14
- package/dist/controllers/flash_controller.js +328 -211
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +12 -3
- package/dist/controllers/focus_controller.js +654 -239
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +153 -136
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +29 -96
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +41 -9
- package/dist/controllers/frame_loading_controller.js +263 -198
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +22 -2
- package/dist/controllers/highlight_controller.js +119 -70
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +65 -4
- package/dist/controllers/hover_card_controller.js +335 -118
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +58 -4
- package/dist/controllers/idle_controller.js +316 -65
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +5 -2
- package/dist/controllers/input_mask_controller.js +116 -61
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.d.ts +32 -13
- package/dist/controllers/intersection_controller.js +147 -103
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.d.ts +16 -0
- package/dist/controllers/lazy_frame_controller.js +9 -56
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +84 -10
- package/dist/controllers/listbox_controller.js +413 -153
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.d.ts +1 -1
- package/dist/controllers/local_time_controller.js +54 -64
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.d.ts +11 -3
- package/dist/controllers/masonry_controller.js +70 -95
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +40 -9
- package/dist/controllers/menu_controller.js +254 -167
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +15 -4
- package/dist/controllers/menubar_controller.js +206 -331
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.d.ts +20 -0
- package/dist/controllers/meter_controller.js +159 -71
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +50 -7
- package/dist/controllers/multi_select_controller.js +797 -388
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +19 -1
- package/dist/controllers/navigation_menu_controller.js +156 -231
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.d.ts +10 -0
- package/dist/controllers/nested_form_controller.js +373 -174
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.d.ts +23 -0
- package/dist/controllers/network_status_controller.js +241 -45
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +62 -10
- package/dist/controllers/number_input_controller.js +387 -227
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.js +28 -75
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +38 -7
- package/dist/controllers/otp_controller.js +344 -196
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +13 -12
- package/dist/controllers/overflow_indicator_controller.js +167 -93
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +66 -9
- package/dist/controllers/overflow_menu_controller.js +408 -277
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +52 -15
- package/dist/controllers/pagination_controller.js +342 -120
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +60 -9
- package/dist/controllers/password_reveal_controller.js +424 -99
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +14 -5
- package/dist/controllers/password_strength_controller.js +99 -153
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.d.ts +11 -4
- package/dist/controllers/persist_controller.js +252 -96
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +19 -7
- package/dist/controllers/pointer_drag_controller.js +263 -164
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +46 -4
- package/dist/controllers/popover_controller.js +267 -94
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +44 -4
- package/dist/controllers/portal_controller.js +209 -72
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.d.ts +10 -4
- package/dist/controllers/preview_guard_controller.js +243 -112
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +11 -0
- package/dist/controllers/progress_controller.js +132 -59
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +19 -8
- package/dist/controllers/radio_group_controller.js +138 -133
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +92 -4
- package/dist/controllers/range_slider_controller.js +245 -112
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +33 -6
- package/dist/controllers/rating_controller.js +429 -194
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +26 -2
- package/dist/controllers/read_more_controller.js +185 -47
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/reading_progress_controller.d.ts +1 -1
- package/dist/controllers/reading_progress_controller.js +147 -135
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.d.ts +18 -4
- package/dist/controllers/relative_time_controller.js +145 -110
- package/dist/controllers/relative_time_controller.js.map +1 -1
- package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
- package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
- package/dist/controllers/reset_on_restore_controller.js.map +1 -0
- package/dist/controllers/resizable_controller.d.ts +44 -4
- package/dist/controllers/resizable_controller.js +294 -118
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.js +39 -84
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.d.ts +7 -3
- package/dist/controllers/scroll_area_controller.js +428 -221
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.js +0 -53
- package/dist/controllers/scroll_restore_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
- package/dist/controllers/scroll_visibility_controller.js +395 -146
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +9 -3
- package/dist/controllers/scrollspy_controller.js +181 -236
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +59 -17
- package/dist/controllers/separator_controller.js +288 -132
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +81 -7
- package/dist/controllers/sidebar_controller.js +1071 -375
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.d.ts +23 -0
- package/dist/controllers/skeleton_controller.js +248 -92
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +72 -8
- package/dist/controllers/slider_controller.js +211 -96
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.d.ts +21 -3
- package/dist/controllers/smart_sticky_header_controller.js +163 -54
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.js +13 -110
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.d.ts +39 -4
- package/dist/controllers/spinner_controller.js +277 -161
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +24 -17
- package/dist/controllers/step_indicator_controller.js +107 -53
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +43 -9
- package/dist/controllers/stepper_controller.js +171 -48
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +25 -0
- package/dist/controllers/stick_to_bottom_controller.js +310 -116
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/sticky_observer_controller.d.ts +11 -0
- package/dist/controllers/sticky_observer_controller.js +214 -49
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +22 -6
- package/dist/controllers/submit_once_controller.js +294 -192
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +40 -7
- package/dist/controllers/switch_controller.js +107 -43
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +13 -1
- package/dist/controllers/tabs_controller.js +50 -25
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +27 -3
- package/dist/controllers/tags_input_controller.js +290 -149
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.d.ts +10 -0
- package/dist/controllers/textarea_autosize_controller.js +71 -58
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +37 -8
- package/dist/controllers/theme_controller.js +148 -80
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +18 -1
- package/dist/controllers/time_picker_controller.js +162 -81
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +85 -23
- package/dist/controllers/toast_controller.js +365 -193
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +60 -9
- package/dist/controllers/toggle_group_controller.js +194 -99
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +92 -90
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +52 -2
- package/dist/controllers/tooltip_controller.js +185 -125
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.d.ts +22 -4
- package/dist/controllers/transition_controller.js +123 -115
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +59 -18
- package/dist/controllers/tree_view_controller.js +248 -290
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +33 -3
- package/dist/index.js +8649 -8572
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +95 -9
- package/dist/inspector/cli.js +343 -77
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +390 -96
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +45 -45
- package/dist/inspector/manifest.json +1308 -155
- package/dist/positioning/index.d.ts +8 -0
- package/dist/positioning/index.js +148 -45
- package/dist/positioning/index.js.map +1 -1
- package/package.json +7 -7
- package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
- package/dist/controllers/reset_before_cache_controller.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/safe_timeout.ts","../../src/utils/string_list.ts","../../src/controllers/idle_controller.ts"],"names":[],"mappings":"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","file":"idle_controller.js","sourcesContent":["/**\n * Self-cleaning timer registries shared by Stimeo controllers.\n *\n * Stimulus controllers frequently schedule `setTimeout` / `setInterval` work\n * (auto-dismiss, debouncing, polling). When the element leaves the DOM — a\n * Turbo Drive navigation, a Turbo Stream replacement, or any `disconnect()` —\n * orphaned timers keep firing against a detached controller, leaking memory and\n * mutating stale state. {@link SafeTimeout} and {@link SafeInterval} track every\n * timer they create so a single {@link TimerRegistry.clearAll | clearAll()} call\n * in `disconnect()` tears them all down.\n *\n * These are intentionally low-level primitives: they own *registration and\n * cleanup only*. Higher-level policy (pause/resume, remaining-time accounting)\n * stays in the individual controllers so per-widget semantics are not flattened\n * into a lowest-common-denominator helper.\n */\n\n/**\n * Largest delay a timer can hold: the platform stores it in a 32-bit signed\n * integer, and anything larger overflows to `1`, so a delay meant to be far in\n * the future fires almost immediately. A declared delay above this bound names\n * no delay at all, and a controller reading one falls back to its default.\n */\nexport const MAX_TIMER_DELAY_MS = 2_147_483_647;\n\n/**\n * Shared registry bookkeeping for the timeout/interval variants.\n *\n * Subclasses provide the scheduling primitive ({@link schedule}) and its matching\n * canceller ({@link cancel}); this base owns the set of live ids plus the\n * per-id and bulk teardown shared by both.\n */\nabstract class TimerRegistry {\n /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */\n protected readonly ids = new Set<number>();\n\n /** Schedules the underlying platform timer and returns its id. */\n protected abstract schedule(callback: () => void, delay: number): number;\n\n /** Cancels the underlying platform timer for `id`. */\n protected abstract cancel(id: number): void;\n\n /**\n * Cancels a single tracked timer.\n *\n * No-ops if the id is unknown (already cleared, fired, or never owned by this\n * registry), so callers can clear defensively without guarding.\n */\n clear(id: number): void {\n if (this.ids.delete(id)) {\n this.cancel(id);\n }\n }\n\n /**\n * Cancels every tracked timer. Call this from a controller's `disconnect()`\n * to guarantee no timer outlives the element.\n */\n clearAll(): void {\n for (const id of this.ids) {\n this.cancel(id);\n }\n this.ids.clear();\n }\n\n /** Number of timers currently tracked (pending). */\n get size(): number {\n return this.ids.size;\n }\n}\n\n/**\n * `setTimeout` wrapper that auto-forgets each timer once it fires and supports\n * bulk teardown on disconnect.\n *\n * @example\n * ```ts\n * #timers = new SafeTimeout();\n *\n * connect() {\n * this.#timers.set(() => this.dismiss(), 5000);\n * }\n *\n * disconnect() {\n * this.#timers.clearAll();\n * }\n * ```\n */\nexport class SafeTimeout extends TimerRegistry {\n /**\n * Schedules `callback` after `delay` ms and returns the timer id.\n *\n * The id is removed from the registry automatically when the timeout fires,\n * so {@link TimerRegistry.size | size} reflects only still-pending timers.\n */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(() => {\n this.ids.delete(id);\n callback();\n }, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setTimeout(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearTimeout(id);\n }\n}\n\n/**\n * `setInterval` wrapper that tracks every interval for bulk teardown on\n * disconnect. Unlike {@link SafeTimeout}, intervals are retained until they are\n * explicitly cleared because they fire repeatedly.\n *\n * @example\n * ```ts\n * #intervals = new SafeInterval();\n *\n * connect() {\n * this.#intervals.set(() => this.tick(), 1000);\n * }\n *\n * disconnect() {\n * this.#intervals.clearAll();\n * }\n * ```\n */\nexport class SafeInterval extends TimerRegistry {\n /** Schedules a repeating `callback` every `delay` ms and returns the timer id. */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(callback, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setInterval(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearInterval(id);\n }\n}\n","/**\n * JSON string-list parsing for the controllers that take a list as a Value.\n *\n * Stimulus offers an `Array` Value type, but its reader runs `JSON.parse` inside\n * the value observer — **before** the `<name>ValueChanged` callback — and\n * rethrows on malformed text. The throw propagates out of the observer that runs\n * during connection, so a single unparseable attribute stops the controller from\n * connecting at all: no lifecycle callback runs and the element is left inert\n * rather than degraded. A consumer cannot guard against it either, because the\n * callback that would hold the `try` never runs.\n *\n * Declaring the attribute as a `String` Value and parsing it here keeps a\n * malformed declaration local to the value it declares. The attribute text is\n * identical either way (`'[\"a\", \"b\"]'`), so markup does not change with the\n * declaration.\n */\n\n/**\n * Parses `raw` as a JSON array of strings.\n *\n * Returns `fallback` when the text is absent, unparseable, or not an array, so a\n * malformed declaration behaves like an omitted one. A parsed array keeps only\n * its string entries: every caller indexes or compares strings, and a stray\n * number would otherwise sit in the list without ever matching.\n *\n * An explicit `\"[]\"` is honoured as an empty list rather than falling back — it\n * is the only way to declare \"none\" against a non-empty default.\n *\n * @param raw - the attribute text, e.g. `'[\"password\"]'`.\n * @param fallback - the list to use when `raw` declares nothing usable.\n */\nexport function parseStringList(raw: string, fallback: readonly string[] = []): string[] {\n // The common case is an undeclared attribute, and `JSON.parse(\"\")` answers it\n // by throwing. Reaching the same fallback without building an exception keeps\n // the callers that ask per element off that path.\n const text = raw.trim();\n if (text.length === 0) return [...fallback];\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return [...fallback];\n }\n\n if (!Array.isArray(parsed)) return [...fallback];\n return parsed.filter((entry): entry is string => typeof entry === \"string\");\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { SafeTimeout } from \"../utils/safe_timeout\";\nimport { parseStringList } from \"../utils/string_list\";\n\n/** Activity signals watched unless the consumer declares its own list. */\nconst DEFAULT_ACTIVITY_EVENTS = [\n \"mousemove\",\n \"mousedown\",\n \"keydown\",\n \"wheel\",\n \"touchstart\",\n \"scroll\",\n];\n\n/**\n * Headless inactivity / session-timeout detector: fires `idle` after `timeout` ms\n * with no user activity, an optional `prompt` `promptBefore` ms earlier, and\n * `active` when the user returns (no dedicated APG pattern; supports WCAG 2.2.1 by\n * giving the app a warning hook before a timeout).\n *\n * Markup contract (identifier: `stimeo--idle`):\n * <body data-controller=\"stimeo--idle\"\n * data-stimeo--idle-timeout-value=\"900000\"\n * data-stimeo--idle-prompt-before-value=\"60000\"\n * data-action=\"stimeo--idle:prompt->session#warn\n * stimeo--idle:idle->session#logout\"></body>\n *\n * Activity events (`events`, passive) are watched on `document` with capture so\n * non-bubbling ones like `scroll` are seen anywhere; returning to a hidden tab\n * (`visibilitychange` → visible) counts as activity too. While idle the controller\n * element carries `data-idle`.\n *\n * `prompt` dispatches `{ remaining }`; `idle` and `active` dispatch `{}`.\n *\n * @remarks\n * Behavior only — it renders no warning UI (pair with Dialog/Confirm) and never\n * touches the server session. Timers are owned by `SafeTimeout` and the\n * listeners are removed on `disconnect()` (Turbo navigation included). Place one on\n * the root element. Every visit reconnects the controller and re-arms the timeout\n * from that moment — `data-turbo-permanent` keeps the element, not the elapsed count.\n */\nexport class IdleController extends Controller<HTMLElement> {\n static override values = {\n timeout: { type: Number, default: 900_000 },\n promptBefore: { type: Number, default: 0 },\n // A JSON list read through `parseStringList` rather than Stimulus's `Array`\n // type: that reader throws out of the value observer before any callback\n // runs, so one malformed attribute would stop the detector connecting.\n events: { type: String, default: \"\" },\n };\n static events = [\"prompt\", \"idle\", \"active\"] as const;\n\n declare timeoutValue: number;\n declare promptBeforeValue: number;\n declare eventsValue: string;\n\n readonly #timeouts = new SafeTimeout();\n #idle = false;\n #prompted = false;\n /** Timestamp of the last activity; the timers self-reschedule against it. */\n #lastActivity = 0;\n /**\n * Activity types actually registered on `document`, so `disconnect()` unbinds the\n * same set even when `events` changed while connected (a Turbo morph can rewrite\n * the Value in place, and the removal must match the registration, not the Value).\n */\n #boundEvents: string[] = [];\n\n readonly #onActivity = (): void => {\n // Hot path (fires on every mousemove/scroll/wheel): just record the time. The\n // prompt/idle timers re-check this when they fire and reschedule if needed, so we\n // never tear down and re-create timers on each event (no per-event timer churn).\n this.#lastActivity = Date.now();\n if (this.#idle || this.#prompted) {\n // We were already idle/prompted, so the timers have lapsed — wake and re-arm.\n this.#idle = false;\n this.#prompted = false;\n this.element.removeAttribute(\"data-idle\");\n this.dispatch(\"active\", { detail: {} });\n this.#arm();\n }\n };\n\n readonly #onVisibility = (): void => {\n // Returning to the tab is activity; leaving it keeps the clock running (being\n // away counts toward the timeout).\n if (document.visibilityState === \"visible\") this.#onActivity();\n };\n\n override connect(): void {\n // Connecting always starts a fresh cycle (#arm() re-bases the clock), so an idle\n // marker that arrived with the DOM — a restored Turbo snapshot, a moved element —\n // describes a period this instance is not in. Drop it, or `data-idle` claims the\n // user is idle for the whole next active window with no `active` to correct it.\n this.#idle = false;\n this.#prompted = false;\n this.element.removeAttribute(\"data-idle\");\n this.#boundEvents = parseStringList(this.eventsValue, DEFAULT_ACTIVITY_EVENTS);\n for (const type of this.#boundEvents) {\n document.addEventListener(type, this.#onActivity, { passive: true, capture: true });\n }\n document.addEventListener(\"visibilitychange\", this.#onVisibility);\n this.#arm();\n }\n\n override disconnect(): void {\n for (const type of this.#boundEvents) {\n document.removeEventListener(type, this.#onActivity, { capture: true });\n }\n this.#boundEvents = [];\n document.removeEventListener(\"visibilitychange\", this.#onVisibility);\n this.#timeouts.clearAll();\n }\n\n /** Schedules the prompt and idle checks from the current activity baseline. */\n #arm(): void {\n this.#timeouts.clearAll();\n this.#lastActivity = Date.now();\n const { promptBeforeValue: prompt, timeoutValue: timeout } = this;\n if (prompt > 0 && prompt < timeout) {\n this.#timeouts.set(() => this.#checkPrompt(), timeout - prompt);\n }\n this.#timeouts.set(() => this.#checkIdle(), timeout);\n }\n\n /**\n * Idle-timer callback: go idle only if there has genuinely been no activity for\n * `timeout`; otherwise reschedule for the remaining time. This lets activity events\n * stay O(1) (a timestamp write) while the deadline still tracks the last activity.\n */\n #checkIdle(): void {\n const remaining = this.timeoutValue - (Date.now() - this.#lastActivity);\n if (remaining > 0) {\n this.#timeouts.set(() => this.#checkIdle(), remaining);\n return;\n }\n this.#idle = true;\n this.#prompted = false;\n this.element.setAttribute(\"data-idle\", \"true\");\n this.dispatch(\"idle\", { detail: {} });\n }\n\n /** Prompt-timer callback: warn at `promptBefore` before the idle deadline. */\n #checkPrompt(): void {\n const remaining =\n this.timeoutValue - this.promptBeforeValue - (Date.now() - this.#lastActivity);\n if (remaining > 0) {\n this.#timeouts.set(() => this.#checkPrompt(), remaining);\n return;\n }\n this.#prompted = true;\n this.dispatch(\"prompt\", { detail: { remaining: this.promptBeforeValue } });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/listener_set.ts","../../src/utils/timer_bounds.ts","../../src/utils/number_bounds.ts","../../src/utils/coerce.ts","../../src/utils/number_value.ts","../../src/utils/safe_timeout.ts","../../src/utils/shared_registry.ts","../../src/utils/state_regions.ts","../../src/utils/string_list.ts","../../src/controllers/idle_controller.ts"],"names":[],"mappings":"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","file":"idle_controller.js","sourcesContent":["/**\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 * Largest delay a timer can hold: the platform stores it in a 32-bit signed\n * integer, and anything larger overflows to `1`, so a delay meant to be far in\n * the future fires almost immediately. A declared delay above this bound names\n * no delay at all, and a controller reading one falls back to its default.\n */\nexport const MAX_TIMER_DELAY_MS = 2_147_483_647;\n","import { MAX_TIMER_DELAY_MS } from \"./timer_bounds\";\n\n/** Semantic constraints on an already decoded number, independent of the DOM. */\nexport interface NumberBounds {\n /** Finite numbers are required, including when this field is omitted. */\n readonly finite?: true;\n /** Inclusive lower endpoint. */\n readonly min?: number;\n /** Inclusive upper endpoint. */\n readonly max?: number;\n /** Exclusive lower endpoint. */\n readonly exclusiveMin?: number;\n /** Require a number without a fractional part. */\n readonly integer?: boolean;\n /** Exact decoded numbers accepted by a discrete contract. */\n readonly allowedValues?: readonly number[];\n /** JSON-safe exceptions to finite-number checking for unbounded endpoints. */\n readonly allowInfinity?: \"negative\" | \"positive\" | \"both\";\n}\n\n/** One constraint for each Number Value, including shorthand declarations. */\nexport type NumberValueConstraints<Values> = {\n readonly [Key in keyof Values as Values[Key] extends\n | NumberConstructor\n | { type: NumberConstructor }\n ? Key\n : never]: NumberBounds;\n};\n\n/** Reusable semantic domains; fractional inputs remain fractional unless excluded. */\nexport const NUMBER_BOUNDS = {\n finite: { finite: true },\n nonNegative: { finite: true, min: 0 },\n positive: { finite: true, exclusiveMin: 0 },\n nonNegativeInteger: { finite: true, min: 0, integer: true },\n positiveInteger: { finite: true, exclusiveMin: 0, integer: true },\n lowerBound: { finite: true, allowInfinity: \"negative\" },\n upperBound: { finite: true, allowInfinity: \"positive\" },\n timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS },\n positiveTimer: { finite: true, exclusiveMin: 0, max: MAX_TIMER_DELAY_MS },\n} as const satisfies Record<string, NumberBounds>;\n\n/**\n * Checks all declared bounds without rounding, clamping, or coercing the input.\n * Infinity exceptions relax only finite checking; every other bound still applies.\n */\nexport function matchesNumberBounds(value: number, bounds: NumberBounds): boolean {\n if (!Number.isFinite(value)) {\n const direction = value === Infinity ? \"positive\" : value === -Infinity ? \"negative\" : null;\n if (direction === null) return false;\n if (bounds.allowInfinity !== \"both\" && bounds.allowInfinity !== direction) return false;\n }\n if (bounds.min !== undefined && value < bounds.min) return false;\n if (bounds.max !== undefined && value > bounds.max) return false;\n if (bounds.exclusiveMin !== undefined && value <= bounds.exclusiveMin) return false;\n if (bounds.integer && !Number.isInteger(value)) return false;\n if (bounds.allowedValues !== undefined && !bounds.allowedValues.includes(value)) return false;\n return true;\n}\n\n/** Decodes a Number Value literal; action params use their own JSON decoder. */\nexport function decodeNumberValue(raw: string): number {\n return Number(raw.replace(/_/g, \"\"));\n}\n","/**\n * Numeric coercion for controllers that accept a number through a Value, an action\n * param, or an event detail.\n *\n * Stimulus decodes action params as JSON when possible and leaves other text\n * untouched. CustomEvent detail can also supply a number or numeric string.\n * The reader accepts those two types while rejecting blank and non-finite input.\n */\n\nimport { matchesNumberBounds, type NumberBounds } from \"./number_bounds\";\n\n/**\n * Reads a finite number or nonblank numeric string; other input returns `null`.\n * Blank strings cannot silently reset a value to zero.\n */\nexport function toFiniteNumber(raw: unknown): number | null {\n if (typeof raw !== \"number\" && typeof raw !== \"string\") return null;\n if (typeof raw === \"string\" && raw.trim().length === 0) return null;\n const value = Number(raw);\n return Number.isFinite(value) ? value : null;\n}\n\n/**\n * Returns an accepted decoded Number Value or the caller's fallback unchanged.\n * The caller owns the fallback contract; this reader does not parse or write a Value.\n */\nexport function readNumber(raw: number, fallback: number, bounds: NumberBounds): number {\n return matchesNumberBounds(raw, bounds) ? raw : fallback;\n}\n","import { readNumber } from \"./coerce\";\nimport { matchesNumberBounds, type NumberBounds } from \"./number_bounds\";\n\n/** The instance identity carried by a Number Value read. */\nexport interface NumberValueOwner {\n readonly identifier: string;\n readonly element: Element;\n}\n\n/** Reads current declarations at the point of use without rewriting their attributes. */\nexport class NumberValueReader {\n /** The last rejected literal per Value; no history grows for an unchanged instance. */\n readonly #lastRejected = new Map<string, string>();\n\n /** Resolves the declared number using its own fallback and shared class contract. */\n read(\n owner: NumberValueOwner,\n name: string,\n raw: number,\n fallback: number,\n bounds: NumberBounds,\n ): number {\n const resolved = readNumber(raw, fallback, bounds);\n if (matchesNumberBounds(raw, bounds)) {\n this.#lastRejected.delete(name);\n return resolved;\n }\n const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;\n const literal = owner.element.getAttribute(attribute);\n if (literal === null) {\n this.#lastRejected.delete(name);\n return resolved;\n }\n if (this.#lastRejected.get(name) !== literal) {\n this.#lastRejected.set(name, literal);\n console.warn(\n `Stimeo UI: \"${owner.identifier}\" has an invalid number Value \"${name}\" declaration ${JSON.stringify(literal)}; using ${fallback}.`,\n );\n }\n return resolved;\n }\n}\n","/**\n * Self-cleaning timer registries shared by Stimeo controllers.\n *\n * Stimulus controllers frequently schedule `setTimeout` / `setInterval` work\n * (auto-dismiss, debouncing, polling). When the element leaves the DOM — a\n * Turbo Drive navigation, a Turbo Stream replacement, or any `disconnect()` —\n * orphaned timers keep firing against a detached controller, leaking memory and\n * mutating stale state. {@link SafeTimeout} and {@link SafeInterval} track every\n * timer they create so a single {@link TimerRegistry.clearAll | clearAll()} call\n * in `disconnect()` tears them all down.\n *\n * These are intentionally low-level primitives: they own *registration and\n * cleanup only*. Higher-level policy (pause/resume, remaining-time accounting)\n * stays in the individual controllers so per-widget semantics are not flattened\n * into a lowest-common-denominator helper.\n */\n\nexport { MAX_TIMER_DELAY_MS } from \"./timer_bounds\";\n\n/**\n * Shared registry bookkeeping for the timeout/interval variants.\n *\n * Subclasses provide the scheduling primitive ({@link schedule}) and its matching\n * canceller ({@link cancel}); this base owns the set of live ids plus the\n * per-id and bulk teardown shared by both.\n */\nabstract class TimerRegistry {\n /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */\n protected readonly ids = new Set<number>();\n\n /** Schedules the underlying platform timer and returns its id. */\n protected abstract schedule(callback: () => void, delay: number): number;\n\n /** Cancels the underlying platform timer for `id`. */\n protected abstract cancel(id: number): void;\n\n /**\n * Cancels a single tracked timer.\n *\n * No-ops if the id is unknown (already cleared, fired, or never owned by this\n * registry), so callers can clear defensively without guarding.\n */\n clear(id: number): void {\n if (this.ids.delete(id)) {\n this.cancel(id);\n }\n }\n\n /**\n * Cancels every tracked timer. Call this from a controller's `disconnect()`\n * to guarantee no timer outlives the element.\n */\n clearAll(): void {\n for (const id of this.ids) {\n this.cancel(id);\n }\n this.ids.clear();\n }\n\n /** Number of timers currently tracked (pending). */\n get size(): number {\n return this.ids.size;\n }\n}\n\n/**\n * `setTimeout` wrapper that auto-forgets each timer once it fires and supports\n * bulk teardown on disconnect.\n *\n * @example\n * ```ts\n * #timers = new SafeTimeout();\n *\n * connect() {\n * this.#timers.set(() => this.dismiss(), 5000);\n * }\n *\n * disconnect() {\n * this.#timers.clearAll();\n * }\n * ```\n */\nexport class SafeTimeout extends TimerRegistry {\n /**\n * Schedules `callback` after `delay` ms and returns the timer id.\n *\n * The id is removed from the registry automatically when the timeout fires,\n * so {@link TimerRegistry.size | size} reflects only still-pending timers.\n */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(() => {\n this.ids.delete(id);\n callback();\n }, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setTimeout(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearTimeout(id);\n }\n}\n\n/**\n * `setInterval` wrapper that tracks every interval for bulk teardown on\n * disconnect. Unlike {@link SafeTimeout}, intervals are retained until they are\n * explicitly cleared because they fire repeatedly.\n *\n * @example\n * ```ts\n * #intervals = new SafeInterval();\n *\n * connect() {\n * this.#intervals.set(() => this.tick(), 1000);\n * }\n *\n * disconnect() {\n * this.#intervals.clearAll();\n * }\n * ```\n */\nexport class SafeInterval extends TimerRegistry {\n /** Schedules a repeating `callback` every `delay` ms and returns the timer id. */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(callback, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setInterval(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearInterval(id);\n }\n}\n","/**\n * One registry per page for every copy of the library.\n *\n * Each `stimeo-ui/controllers/*` file, and each opt-in entry, is built on its own and carries\n * its own copy of every utility it uses, so a page that imports controllers one by one, or one\n * of them next to the barrel, evaluates a utility several times. A registry that coordinates\n * instances of different controllers — the stacks of leased values, the loans of a\n * `tabindex`, the Escape layers, the focus traps — must not be split by that: a dialog inside\n * a drawer, or a character counter and a form field on one input, coordinate whichever files\n * they came from. So such a registry lives once on `globalThis`, under a `Symbol.for` key the\n * copies agree on, and the first copy to ask for it creates it.\n *\n * The key names the registry and the version of its shape\n * (`stimeo-ui.<utility>.registry.v<n>`). Copies whose registries have the same shape share\n * one; a release that changes the shape changes the version, so a copy of another release\n * keeps a registry of its own instead of reading entries it does not understand — the two\n * then coordinate within each release only. An entry another copy put in a registry is read\n * through its fields alone, never through `#private` members, which belong to the class of\n * the copy that created it.\n */\n\n/**\n * The registry stored under `key`, created by `create` on first use.\n *\n * @param key - `stimeo-ui.<utility>.registry.v<n>`: the version is that of the shape `create`\n * returns.\n * @param create - Builds the empty registry.\n */\nexport function sharedRegistry<T extends object>(key: string, create: () => T): T {\n const symbol = Symbol.for(key);\n const scope = globalThis as unknown as Record<symbol, T | undefined>;\n const existing = scope[symbol];\n if (existing !== undefined) return existing;\n const registry = create();\n Object.defineProperty(globalThis, symbol, { value: registry });\n return registry;\n}\n","import { sharedRegistry } from \"./shared_registry\";\n\n/** What a consumer declares once about the regions one boolean state reveals. */\nexport interface StateRegionsOptions {\n /** Returns the declared regions shown while the state holds. */\n readonly whenTrue: () => readonly HTMLElement[];\n /**\n * Returns the declared regions shown while the state does not hold. Omitted by a\n * state whose other side has nothing of its own to show.\n */\n readonly whenFalse?: () => readonly HTMLElement[];\n}\n\n/** One instance's hold on one region's `hidden`, in the stack every instance shares. */\ninterface RegionHold {\n /** The instance holding it; `null` for a hold read from a copy's record until it is taken. */\n instance: object | null;\n readonly record: string;\n /** What the author wrote: the `hidden` it hands back with nothing beneath it. */\n readonly authored: string | null;\n /** Whether the visibility it wrote last hides the region. */\n written: boolean;\n /** Whether it hands back what the hold beneath it wrote last instead. */\n linked: boolean;\n}\n\n/** What an instance's record on a region says. */\ninterface RegionRecord {\n readonly authored: string | null;\n readonly written: boolean;\n readonly linked: boolean;\n /** The records of the other owners' holds beneath it, nearest first. */\n readonly beneath: readonly string[];\n}\n\n/** The holds on each region and every instance's record name. */\ninterface StateRegionsRegistry {\n readonly stacks: WeakMap<HTMLElement, RegionHold[]>;\n readonly names: Set<string>;\n}\n\n/**\n * Owns `hidden` on the regions a boolean state reveals.\n *\n * A region is a **declared** target, so the consumer decides whether one exists at\n * all; where it does, which side is shown is a pure function of the state and the\n * controller writes it unconditionally. An authored `hidden` is therefore not read\n * back — the first reflection after a connection settles it, and a restored DOM\n * cannot leave a region contradicting the state it belongs to.\n *\n * The regions arrive as the caller's own target arrays, which Stimulus has already\n * scoped to this instance: a nested controller registered under the same identifier\n * keeps its own regions. `host` narrows that set further, to the regions inside one\n * trigger or item, so a widget with several of them reflects each independently.\n *\n * **Two sides are a pair.** Where the state has a `whenFalse` side and only one half\n * sits inside `host`, the half that is there is left in view: hiding it would take\n * the only label with it, and the static check names the missing one instead. What\n * the author wrote on a lone half is theirs and stays — only a half this instance\n * took out of view is given back, which is what a pair losing one side at runtime\n * leaves behind.\n *\n * `hidden` moves only where it changes, and the record below only where what it says changes:\n * a transition costs a consumer watching its own subtree for attribute mutations two of them on\n * each region it moves — `hidden` and the record — and a reflection that moves nothing costs\n * none. A write that moves no `hidden` — the page moved the region first, or another\n * instance's hold lies above this one — changes only the record.\n *\n * **Every instance shares one stack of holds per region**, whatever its owner and whichever\n * file of the package constructed it (the stacks are one per page): the holds sit in the order\n * their instances first wrote the region, each with the visibility it wrote last. The region's\n * visibility is the highest hold's that carries it, and the page's when none does. A write\n * moves `hidden` unless a hold above the writer carries the region's visibility, so the top\n * hold always shows its writes and the page's visibility gives way to the next write of any\n * hold. A release gives a visibility back only from the hold the region's visibility is: the\n * one the hold beneath it wrote last, or the author's `hidden` once nothing is beneath it; any\n * other release only leaves the stack. Holds that wrote the same visibility are told apart by\n * the stack: the higher one keeps it. A first write over a top hold whose visibility the page\n * has replaced takes the region's `hidden` as the author's. Two instances of one owner on one\n * region stack the same way.\n *\n * **The stack travels with the region.** While the region carries anything but what the author\n * wrote under its top hold, or a hold lies on another hold or is covered by one, the region\n * carries that hold's owner's record `data-<owner>-hidden-region`: JSON of what the author\n * wrote (a string, or `null` for an absent attribute), whether the visibility written last\n * hides the region, `false` where the hold hands back the author's `hidden` rather than the\n * latest visibility of the hold beneath it, and the records of every hold beneath it, nearest\n * first; the last release of an owner removes it. A copy of the region — a page Turbo restores\n * from its cache — therefore carries the stack, read back under the record names of the\n * instances constructed so far and ordered by the records each hold lists beneath it, and an\n * instance that reflects, gives back or releases the copy takes the hold its owner's record\n * describes over in place. Complete records for distinct owners preserve their order across\n * a copy, including release without a new reflection. Records describe owners, not instances:\n * one owner holding the region more than once leaves only its topmost hold's record, which\n * the first of its instances to write or release on the copy takes. Its lower holds and\n * positions are absent; mutually listed owners cannot express their full live order. A hold\n * that showed the author's `hidden` alone carries no record and is taken by its first write\n * on the copy. Missing or unreadable records also leave their holds unknown, so copying the\n * DOM does not promise identical decisions for every live stack. A new stack with no readable\n * record takes the current value as the author's; a morph can remove records while existing\n * holds remain in memory until released.\n *\n * @example\n * ```ts\n * readonly #labels = new StateRegions(\n * {\n * whenTrue: () => this.expandedLabelTargets,\n * whenFalse: () => this.collapsedLabelTargets,\n * },\n * this.identifier,\n * );\n *\n * #reflect(): void {\n * this.#labels.reflect(this.triggerTarget, this.#expanded);\n * }\n * ```\n */\nexport class StateRegions {\n /** The holds on each region, bottom first, and the record names; one per page. */\n static readonly #registry = sharedRegistry(\n \"stimeo-ui.state-regions.registry.v1\",\n (): StateRegionsRegistry => ({ stacks: new WeakMap(), names: new Set() }),\n );\n readonly #whenTrue: () => readonly HTMLElement[];\n readonly #whenFalse: (() => readonly HTMLElement[]) | null;\n readonly #record: string;\n\n /**\n * @param options - The declared regions of each side.\n * @param owner - Names the record on a region; the controller's identifier.\n */\n constructor(options: StateRegionsOptions, owner: string) {\n this.#whenTrue = options.whenTrue;\n this.#whenFalse = options.whenFalse ?? null;\n this.#record = `data-${owner}-hidden-region`;\n StateRegions.#registry.names.add(this.#record);\n }\n\n /** Shows the regions inside `host` that belong to `isTrue` and hides the others. */\n reflect(host: Element, isTrue: boolean): void {\n const shown = this.#inside(host, this.#whenTrue());\n if (!this.#whenFalse) {\n for (const region of shown) this.#write(region, !isTrue);\n return;\n }\n const hiddenSide = this.#inside(host, this.#whenFalse());\n if (shown.length === 0 || hiddenSide.length === 0) {\n for (const region of shown) this.#give(region);\n for (const region of hiddenSide) this.#give(region);\n return;\n }\n for (const region of shown) this.#write(region, !isTrue);\n for (const region of hiddenSide) this.#write(region, isTrue);\n }\n\n /**\n * Restores the regions this instance wrote inside `host` to what the hold beneath it wrote last\n * or the authored `hidden`, unless changed since; found by scan, as the declared lists stop\n * resolving with targets.\n */\n release(host: Element): void {\n for (const region of [host, ...host.querySelectorAll(\"*\")]) {\n if (!(region instanceof HTMLElement)) continue;\n const stack = this.#held(region);\n const hold = stack && this.#take(stack);\n if (!stack || !hold) continue;\n const owns = region.hidden === hold.written && !this.#covered(region, stack, hold);\n const index = stack.indexOf(hold);\n stack.splice(index, 1);\n const above = stack[index];\n if (above) above.linked &&= hold.linked;\n if (owns) {\n const below = hold.linked ? stack[index - 1] : undefined;\n if (below) {\n if (region.hidden !== below.written) region.hidden = below.written;\n } else if (region.getAttribute(\"hidden\") !== hold.authored) {\n if (hold.authored === null) region.removeAttribute(\"hidden\");\n else region.setAttribute(\"hidden\", hold.authored);\n }\n }\n this.#mark(region, stack);\n }\n }\n\n /** Writes `hidden` where it moves, unless a hold above this one carries the region's. */\n #write(region: HTMLElement, hidden: boolean): void {\n const stack = this.#stack(region);\n const hold = this.#take(stack) ?? this.#acquire(region, stack);\n const shows = !this.#covered(region, stack, hold);\n hold.written = hidden;\n if (shows && region.hidden !== hidden) region.hidden = hidden;\n this.#mark(region, stack);\n }\n\n /** Returns a region this instance hid; one it never hid keeps what it carries. */\n #give(region: HTMLElement): void {\n const stack = this.#held(region);\n if (stack && this.#take(stack)?.written) this.#write(region, false);\n }\n\n /** The region's stack, while it has one or carries this owner's record. */\n #held(region: HTMLElement): RegionHold[] | undefined {\n return StateRegions.#registry.stacks.has(region) || region.hasAttribute(this.#record)\n ? this.#stack(region)\n : undefined;\n }\n\n /** The region's stack, read from its records the first time. */\n #stack(region: HTMLElement): RegionHold[] {\n const stacks = StateRegions.#registry.stacks;\n const stack = stacks.get(region) ?? this.#seed(region);\n stacks.set(region, stack);\n return stack;\n }\n\n /** The holds a copy's records describe, each above every hold its record lists beneath. */\n #seed(region: HTMLElement): RegionHold[] {\n const found: (RegionRecord & { readonly record: string })[] = [];\n for (const record of StateRegions.#registry.names) {\n const read = this.#recorded(region, record);\n if (read) found.push({ ...read, record });\n }\n const depth = ({ beneath }: (typeof found)[number]) =>\n beneath.filter((name) => found.some((other) => other.record === name)).length;\n return found\n .sort((one, other) => depth(one) - depth(other))\n .map(({ record, authored, written, linked }, index) => ({\n instance: null,\n record,\n authored,\n written,\n linked: linked && index > 0,\n }));\n }\n\n /** Its hold in `stack`, taking over one read from its record. */\n #take(stack: RegionHold[]): RegionHold | undefined {\n const hold =\n stack.find((held) => held.instance === this) ??\n stack.find((held) => held.instance === null && held.record === this.#record);\n if (hold) hold.instance = this;\n return hold;\n }\n\n /** Whether a hold above `hold` carries the region's visibility, then that hold's. */\n #covered(region: HTMLElement, stack: readonly RegionHold[], hold: RegionHold): boolean {\n return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === region.hidden);\n }\n\n /** A new hold on top, over the top hold while the region shows what it wrote. */\n #acquire(region: HTMLElement, stack: RegionHold[]): RegionHold {\n const top = stack.at(-1);\n const over = top && region.hidden === top.written ? top : undefined;\n const hold: RegionHold = {\n instance: this,\n record: this.#record,\n authored: over ? over.authored : region.getAttribute(\"hidden\"),\n written: false,\n linked: over !== undefined,\n };\n stack.push(hold);\n return hold;\n }\n\n /** Leaves each owner's record of its topmost hold, or none where nothing needs one. */\n #mark(region: HTMLElement, stack: readonly RegionHold[]): void {\n const records = new Map<string, string | null>([[this.#record, null]]);\n stack.forEach((hold, index) => {\n const names = stack.slice(0, index).map((under) => under.record);\n const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);\n const marker = hold.linked || beneath.length === 0 ? [] : [false];\n records.set(\n hold.record,\n region.getAttribute(\"hidden\") === hold.authored &&\n beneath.length === 0 &&\n index === stack.length - 1\n ? null\n : JSON.stringify([hold.authored, hold.written, ...marker, ...beneath]),\n );\n });\n for (const [name, recorded] of records) {\n if (region.getAttribute(name) === recorded) continue;\n if (recorded === null) region.removeAttribute(name);\n else region.setAttribute(name, recorded);\n }\n }\n\n /**\n * The author's `hidden`, the visibility written last, whether it hands back the visibility\n * beneath it (`false` before the names when it does not) and the records beneath that the\n * record `name` on `region` holds.\n */\n #recorded(region: HTMLElement, name: string): RegionRecord | null {\n try {\n const parsed: unknown = JSON.parse(region.getAttribute(name) ?? \"null\");\n if (!Array.isArray(parsed)) return null;\n const [authored, written, ...rest] = parsed as unknown[];\n if (authored !== null && typeof authored !== \"string\") return null;\n const linked = rest[0] !== false;\n const beneath = linked ? rest : rest.slice(1);\n if (!beneath.every((under): under is string => typeof under === \"string\")) return null;\n return typeof written === \"boolean\" ? { authored, written, linked, beneath } : null;\n } catch {\n return null;\n }\n }\n\n /** The declared regions that sit within `host`, which may be the host itself. */\n #inside(host: Element, regions: readonly HTMLElement[]): HTMLElement[] {\n return regions.filter((region) => host.contains(region));\n }\n}\n","/**\n * JSON string-list parsing for the controllers that take a list as a Value.\n *\n * Stimulus offers an `Array` Value type, but its reader runs `JSON.parse` inside\n * the value observer — **before** the `<name>ValueChanged` callback — and\n * rethrows on malformed text. The throw propagates out of the observer that runs\n * during connection, so a single unparseable attribute stops the controller from\n * connecting at all: no lifecycle callback runs and the element is left inert\n * rather than degraded. A consumer cannot guard against it either, because the\n * callback that would hold the `try` never runs.\n *\n * Declaring the attribute as a `String` Value and parsing it here keeps a\n * malformed declaration local to the value it declares. The attribute text is\n * identical either way (`'[\"a\", \"b\"]'`), so markup does not change with the\n * declaration.\n */\n\n/**\n * Parses `raw` as a JSON array of strings.\n *\n * Returns `fallback` when the text is absent, unparseable, or not an array, so a\n * malformed declaration behaves like an omitted one. A parsed array keeps only\n * its string entries: every caller indexes or compares strings, and a stray\n * number would otherwise sit in the list without ever matching.\n *\n * An explicit `\"[]\"` is honoured as an empty list rather than falling back — it\n * is the only way to declare \"none\" against a non-empty default.\n *\n * @param raw - the attribute text, e.g. `'[\"password\"]'`.\n * @param fallback - the list to use when `raw` declares nothing usable.\n */\nexport function parseStringList(raw: string, fallback: readonly string[] = []): string[] {\n // The common case is an undeclared attribute, and `JSON.parse(\"\")` answers it\n // by throwing. Reaching the same fallback without building an exception keeps\n // the callers that ask per element off that path.\n const text = raw.trim();\n if (text.length === 0) return [...fallback];\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return [...fallback];\n }\n\n if (!Array.isArray(parsed)) return [...fallback];\n return parsed.filter((entry): entry is string => typeof entry === \"string\");\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { ListenerSet } from \"../utils/listener_set\";\nimport { NUMBER_BOUNDS, type NumberValueConstraints } from \"../utils/number_bounds\";\nimport { NumberValueReader } from \"../utils/number_value\";\nimport { MAX_TIMER_DELAY_MS, SafeTimeout } from \"../utils/safe_timeout\";\nimport { StateRegions } from \"../utils/state_regions\";\nimport { parseStringList } from \"../utils/string_list\";\n\n/** Activity signals watched unless the consumer declares its own list. */\nconst DEFAULT_ACTIVITY_EVENTS = [\n \"mousemove\",\n \"mousedown\",\n \"keydown\",\n \"wheel\",\n \"touchstart\",\n \"scroll\",\n];\n\n/**\n * Headless inactivity / session-timeout detector: fires `idle` after `timeout` ms\n * with no user activity, an optional `prompt` `promptBefore` ms earlier, and\n * `active` when the user returns (no dedicated APG pattern; supports WCAG 2.2.1 by\n * giving the app a warning hook before a timeout).\n *\n * Markup contract (identifier: `stimeo--idle`):\n * <body data-controller=\"stimeo--idle\"\n * data-stimeo--idle-timeout-value=\"900000\"\n * data-stimeo--idle-prompt-before-value=\"60000\"\n * data-action=\"stimeo--idle:prompt->session#warn\n * stimeo--idle:idle->session#logout\">\n * <p data-stimeo--idle-target=\"prompt\" hidden>Your session expires in a minute.</p>\n * <p data-stimeo--idle-target=\"idle\" hidden>You have been signed out.</p>\n * </body>\n *\n * Activity events (`events`, passive) are watched on `document` with capture so\n * non-bubbling ones like `scroll` are seen anywhere; returning to a hidden tab\n * (`visibilitychange` → visible) counts as activity too. The controller element carries\n * `data-prompt` while the warning stands and `data-idle` once the timeout is reached,\n * and the optional `prompt` / `idle` targets are the regions those phases reveal:\n * declared where the page has something to show, shown while their phase holds and\n * hidden otherwise, whatever visibility the markup was authored with.\n *\n * `prompt` dispatches `{ remaining }`, the milliseconds left before `idle`: `promptBefore`\n * when the warning comes on schedule, and less when a declaration changed mid-cycle has\n * already put the cycle inside the warning window. `idle` and `active` dispatch `{}`.\n *\n * The three Values follow a change made while connected (a Turbo morph, a script).\n * `events` swaps the watched set: the old listeners go and the declared ones are added\n * once. `timeout` and `promptBefore` move the pending checks to the deadlines the new\n * declaration sets from the same last activity, so the time already spent inactive\n * counts; a deadline the elapsed time has already passed is acted on at once, and a\n * cycle past both deadlines goes idle without a warning for a window that is over. A\n * phase already reached stays: a raised warning goes only on activity or at the\n * timeout, and an idle detector schedules nothing until activity arms the next cycle\n * with the declaration in force.\n *\n * @remarks\n * Behavior only — it renders no warning UI (pair with Dialog/Confirm) and never\n * touches the server session. Timers are owned by `SafeTimeout` and the\n * listeners are removed on `disconnect()` (Turbo navigation included). Place one on\n * the root element. Every visit reconnects the controller and re-arms the timeout\n * from that moment — `data-turbo-permanent` keeps the element, not the elapsed count.\n */\nexport class IdleController extends Controller<HTMLElement> {\n /** Numeric read boundaries share one reader for this controller instance. */\n readonly #numbers = new NumberValueReader();\n\n static override values = {\n timeout: { type: Number, default: 900_000 },\n promptBefore: { type: Number, default: 0 },\n // A JSON list read through `parseStringList` rather than Stimulus's `Array`\n // type: that reader throws out of the value observer before any callback\n // runs, so one malformed attribute would stop the detector connecting.\n events: { type: String, default: \"\" },\n };\n\n static valueConstraints = {\n timeout: NUMBER_BOUNDS.positiveTimer,\n promptBefore: NUMBER_BOUNDS.nonNegative,\n } satisfies NumberValueConstraints<typeof IdleController.values>;\n static override targets = [\"prompt\", \"idle\"];\n static events = [\"prompt\", \"idle\", \"active\"] as const;\n\n declare timeoutValue: number;\n declare promptBeforeValue: number;\n declare eventsValue: string;\n declare readonly promptTargets: HTMLElement[];\n declare readonly idleTargets: HTMLElement[];\n\n /** The regions of the warning window, revealed alongside `data-prompt`. */\n readonly #promptRegions = new StateRegions(\n { whenTrue: () => this.promptTargets },\n this.identifier,\n );\n /** The regions of the elapsed timeout, revealed alongside `data-idle`. */\n readonly #idleRegions = new StateRegions({ whenTrue: () => this.idleTargets }, this.identifier);\n\n readonly #timeouts = new SafeTimeout();\n #idle = false;\n #prompted = false;\n /** Timestamp of the last activity; the timers self-reschedule against it. */\n #lastActivity = 0;\n /** Whether the controller is between `connect()` and `disconnect()`. */\n #connected = false;\n /**\n * The activity listeners on `document`. Releasing them goes through the signal they\n * were registered with, so the removal always matches the registration — capture flag\n * included — whatever `events` says by then.\n */\n readonly #activityListeners = new ListenerSet();\n /** The `visibilitychange` listener, held for the whole connection. */\n readonly #visibilityListeners = new ListenerSet();\n /** Activity types the listeners were last registered for, which a new `events` is compared with. */\n #boundEvents: string[] = [];\n\n readonly #onActivity = (): void => {\n // Hot path (fires on every mousemove/scroll/wheel): just record the time. The\n // prompt/idle timers re-check this when they fire and reschedule if needed, so we\n // never tear down and re-create timers on each event (no per-event timer churn).\n this.#lastActivity = Date.now();\n if (this.#idle || this.#prompted) {\n // We were already idle/prompted, so the timers have lapsed — wake and re-arm.\n this.#idle = false;\n this.#prompted = false;\n this.#reflect();\n this.dispatch(\"active\", { detail: {} });\n this.#arm();\n }\n };\n\n readonly #onVisibility = (): void => {\n // Returning to the tab is activity; leaving it keeps the clock running (being\n // away counts toward the timeout).\n if (document.visibilityState === \"visible\") this.#onActivity();\n };\n\n override connect(): void {\n // Connecting always starts a fresh cycle (#arm() re-bases the clock), so the phase\n // that arrived with the DOM — a restored Turbo snapshot, a moved element — describes\n // a period this instance is not in. Reflecting the fresh cycle drops it; leaving it\n // would claim the user is idle for the whole next active window with no `active` to\n // correct it. Normalizing is not a transition, so nothing is dispatched here.\n this.#connected = true;\n this.#idle = false;\n this.#prompted = false;\n this.#reflect();\n this.#bindActivity(parseStringList(this.eventsValue, DEFAULT_ACTIVITY_EVENTS));\n this.#visibilityListeners.add(document, \"visibilitychange\", this.#onVisibility);\n this.#arm();\n }\n\n override disconnect(): void {\n this.#connected = false;\n this.#activityListeners.dispose();\n this.#visibilityListeners.dispose();\n this.#timeouts.clearAll();\n }\n\n /**\n * Swaps the watched activity set for the one a runtime change declares. A declaration\n * that parses to the list already registered changes nothing. Stimulus also calls this\n * ahead of `connect()`, which registers the list itself.\n */\n eventsValueChanged(): void {\n if (!this.#connected) return;\n const types = parseStringList(this.eventsValue, DEFAULT_ACTIVITY_EVENTS);\n if (\n types.length === this.#boundEvents.length &&\n types.every((type, index) => type === this.#boundEvents[index])\n )\n return;\n this.#bindActivity(types);\n }\n\n /**\n * Moves the pending checks to the deadlines a runtime change of `timeout` sets, from the\n * same last activity. Stimulus also calls this ahead of `connect()`, which arms the cycle\n * itself.\n */\n timeoutValueChanged(): void {\n if (this.#connected) this.#schedule();\n }\n\n /**\n * Moves the pending warning to the deadline a runtime change of `promptBefore` sets, from\n * the same last activity. Stimulus also calls this ahead of `connect()`, which arms the\n * cycle itself.\n */\n promptBeforeValueChanged(): void {\n if (this.#connected) this.#schedule();\n }\n\n /** Replaces the registered activity listeners with one passive capture listener per type. */\n #bindActivity(types: string[]): void {\n this.#activityListeners.dispose();\n for (const type of types) {\n this.#activityListeners.add(document, type, this.#onActivity, {\n passive: true,\n capture: true,\n });\n }\n this.#boundEvents = types;\n }\n\n /** Starts a cycle: the activity baseline is now, and the checks are scheduled from it. */\n #arm(): void {\n this.#lastActivity = Date.now();\n this.#schedule();\n }\n\n /**\n * Schedules the pending checks from the last activity and the current declarations,\n * replacing whatever was pending. A deadline already behind the elapsed time is checked\n * at once. The idle check is scheduled first, so when both deadlines have passed it runs\n * first and the warning check finds the cycle over. A warning that comes due at once\n * reports the time left from now rather than the whole window. A raised warning is not\n * scheduled again, and an idle detector schedules nothing.\n *\n * @stimeoRuntimeOnly `timeout` and `promptBefore` time the checks this call schedules.\n */\n #schedule(): void {\n this.#timeouts.clearAll();\n if (this.#idle) return;\n const elapsed = Date.now() - this.#lastActivity;\n const timeout = this.#safeTimeout;\n const prompt = this.#safePromptBefore;\n this.#timeouts.set(() => this.#checkIdle(), this.#delayFor(timeout - elapsed));\n if (this.#prompted || prompt <= 0 || prompt >= timeout) return;\n const left = Math.min(prompt, timeout - elapsed);\n this.#timeouts.set(() => this.#checkPrompt(left), this.#delayFor(timeout - prompt - elapsed));\n }\n\n /** The platform delay for a deadline `remaining` ms away: at once when past, capped when far. */\n #delayFor(remaining: number): number {\n return Math.min(Math.max(remaining, 0), MAX_TIMER_DELAY_MS);\n }\n\n /**\n * Idle-timer callback: go idle only if there has genuinely been no activity for\n * `timeout`; otherwise reschedule for the remaining time. This lets activity events\n * stay O(1) (a timestamp write) while the deadline still tracks the last activity.\n * A clock rewind can put that deadline beyond one platform timer; capped waits\n * recheck the same baseline without advancing the idle transition.\n *\n * @stimeoRuntimeOnly `timeout` sets the deadline this one check compares against; the phase it\n * shows follows the elapsed time.\n */\n #checkIdle(): void {\n const remaining = this.#safeTimeout - (Date.now() - this.#lastActivity);\n if (remaining > 0) {\n this.#timeouts.set(() => this.#checkIdle(), Math.min(remaining, MAX_TIMER_DELAY_MS));\n return;\n }\n this.#idle = true;\n this.#prompted = false;\n this.#reflect();\n this.dispatch(\"idle\", { detail: {} });\n }\n\n /**\n * Prompt-timer callback: warn at `promptBefore` before the idle deadline. A window\n * narrowed while the cycle runs can push the warning onto the deadline or past it,\n * and the phase it belongs to is the one before idle, so a lapsed cycle keeps the\n * phase it reached. The next window opens with the cycle that activity arms.\n * Clock rewinds use capped waits while retaining the same warning deadline.\n *\n * @param left - the time before idle that the warning reports; a check that has to wait\n * again reports the whole window when it comes due.\n * @stimeoRuntimeOnly `timeout` and `promptBefore` set the deadlines this one check compares\n * against; the phase it shows follows the elapsed time.\n */\n #checkPrompt(left = this.#safePromptBefore): void {\n if (this.#idle) return;\n const remaining =\n this.#safeTimeout - this.#safePromptBefore - (Date.now() - this.#lastActivity);\n if (remaining > 0) {\n this.#timeouts.set(() => this.#checkPrompt(), Math.min(remaining, MAX_TIMER_DELAY_MS));\n return;\n }\n this.#prompted = true;\n this.#reflect();\n this.dispatch(\"prompt\", { detail: { remaining: left } });\n }\n\n /** Applies the current phase to a warning region inserted or replaced at runtime. */\n promptTargetConnected(): void {\n this.#promptRegions.reflect(this.element, this.#prompted);\n }\n\n /** Applies the current phase to an idle region inserted or replaced at runtime. */\n idleTargetConnected(): void {\n this.#idleRegions.reflect(this.element, this.#idle);\n }\n\n /**\n * Writes the phase to the element and to the declared regions. Both hooks and both\n * regions are a pure function of the two flags, so every place that moves a flag ends\n * here and the markup can never disagree with the phase the detector is in.\n */\n #reflect(): void {\n const { element } = this;\n if (this.#prompted) element.setAttribute(\"data-prompt\", \"true\");\n else element.removeAttribute(\"data-prompt\");\n if (this.#idle) element.setAttribute(\"data-idle\", \"true\");\n else element.removeAttribute(\"data-idle\");\n this.#promptRegions.reflect(element, this.#prompted);\n this.#idleRegions.reflect(element, this.#idle);\n }\n /** Current `timeout` declaration resolved against its numeric contract. */\n get #safeTimeout(): number {\n return this.#numbers.read(\n this,\n \"timeout\",\n this.timeoutValue,\n IdleController.values.timeout.default,\n IdleController.valueConstraints.timeout,\n );\n }\n\n /** Current `promptBefore` declaration resolved against its numeric contract. */\n get #safePromptBefore(): number {\n return this.#numbers.read(\n this,\n \"promptBefore\",\n this.promptBeforeValue,\n IdleController.values.promptBefore.default,\n IdleController.valueConstraints.promptBefore,\n );\n }\n}\n"]}
|
|
@@ -24,7 +24,7 @@ declare function applyMask(value: string, pattern: string, tokens: ReadonlyMap<s
|
|
|
24
24
|
* Headless **input mask** — formats a field in place against a fixed pattern
|
|
25
25
|
* (`9`=digit, `a`=letter, `*`=alphanumeric, others literal), preserving the caret,
|
|
26
26
|
* rejecting invalid characters, and syncing the raw value to a hidden field. No
|
|
27
|
-
* dedicated APG pattern;
|
|
27
|
+
* dedicated APG pattern; `stimeo--currency-input` owns money-specific formatting.
|
|
28
28
|
*
|
|
29
29
|
* Markup contract (identifier: `stimeo--input-mask`, on the `<input>`):
|
|
30
30
|
* <input type="text" inputmode="numeric"
|
|
@@ -53,7 +53,10 @@ declare function applyMask(value: string, pattern: string, tokens: ReadonlyMap<s
|
|
|
53
53
|
* exactly where it was. Backspace and Delete always progress: when the keystroke
|
|
54
54
|
* only removed literals the mask re-inserts, the significant character it aimed at
|
|
55
55
|
* goes instead. Events fired mid-IME-composition are ignored; the confirmed text is
|
|
56
|
-
* formatted once on `compositionend`.
|
|
56
|
+
* formatted once on `compositionend`. A `pattern` / `tokens` / `unmaskToHidden`
|
|
57
|
+
* change that arrives mid-composition leaves the uncommitted text alone and applies
|
|
58
|
+
* with that same format: as the user's `change` when the composition committed
|
|
59
|
+
* text, as `reconcile` when it ended on the text it began with.
|
|
57
60
|
*
|
|
58
61
|
* It reflects `data-mask-complete` / `data-mask-empty`, dispatches
|
|
59
62
|
* `stimeo--input-mask:change` only when a user edit moves the committed value, and
|
|
@@ -1,55 +1,71 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
var COMPOSITION_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
4
|
+
"insertCompositionText",
|
|
5
|
+
"insertFromComposition",
|
|
6
|
+
"deleteCompositionText",
|
|
7
|
+
"deleteByComposition"
|
|
8
|
+
]);
|
|
6
9
|
var CompositionTracker = class {
|
|
7
10
|
#observedTargets = /* @__PURE__ */ new Set();
|
|
8
11
|
#activeTargets = /* @__PURE__ */ new Set();
|
|
9
12
|
#onStart;
|
|
10
13
|
#onEnd;
|
|
14
|
+
#confirmedTarget = null;
|
|
11
15
|
constructor(options = {}) {
|
|
12
16
|
this.#onStart = options.onStart;
|
|
13
17
|
this.#onEnd = options.onEnd;
|
|
14
18
|
}
|
|
15
|
-
/** Starts lifecycle tracking for `target`; repeated calls are idempotent. */
|
|
16
19
|
observe(target) {
|
|
17
20
|
if (this.#observedTargets.has(target)) return;
|
|
18
21
|
target.addEventListener("compositionstart", this.#handleStart);
|
|
19
22
|
target.addEventListener("compositionend", this.#handleEnd);
|
|
23
|
+
target.addEventListener("keydown", this.#handleKeydown);
|
|
20
24
|
this.#observedTargets.add(target);
|
|
21
25
|
}
|
|
22
|
-
/** Stops tracking one target and clears any active composition it owned. */
|
|
23
26
|
unobserve(target) {
|
|
24
27
|
if (!this.#observedTargets.delete(target)) return;
|
|
25
28
|
target.removeEventListener("compositionstart", this.#handleStart);
|
|
26
29
|
target.removeEventListener("compositionend", this.#handleEnd);
|
|
30
|
+
target.removeEventListener("keydown", this.#handleKeydown);
|
|
27
31
|
this.#activeTargets.delete(target);
|
|
32
|
+
if (this.#confirmedTarget === target) this.#confirmedTarget = null;
|
|
28
33
|
}
|
|
29
|
-
/** Releases every listener and clears state so reconnect starts cleanly. */
|
|
30
34
|
disconnect() {
|
|
31
35
|
for (const target of this.#observedTargets) {
|
|
32
36
|
target.removeEventListener("compositionstart", this.#handleStart);
|
|
33
37
|
target.removeEventListener("compositionend", this.#handleEnd);
|
|
38
|
+
target.removeEventListener("keydown", this.#handleKeydown);
|
|
34
39
|
}
|
|
35
40
|
this.#observedTargets.clear();
|
|
36
41
|
this.#activeTargets.clear();
|
|
42
|
+
this.#confirmedTarget = null;
|
|
37
43
|
}
|
|
38
|
-
/** True when lifecycle tracking or the current event reports composition. */
|
|
39
44
|
isComposing(event) {
|
|
40
45
|
return this.#activeTargets.size > 0 || event?.isComposing === true;
|
|
41
46
|
}
|
|
47
|
+
consumesConfirmedInput(event) {
|
|
48
|
+
const confirmed = this.#confirmedTarget;
|
|
49
|
+
this.#confirmedTarget = null;
|
|
50
|
+
if (confirmed === null || confirmed !== event.target) return false;
|
|
51
|
+
const inputType = event.inputType;
|
|
52
|
+
return !inputType || COMPOSITION_INPUT_TYPES.has(inputType);
|
|
53
|
+
}
|
|
42
54
|
#handleStart = (event) => {
|
|
55
|
+
this.#confirmedTarget = null;
|
|
43
56
|
if (event.currentTarget) this.#activeTargets.add(event.currentTarget);
|
|
44
57
|
this.#onStart?.(event);
|
|
45
58
|
};
|
|
46
59
|
#handleEnd = (event) => {
|
|
47
60
|
if (event.currentTarget) this.#activeTargets.delete(event.currentTarget);
|
|
61
|
+
this.#confirmedTarget = event.target;
|
|
48
62
|
this.#onEnd?.(event);
|
|
49
63
|
};
|
|
64
|
+
#handleKeydown = () => {
|
|
65
|
+
this.#confirmedTarget = null;
|
|
66
|
+
};
|
|
50
67
|
};
|
|
51
68
|
|
|
52
|
-
// src/utils/declared_value.ts
|
|
53
69
|
function parseDeclared(raw, parse, fallback) {
|
|
54
70
|
try {
|
|
55
71
|
return parse(raw);
|
|
@@ -73,7 +89,6 @@ function parseJsonObject(raw) {
|
|
|
73
89
|
return parsed;
|
|
74
90
|
}
|
|
75
91
|
|
|
76
|
-
// src/utils/focus_candidate.ts
|
|
77
92
|
function inheritsFieldsetDisabled(control) {
|
|
78
93
|
let fieldset = control.closest("fieldset[disabled]");
|
|
79
94
|
while (fieldset) {
|
|
@@ -84,7 +99,6 @@ function inheritsFieldsetDisabled(control) {
|
|
|
84
99
|
return false;
|
|
85
100
|
}
|
|
86
101
|
|
|
87
|
-
// src/utils/half_width.ts
|
|
88
102
|
var FULL_WIDTH_SHIFT = 65248;
|
|
89
103
|
function halfWidthChar(char) {
|
|
90
104
|
if (char >= "\uFF01" && char <= "\uFF5E") {
|
|
@@ -93,13 +107,76 @@ function halfWidthChar(char) {
|
|
|
93
107
|
return char === "\u3000" ? " " : char;
|
|
94
108
|
}
|
|
95
109
|
|
|
96
|
-
|
|
110
|
+
var ListenerSet = class {
|
|
111
|
+
#abort = new AbortController();
|
|
112
|
+
add(target, type, handler, options) {
|
|
113
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
114
|
+
}
|
|
115
|
+
dispose() {
|
|
116
|
+
this.#abort.abort();
|
|
117
|
+
this.#abort = new AbortController();
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
var MicrotaskCoalescer = class {
|
|
122
|
+
#run;
|
|
123
|
+
#queued = false;
|
|
124
|
+
#active = false;
|
|
125
|
+
#generation = 0;
|
|
126
|
+
constructor(run) {
|
|
127
|
+
this.#run = run;
|
|
128
|
+
}
|
|
129
|
+
activate() {
|
|
130
|
+
this.#active = true;
|
|
131
|
+
}
|
|
132
|
+
cancel() {
|
|
133
|
+
this.#active = false;
|
|
134
|
+
this.#queued = false;
|
|
135
|
+
this.#generation += 1;
|
|
136
|
+
}
|
|
137
|
+
schedule() {
|
|
138
|
+
if (!this.#active || this.#queued) return;
|
|
139
|
+
this.#queued = true;
|
|
140
|
+
const generation = this.#generation;
|
|
141
|
+
queueMicrotask(() => {
|
|
142
|
+
if (generation !== this.#generation || !this.#queued || !this.#active) return;
|
|
143
|
+
this.#queued = false;
|
|
144
|
+
this.#run();
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
var MorphRenderWatcher = class {
|
|
150
|
+
#listeners = new ListenerSet();
|
|
151
|
+
#pass;
|
|
152
|
+
#element = null;
|
|
153
|
+
#onMorph = () => this.#pass.schedule();
|
|
154
|
+
constructor(run) {
|
|
155
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
156
|
+
}
|
|
157
|
+
observe(element) {
|
|
158
|
+
if (this.#element === element) return;
|
|
159
|
+
this.disconnect();
|
|
160
|
+
this.#element = element;
|
|
161
|
+
this.#pass.activate();
|
|
162
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
163
|
+
}
|
|
164
|
+
schedule() {
|
|
165
|
+
this.#pass.schedule();
|
|
166
|
+
}
|
|
167
|
+
disconnect() {
|
|
168
|
+
this.#listeners.dispose();
|
|
169
|
+
this.#pass.cancel();
|
|
170
|
+
this.#element = null;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
97
174
|
var DEFAULT_TOKENS = {
|
|
98
175
|
"9": "\\d",
|
|
99
176
|
a: "[A-Za-z]",
|
|
100
177
|
"*": "[A-Za-z0-9]"
|
|
101
178
|
};
|
|
102
|
-
var UNMASK_ATTR = "
|
|
179
|
+
var UNMASK_ATTR = "unmask";
|
|
103
180
|
function acceptedForm(regex, char) {
|
|
104
181
|
if (regex.test(char)) return char;
|
|
105
182
|
const half = halfWidthChar(char);
|
|
@@ -176,6 +253,9 @@ function tokenCharOffset(tokenFlags, n) {
|
|
|
176
253
|
return -1;
|
|
177
254
|
}
|
|
178
255
|
var InputMaskController = class extends Controller {
|
|
256
|
+
get #unmaskAttribute() {
|
|
257
|
+
return `data-${this.identifier}-${UNMASK_ATTR}`;
|
|
258
|
+
}
|
|
179
259
|
static values = {
|
|
180
260
|
pattern: { type: String, default: "" },
|
|
181
261
|
tokens: { type: String, default: "" },
|
|
@@ -183,14 +263,19 @@ var InputMaskController = class extends Controller {
|
|
|
183
263
|
};
|
|
184
264
|
static actions = ["format"];
|
|
185
265
|
static events = ["change", "reconcile"];
|
|
186
|
-
/** The value this controller last committed, and the baseline both events compare. */
|
|
187
266
|
#lastValue = null;
|
|
188
267
|
#started = false;
|
|
189
|
-
/** Validated token map; the hot path never parses the `tokens` declaration. */
|
|
190
268
|
#tokens = compileTokens("");
|
|
191
|
-
|
|
192
|
-
#composition = new CompositionTracker({
|
|
193
|
-
|
|
269
|
+
#textBeforeComposition = "";
|
|
270
|
+
#composition = new CompositionTracker({
|
|
271
|
+
onStart: () => {
|
|
272
|
+
this.#textBeforeComposition = this.element.value;
|
|
273
|
+
},
|
|
274
|
+
onEnd: () => {
|
|
275
|
+
const committedNothing = this.element.value === this.#textBeforeComposition;
|
|
276
|
+
this.#reformat(committedNothing ? "reconcile" : "edit");
|
|
277
|
+
}
|
|
278
|
+
});
|
|
194
279
|
tokensValueChanged() {
|
|
195
280
|
this.#tokens = compileTokens(this.tokensValue);
|
|
196
281
|
if (this.#started) this.#reformat("reconcile");
|
|
@@ -198,7 +283,6 @@ var InputMaskController = class extends Controller {
|
|
|
198
283
|
patternValueChanged() {
|
|
199
284
|
if (this.#started) this.#reformat("reconcile");
|
|
200
285
|
}
|
|
201
|
-
/** Clears a sink it stops maintaining so a submit cannot carry a stale raw value. */
|
|
202
286
|
unmaskToHiddenValueChanged() {
|
|
203
287
|
if (!this.#started) return;
|
|
204
288
|
if (!this.unmaskToHiddenValue) {
|
|
@@ -207,26 +291,25 @@ var InputMaskController = class extends Controller {
|
|
|
207
291
|
}
|
|
208
292
|
this.#reformat("reconcile");
|
|
209
293
|
}
|
|
294
|
+
#morph = new MorphRenderWatcher(() => this.#reformat("reconcile"));
|
|
210
295
|
connect() {
|
|
296
|
+
this.#morph.observe(this.element);
|
|
211
297
|
this.#started = true;
|
|
212
298
|
this.#composition.observe(this.element);
|
|
213
299
|
this.#lastValue = this.element.value;
|
|
214
300
|
this.#reformat("reconcile");
|
|
215
301
|
}
|
|
216
302
|
disconnect() {
|
|
303
|
+
this.#morph.disconnect();
|
|
217
304
|
this.#started = false;
|
|
218
305
|
this.#composition.disconnect();
|
|
219
306
|
}
|
|
220
|
-
/** Formats the field on input, preserving the caret. Bound via `data-action`. */
|
|
221
307
|
format(event) {
|
|
222
308
|
if (this.#composition.isComposing(event)) return;
|
|
223
309
|
this.#reformat("edit", deletionOf(event));
|
|
224
310
|
}
|
|
225
|
-
/**
|
|
226
|
-
* Core reformat: mask the current value, restore the caret, sync the sink and
|
|
227
|
-
* the state hooks, and report a moved value under the event `cause` selects.
|
|
228
|
-
*/
|
|
229
311
|
#reformat(cause, deletion = null) {
|
|
312
|
+
if (this.#composition.isComposing()) return;
|
|
230
313
|
const input = this.element;
|
|
231
314
|
const raw = input.value;
|
|
232
315
|
if (this.patternValue === "") {
|
|
@@ -255,8 +338,10 @@ var InputMaskController = class extends Controller {
|
|
|
255
338
|
this.#lastValue = raw;
|
|
256
339
|
return;
|
|
257
340
|
}
|
|
258
|
-
input.value
|
|
259
|
-
|
|
341
|
+
if (input.value !== result.masked) {
|
|
342
|
+
input.value = result.masked;
|
|
343
|
+
this.#restoreCaret(result.tokenFlags, anchor);
|
|
344
|
+
}
|
|
260
345
|
this.#publish(result, result.masked);
|
|
261
346
|
if (result.masked === this.#lastValue) return;
|
|
262
347
|
this.#lastValue = result.masked;
|
|
@@ -264,7 +349,6 @@ var InputMaskController = class extends Controller {
|
|
|
264
349
|
if (cause === "edit") this.dispatch("change", { detail });
|
|
265
350
|
else this.dispatch("reconcile", { detail });
|
|
266
351
|
}
|
|
267
|
-
/** Syncs the raw-value sink and the state hooks for the `shown` field text. */
|
|
268
352
|
#publish(result, shown) {
|
|
269
353
|
if (this.unmaskToHiddenValue) {
|
|
270
354
|
const sink = this.#resolveSink();
|
|
@@ -273,15 +357,9 @@ var InputMaskController = class extends Controller {
|
|
|
273
357
|
this.#flag("data-mask-complete", result.complete);
|
|
274
358
|
this.#flag("data-mask-empty", shown.length === 0);
|
|
275
359
|
}
|
|
276
|
-
/**
|
|
277
|
-
* How many token slots the text before the caret fills. Masking that prefix is
|
|
278
|
-
* what makes a rejected character — or a literal that also matches a token —
|
|
279
|
-
* count exactly as the rendering counts it, so the caret cannot drift.
|
|
280
|
-
*/
|
|
281
360
|
#significantBefore(prefix) {
|
|
282
361
|
return applyMask(prefix, this.patternValue, this.#tokens).unmasked.length;
|
|
283
362
|
}
|
|
284
|
-
/** Places the caret after the `n`-th token char (skipping following literals). */
|
|
285
363
|
#restoreCaret(tokenFlags, n) {
|
|
286
364
|
let position;
|
|
287
365
|
if (n <= 0) {
|
|
@@ -303,31 +381,14 @@ var InputMaskController = class extends Controller {
|
|
|
303
381
|
} catch {
|
|
304
382
|
}
|
|
305
383
|
}
|
|
306
|
-
/**
|
|
307
|
-
* The hidden raw-value sink for this input, resolved so several masked inputs
|
|
308
|
-
* can coexist in one form:
|
|
309
|
-
*
|
|
310
|
-
* 1. **Explicit pairing** — a sink whose attribute value names this input's
|
|
311
|
-
* `id` (`data-stimeo--input-mask-unmask="zip"`).
|
|
312
|
-
* 2. **Nearest container** — otherwise, walking up from the input, the first
|
|
313
|
-
* *value-less* sink in the closest ancestor. Wrapped input+sink pairs each
|
|
314
|
-
* find their own sink, and the single form-level sink keeps working
|
|
315
|
-
* unchanged. A sink claimed by another input's id is never matched here.
|
|
316
|
-
*
|
|
317
|
-
* Both steps only ever consider the associated form's own controls, so a sink
|
|
318
|
-
* belonging to a different form (or to none) is never written, while one the
|
|
319
|
-
* `form` attribute associates from elsewhere in the document still resolves —
|
|
320
|
-
* for either the input or the sink, neither of which needs to contain or be
|
|
321
|
-
* contained by the form. A form-less input reads the document instead.
|
|
322
|
-
*/
|
|
323
384
|
#resolveSink() {
|
|
324
385
|
const candidates = this.#sinkCandidates();
|
|
325
386
|
const id = this.element.id;
|
|
326
387
|
if (id.length > 0) {
|
|
327
|
-
const paired = candidates.find((sink) => sink.getAttribute(
|
|
388
|
+
const paired = candidates.find((sink) => sink.getAttribute(this.#unmaskAttribute) === id);
|
|
328
389
|
if (paired) return paired;
|
|
329
390
|
}
|
|
330
|
-
const free = candidates.filter((sink) => sink.getAttribute(
|
|
391
|
+
const free = candidates.filter((sink) => sink.getAttribute(this.#unmaskAttribute) === "");
|
|
331
392
|
for (let node = this.element.parentElement; node !== null; node = node.parentElement) {
|
|
332
393
|
const ancestor = node;
|
|
333
394
|
const sink = free.find((candidate) => ancestor.contains(candidate));
|
|
@@ -335,22 +396,17 @@ var InputMaskController = class extends Controller {
|
|
|
335
396
|
}
|
|
336
397
|
return null;
|
|
337
398
|
}
|
|
338
|
-
/**
|
|
339
|
-
* Every sink the input's form owns, in document order — one collection read per
|
|
340
|
-
* resolution, so the cost tracks the form's own controls and not the depth of
|
|
341
|
-
* the markup around it. `form.elements` lists controls the `form` attribute
|
|
342
|
-
* associates from anywhere in the document, and lists nothing another form owns.
|
|
343
|
-
*/
|
|
344
399
|
#sinkCandidates() {
|
|
345
400
|
const form = this.element.form;
|
|
346
401
|
if (form === null) {
|
|
347
|
-
return Array.from(
|
|
402
|
+
return Array.from(
|
|
403
|
+
document.querySelectorAll(`input[${this.#unmaskAttribute}]`)
|
|
404
|
+
);
|
|
348
405
|
}
|
|
349
406
|
return Array.from(form.elements).filter(
|
|
350
|
-
(element) => element instanceof HTMLInputElement && element.hasAttribute(
|
|
407
|
+
(element) => element instanceof HTMLInputElement && element.hasAttribute(this.#unmaskAttribute)
|
|
351
408
|
);
|
|
352
409
|
}
|
|
353
|
-
/** Sets a boolean `data-*` flag to `"true"` when `on`, else removes it. */
|
|
354
410
|
#flag(name, on) {
|
|
355
411
|
if (on) this.element.setAttribute(name, "true");
|
|
356
412
|
else this.element.removeAttribute(name);
|
|
@@ -358,5 +414,4 @@ var InputMaskController = class extends Controller {
|
|
|
358
414
|
};
|
|
359
415
|
|
|
360
416
|
export { InputMaskController, applyMask };
|
|
361
|
-
//# sourceMappingURL=input_mask_controller.js.map
|
|
362
417
|
//# sourceMappingURL=input_mask_controller.js.map
|