stimeo-ui 0.15.0 → 0.16.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 +178 -0
- package/README.md +120 -0
- package/dist/cable/index.js +63 -15
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.d.ts +30 -3
- package/dist/controllers/accordion_controller.js +98 -9
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +8 -7
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +3 -2
- package/dist/controllers/announcer_controller.js +8 -1
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.js +43 -2
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +1 -1
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.d.ts +10 -7
- package/dist/controllers/breadcrumb_controller.js +9 -8
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +4 -4
- package/dist/controllers/bulk_select_controller.js +7 -6
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +85 -23
- package/dist/controllers/calendar_controller.js +323 -123
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +67 -22
- package/dist/controllers/carousel_controller.js +263 -38
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +1 -1
- package/dist/controllers/character_counter_controller.js +40 -2
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.js +54 -10
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +28 -2
- package/dist/controllers/clipboard_controller.js +63 -5
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +25 -1
- package/dist/controllers/collapsible_controller.js +99 -14
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +29 -8
- package/dist/controllers/color_picker_controller.js +80 -34
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +10 -1
- package/dist/controllers/combobox_controller.js +106 -16
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +3 -3
- package/dist/controllers/command_palette_controller.js +35 -3
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.js +56 -11
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.js +3 -0
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +6 -0
- package/dist/controllers/context_menu_controller.js +32 -12
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +30 -1
- package/dist/controllers/countdown_controller.js +129 -26
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +74 -15
- package/dist/controllers/currency_input_controller.js +205 -58
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +63 -18
- package/dist/controllers/data_grid_controller.js +195 -29
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +34 -6
- package/dist/controllers/date_range_picker_controller.js +139 -27
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +10 -3
- package/dist/controllers/dialog_controller.js +35 -8
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.js +22 -4
- 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 +14 -1
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.js +1 -0
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +18 -10
- package/dist/controllers/drawer_controller.js +54 -19
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +9 -3
- package/dist/controllers/dropdown_controller.js +36 -9
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.js +34 -0
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.js +144 -51
- 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 +20 -6
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +26 -6
- package/dist/controllers/flash_controller.js +305 -46
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.js +1 -0
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +7 -5
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +19 -13
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.js +16 -2
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.js +19 -2
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +10 -2
- package/dist/controllers/hover_card_controller.js +40 -14
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +16 -3
- package/dist/controllers/idle_controller.js +90 -5
- 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 +65 -9
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.js +3 -0
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.js +11 -2
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +50 -8
- package/dist/controllers/listbox_controller.js +203 -45
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.js +0 -2
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +9 -3
- package/dist/controllers/menu_controller.js +45 -16
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +11 -0
- package/dist/controllers/menubar_controller.js +58 -24
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.js +9 -5
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +18 -3
- package/dist/controllers/multi_select_controller.js +278 -104
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +11 -0
- package/dist/controllers/navigation_menu_controller.js +48 -15
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.js +37 -8
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.js +9 -1
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +36 -8
- package/dist/controllers/number_input_controller.js +124 -21
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.js +14 -1
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +22 -7
- package/dist/controllers/otp_controller.js +198 -55
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +8 -12
- package/dist/controllers/overflow_indicator_controller.js +84 -13
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +26 -6
- package/dist/controllers/overflow_menu_controller.js +112 -41
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +19 -11
- package/dist/controllers/pagination_controller.js +74 -28
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +15 -1
- package/dist/controllers/password_reveal_controller.js +59 -2
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/persist_controller.js +30 -8
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.js +131 -52
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +9 -3
- package/dist/controllers/popover_controller.js +45 -11
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +1 -1
- package/dist/controllers/portal_controller.js +6 -2
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.js +16 -1
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.js +8 -4
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +6 -4
- package/dist/controllers/radio_group_controller.js +42 -17
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +49 -1
- package/dist/controllers/range_slider_controller.js +88 -42
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +14 -3
- package/dist/controllers/rating_controller.js +39 -15
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +24 -2
- package/dist/controllers/read_more_controller.js +69 -2
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/resizable_controller.d.ts +16 -2
- package/dist/controllers/resizable_controller.js +82 -22
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.js +5 -1
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.js +0 -1
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +3 -2
- package/dist/controllers/scrollspy_controller.js +6 -0
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +41 -14
- package/dist/controllers/separator_controller.js +66 -37
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +20 -3
- package/dist/controllers/sidebar_controller.js +77 -18
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.js +6 -1
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +45 -7
- package/dist/controllers/slider_controller.js +82 -47
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.js +11 -1
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.js +17 -2
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.js +10 -2
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +19 -17
- package/dist/controllers/step_indicator_controller.js +18 -17
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +34 -9
- package/dist/controllers/stepper_controller.js +101 -19
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +19 -0
- package/dist/controllers/stick_to_bottom_controller.js +75 -5
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +3 -2
- package/dist/controllers/submit_once_controller.js +16 -3
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +29 -7
- package/dist/controllers/switch_controller.js +101 -10
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +12 -0
- package/dist/controllers/tabs_controller.js +21 -2
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +15 -3
- package/dist/controllers/tags_input_controller.js +209 -59
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +20 -3
- package/dist/controllers/theme_controller.js +64 -14
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.js +23 -8
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +52 -15
- package/dist/controllers/toast_controller.js +337 -54
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +49 -7
- package/dist/controllers/toggle_group_controller.js +159 -23
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +32 -0
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +8 -0
- package/dist/controllers/tooltip_controller.js +39 -13
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.js +4 -0
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +39 -8
- package/dist/controllers/tree_view_controller.js +169 -16
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +4588 -1535
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +72 -6
- package/dist/inspector/cli.js +262 -51
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +309 -51
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +40 -40
- package/dist/inspector/manifest.json +525 -59
- package/dist/positioning/index.js +2 -0
- package/dist/positioning/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/arrow_step.ts","../../src/utils/attribute_lease.ts","../../src/utils/before_cache_reset.ts","../../src/utils/composition_tracker.ts","../../src/utils/microtask_coalescer.ts","../../src/utils/safe_timeout.ts","../../src/utils/stepped_value.ts","../../src/controllers/number_input_controller.ts"],"names":["position"],"mappings":";;;;;AA+FO,SAAS,oBAAA,CACd,KAAA,EACA,KAAA,GAAkC,EAAC,EAC1B;AACT,EAAA,IAAI,CAAC,KAAA,CAAM,GAAA,CAAI,UAAA,CAAW,OAAO,GAAG,OAAO,KAAA;AAC3C,EAAA,OACG,KAAA,CAAM,MAAA,IAAU,CAAC,KAAA,CAAM,QAAA,CAAS,KAAK,CAAA,IACrC,KAAA,CAAM,OAAA,IAAW,CAAC,KAAA,CAAM,QAAA,CAAS,MAAM,KACvC,KAAA,CAAM,OAAA,IAAW,CAAC,KAAA,CAAM,QAAA,CAAS,MAAM,CAAA,IACvC,KAAA,CAAM,QAAA,IAAY,CAAC,KAAA,CAAM,QAAA,CAAS,OAAO,CAAA;AAE9C;;;AClFO,IAAM,iBAAN,MAAkD;AAAA,EAC9C,UAAA;AAAA,EACA,QAAA,uBAAe,GAAA,EAA6B;AAAA;AAAA,EAGrD,YAAY,SAAA,EAAmB;AAC7B,IAAA,IAAA,CAAK,UAAA,GAAa,SAAA;AAAA,EACpB;AAAA;AAAA,EAGA,KAAA,CAAM,SAAY,KAAA,EAA4B;AAC5C,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,OAAO,CAAA;AAC1C,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,QAAA,CAAS,OAAA,GAAU,KAAA;AAAA,IACrB,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,QAAA,CAAS,IAAI,OAAA,EAAS;AAAA,QACzB,QAAA,EAAU,OAAA,CAAQ,YAAA,CAAa,IAAA,CAAK,UAAU,CAAA;AAAA,QAC9C,OAAA,EAAS;AAAA,OACV,CAAA;AAAA,IACH;AAEA,IAAA,IAAA,CAAK,QAAA,CAAS,SAAS,KAAK,CAAA;AAAA,EAC9B;AAAA;AAAA,EAGA,OAAO,OAAA,EAAkB;AACvB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,OAAO,CAAA;AACxC,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,IAAA,CAAK,QAAA,CAAS,OAAO,OAAO,CAAA;AAC5B,IAAA,MAAM,aAAa,OAAA,CAAQ,YAAA,CAAa,IAAA,CAAK,UAAU,MAAM,MAAA,CAAO,OAAA;AACpE,IAAA,IAAI,UAAA,EAAY,IAAA,CAAK,QAAA,CAAS,OAAA,EAAS,OAAO,QAAQ,CAAA;AAAA,EACxD;AAAA;AAAA,EAGA,QAAA,CAAS,SAAY,KAAA,EAA4B;AAC/C,IAAA,IAAI,OAAA,CAAQ,YAAA,CAAa,IAAA,CAAK,UAAU,MAAM,KAAA,EAAO;AACrD,IAAA,IAAI,KAAA,KAAU,IAAA,EAAM,OAAA,CAAQ,eAAA,CAAgB,KAAK,UAAU,CAAA;AAAA,SACtD,OAAA,CAAQ,YAAA,CAAa,IAAA,CAAK,UAAA,EAAY,KAAK,CAAA;AAAA,EAClD;AAAA;AAAA,EAGA,SAAA,GAAkB;AAChB,IAAA,KAAA,MAAW,OAAA,IAAW,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,OAAO,CAAA;AAAA,EAC7E;AACF,CAAA;;;ACzBO,IAAM,gBAAA,GAAN,MAAM,iBAAA,CAAiB;AAAA;AAAA,EAE5B,OAAgB,YAAA,mBAAe,IAAI,GAAA,EAAsB;AAAA;AAAA,EAGzD,OAAgB,iBAAiB,MAAY;AAC3C,IAAA,KAAA,MAAW,UAAA,IAAc,iBAAA,CAAiB,YAAA,EAAc,UAAA,CAAW,OAAA,EAAQ;AAAA,EAC7E,CAAA;AAAA,EAES,OAAA;AAAA;AAAA,EAGT,YAAY,MAAA,EAAoB;AAC9B,IAAA,IAAA,CAAK,OAAA,GAAU,MAAA;AAAA,EACjB;AAAA;AAAA,EAGA,QAAA,GAAiB;AACf,IAAA,MAAM,KAAA,GAAQ,iBAAA,CAAiB,YAAA,CAAa,IAAA,KAAS,CAAA;AACrD,IAAA,iBAAA,CAAiB,YAAA,CAAa,IAAI,IAAI,CAAA;AACtC,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,QAAA,CAAS,gBAAA,CAAiB,oBAAA,EAAsB,iBAAA,CAAiB,cAAc,CAAA;AAAA,IACjF;AAAA,EACF;AAAA;AAAA,EAGA,UAAA,GAAmB;AACjB,IAAA,iBAAA,CAAiB,YAAA,CAAa,OAAO,IAAI,CAAA;AACzC,IAAA,IAAI,iBAAA,CAAiB,YAAA,CAAa,IAAA,GAAO,CAAA,EAAG;AAC5C,IAAA,QAAA,CAAS,mBAAA,CAAoB,oBAAA,EAAsB,iBAAA,CAAiB,cAAc,CAAA;AAAA,EACpF;AACF,CAAA;;;ACnCO,IAAM,qBAAN,MAAyB;AAAA,EACrB,gBAAA,uBAAuB,GAAA,EAAiB;AAAA,EACxC,cAAA,uBAAqB,GAAA,EAAiB;AAAA,EACtC,QAAA;AAAA,EACA,MAAA;AAAA,EAET,WAAA,CAAY,OAAA,GAAqC,EAAC,EAAG;AACnD,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,OAAA;AACxB,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,KAAA;AAAA,EACxB;AAAA;AAAA,EAGA,QAAQ,MAAA,EAA2B;AACjC,IAAA,IAAI,IAAA,CAAK,gBAAA,CAAiB,GAAA,CAAI,MAAM,CAAA,EAAG;AACvC,IAAA,MAAA,CAAO,gBAAA,CAAiB,kBAAA,EAAoB,IAAA,CAAK,YAAY,CAAA;AAC7D,IAAA,MAAA,CAAO,gBAAA,CAAiB,gBAAA,EAAkB,IAAA,CAAK,UAAU,CAAA;AACzD,IAAA,IAAA,CAAK,gBAAA,CAAiB,IAAI,MAAM,CAAA;AAAA,EAClC;AAAA;AAAA,EAGA,UAAU,MAAA,EAA2B;AACnC,IAAA,IAAI,CAAC,IAAA,CAAK,gBAAA,CAAiB,MAAA,CAAO,MAAM,CAAA,EAAG;AAC3C,IAAA,MAAA,CAAO,mBAAA,CAAoB,kBAAA,EAAoB,IAAA,CAAK,YAAY,CAAA;AAChE,IAAA,MAAA,CAAO,mBAAA,CAAoB,gBAAA,EAAkB,IAAA,CAAK,UAAU,CAAA;AAC5D,IAAA,IAAA,CAAK,cAAA,CAAe,OAAO,MAAM,CAAA;AAAA,EACnC;AAAA;AAAA,EAGA,UAAA,GAAmB;AACjB,IAAA,KAAA,MAAW,MAAA,IAAU,KAAK,gBAAA,EAAkB;AAC1C,MAAA,MAAA,CAAO,mBAAA,CAAoB,kBAAA,EAAoB,IAAA,CAAK,YAAY,CAAA;AAChE,MAAA,MAAA,CAAO,mBAAA,CAAoB,gBAAA,EAAkB,IAAA,CAAK,UAAU,CAAA;AAAA,IAC9D;AACA,IAAA,IAAA,CAAK,iBAAiB,KAAA,EAAM;AAC5B,IAAA,IAAA,CAAK,eAAe,KAAA,EAAM;AAAA,EAC5B;AAAA;AAAA,EAGA,YAAY,KAAA,EAAoC;AAC9C,IAAA,OAAO,IAAA,CAAK,cAAA,CAAe,IAAA,GAAO,CAAA,IAAK,OAAO,WAAA,KAAgB,IAAA;AAAA,EAChE;AAAA,EAES,YAAA,GAAe,CAAC,KAAA,KAAuB;AAC9C,IAAA,IAAI,MAAM,aAAA,EAAe,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,MAAM,aAAa,CAAA;AACpE,IAAA,IAAA,CAAK,WAAW,KAAK,CAAA;AAAA,EACvB,CAAA;AAAA,EAES,UAAA,GAAa,CAAC,KAAA,KAAuB;AAC5C,IAAA,IAAI,MAAM,aAAA,EAAe,IAAA,CAAK,cAAA,CAAe,MAAA,CAAO,MAAM,aAAa,CAAA;AACvE,IAAA,IAAA,CAAK,SAAS,KAAK,CAAA;AAAA,EACrB,CAAA;AACF,CAAA;;;ACrCO,IAAM,qBAAN,MAAyB;AAAA,EACrB,IAAA;AAAA,EACT,OAAA,GAAU,KAAA;AAAA,EACV,OAAA,GAAU,KAAA;AAAA,EACV,WAAA,GAAc,CAAA;AAAA;AAAA,EAGd,YAAY,GAAA,EAAiB;AAC3B,IAAA,IAAA,CAAK,IAAA,GAAO,GAAA;AAAA,EACd;AAAA;AAAA,EAGA,QAAA,GAAiB;AACf,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AAAA,EACjB;AAAA;AAAA,EAGA,MAAA,GAAe;AACb,IAAA,IAAA,CAAK,OAAA,GAAU,KAAA;AACf,IAAA,IAAA,CAAK,OAAA,GAAU,KAAA;AACf,IAAA,IAAA,CAAK,WAAA,IAAe,CAAA;AAAA,EACtB;AAAA;AAAA,EAGA,QAAA,GAAiB;AACf,IAAA,IAAI,CAAC,IAAA,CAAK,OAAA,IAAW,IAAA,CAAK,OAAA,EAAS;AACnC,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AACf,IAAA,MAAM,aAAa,IAAA,CAAK,WAAA;AACxB,IAAA,cAAA,CAAe,MAAM;AAEnB,MAAA,IAAI,UAAA,KAAe,KAAK,WAAA,IAAe,CAAC,KAAK,OAAA,IAAW,CAAC,KAAK,OAAA,EAAS;AACvE,MAAA,IAAA,CAAK,OAAA,GAAU,KAAA;AACf,MAAA,IAAA,CAAK,IAAA,EAAK;AAAA,IACZ,CAAC,CAAA;AAAA,EACH;AACF,CAAA;;;ACxDA,IAAe,gBAAf,MAA6B;AAAA;AAAA,EAER,GAAA,uBAAU,GAAA,EAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAczC,MAAM,EAAA,EAAkB;AACtB,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,MAAA,CAAO,EAAE,CAAA,EAAG;AACvB,MAAA,IAAA,CAAK,OAAO,EAAE,CAAA;AAAA,IAChB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAA,GAAiB;AACf,IAAA,KAAA,MAAW,EAAA,IAAM,KAAK,GAAA,EAAK;AACzB,MAAA,IAAA,CAAK,OAAO,EAAE,CAAA;AAAA,IAChB;AACA,IAAA,IAAA,CAAK,IAAI,KAAA,EAAM;AAAA,EACjB;AAAA;AAAA,EAGA,IAAI,IAAA,GAAe;AACjB,IAAA,OAAO,KAAK,GAAA,CAAI,IAAA;AAAA,EAClB;AACF,CAAA;AAmBO,IAAM,WAAA,GAAN,cAA0B,aAAA,CAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO7C,GAAA,CAAI,UAAsB,KAAA,EAAuB;AAC/C,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,QAAA,CAAS,MAAM;AAC7B,MAAA,IAAA,CAAK,GAAA,CAAI,OAAO,EAAE,CAAA;AAClB,MAAA,QAAA,EAAS;AAAA,IACX,GAAG,KAAK,CAAA;AACR,IAAA,IAAA,CAAK,GAAA,CAAI,IAAI,EAAE,CAAA;AACf,IAAA,OAAO,EAAA;AAAA,EACT;AAAA,EAEU,QAAA,CAAS,UAAsB,KAAA,EAAuB;AAC9D,IAAA,OAAO,MAAA,CAAO,UAAA,CAAW,QAAA,EAAU,KAAK,CAAA;AAAA,EAC1C;AAAA,EAEU,OAAO,EAAA,EAAkB;AACjC,IAAA,MAAA,CAAO,aAAa,EAAE,CAAA;AAAA,EACxB;AACF,CAAA;AAoBO,IAAM,YAAA,GAAN,cAA2B,aAAA,CAAc;AAAA;AAAA,EAE9C,GAAA,CAAI,UAAsB,KAAA,EAAuB;AAC/C,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,QAAA,CAAS,QAAA,EAAU,KAAK,CAAA;AACxC,IAAA,IAAA,CAAK,GAAA,CAAI,IAAI,EAAE,CAAA;AACf,IAAA,OAAO,EAAA;AAAA,EACT;AAAA,EAEU,QAAA,CAAS,UAAsB,KAAA,EAAuB;AAC9D,IAAA,OAAO,MAAA,CAAO,WAAA,CAAY,QAAA,EAAU,KAAK,CAAA;AAAA,EAC3C;AAAA,EAEU,OAAO,EAAA,EAAkB;AACjC,IAAA,MAAA,CAAO,cAAc,EAAE,CAAA;AAAA,EACzB;AACF,CAAA;;;ACrIO,SAAS,cAAc,IAAA,EAAsB;AAClD,EAAA,OAAO,OAAO,QAAA,CAAS,IAAI,CAAA,IAAK,IAAA,GAAO,IAAI,IAAA,GAAO,CAAA;AACpD;AAUO,SAAS,gBAAA,CAAiB,KAAa,KAAA,EAA6B;AACzE,EAAA,IAAI,EAAE,KAAA,CAAM,GAAA,IAAO,KAAA,CAAM,GAAA,CAAA,SAAa,cAAA,CAAe,KAAA,CAAM,GAAA,EAAK,KAAA,CAAM,GAAG,CAAA;AAEzE,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,CAAM,GAAG,CAAA,GAAI,eAAe,KAAA,CAAM,GAAA,EAAK,KAAA,CAAM,GAAG,CAAA,GAAI,GAAA;AACzE,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,GAAA,EAAK,KAAK,GAAA,CAAI,KAAA,CAAM,GAAA,EAAK,KAAK,CAAC,CAAA;AAC9D,EAAA,MAAM,IAAA,GAAO,aAAA,CAAc,KAAA,CAAM,IAAI,CAAA;AACrC,EAAA,MAAM,IAAA,GAAO,SAAS,KAAK,CAAA;AAC3B,EAAA,MAAM,aAAuB,EAAC;AAE9B,EAAA,IAAI,MAAA,CAAO,SAAS,KAAA,CAAM,GAAG,GAAG,UAAA,CAAW,IAAA,CAAK,MAAM,GAAG,CAAA;AACzD,EAAA,IAAI,MAAA,CAAO,SAAS,KAAA,CAAM,GAAG,GAAG,UAAA,CAAW,IAAA,CAAK,MAAM,GAAG,CAAA;AAEzD,EAAA,MAAM,YAAA,GAAA,CAAgB,UAAU,IAAA,IAAQ,IAAA;AACxC,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,YAAY,CAAA,EAAG;AACjC,IAAA,gBAAA,CAAiB,YAAY,IAAA,CAAK,KAAA,CAAM,YAAY,CAAA,EAAG,KAAA,EAAO,MAAM,IAAI,CAAA;AACxE,IAAA,gBAAA,CAAiB,YAAY,IAAA,CAAK,IAAA,CAAK,YAAY,CAAA,EAAG,KAAA,EAAO,MAAM,IAAI,CAAA;AAAA,EACzE;AAEA,EAAA,IAAI,UAAA,CAAW,MAAA,KAAW,CAAA,EAAG,OAAO,OAAA;AACpC,EAAA,IAAI,OAAA,GAAU,WAAW,CAAC,CAAA;AAC1B,EAAA,IAAI,eAAA,GAAkB,IAAA,CAAK,GAAA,CAAI,OAAA,GAAU,OAAO,CAAA;AAChD,EAAA,KAAA,MAAW,SAAA,IAAa,UAAA,CAAW,KAAA,CAAM,CAAC,CAAA,EAAG;AAC3C,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,OAAA,GAAU,SAAS,CAAA;AAC7C,IAAA,IACE,WAAW,eAAA,IACV,WAAA,CAAY,UAAU,eAAe,CAAA,IAAK,YAAY,OAAA,EACvD;AACA,MAAA,OAAA,GAAU,SAAA;AACV,MAAA,eAAA,GAAkB,QAAA;AAAA,IACpB;AAAA,EACF;AACA,EAAA,OAAO,OAAA;AACT;AASO,SAAS,gBAAA,CAAiB,OAAA,EAAiB,KAAA,EAAe,KAAA,EAA6B;AAC5F,EAAA,MAAM,KAAA,GAAQ,gBAAA,CAAiB,OAAA,EAAS,KAAK,CAAA;AAC7C,EAAA,MAAM,WAAW,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,KAAA,CAAM,KAAK,CAAC,CAAA;AAC3C,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,QAAQ,CAAA,IAAK,QAAA,KAAa,GAAG,OAAO,KAAA;AACzD,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,IAAA,CAAK,KAAK,CAAA;AACjC,EAAA,MAAM,QAAA,GAAW,oBAAA,CAAqB,KAAA,EAAO,SAAA,EAAW,KAAK,CAAA;AAK7D,EAAA,MAAM,MAAM,QAAA,GAAW,SAAA,IAAa,WAAW,CAAA,CAAA,GAAK,aAAA,CAAc,MAAM,IAAI,CAAA;AAC5E,EAAA,OAAO,gBAAA,CAAiB,KAAK,KAAK,CAAA;AACpC;AAGA,SAAS,oBAAA,CAAqB,OAAA,EAAiB,SAAA,EAAmB,KAAA,EAA6B;AAC7F,EAAA,MAAM,IAAA,GAAO,aAAA,CAAc,KAAA,CAAM,IAAI,CAAA;AACrC,EAAA,MAAM,IAAA,GAAO,SAAS,KAAK,CAAA;AAC3B,EAAA,MAAM,aAAuB,EAAC;AAE9B,EAAA,IAAI,YAAY,CAAA,EAAG;AACjB,IAAA,MAAMA,SAAAA,GAAAA,CAAY,UAAU,IAAA,IAAQ,IAAA;AACpC,IAAA,IAAI,MAAA,CAAO,QAAA,CAASA,SAAQ,CAAA,EAAG;AAC7B,MAAA,IAAI,SAAA,GAAY,IAAA,CAAK,KAAA,CAAMA,SAAQ,CAAA,GAAI,CAAA;AACvC,MAAA,IAAI,YAAY,cAAA,CAAe,IAAA,GAAO,SAAA,GAAY,IAAA,EAAM,MAAM,IAAI,CAAA;AAClE,MAAA,IAAI,SAAA,GAAY,OAAA,IAAW,WAAA,CAAY,SAAA,EAAW,OAAO,CAAA,EAAG;AAC1D,QAAA,SAAA,IAAa,CAAA;AACb,QAAA,SAAA,GAAY,cAAA,CAAe,IAAA,GAAO,SAAA,GAAY,IAAA,EAAM,MAAM,IAAI,CAAA;AAAA,MAChE;AACA,MAAA,IACE,SAAA,GAAY,OAAA,IACZ,CAAC,WAAA,CAAY,SAAA,EAAW,OAAO,CAAA,IAC/B,MAAA,CAAO,SAAA,EAAW,KAAA,CAAM,GAAA,EAAK,KAAA,CAAM,GAAG,CAAA,EACtC;AACA,QAAA,UAAA,CAAW,IAAA,CAAK,KAAA,CAAM,SAAA,EAAW,KAAK,CAAC,CAAA;AAAA,MACzC;AAAA,IACF;AACA,IAAA,OAAO,MAAM,IAAA,CAAK,GAAA,CAAI,GAAG,UAAU,GAAG,KAAK,CAAA;AAAA,EAC7C;AAEA,EAAA,MAAM,QAAA,GAAA,CAAY,UAAU,IAAA,IAAQ,IAAA;AACpC,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,QAAQ,CAAA,EAAG;AAC7B,IAAA,IAAI,SAAA,GAAY,IAAA,CAAK,IAAA,CAAK,QAAQ,CAAA,GAAI,CAAA;AACtC,IAAA,IAAI,YAAY,cAAA,CAAe,IAAA,GAAO,SAAA,GAAY,IAAA,EAAM,MAAM,IAAI,CAAA;AAClE,IAAA,IAAI,SAAA,GAAY,OAAA,IAAW,WAAA,CAAY,SAAA,EAAW,OAAO,CAAA,EAAG;AAC1D,MAAA,SAAA,IAAa,CAAA;AACb,MAAA,SAAA,GAAY,cAAA,CAAe,IAAA,GAAO,SAAA,GAAY,IAAA,EAAM,MAAM,IAAI,CAAA;AAAA,IAChE;AACA,IAAA,IACE,SAAA,GAAY,OAAA,IACZ,CAAC,WAAA,CAAY,SAAA,EAAW,OAAO,CAAA,IAC/B,MAAA,CAAO,SAAA,EAAW,KAAA,CAAM,GAAA,EAAK,KAAA,CAAM,GAAG,CAAA,EACtC;AACA,MAAA,UAAA,CAAW,IAAA,CAAK,KAAA,CAAM,SAAA,EAAW,KAAK,CAAC,CAAA;AAAA,IACzC;AAAA,EACF;AACA,EAAA,OAAO,MAAM,IAAA,CAAK,GAAA,CAAI,GAAG,UAAU,GAAG,KAAK,CAAA;AAC7C;AAGA,SAAS,gBAAA,CACP,UAAA,EACA,KAAA,EACA,KAAA,EACA,MACA,IAAA,EACM;AACN,EAAA,MAAM,YAAY,cAAA,CAAe,IAAA,GAAO,KAAA,GAAQ,IAAA,EAAM,MAAM,IAAI,CAAA;AAChE,EAAA,IAAI,MAAA,CAAO,SAAA,EAAW,KAAA,CAAM,GAAA,EAAK,KAAA,CAAM,GAAG,CAAA,EAAG,UAAA,CAAW,IAAA,CAAK,KAAA,CAAM,SAAA,EAAW,KAAK,CAAC,CAAA;AACtF;AAGA,SAAS,SAAS,KAAA,EAA6B;AAC7C,EAAA,IAAI,KAAA,CAAM,SAAS,MAAA,IAAa,MAAA,CAAO,SAAS,KAAA,CAAM,IAAI,CAAA,EAAG,OAAO,KAAA,CAAM,IAAA;AAC1E,EAAA,OAAO,OAAO,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA,GAAI,MAAM,GAAA,GAAM,CAAA;AAClD;AAGA,SAAS,cAAA,CAAe,KAAA,EAAe,IAAA,EAAc,IAAA,EAAsB;AACzE,EAAA,MAAM,SAAA,GAAY,KAAK,GAAA,CAAI,aAAA,CAAc,IAAI,CAAA,EAAG,aAAA,CAAc,IAAI,CAAC,CAAA;AAInE,EAAA,OAAO,aAAa,GAAA,GAAM,MAAA,CAAO,MAAM,OAAA,CAAQ,SAAS,CAAC,CAAA,GAAI,KAAA;AAC/D;AAGA,SAAS,cAAc,KAAA,EAAuB;AAC5C,EAAA,MAAM,CAAC,WAAA,GAAc,EAAA,EAAI,YAAY,IAAI,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA,CAAE,QAAA,EAAS,CAAE,WAAA,EAAY,CAAE,MAAM,GAAG,CAAA;AAC3F,EAAA,MAAM,iBAAiB,WAAA,CAAY,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,GAAG,MAAA,IAAU,CAAA;AAC5D,EAAA,MAAM,QAAA,GAAW,YAAA,KAAiB,MAAA,GAAY,CAAA,GAAI,OAAO,YAAY,CAAA;AACrE,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,cAAA,GAAiB,QAAQ,CAAA;AAC9C;AAGA,SAAS,MAAA,CAAO,KAAA,EAAe,GAAA,EAAa,GAAA,EAAsB;AAChE,EAAA,OAAA,CAAQ,KAAA,GAAQ,GAAA,IAAO,WAAA,CAAY,KAAA,EAAO,GAAG,OAAO,KAAA,GAAQ,GAAA,IAAO,WAAA,CAAY,KAAA,EAAO,GAAG,CAAA,CAAA;AAC3F;AAGA,SAAS,KAAA,CAAM,OAAe,KAAA,EAA6B;AACzD,EAAA,OAAO,IAAA,CAAK,IAAI,KAAA,CAAM,GAAA,EAAK,KAAK,GAAA,CAAI,KAAA,CAAM,GAAA,EAAK,KAAK,CAAC,CAAA;AACvD;AAGA,SAAS,WAAA,CAAY,MAAc,KAAA,EAAwB;AACzD,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,KAAK,CAAC,CAAA;AACtD,EAAA,OACE,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA,IACpB,OAAO,QAAA,CAAS,KAAK,CAAA,IACrB,IAAA,CAAK,GAAA,CAAI,IAAA,GAAO,KAAK,CAAA,IAAK,OAAO,OAAA,GAAU,KAAA;AAE/C;AAGA,SAAS,cAAA,CAAe,KAAa,GAAA,EAAqB;AACxD,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,GAAG,CAAA,EAAG,OAAO,GAAA;AACjC,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,GAAG,CAAA,EAAG,OAAO,GAAA;AACjC,EAAA,OAAO,CAAA;AACT;;;AC/KA,IAAM,cAAA,GAAiB,4BAAA;AAqDhB,IAAM,qBAAA,GAAN,MAAM,sBAAA,SAA8B,UAAA,CAAwB;AAAA,EACjE,OAAgB,OAAA,GAAU,CAAC,OAAA,EAAS,aAAa,WAAW,CAAA;AAAA,EAC5D,OAAgB,MAAA,GAAS;AAAA,IACvB,KAAK,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,OAAO,iBAAA,EAAkB;AAAA,IACvD,KAAK,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,OAAO,iBAAA,EAAkB;AAAA,IACvD,IAAA,EAAM,EAAE,IAAA,EAAM,MAAA,EAAQ,SAAS,CAAA,EAAE;AAAA,IACjC,QAAA,EAAU,EAAE,IAAA,EAAM,MAAA,EAAQ,SAAS,CAAA;AAAE,GACvC;AAAA,EACA,OAAO,OAAA,GAAU,CAAC,WAAA,EAAa,WAAA,EAAa,WAAW,WAAW,CAAA;AAAA,EAClE,OAAO,MAAA,GAAS,CAAC,QAAA,EAAU,WAAW,CAAA;AAAA;AAAA,EAgBtC,OAAgB,cAAA,GAAiB,GAAA;AAAA;AAAA,EAEjC,OAAgB,eAAA,GAAkB,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOlC,OAAgB,kBAAA,GAAqB,GAAA;AAAA;AAAA,EAGrC,aAAA,GAAwC,IAAA;AAAA;AAAA,EAE/B,aAAA,uBAAoB,GAAA,EAAwC;AAAA;AAAA,EAE5D,YAAA,GAAe,IAAI,kBAAA,EAAmB;AAAA;AAAA,EAEtC,eAAe,IAAI,gBAAA,CAAiB,MAAM,IAAA,CAAK,iBAAiB,CAAA;AAAA,EAChE,aAAA,GAAgB,IAAI,cAAA,CAAiC,eAAe,CAAA;AAAA,EACpE,aAAA,GAAgB,IAAI,cAAA,CAAiC,eAAe,CAAA;AAAA,EACpE,aAAA,GAAgB,IAAI,cAAA,CAAiC,eAAe,CAAA;AAAA;AAAA,EAE7E,UAAA,GAA4B,IAAA;AAAA;AAAA,EAEnB,aAAA,GAAgB,IAAI,WAAA,EAAY;AAAA;AAAA,EAEhC,cAAA,GAAiB,IAAI,YAAA,EAAa;AAAA;AAAA,EAE3C,WAAA,GAAc,KAAA;AAAA;AAAA,EAEd,mBAAA,GAAsB,KAAA;AAAA;AAAA,EAEtB,kBAAA,GAAqB,KAAA;AAAA;AAAA,EAErB,WAAA,GAAwC,IAAA;AAAA,EACxC,cAAA,GAAgC,IAAA;AAAA;AAAA,EAEvB,WAAW,IAAI,kBAAA,CAAmB,MAAM,IAAA,CAAK,YAAY,CAAA;AAAA;AAAA,EAGzD,OAAA,GAAgB;AACvB,IAAA,IAAA,CAAK,SAAS,QAAA,EAAS;AACvB,IAAA,IAAA,CAAK,aAAa,QAAA,EAAS;AAC3B,IAAA,IAAA,CAAK,aAAA,GAAgB,IAAI,eAAA,EAAgB;AACzC,IAAA,MAAM,EAAE,MAAA,EAAO,GAAI,IAAA,CAAK,aAAA;AACxB,IAAA,KAAA,MAAW,UAAU,IAAA,CAAK,gBAAA,EAAkB,IAAA,CAAK,WAAA,CAAY,QAAQ,CAAC,CAAA;AACtE,IAAA,KAAA,MAAW,UAAU,IAAA,CAAK,gBAAA,EAAkB,IAAA,CAAK,WAAA,CAAY,QAAQ,EAAE,CAAA;AAIvE,IAAA,MAAA,CAAO,iBAAiB,WAAA,EAAa,IAAA,CAAK,aAAA,EAAe,EAAE,QAAQ,CAAA;AACnE,IAAA,MAAA,CAAO,iBAAiB,eAAA,EAAiB,IAAA,CAAK,aAAA,EAAe,EAAE,QAAQ,CAAA;AACvE,IAAA,MAAA,CAAO,iBAAiB,MAAA,EAAQ,IAAA,CAAK,aAAA,EAAe,EAAE,QAAQ,CAAA;AAC9D,IAAA,IAAA,CAAK,UAAA,EAAW;AAAA,EAClB;AAAA;AAAA,EAGS,UAAA,GAAmB;AAC1B,IAAA,IAAA,CAAK,SAAS,MAAA,EAAO;AACrB,IAAA,IAAA,CAAK,aAAa,UAAA,EAAW;AAC7B,IAAA,IAAA,CAAK,aAAa,UAAA,EAAW;AAC7B,IAAA,IAAA,CAAK,eAAe,KAAA,EAAM;AAC1B,IAAA,IAAA,CAAK,aAAA,GAAgB,IAAA;AACrB,IAAA,MAAM,OAAA,uBAAc,GAAA,CAAI;AAAA,MACtB,GAAG,IAAA,CAAK,aAAA,CAAc,IAAA,EAAK;AAAA,MAC3B,GAAG,IAAA,CAAK,gBAAA;AAAA,MACR,GAAG,IAAA,CAAK;AAAA,KACT,CAAA;AACD,IAAA,KAAA,MAAW,SAAS,IAAA,CAAK,aAAA,CAAc,MAAA,EAAO,QAAS,KAAA,EAAM;AAC7D,IAAA,IAAA,CAAK,cAAc,KAAA,EAAM;AACzB,IAAA,KAAA,MAAW,MAAA,IAAU,OAAA,EAAS,IAAA,CAAK,mBAAA,CAAoB,MAAM,CAAA;AAC7D,IAAA,IAAA,CAAK,UAAU,KAAK,CAAA;AACpB,IAAA,IAAA,CAAK,mBAAA,GAAsB,KAAA;AAC3B,IAAA,IAAA,CAAK,kBAAA,GAAqB,KAAA;AAC1B,IAAA,IAAA,CAAK,cAAc,QAAA,EAAS;AAC5B,IAAA,IAAA,CAAK,eAAe,QAAA,EAAS;AAC7B,IAAA,IAAA,CAAK,cAAc,SAAA,EAAU;AAC7B,IAAA,IAAA,CAAK,cAAc,SAAA,EAAU;AAC7B,IAAA,IAAA,CAAK,cAAc,SAAA,EAAU;AAC7B,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAAA,EACpB;AAAA;AAAA,EAGA,eAAA,GAAwB;AACtB,IAAA,IAAA,CAAK,SAAS,QAAA,EAAS;AAAA,EACzB;AAAA;AAAA,EAGA,eAAA,GAAwB;AACtB,IAAA,IAAA,CAAK,SAAS,QAAA,EAAS;AAAA,EACzB;AAAA;AAAA,EAGA,gBAAA,GAAyB;AACvB,IAAA,IAAA,CAAK,SAAS,QAAA,EAAS;AAAA,EACzB;AAAA;AAAA,EAGA,qBAAqB,KAAA,EAA+B;AAClD,IAAA,IAAA,CAAK,YAAA,CAAa,QAAQ,KAAK,CAAA;AAC/B,IAAA,IAAA,CAAK,SAAS,QAAA,EAAS;AAAA,EACzB;AAAA;AAAA,EAGA,wBAAwB,KAAA,EAA+B;AACrD,IAAA,IAAA,CAAK,YAAA,CAAa,UAAU,KAAK,CAAA;AACjC,IAAA,IAAA,CAAK,kBAAkB,KAAK,CAAA;AAC5B,IAAA,IAAA,CAAK,UAAU,KAAK,CAAA;AACpB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,IAAA,IAAA,CAAK,SAAS,QAAA,EAAS;AAAA,EACzB;AAAA;AAAA,EAGA,yBAAyB,MAAA,EAAiC;AACxD,IAAA,IAAA,CAAK,WAAA,CAAY,QAAQ,CAAC,CAAA;AAC1B,IAAA,IAAA,CAAK,SAAS,QAAA,EAAS;AAAA,EACzB;AAAA;AAAA,EAGA,4BAA4B,MAAA,EAAiC;AAC3D,IAAA,IAAA,CAAK,cAAc,MAAM,CAAA;AACzB,IAAA,IAAA,CAAK,SAAS,QAAA,EAAS;AAAA,EACzB;AAAA;AAAA,EAGA,yBAAyB,MAAA,EAAiC;AACxD,IAAA,IAAA,CAAK,WAAA,CAAY,QAAQ,EAAE,CAAA;AAC3B,IAAA,IAAA,CAAK,SAAS,QAAA,EAAS;AAAA,EACzB;AAAA;AAAA,EAGA,4BAA4B,MAAA,EAAiC;AAC3D,IAAA,IAAA,CAAK,cAAc,MAAM,CAAA;AACzB,IAAA,IAAA,CAAK,SAAS,QAAA,EAAS;AAAA,EACzB;AAAA;AAAA,EAGA,SAAA,GAAkB;AAChB,IAAA,IAAI,IAAA,CAAK,yBAAwB,EAAG;AACpC,IAAA,IAAI,CAAC,KAAK,cAAA,EAAgB;AAC1B,IAAA,IAAA,CAAK,YAAY,CAAC,CAAA;AAClB,IAAA,IAAA,CAAK,YAAY,KAAA,EAAM;AAAA,EACzB;AAAA;AAAA,EAGA,SAAA,GAAkB;AAChB,IAAA,IAAI,IAAA,CAAK,yBAAwB,EAAG;AACpC,IAAA,IAAI,CAAC,KAAK,cAAA,EAAgB;AAC1B,IAAA,IAAA,CAAK,YAAY,EAAE,CAAA;AACnB,IAAA,IAAA,CAAK,YAAY,KAAA,EAAM;AAAA,EACzB;AAAA;AAAA,EAGA,OAAA,GAAgB;AACd,IAAA,IAAI,CAAC,KAAK,cAAA,EAAgB;AAC1B,IAAA,IAAI,IAAA,CAAK,WAAA,CAAY,KAAA,CAAM,IAAA,OAAW,EAAA,EAAI;AACxC,MAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,MAAA,IAAA,CAAK,aAAA,EAAc;AACnB,MAAA;AAAA,IACF;AACA,IAAA,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,aAAA,EAAe,CAAA;AAAA,EACnC;AAAA;AAAA,EAGA,UAAU,KAAA,EAA4B;AACpC,IAAA,IAAI,CAAC,KAAK,cAAA,EAAgB;AAC1B,IAAA,IAAI,IAAA,CAAK,YAAA,CAAa,WAAA,CAAY,KAAK,CAAA,EAAG;AAC1C,IAAA,IAAI,oBAAA,CAAqB,KAAK,CAAA,EAAG;AACjC,IAAA,IAAI,IAAA,GAAsB,IAAA;AAC1B,IAAA,QAAQ,MAAM,GAAA;AAAK,MACjB,KAAK,SAAA;AACH,QAAA,IAAA,GAAO,iBAAiB,IAAA,CAAK,aAAA,EAAc,EAAG,CAAA,EAAG,KAAK,aAAa,CAAA;AACnE,QAAA;AAAA,MACF,KAAK,WAAA;AACH,QAAA,IAAA,GAAO,iBAAiB,IAAA,CAAK,aAAA,EAAc,EAAG,EAAA,EAAI,KAAK,aAAa,CAAA;AACpE,QAAA;AAAA,MACF,KAAK,QAAA;AACH,QAAA,IAAA,GACE,IAAA,CAAK,aAAA,GAAgB,CAAA,GACjB,IAAA,CAAK,eAAc,GAAI,IAAA,CAAK,aAAA,GAC5B,gBAAA,CAAiB,IAAA,CAAK,aAAA,EAAc,EAAG,EAAA,EAAI,KAAK,aAAa,CAAA;AACnE,QAAA;AAAA,MACF,KAAK,UAAA;AACH,QAAA,IAAA,GACE,IAAA,CAAK,aAAA,GAAgB,CAAA,GACjB,IAAA,CAAK,eAAc,GAAI,IAAA,CAAK,aAAA,GAC5B,gBAAA,CAAiB,IAAA,CAAK,aAAA,EAAc,EAAG,GAAA,EAAK,KAAK,aAAa,CAAA;AACpE,QAAA;AAAA,MACF,KAAK,MAAA;AACH,QAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,IAAA,CAAK,QAAQ,CAAA,EAAG;AACrC,QAAA,IAAA,GAAO,IAAA,CAAK,QAAA;AACZ,QAAA;AAAA,MACF,KAAK,KAAA;AACH,QAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,IAAA,CAAK,QAAQ,CAAA,EAAG;AACrC,QAAA,IAAA,GAAO,IAAA,CAAK,QAAA;AACZ,QAAA;AAAA,MACF;AACE,QAAA;AAAA;AAEJ,IAAA,KAAA,CAAM,cAAA,EAAe;AACrB,IAAA,IAAA,CAAK,QAAQ,IAAI,CAAA;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAA,CAAY,QAA2B,SAAA,EAAyB;AAC9D,IAAA,IAAI,IAAA,CAAK,aAAA,CAAc,GAAA,CAAI,MAAM,CAAA,EAAG;AACpC,IAAA,MAAM,KAAA,GAAQ,IAAI,eAAA,EAAgB;AAClC,IAAA,IAAA,CAAK,aAAA,CAAc,GAAA,CAAI,MAAA,EAAQ,KAAK,CAAA;AACpC,IAAA,MAAA,CAAO,gBAAA,CAAiB,eAAe,CAAC,KAAA,KAAU,KAAK,QAAA,CAAS,KAAA,EAAO,MAAA,EAAQ,SAAS,CAAA,EAAG;AAAA,MACzF,QAAQ,KAAA,CAAM;AAAA,KACf,CAAA;AACD,IAAA,MAAA,CAAO,gBAAA;AAAA,MACL,cAAA;AAAA,MACA,CAAC,KAAA,KAAU;AACT,QAAA,IAAI,IAAA,CAAK,gBAAgB,MAAA,IAAU,IAAA,CAAK,aAAa,KAAK,CAAA,OAAQ,SAAA,EAAU;AAAA,MAC9E,CAAA;AAAA,MACA,EAAE,MAAA,EAAQ,KAAA,CAAM,MAAA;AAAO,KACzB;AAAA,EACF;AAAA;AAAA,EAGA,cAAc,MAAA,EAAiC;AAC7C,IAAA,IAAA,CAAK,aAAA,CAAc,GAAA,CAAI,MAAM,CAAA,EAAG,KAAA,EAAM;AACtC,IAAA,IAAA,CAAK,aAAA,CAAc,OAAO,MAAM,CAAA;AAChC,IAAA,IAAI,IAAA,CAAK,WAAA,KAAgB,MAAA,EAAQ,IAAA,CAAK,UAAU,KAAK,CAAA;AACrD,IAAA,IAAA,CAAK,oBAAoB,MAAM,CAAA;AAAA,EACjC;AAAA;AAAA,EAGA,QAAA,CAAS,KAAA,EAAc,MAAA,EAA2B,SAAA,EAAyB;AAEzE,IAAA,MAAM,gBAAiB,KAAA,CAAuB,MAAA;AAC9C,IAAA,IAAI,OAAO,aAAA,KAAkB,QAAA,IAAY,aAAA,KAAkB,CAAA,EAAG;AAC9D,IAAA,IAAI,MAAA,CAAO,QAAA,IAAY,CAAC,IAAA,CAAK,cAAA,EAAgB;AAC7C,IAAA,KAAA,CAAM,cAAA,EAAe;AACrB,IAAA,IAAA,CAAK,YAAY,KAAA,EAAM;AAEvB,IAAA,IAAA,CAAK,UAAU,KAAK,CAAA;AACpB,IAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AACnB,IAAA,IAAA,CAAK,WAAA,GAAc,MAAA;AACnB,IAAA,IAAA,CAAK,cAAA,GAAiB,IAAA,CAAK,UAAA,CAAW,KAAK,CAAA;AAC3C,IAAA,IAAA,CAAK,mBAAA,GAAsB,KAAA;AAC3B,IAAA,IAAA,CAAK,kBAAA,GAAqB,KAAA;AAC1B,IAAA,IAAA,CAAK,aAAA,CAAc,IAAI,MAAM;AAC3B,MAAA,IAAI,CAAC,IAAA,CAAK,WAAA,CAAY,SAAS,CAAA,EAAG;AAChC,QAAA,IAAA,CAAK,SAAA,EAAU;AACf,QAAA;AAAA,MACF;AACA,MAAA,IAAA,CAAK,mBAAA,GAAsB,IAAA;AAC3B,MAAA,IAAA,CAAK,cAAA,CAAe,IAAI,MAAM;AAC5B,QAAA,IAAI,CAAC,IAAA,CAAK,WAAA,CAAY,SAAS,CAAA,OAAQ,SAAA,EAAU;AAAA,MACnD,CAAA,EAAG,uBAAsB,eAAe,CAAA;AAAA,IAC1C,CAAA,EAAG,uBAAsB,cAAc,CAAA;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAA,CAAU,wBAAwB,IAAA,EAAY;AAC5C,IAAA,IAAI,CAAC,KAAK,WAAA,EAAa;AACvB,IAAA,IAAA,CAAK,WAAA,GAAc,KAAA;AACnB,IAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AACnB,IAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AACtB,IAAA,IAAA,CAAK,cAAc,QAAA,EAAS;AAC5B,IAAA,IAAA,CAAK,eAAe,QAAA,EAAS;AAC7B,IAAA,IAAI,qBAAA,IAAyB,KAAK,mBAAA,EAAqB;AACrD,MAAA,IAAA,CAAK,kBAAA,GAAqB,IAAA;AAC1B,MAAA,IAAA,CAAK,aAAA,CAAc,IAAI,MAAM;AAC3B,QAAA,IAAA,CAAK,kBAAA,GAAqB,KAAA;AAAA,MAC5B,CAAA,EAAG,uBAAsB,kBAAkB,CAAA;AAAA,IAC7C,CAAA,MAAA,IAAW,CAAC,qBAAA,EAAuB;AACjC,MAAA,IAAA,CAAK,kBAAA,GAAqB,KAAA;AAAA,IAC5B;AAAA,EACF;AAAA;AAAA,EAGS,aAAA,GAAgB,CAAC,KAAA,KAAuB;AAC/C,IAAA,IAAI,IAAA,CAAK,YAAA,CAAa,KAAK,CAAA,OAAQ,SAAA,EAAU;AAAA,EAC/C,CAAA;AAAA;AAAA,EAGS,gBAAgB,MAAY;AACnC,IAAA,IAAA,CAAK,SAAA,EAAU;AAAA,EACjB,CAAA;AAAA;AAAA,EAGA,WAAW,KAAA,EAA6B;AACtC,IAAA,OAAQ,MAAgC,SAAA,IAAa,IAAA;AAAA,EACvD;AAAA;AAAA,EAGA,aAAa,KAAA,EAAuB;AAClC,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,UAAA,CAAW,KAAK,CAAA;AACvC,IAAA,OACE,IAAA,CAAK,gBACJ,IAAA,CAAK,cAAA,KAAmB,QAAQ,SAAA,KAAc,IAAA,IAAQ,cAAc,IAAA,CAAK,cAAA,CAAA;AAAA,EAE9E;AAAA;AAAA,EAGA,uBAAA,GAAmC;AACjC,IAAA,IAAI,CAAC,IAAA,CAAK,kBAAA,EAAoB,OAAO,KAAA;AACrC,IAAA,IAAA,CAAK,kBAAA,GAAqB,KAAA;AAC1B,IAAA,IAAA,CAAK,cAAc,QAAA,EAAS;AAC5B,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,QAAQ,GAAA,EAAsB;AAC5B,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA;AACjC,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,UAAA,KAAe,IAAA,IAAQ,UAAU,IAAA,CAAK,UAAA;AAC3D,IAAA,IAAA,CAAK,OAAO,KAAK,CAAA;AACjB,IAAA,IAAA,CAAK,UAAA,GAAa,KAAA;AAClB,IAAA,IAAI,OAAA,OAAc,QAAA,CAAS,QAAA,EAAU,EAAE,MAAA,EAAQ,EAAE,KAAA,EAAM,EAAG,CAAA;AAC1D,IAAA,OAAO,OAAA;AAAA,EACT;AAAA;AAAA,EAGA,YAAY,KAAA,EAAwB;AAClC,IAAA,IAAI,CAAC,IAAA,CAAK,cAAA,EAAgB,OAAO,KAAA;AACjC,IAAA,OAAO,IAAA,CAAK,QAAQ,gBAAA,CAAiB,IAAA,CAAK,eAAc,EAAG,KAAA,EAAO,IAAA,CAAK,aAAa,CAAC,CAAA;AAAA,EACvF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,UAAA,GAAmB;AACjB,IAAA,IAAI,CAAC,KAAK,cAAA,EAAgB;AACxB,MAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,MAAA;AAAA,IACF;AACA,IAAA,IAAI,IAAA,CAAK,WAAA,CAAY,KAAA,CAAM,IAAA,OAAW,EAAA,EAAI;AACxC,MAAA,MAAM,WAAW,IAAA,CAAK,UAAA;AACtB,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,eAAe,CAAA;AAClD,MAAA,IAAA,CAAK,OAAO,KAAK,CAAA;AACjB,MAAA,IAAA,CAAK,UAAA,GAAa,KAAA;AAIlB,MAAA,IAAI,QAAA,KAAa,IAAA,IAAQ,KAAA,KAAU,QAAA,EAAU;AAC3C,QAAA,IAAA,CAAK,SAAS,WAAA,EAAa,EAAE,QAAQ,EAAE,KAAA,IAAS,CAAA;AAAA,MAClD;AAAA,IACF,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,MAAA,IAAA,CAAK,aAAA,EAAc;AAAA,IACrB;AAAA,EACF;AAAA;AAAA,EAGA,OAAO,KAAA,EAAqB;AAC1B,IAAA,MAAM,QAAQ,IAAA,CAAK,WAAA;AACnB,IAAA,KAAA,CAAM,KAAA,GAAQ,OAAO,KAAK,CAAA;AAC1B,IAAA,IAAA,CAAK,cAAA,CAAe,OAAO,KAAK,CAAA;AAChC,IAAA,IAAA,CAAK,eAAe,KAAK,CAAA;AAAA,EAC3B;AAAA;AAAA,EAGA,aAAA,GAAsB;AACpB,IAAA,IAAA,CAAK,cAAA,CAAe,IAAA,CAAK,WAAA,EAAa,IAAI,CAAA;AAC1C,IAAA,IAAA,CAAK,cAAA,CAAe,IAAA,CAAK,aAAA,EAAe,CAAA;AAAA,EAC1C;AAAA;AAAA,EAGA,cAAA,CAAe,OAAyB,KAAA,EAA4B;AAClE,IAAA,IAAI,KAAA,CAAM,YAAA,CAAa,MAAM,CAAA,KAAM,YAAA,EAAc;AAC/C,MAAA,IAAA,CAAK,kBAAkB,KAAK,CAAA;AAC5B,MAAA;AAAA,IACF;AACA,IAAA,IAAA,CAAK,aAAA,CAAc,MAAM,KAAA,EAAO,KAAA,KAAU,OAAO,IAAA,GAAO,MAAA,CAAO,KAAK,CAAC,CAAA;AACrE,IAAA,IAAA,CAAK,aAAA,CAAc,KAAA,CAAM,KAAA,EAAO,MAAA,CAAO,QAAA,CAAS,IAAA,CAAK,QAAQ,CAAA,GAAI,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,GAAI,IAAI,CAAA;AAC7F,IAAA,IAAA,CAAK,aAAA,CAAc,KAAA,CAAM,KAAA,EAAO,MAAA,CAAO,QAAA,CAAS,IAAA,CAAK,QAAQ,CAAA,GAAI,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,GAAI,IAAI,CAAA;AAAA,EAC/F;AAAA;AAAA,EAGA,kBAAkB,KAAA,EAA+B;AAC/C,IAAA,IAAA,CAAK,aAAA,CAAc,OAAO,KAAK,CAAA;AAC/B,IAAA,IAAA,CAAK,aAAA,CAAc,OAAO,KAAK,CAAA;AAC/B,IAAA,IAAA,CAAK,aAAA,CAAc,OAAO,KAAK,CAAA;AAAA,EACjC;AAAA;AAAA,EAGA,eAAe,KAAA,EAAqB;AAClC,IAAA,KAAA,MAAW,MAAA,IAAU,KAAK,gBAAA,EAAkB,IAAA,CAAK,cAAc,MAAA,EAAQ,KAAA,GAAQ,KAAK,QAAQ,CAAA;AAC5F,IAAA,KAAA,MAAW,MAAA,IAAU,KAAK,gBAAA,EAAkB,IAAA,CAAK,cAAc,MAAA,EAAQ,KAAA,GAAQ,KAAK,QAAQ,CAAA;AAAA,EAC9F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAA,CAAc,QAA2B,OAAA,EAAwB;AAC/D,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,IAAI,MAAA,CAAO,YAAA,CAAa,cAAc,CAAA,EAAG;AACvC,QAAA,MAAA,CAAO,QAAA,GAAW,KAAA;AAClB,QAAA,MAAA,CAAO,gBAAgB,cAAc,CAAA;AAAA,MACvC;AACA,MAAA;AAAA,IACF;AACA,IAAA,IAAI,OAAO,QAAA,EAAU;AACrB,IAAA,IAAI,SAAS,aAAA,KAAkB,MAAA,IAAU,KAAK,cAAA,EAAgB,IAAA,CAAK,YAAY,KAAA,EAAM;AACrF,IAAA,MAAA,CAAO,QAAA,GAAW,IAAA;AAClB,IAAA,MAAA,CAAO,YAAA,CAAa,gBAAgB,EAAE,CAAA;AAAA,EACxC;AAAA;AAAA,EAGA,oBAAoB,MAAA,EAAiC;AACnD,IAAA,IAAI,CAAC,MAAA,CAAO,YAAA,CAAa,cAAc,CAAA,EAAG;AAC1C,IAAA,MAAA,CAAO,QAAA,GAAW,KAAA;AAClB,IAAA,MAAA,CAAO,gBAAgB,cAAc,CAAA;AAAA,EACvC;AAAA;AAAA,EAGA,aAAA,GAAwB;AACtB,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,IAAA,CAAK,WAAA,CAAY,KAAK,CAAA;AAC5C,IAAA,IAAI,MAAA,CAAO,QAAA,CAAS,MAAM,CAAA,IAAK,IAAA,CAAK,YAAY,KAAA,CAAM,IAAA,EAAK,KAAM,EAAA,EAAI,OAAO,MAAA;AAC5E,IAAA,OAAO,OAAO,QAAA,CAAS,IAAA,CAAK,QAAQ,CAAA,GAAI,KAAK,QAAA,GAAW,CAAA;AAAA,EAC1D;AAAA;AAAA,EAGA,WAAW,GAAA,EAAqB;AAC9B,IAAA,OAAO,gBAAA,CAAiB,GAAA,EAAK,IAAA,CAAK,aAAa,CAAA;AAAA,EACjD;AAAA;AAAA,EAGA,IAAI,aAAA,GAAgB;AAClB,IAAA,OAAO,EAAE,KAAK,IAAA,CAAK,QAAA,EAAU,KAAK,IAAA,CAAK,QAAA,EAAU,IAAA,EAAM,IAAA,CAAK,SAAA,EAAU;AAAA,EACxE;AAAA;AAAA,EAEA,eAAA,GAAwB;AACtB,IAAA,IAAA,CAAK,cAAc,SAAA,EAAU;AAC7B,IAAA,IAAA,CAAK,cAAc,SAAA,EAAU;AAC7B,IAAA,IAAA,CAAK,cAAc,SAAA,EAAU;AAAA,EAC/B;AACF","file":"number_input_controller.js","sourcesContent":["import { isRtl } from \"./logical_scroll\";\n\n/**\n * Turns an arrow key into a **logical** step: `+1` for \"next\", `-1` for\n * \"previous\", `0` when the key names neither.\n *\n * APG defines the horizontal pair as *next / previous* and says a vertical\n * arrangement swaps in Down/Up for the same meaning — so the pair is one axis's\n * spelling of an order, and the order reverses with the writing direction. Only\n * the horizontal pair reverses. Down/Up name an axis the writing direction does\n * not mirror, and returning them unchanged is the point: many controllers fold\n * both pairs into one branch, where swapping the branches under RTL would flip\n * the vertical axis too — a bug that reads as \"the arrows work\" until someone\n * presses Down.\n *\n * **Direction is read from the element the caller passes, which should be the\n * container that lays the items out** — not the focused child. A child may carry\n * its own `dir` (an LTR input inside an RTL form is ordinary authoring), and\n * probing per handler makes two handlers disagree at the boundary between them.\n *\n * This decides direction only. Whether the axis is even active (an\n * `orientation=\"horizontal\"` widget ignoring Down/Up), how far the step lands,\n * and what wrapping does all stay with the caller.\n *\n * **It encodes the list-order convention: `ArrowDown` is *next*.** Widgets that\n * pair the arrows by *value* instead — `ArrowUp` meaning \"more\", as a rating or a\n * slider does — must not use this, or their vertical axis inverts. Reverse the\n * horizontal pair on its own there.\n *\n * @example\n * ```ts\n * const step = logicalArrowStep(event.key, this.element);\n * if (step === 0) return;\n * this.#roving.setActive(rovingMove(current, length, step, \"wrap\"), { focus: true });\n * ```\n */\nexport function logicalArrowStep(key: string, element: Element): 1 | -1 | 0 {\n if (key === \"ArrowDown\") return 1;\n if (key === \"ArrowUp\") return -1;\n if (key !== \"ArrowRight\" && key !== \"ArrowLeft\") return 0;\n const forward = isRtl(element) ? \"ArrowLeft\" : \"ArrowRight\";\n return key === forward ? 1 : -1;\n}\n\n/**\n * Rewrites `key` so an existing LTR-shaped branch keeps working under RTL:\n * `ArrowRight` and `ArrowLeft` trade places, everything else passes through.\n *\n * The alternative — negating a delta — silently breaks handlers whose two\n * horizontal branches are **not mirror images**. A grid that clamps one edge but\n * not the other, or a segmented field guarding `index > 0` on one side and\n * `index < length - 1` on the other, ends up applying the wrong guard to the\n * wrong direction. Swapping the key leaves each branch, guards and all, exactly\n * where its author put it.\n *\n * Same rule as {@link logicalArrowStep} about which element to read: pass the\n * container that lays the items out, not the focused child.\n *\n * @example\n * ```ts\n * switch (logicalArrowKey(event.key, this.element)) {\n * case \"ArrowLeft\": // \"previous\" — whatever direction that is on screen\n * ```\n */\nexport function logicalArrowKey(key: string, element: Element): string {\n if (key !== \"ArrowRight\" && key !== \"ArrowLeft\") return key;\n if (!isRtl(element)) return key;\n return key === \"ArrowRight\" ? \"ArrowLeft\" : \"ArrowRight\";\n}\n\n/** Modifiers a widget may claim on an arrow key, named for the `allow` list. */\nexport type ArrowModifier = \"alt\" | \"ctrl\" | \"meta\" | \"shift\";\n\n/**\n * True when an arrow key arrived carrying a modifier the widget must leave to\n * the browser: return without calling `preventDefault()` and without moving any\n * state.\n *\n * A bare arrow belongs to the widget; a chorded one usually does not.\n * `Alt`/`Meta` plus a horizontal arrow is history back/forward on every desktop\n * browser, and a widget that swallows it makes the shortcut work or not\n * depending on where focus happens to sit — a coin-flip the user cannot see.\n *\n * `allow` is for the combinations APG assigns to a pattern **and the widget\n * actually implements** — today only Combobox's optional `Alt+Down`/`Alt+Up`.\n * Listing one the widget does not implement defeats the point: the chord then\n * runs the plain-arrow branch, which is exactly what this guard exists to stop.\n * Non-arrow keys return `false`, so chorded letters and\n * `Control+Home`/`Control+End` are untouched.\n *\n * @example\n * ```ts\n * if (isReservedArrowChord(event)) return;\n * ```\n */\nexport function isReservedArrowChord(\n event: KeyboardEvent,\n allow: readonly ArrowModifier[] = [],\n): boolean {\n if (!event.key.startsWith(\"Arrow\")) return false;\n return (\n (event.altKey && !allow.includes(\"alt\")) ||\n (event.ctrlKey && !allow.includes(\"ctrl\")) ||\n (event.metaKey && !allow.includes(\"meta\")) ||\n (event.shiftKey && !allow.includes(\"shift\"))\n );\n}\n\n/**\n * True when a press arrived carrying any modifier, for the keys the browser and\n * the OS own outright: return without calling `preventDefault()` and without\n * moving any state.\n *\n * `Control+Home` and `Control+End` jump the document to its ends, and a widget\n * that swallows them makes the shortcut work or not depending on where focus\n * happens to sit. {@link isReservedArrowChord} answers the same question for the\n * arrows, but returns `false` for every other key so that this one can decide.\n *\n * There is no `allow` list here on purpose. APG assigns no modifier chord to\n * `Home`/`End`, so a widget that wanted one would be claiming a combination the\n * pattern never gave it.\n *\n * @example\n * ```ts\n * case \"Home\":\n * case \"End\":\n * if (hasModifierChord(event)) return;\n * ```\n */\nexport function hasModifierChord(event: KeyboardEvent): boolean {\n return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;\n}\n","/** One temporarily controlled attribute value and the authored value it displaced. */\ninterface AttributeLeaseRecord {\n readonly original: string | null;\n written: string | null;\n}\n\n/**\n * Temporarily controls one attribute across a changing set of elements.\n *\n * The first write remembers the authored value, including the distinction between\n * an absent attribute and an authored empty string. Returning a lease restores that\n * value only while the attribute still matches the controller's last write. If a\n * consumer changed it in the meantime, the consumer owns the new value and teardown\n * leaves it alone.\n *\n * A `null` write deliberately removes the attribute while retaining the lease. This\n * is useful for derived ARIA whose valid absence is itself controller state, such as\n * an unbounded `aria-valuemin` or a blank spinbutton's `aria-valuenow`.\n *\n * The lease has no lifecycle of its own, so it never subscribes to document events.\n * A consumer that must not let a derived value become the authored baseline of a\n * cached page returns its leases from its own `turbo:before-cache` rewind, where the\n * matching `disconnect()` also releases the subscription.\n */\nexport class AttributeLease<T extends Element = Element> {\n readonly #attribute: string;\n readonly #records = new Map<T, AttributeLeaseRecord>();\n\n /** @param attribute - The attribute whose temporary values this lease owns. */\n constructor(attribute: string) {\n this.#attribute = attribute;\n }\n\n /** Writes or removes the leased attribute while preserving its authored value. */\n write(element: T, value: string | null): void {\n const existing = this.#records.get(element);\n if (existing) {\n existing.written = value;\n } else {\n this.#records.set(element, {\n original: element.getAttribute(this.#attribute),\n written: value,\n });\n }\n\n this.#reflect(element, value);\n }\n\n /** Returns one lease without overwriting a value subsequently authored by a consumer. */\n return(element: T): void {\n const record = this.#records.get(element);\n if (!record) return;\n this.#records.delete(element);\n const stillOwned = element.getAttribute(this.#attribute) === record.written;\n if (stillOwned) this.#reflect(element, record.original);\n }\n\n /** Reflects only a real value transition, avoiding self-triggered mutation work. */\n #reflect(element: T, value: string | null): void {\n if (element.getAttribute(this.#attribute) === value) return;\n if (value === null) element.removeAttribute(this.#attribute);\n else element.setAttribute(this.#attribute, value);\n }\n\n /** Returns every outstanding lease using the same ownership check as {@link return}. */\n returnAll(): void {\n for (const element of Array.from(this.#records.keys())) this.return(element);\n }\n}\n","/**\n * Runs a controller's \"return to the initial state\" pass just before Turbo\n * caches the page.\n *\n * **`disconnect()` cannot do this job, for two independent reasons.** Turbo\n * queues the clone from this event rather than taking it here, and the body swap\n * that runs the controller's `disconnect()` is queued separately — so which of\n * the two lands first is not something a controller can rely on, and a rewind\n * written in `disconnect()` may reach only the DOM being thrown away. In the\n * other direction, `disconnect()` also fires on an in-page move (Stimulus tears\n * down and reconnects the same element), where rewinding would wipe a\n * legitimately in-progress interaction — a spinner mid-load would vanish. One\n * timing is unreliable, the other is too eager; `turbo:before-cache` is the only\n * point that is exactly \"the page is about to be frozen\".\n *\n * Scope is the subscription only: registering on `activate()`, unregistering on\n * `deactivate()`, and one shared document listener no matter how many instances\n * are live. *What* to return to its initial state — which `data-state`, which\n * `hidden`, which `aria-busy` — stays in the controller, because no two\n * consumers answer it the same way.\n *\n * **Rewind state, not appearance.** The pass writes attributes the controller\n * itself owns; the visual result of those attributes is the consumer's CSS, and\n * a library that reached for style or class names would be guessing at markup\n * it does not own.\n *\n * Both entry points are idempotent, so the lifecycle hooks can call them\n * unconditionally: a second `activate()` does not double-subscribe and does not\n * make the callback run twice, and `deactivate()` on an instance that never\n * subscribed is a no-op.\n *\n * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and only the contract belongs on the members.\n *\n * @example\n * ```ts\n * readonly #beforeCache = new BeforeCacheReset(() => this.#rewind());\n *\n * connect() { this.#beforeCache.activate(); }\n * disconnect() { this.#beforeCache.deactivate(); }\n * ```\n */\nexport class BeforeCacheReset {\n /** Every subscribed instance, iterated by the one shared document listener. */\n static readonly #subscribers = new Set<BeforeCacheReset>();\n\n /** The shared listener; installed while at least one instance is subscribed. */\n static readonly #onBeforeCache = (): void => {\n for (const subscriber of BeforeCacheReset.#subscribers) subscriber.#rewind();\n };\n\n readonly #rewind: () => void;\n\n /** @param rewind - the pass that returns this controller's state to its initial form. */\n constructor(rewind: () => void) {\n this.#rewind = rewind;\n }\n\n /** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */\n activate(): void {\n const first = BeforeCacheReset.#subscribers.size === 0;\n BeforeCacheReset.#subscribers.add(this);\n if (first) {\n document.addEventListener(\"turbo:before-cache\", BeforeCacheReset.#onBeforeCache);\n }\n }\n\n /** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */\n deactivate(): void {\n BeforeCacheReset.#subscribers.delete(this);\n if (BeforeCacheReset.#subscribers.size > 0) return;\n document.removeEventListener(\"turbo:before-cache\", BeforeCacheReset.#onBeforeCache);\n }\n}\n","/** Minimal event shape carrying the platform's per-event composition signal. */\nexport interface CompositionSignal {\n readonly isComposing?: boolean;\n}\n\n/** Hooks that keep component-specific work outside {@link CompositionTracker}. */\nexport interface CompositionTrackerOptions {\n /** Runs after lifecycle state is set for a `compositionstart` event. */\n readonly onStart?: (event: Event) => void;\n /** Runs after lifecycle state is cleared for a `compositionend` event. */\n readonly onEnd?: (event: Event) => void;\n}\n\n/**\n * Owns IME composition listeners and transient state for one or more event targets.\n *\n * Some browsers omit `KeyboardEvent.isComposing` on the keydown that confirms a\n * conversion. Tracking `compositionstart` through `compositionend` supplies the\n * missing lifecycle signal without relying on deprecated numeric key codes.\n * Component policy stays with the consumer: filtering, validation, and submission\n * belong in the optional {@link CompositionTrackerOptions.onEnd} callback.\n *\n * @example\n * ```ts\n * #composition = new CompositionTracker({ onEnd: () => this.filter() });\n *\n * connect(): void {\n * this.#composition.observe(this.inputTarget);\n * }\n *\n * disconnect(): void {\n * this.#composition.disconnect();\n * }\n *\n * onKeydown(event: KeyboardEvent): void {\n * if (this.#composition.isComposing(event)) return;\n * }\n * ```\n */\nexport class CompositionTracker {\n readonly #observedTargets = new Set<EventTarget>();\n readonly #activeTargets = new Set<EventTarget>();\n readonly #onStart: ((event: Event) => void) | undefined;\n readonly #onEnd: ((event: Event) => void) | undefined;\n\n constructor(options: CompositionTrackerOptions = {}) {\n this.#onStart = options.onStart;\n this.#onEnd = options.onEnd;\n }\n\n /** Starts lifecycle tracking for `target`; repeated calls are idempotent. */\n observe(target: EventTarget): void {\n if (this.#observedTargets.has(target)) return;\n target.addEventListener(\"compositionstart\", this.#handleStart);\n target.addEventListener(\"compositionend\", this.#handleEnd);\n this.#observedTargets.add(target);\n }\n\n /** Stops tracking one target and clears any active composition it owned. */\n unobserve(target: EventTarget): void {\n if (!this.#observedTargets.delete(target)) return;\n target.removeEventListener(\"compositionstart\", this.#handleStart);\n target.removeEventListener(\"compositionend\", this.#handleEnd);\n this.#activeTargets.delete(target);\n }\n\n /** Releases every listener and clears state so reconnect starts cleanly. */\n disconnect(): void {\n for (const target of this.#observedTargets) {\n target.removeEventListener(\"compositionstart\", this.#handleStart);\n target.removeEventListener(\"compositionend\", this.#handleEnd);\n }\n this.#observedTargets.clear();\n this.#activeTargets.clear();\n }\n\n /** True when lifecycle tracking or the current event reports composition. */\n isComposing(event?: CompositionSignal): boolean {\n return this.#activeTargets.size > 0 || event?.isComposing === true;\n }\n\n readonly #handleStart = (event: Event): void => {\n if (event.currentTarget) this.#activeTargets.add(event.currentTarget);\n this.#onStart?.(event);\n };\n\n readonly #handleEnd = (event: Event): void => {\n if (event.currentTarget) this.#activeTargets.delete(event.currentTarget);\n this.#onEnd?.(event);\n };\n}\n","/**\n * Collapses many Stimulus lifecycle callbacks from one DOM mutation into a\n * single pass.\n *\n * Stimulus fires `<name>TargetConnected` / `Disconnected` once per element and\n * `<name>ValueChanged` once per changed attribute. Replacing a list of N options\n * or morphing several render Values therefore delivers N callbacks — but the\n * useful unit of work is \"reconcile against the resulting declarative input\",\n * once, after the batch has settled. Every controller with reconcilable targets\n * or render Values needs the same shape: a `queued` flag plus `queueMicrotask`.\n *\n * **A microtask is the right horizon, and the reason is specific.** Stimulus\n * drives these callbacks from a `MutationObserver`, whose own callback already\n * runs as a microtask with the whole batch in hand; scheduling one more lands\n * after the last sibling callback of that batch and still before paint or any\n * event handler. A timer would be later than it needs to be, and reconciling\n * synchronously would run once per element against a half-applied DOM.\n *\n * **The two guards are not the same guard.** Scheduling is refused before the\n * controller connects, and running is refused after it disconnects:\n *\n * - **Before `connect()`** — Stimulus delivers initial target and Value callbacks\n * ahead of `connect()`. Reconciling there would compute output against a\n * controller whose own state has not been initialised, and `connect()` is\n * about to do a full pass anyway.\n * - **After `disconnect()`** — Stimulus fires a callback for **every** target\n * during teardown, and a microtask queued just before it would otherwise run\n * against a detached tree. {@link MicrotaskCoalescer.cancel} exists for the\n * teardown path to drop the pending pass outright.\n *\n * Both guards are part of one contract here rather than something each consumer\n * has to remember separately.\n *\n * Scope is the scheduling only. *What* to reconcile — keep the surviving active\n * option, fall back to the next / previous / first visible one, rebuild derived\n * chips or hidden fields — stays in the controller, because no two consumers\n * answer it the same way.\n *\n * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and only the contract belongs on the members.\n *\n * @example\n * ```ts\n * readonly #reconcile = new MicrotaskCoalescer(() => this.#reconcileOptions());\n *\n * connect() { this.#reconcile.activate(); }\n * disconnect() { this.#reconcile.cancel(); }\n *\n * optionTargetConnected() { this.#reconcile.schedule(); }\n * optionTargetDisconnected() { this.#reconcile.schedule(); }\n * ```\n */\nexport class MicrotaskCoalescer {\n readonly #run: () => void;\n #queued = false;\n #active = false;\n #generation = 0;\n\n /** @param run - the single reconciliation pass, invoked at most once per batch. */\n constructor(run: () => void) {\n this.#run = run;\n }\n\n /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */\n activate(): void {\n this.#active = true;\n }\n\n /** Closes the window and drops any pending pass; call from `disconnect()`. */\n cancel(): void {\n this.#active = false;\n this.#queued = false;\n this.#generation += 1;\n }\n\n /** Requests one pass after the batch settles. Idempotent; inert outside the window. */\n schedule(): void {\n if (!this.#active || this.#queued) return;\n this.#queued = true;\n const generation = this.#generation;\n queueMicrotask(() => {\n // A cancelled callback must not consume a pass queued after reconnect.\n if (generation !== this.#generation || !this.#queued || !this.#active) return;\n this.#queued = false;\n this.#run();\n });\n }\n}\n","/**\n * 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","/** Configuration for a value constrained to a stepped numeric range. */\nexport interface SteppedRange {\n /** Inclusive lower endpoint. May be infinite for an unbounded control. */\n readonly min: number;\n /** Inclusive upper endpoint. May be infinite for an unbounded control. */\n readonly max: number;\n /** Requested step. Non-finite and non-positive values fall back to `1`. */\n readonly step: number;\n /** Step-grid origin. Defaults to a finite `min`, otherwise `0`. */\n readonly base?: number;\n}\n\n/** The positive finite step used at runtime and by keyboard stepping. */\nexport function effectiveStep(step: number): number {\n return Number.isFinite(step) && step > 0 ? step : 1;\n}\n\n/**\n * Clamps a raw value and selects its nearest allowed value.\n *\n * The allowed set is the step grid plus each finite range endpoint. Keeping the\n * endpoints in that set makes an authored `aria-valuemin`/`aria-valuemax`\n * truthful even when the upper endpoint is not aligned to the grid. A value\n * exactly between two allowed values resolves upward.\n */\nexport function snapSteppedValue(raw: number, range: SteppedRange): number {\n if (!(range.min <= range.max)) return finiteFallback(range.min, range.max);\n\n const input = Number.isNaN(raw) ? finiteFallback(range.min, range.max) : raw;\n const clamped = Math.min(range.max, Math.max(range.min, input));\n const step = effectiveStep(range.step);\n const base = stepBase(range);\n const candidates: number[] = [];\n\n if (Number.isFinite(range.min)) candidates.push(range.min);\n if (Number.isFinite(range.max)) candidates.push(range.max);\n\n const gridPosition = (clamped - base) / step;\n if (Number.isFinite(gridPosition)) {\n addGridCandidate(candidates, Math.floor(gridPosition), range, base, step);\n addGridCandidate(candidates, Math.ceil(gridPosition), range, base, step);\n }\n\n if (candidates.length === 0) return clamped;\n let nearest = candidates[0] as number;\n let nearestDistance = Math.abs(clamped - nearest);\n for (const candidate of candidates.slice(1)) {\n const distance = Math.abs(clamped - candidate);\n if (\n distance < nearestDistance ||\n (nearlyEqual(distance, nearestDistance) && candidate > nearest)\n ) {\n nearest = candidate;\n nearestDistance = distance;\n }\n }\n return nearest;\n}\n\n/**\n * Moves through the ordered allowed-value set by an integer number of steps.\n *\n * This differs from adding `step * count` and snapping afterward at an\n * off-grid endpoint: with `max=94, step=10`, one decrement from `94` must land\n * on `90`, and one increment from `90` must reach the advertised maximum `94`.\n */\nexport function stepSteppedValue(current: number, count: number, range: SteppedRange): number {\n const value = snapSteppedValue(current, range);\n const distance = Math.abs(Math.trunc(count));\n if (!Number.isFinite(distance) || distance === 0) return value;\n const direction = Math.sign(count);\n const adjacent = adjacentSteppedValue(value, direction, range);\n\n // The first move accounts for a possibly off-grid endpoint. From there a\n // direct grid jump is equivalent to repeated adjacency, and avoids work that\n // grows with Page-step magnitudes or programmatic counts.\n const raw = adjacent + direction * (distance - 1) * effectiveStep(range.step);\n return snapSteppedValue(raw, range);\n}\n\n/** Returns the next member of the ordered endpoint-plus-grid set. */\nfunction adjacentSteppedValue(current: number, direction: number, range: SteppedRange): number {\n const step = effectiveStep(range.step);\n const base = stepBase(range);\n const candidates: number[] = [];\n\n if (direction > 0) {\n const position = (current - base) / step;\n if (Number.isFinite(position)) {\n let gridIndex = Math.floor(position) + 1;\n let candidate = cleanGridValue(base + gridIndex * step, base, step);\n if (candidate < current || nearlyEqual(candidate, current)) {\n gridIndex += 1;\n candidate = cleanGridValue(base + gridIndex * step, base, step);\n }\n if (\n candidate > current &&\n !nearlyEqual(candidate, current) &&\n within(candidate, range.min, range.max)\n ) {\n candidates.push(clamp(candidate, range));\n }\n }\n return clamp(Math.min(...candidates), range);\n }\n\n const position = (current - base) / step;\n if (Number.isFinite(position)) {\n let gridIndex = Math.ceil(position) - 1;\n let candidate = cleanGridValue(base + gridIndex * step, base, step);\n if (candidate > current || nearlyEqual(candidate, current)) {\n gridIndex -= 1;\n candidate = cleanGridValue(base + gridIndex * step, base, step);\n }\n if (\n candidate < current &&\n !nearlyEqual(candidate, current) &&\n within(candidate, range.min, range.max)\n ) {\n candidates.push(clamp(candidate, range));\n }\n }\n return clamp(Math.max(...candidates), range);\n}\n\n/** Adds one grid value when it falls inside the inclusive range. */\nfunction addGridCandidate(\n candidates: number[],\n index: number,\n range: SteppedRange,\n base: number,\n step: number,\n): void {\n const candidate = cleanGridValue(base + index * step, base, step);\n if (within(candidate, range.min, range.max)) candidates.push(clamp(candidate, range));\n}\n\n/** A finite authored base wins; otherwise the finite lower bound or zero does. */\nfunction stepBase(range: SteppedRange): number {\n if (range.base !== undefined && Number.isFinite(range.base)) return range.base;\n return Number.isFinite(range.min) ? range.min : 0;\n}\n\n/** Removes binary noise without discarding significant integer-place digits. */\nfunction cleanGridValue(value: number, base: number, step: number): number {\n const precision = Math.max(decimalPlaces(base), decimalPlaces(step));\n // `toFixed` accepts at most 100 fractional digits. Below that limit it rounds\n // according to the authored grid precision rather than an arbitrary total\n // significant-digit count, which keeps unit steps distinct at large bases.\n return precision <= 100 ? Number(value.toFixed(precision)) : value;\n}\n\n/** Number of fractional decimal places represented by a finite JS number. */\nfunction decimalPlaces(value: number): number {\n const [coefficient = \"\", exponentText] = Math.abs(value).toString().toLowerCase().split(\"e\");\n const fractionLength = coefficient.split(\".\")[1]?.length ?? 0;\n const exponent = exponentText === undefined ? 0 : Number(exponentText);\n return Math.max(0, fractionLength - exponent);\n}\n\n/** Inclusive range check tolerant only of arithmetic noise at an endpoint. */\nfunction within(value: number, min: number, max: number): boolean {\n return (value > min || nearlyEqual(value, min)) && (value < max || nearlyEqual(value, max));\n}\n\n/** Clamps a candidate whose only overshoot may be floating-point noise. */\nfunction clamp(value: number, range: SteppedRange): number {\n return Math.min(range.max, Math.max(range.min, value));\n}\n\n/** Relative comparison used only to collapse arithmetic noise, not real steps. */\nfunction nearlyEqual(left: number, right: number): boolean {\n const scale = Math.max(Math.abs(left), Math.abs(right));\n return (\n Number.isFinite(left) &&\n Number.isFinite(right) &&\n Math.abs(left - right) <= Number.EPSILON * scale\n );\n}\n\n/** Stable fallback for malformed ranges or `NaN` input. */\nfunction finiteFallback(min: number, max: number): number {\n if (Number.isFinite(min)) return min;\n if (Number.isFinite(max)) return max;\n return 0;\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { isReservedArrowChord } from \"../utils/arrow_step\";\nimport { AttributeLease } from \"../utils/attribute_lease\";\nimport { BeforeCacheReset } from \"../utils/before_cache_reset\";\nimport { CompositionTracker } from \"../utils/composition_tracker\";\nimport { MicrotaskCoalescer } from \"../utils/microtask_coalescer\";\nimport { SafeInterval, SafeTimeout } from \"../utils/safe_timeout\";\nimport { snapSteppedValue, stepSteppedValue } from \"../utils/stepped_value\";\n\n/** Marks a `disabled` state derived and therefore removable by this controller. */\nconst OWNED_DISABLED = \"data-number-input-disabled\";\n\n/**\n * Headless, accessible number / spin-button behavior.\n *\n * Markup contract (identifier: `stimeo--number-input`):\n * <div data-controller=\"stimeo--number-input\"\n * data-stimeo--number-input-min-value=\"0\"\n * data-stimeo--number-input-max-value=\"100\"\n * data-stimeo--number-input-step-value=\"1\">\n * <button type=\"button\" aria-label=\"Decrease\" tabindex=\"-1\"\n * data-stimeo--number-input-target=\"decrement\"\n * data-action=\"click->stimeo--number-input#decrement\">−</button>\n * <input type=\"number\" min=\"0\" max=\"100\" step=\"1\" value=\"0\"\n * data-stimeo--number-input-target=\"input\"\n * data-action=\"change->stimeo--number-input#onInput\n * keydown->stimeo--number-input#onKeydown\" />\n * <button type=\"button\" aria-label=\"Increase\" tabindex=\"-1\"\n * data-stimeo--number-input-target=\"increment\"\n * data-action=\"click->stimeo--number-input#increment\">+</button>\n * </div>\n *\n * Implements the WAI-ARIA APG **Spinbutton** pattern. The arrow/step logic is\n * fully owned by the controller (not delegated to the browser's native number\n * stepping) so the behavior is identical for a native `<input type=\"number\">` and\n * a custom `role=\"spinbutton\"` host.\n *\n * `reconcile` dispatches `{ value: number }`.\n *\n * @remarks\n * Behavior only — the consumer styles the field and buttons. The input is the\n * sole Tab stop; the buttons are `tabindex=\"-1\"` and keep focus on the input\n * (pointerdown is suppressed), so stepping never moves focus away.\n *\n * Behavior provided:\n * - `ArrowUp`/`ArrowDown` step by `step`; `PageUp`/`PageDown` by `pageStep`\n * (default `step × 10`); `Home`/`End` jump to a finite `min`/`max`.\n * - Increment/decrement buttons step too, and are `disabled` at the bounds (focus\n * is returned to the input before a focused button is disabled).\n * - **Press-and-hold auto-repeat**: holding a step button steps once, then after a\n * short delay repeats until release, the bound is reached, or the element\n * disconnects. The `click` binding stays the single-step path (a quick click, a\n * synthesized/programmatic click, or assistive activation), so a normal click\n * never double-steps — the trailing click after a hold is swallowed.\n * - Typed input is clamped and snapped to the step grid on `change`;\n * `stimeo--number-input:change` is dispatched once for each changed committed\n * numeric value with `{ value: number }` detail. When a runtime `min`/`max`/\n * `step` change pulls the committed value with it, that same detail arrives as\n * `stimeo--number-input:reconcile` instead, so a consumer can tell its own edit\n * from the controller's repair. Neither event fires on connect.\n * - `step` must be finite and positive; invalid runtime input falls back to `1`,\n * while finite range endpoints remain reachable off the grid.\n */\nexport class NumberInputController extends Controller<HTMLElement> {\n static override targets = [\"input\", \"increment\", \"decrement\"];\n static override values = {\n min: { type: Number, default: Number.NEGATIVE_INFINITY },\n max: { type: Number, default: Number.POSITIVE_INFINITY },\n step: { type: Number, default: 1 },\n pageStep: { type: Number, default: 0 },\n };\n static actions = [\"decrement\", \"increment\", \"onInput\", \"onKeydown\"] as const;\n static events = [\"change\", \"reconcile\"] as const;\n\n declare readonly inputTarget: HTMLInputElement;\n declare readonly hasInputTarget: boolean;\n declare readonly incrementTarget: HTMLButtonElement;\n declare readonly incrementTargets: HTMLButtonElement[];\n declare readonly hasIncrementTarget: boolean;\n declare readonly decrementTarget: HTMLButtonElement;\n declare readonly decrementTargets: HTMLButtonElement[];\n declare readonly hasDecrementTarget: boolean;\n declare minValue: number;\n declare maxValue: number;\n declare stepValue: number;\n declare pageStepValue: number;\n\n /** Delay (ms) a button must be held before auto-repeat starts. */\n static readonly #HOLD_DELAY_MS = 400;\n /** Interval (ms) between auto-repeat steps once a hold has started. */\n static readonly #HOLD_REPEAT_MS = 80;\n /**\n * Window (ms) after a hold ends during which the trailing synthetic `click` is\n * swallowed. A safety net for the (rare) case where that click never arrives\n * (e.g. the pointer was released off the button): the flag self-clears instead\n * of poisoning the next legitimate click.\n */\n static readonly #SUPPRESS_RESET_MS = 250;\n\n /** Aborts global pointer guards on disconnect so none outlive the element. */\n #globalGuards: AbortController | null = null;\n /** Per-target guards allow an old button to be released without touching its replacement. */\n readonly #buttonGuards = new Map<HTMLButtonElement, AbortController>();\n /** Tracks IME lifecycle on the current input, including confirming keys without a signal. */\n readonly #composition = new CompositionTracker();\n /** Restores authored custom-spinbutton ARIA when a target leaves or the controller stops. */\n readonly #beforeCache = new BeforeCacheReset(() => this.#rewindForCache());\n readonly #ariaValueNow = new AttributeLease<HTMLInputElement>(\"aria-valuenow\");\n readonly #ariaValueMin = new AttributeLease<HTMLInputElement>(\"aria-valuemin\");\n readonly #ariaValueMax = new AttributeLease<HTMLInputElement>(\"aria-valuemax\");\n /** Last reconciled or user-committed numeric value. */\n #lastValue: number | null = null;\n /** Timers for the hold delay and the suppress-reset safety net. */\n readonly #holdTimeouts = new SafeTimeout();\n /** The running auto-repeat interval (one at a time). */\n readonly #holdIntervals = new SafeInterval();\n /** True while a hold is armed/running, making `#stopHold` idempotent. */\n #holdActive = false;\n /** True once a hold actually produced a repeated step (vs. a quick click). */\n #repeatedDuringHold = false;\n /** True when the next `click` is the trailing one after a hold and must be ignored. */\n #suppressNextClick = false;\n /** Button and pointer that own the current hold, so other instances/pointers cannot end it. */\n #holdButton: HTMLButtonElement | null = null;\n #holdPointerId: number | null = null;\n /** Collapses runtime range/step changes into one input reconciliation. */\n readonly #repaint = new MicrotaskCoalescer(() => this.#reconcile());\n\n /** Normalizes any initial value and wires the focus/hold pointer guards. */\n override connect(): void {\n this.#repaint.activate();\n this.#beforeCache.activate();\n this.#globalGuards = new AbortController();\n const { signal } = this.#globalGuards;\n for (const button of this.incrementTargets) this.#wireButton(button, 1);\n for (const button of this.decrementTargets) this.#wireButton(button, -1);\n // A pointer released or focus lost anywhere stops a running hold, even when\n // the button stopped receiving its own pointerup (e.g. it became disabled at\n // the bound, or the pointer was released off it).\n window.addEventListener(\"pointerup\", this.#onPointerEnd, { signal });\n window.addEventListener(\"pointercancel\", this.#onPointerEnd, { signal });\n window.addEventListener(\"blur\", this.#onWindowBlur, { signal });\n this.#reconcile();\n }\n\n /** Releases listeners, derived attributes, timers, and transient value state. */\n override disconnect(): void {\n this.#repaint.cancel();\n this.#composition.disconnect();\n this.#beforeCache.deactivate();\n this.#globalGuards?.abort();\n this.#globalGuards = null;\n const buttons = new Set([\n ...this.#buttonGuards.keys(),\n ...this.incrementTargets,\n ...this.decrementTargets,\n ]);\n for (const guard of this.#buttonGuards.values()) guard.abort();\n this.#buttonGuards.clear();\n for (const button of buttons) this.#releaseButtonState(button);\n this.#stopHold(false);\n this.#repeatedDuringHold = false;\n this.#suppressNextClick = false;\n this.#holdTimeouts.clearAll();\n this.#holdIntervals.clearAll();\n this.#ariaValueNow.returnAll();\n this.#ariaValueMin.returnAll();\n this.#ariaValueMax.returnAll();\n this.#lastValue = null;\n }\n\n /** Reconciles the value against a minimum changed by application code or a Turbo morph. */\n minValueChanged(): void {\n this.#repaint.schedule();\n }\n\n /** Reconciles the value against a maximum changed by application code or a Turbo morph. */\n maxValueChanged(): void {\n this.#repaint.schedule();\n }\n\n /** Reconciles the value against a step changed by application code or a Turbo morph. */\n stepValueChanged(): void {\n this.#repaint.schedule();\n }\n\n /** Tracks and reconciles an input inserted or replaced at runtime. */\n inputTargetConnected(input: HTMLInputElement): void {\n this.#composition.observe(input);\n this.#repaint.schedule();\n }\n\n /** Releases ARIA and composition state owned by an input that left the controller. */\n inputTargetDisconnected(input: HTMLInputElement): void {\n this.#composition.unobserve(input);\n this.#releaseInputAria(input);\n this.#stopHold(false);\n this.#lastValue = null;\n this.#repaint.schedule();\n }\n\n /** Wires focus preservation and hold behavior on a runtime increment target. */\n incrementTargetConnected(button: HTMLButtonElement): void {\n this.#wireButton(button, 1);\n this.#repaint.schedule();\n }\n\n /** Releases only the increment target that actually disconnected. */\n incrementTargetDisconnected(button: HTMLButtonElement): void {\n this.#unwireButton(button);\n this.#repaint.schedule();\n }\n\n /** Wires focus preservation and hold behavior on a runtime decrement target. */\n decrementTargetConnected(button: HTMLButtonElement): void {\n this.#wireButton(button, -1);\n this.#repaint.schedule();\n }\n\n /** Releases only the decrement target that actually disconnected. */\n decrementTargetDisconnected(button: HTMLButtonElement): void {\n this.#unwireButton(button);\n this.#repaint.schedule();\n }\n\n /** Increases by one step. Bound via `data-action` (click). */\n increment(): void {\n if (this.#consumeSuppressedClick()) return;\n if (!this.hasInputTarget) return;\n this.#commitStep(1);\n this.inputTarget.focus();\n }\n\n /** Decreases by one step. Bound via `data-action` (click). */\n decrement(): void {\n if (this.#consumeSuppressedClick()) return;\n if (!this.hasInputTarget) return;\n this.#commitStep(-1);\n this.inputTarget.focus();\n }\n\n /** Clamps and snaps a typed value. Bound via `data-action` (change). */\n onInput(): void {\n if (!this.hasInputTarget) return;\n if (this.inputTarget.value.trim() === \"\") {\n this.#lastValue = null;\n this.#reflectEmpty();\n return;\n }\n this.#commit(this.#currentValue());\n }\n\n /** Keyboard stepping per the APG spinbutton model. */\n onKeydown(event: KeyboardEvent): void {\n if (!this.hasInputTarget) return;\n if (this.#composition.isComposing(event)) return;\n if (isReservedArrowChord(event)) return;\n let next: number | null = null;\n switch (event.key) {\n case \"ArrowUp\":\n next = stepSteppedValue(this.#currentValue(), 1, this.#steppedRange);\n break;\n case \"ArrowDown\":\n next = stepSteppedValue(this.#currentValue(), -1, this.#steppedRange);\n break;\n case \"PageUp\":\n next =\n this.pageStepValue > 0\n ? this.#currentValue() + this.pageStepValue\n : stepSteppedValue(this.#currentValue(), 10, this.#steppedRange);\n break;\n case \"PageDown\":\n next =\n this.pageStepValue > 0\n ? this.#currentValue() - this.pageStepValue\n : stepSteppedValue(this.#currentValue(), -10, this.#steppedRange);\n break;\n case \"Home\":\n if (!Number.isFinite(this.minValue)) return;\n next = this.minValue;\n break;\n case \"End\":\n if (!Number.isFinite(this.maxValue)) return;\n next = this.maxValue;\n break;\n default:\n return;\n }\n event.preventDefault();\n this.#commit(next);\n }\n\n /**\n * Wires a step button: `pointerdown` keeps focus on the input and arms the\n * hold; leaving the button while held stops it (the global listeners cover\n * release/cancel/blur).\n */\n #wireButton(button: HTMLButtonElement, direction: number): void {\n if (this.#buttonGuards.has(button)) return;\n const guard = new AbortController();\n this.#buttonGuards.set(button, guard);\n button.addEventListener(\"pointerdown\", (event) => this.#armHold(event, button, direction), {\n signal: guard.signal,\n });\n button.addEventListener(\n \"pointerleave\",\n (event) => {\n if (this.#holdButton === button && this.#ownsPointer(event)) this.#stopHold();\n },\n { signal: guard.signal },\n );\n }\n\n /** Aborts one target's listeners and returns any state derived onto that button. */\n #unwireButton(button: HTMLButtonElement): void {\n this.#buttonGuards.get(button)?.abort();\n this.#buttonGuards.delete(button);\n if (this.#holdButton === button) this.#stopHold(false);\n this.#releaseButtonState(button);\n }\n\n /** Starts a hold: focus the input, then schedule the first repeat after a delay. */\n #armHold(event: Event, button: HTMLButtonElement, direction: number): void {\n // Ignore secondary buttons (right/middle) when the event exposes one.\n const pointerButton = (event as PointerEvent).button;\n if (typeof pointerButton === \"number\" && pointerButton !== 0) return;\n if (button.disabled || !this.hasInputTarget) return;\n event.preventDefault(); // keep focus on the input rather than the button\n this.inputTarget.focus();\n\n this.#stopHold(false);\n this.#holdActive = true;\n this.#holdButton = button;\n this.#holdPointerId = this.#pointerId(event);\n this.#repeatedDuringHold = false;\n this.#suppressNextClick = false;\n this.#holdTimeouts.set(() => {\n if (!this.#commitStep(direction)) {\n this.#stopHold();\n return;\n }\n this.#repeatedDuringHold = true;\n this.#holdIntervals.set(() => {\n if (!this.#commitStep(direction)) this.#stopHold();\n }, NumberInputController.#HOLD_REPEAT_MS);\n }, NumberInputController.#HOLD_DELAY_MS);\n }\n\n /**\n * Stops the current hold (idempotent). When the hold actually repeated, the\n * trailing synthetic `click` must be ignored, so it is suppressed until the\n * click consumes it or a short safety-net timeout clears it.\n */\n #stopHold(suppressTrailingClick = true): void {\n if (!this.#holdActive) return;\n this.#holdActive = false;\n this.#holdButton = null;\n this.#holdPointerId = null;\n this.#holdTimeouts.clearAll();\n this.#holdIntervals.clearAll();\n if (suppressTrailingClick && this.#repeatedDuringHold) {\n this.#suppressNextClick = true;\n this.#holdTimeouts.set(() => {\n this.#suppressNextClick = false;\n }, NumberInputController.#SUPPRESS_RESET_MS);\n } else if (!suppressTrailingClick) {\n this.#suppressNextClick = false;\n }\n }\n\n /** Ends a hold only for its initiating pointer (legacy/synthetic events remain accepted). */\n readonly #onPointerEnd = (event: Event): void => {\n if (this.#ownsPointer(event)) this.#stopHold();\n };\n\n /** Window deactivation is global and always ends the active hold. */\n readonly #onWindowBlur = (): void => {\n this.#stopHold();\n };\n\n /** Extracts pointer identity when the runtime event exposes it. */\n #pointerId(event: Event): number | null {\n return (event as Partial<PointerEvent>).pointerId ?? null;\n }\n\n /** Whether `event` belongs to the active pointer, with a synthetic-event fallback. */\n #ownsPointer(event: Event): boolean {\n const pointerId = this.#pointerId(event);\n return (\n this.#holdActive &&\n (this.#holdPointerId === null || pointerId === null || pointerId === this.#holdPointerId)\n );\n }\n\n /** Consumes a pending trailing-click suppression; returns true if the click was swallowed. */\n #consumeSuppressedClick(): boolean {\n if (!this.#suppressNextClick) return false;\n this.#suppressNextClick = false;\n this.#holdTimeouts.clearAll();\n return true;\n }\n\n /**\n * Normalizes `raw`, reflects it, and dispatches `{ value: number }` only when\n * the committed numeric value differs from the last reconciled value.\n *\n * @returns Whether the value changed (drives the auto-repeat's bound stop).\n */\n #commit(raw: number): boolean {\n const value = this.#normalize(raw);\n const changed = this.#lastValue === null || value !== this.#lastValue;\n this.#write(value);\n this.#lastValue = value;\n if (changed) this.dispatch(\"change\", { detail: { value } });\n return changed;\n }\n\n /** Commits an adjacent endpoint/grid value and reports whether it moved. */\n #commitStep(count: number): boolean {\n if (!this.hasInputTarget) return false;\n return this.#commit(stepSteppedValue(this.#currentValue(), count, this.#steppedRange));\n }\n\n /**\n * Reflects the current value after a range or step morph, reporting a moved\n * value as `reconcile`.\n *\n * @stimeoRenderRoot\n */\n #reconcile(): void {\n if (!this.hasInputTarget) {\n this.#lastValue = null;\n return;\n }\n if (this.inputTarget.value.trim() !== \"\") {\n const previous = this.#lastValue;\n const value = this.#normalize(this.#currentValue());\n this.#write(value);\n this.#lastValue = value;\n // A range or step the consumer moved at runtime can pull the committed\n // number with it. That is this controller's decision, so it is reported\n // apart from `change`, which stays reserved for user edits.\n if (previous !== null && value !== previous) {\n this.dispatch(\"reconcile\", { detail: { value } });\n }\n } else {\n this.#lastValue = null;\n this.#reflectEmpty();\n }\n }\n\n /** Reflects `value` on the input (and ARIA for non-native hosts) and the buttons. */\n #write(value: number): void {\n const input = this.inputTarget;\n input.value = String(value);\n this.#syncInputAria(input, value);\n this.#updateButtons(value);\n }\n\n /** Reflects a blank value without inventing a numeric `aria-valuenow`. */\n #reflectEmpty(): void {\n this.#syncInputAria(this.inputTarget, null);\n this.#updateButtons(this.#currentValue());\n }\n\n /** Synchronizes controller-derived ARIA while preserving authored teardown values. */\n #syncInputAria(input: HTMLInputElement, value: number | null): void {\n if (input.getAttribute(\"role\") !== \"spinbutton\") {\n this.#releaseInputAria(input);\n return;\n }\n this.#ariaValueNow.write(input, value === null ? null : String(value));\n this.#ariaValueMin.write(input, Number.isFinite(this.minValue) ? String(this.minValue) : null);\n this.#ariaValueMax.write(input, Number.isFinite(this.maxValue) ? String(this.maxValue) : null);\n }\n\n /** Returns all custom-spinbutton ARIA leased on `input`. */\n #releaseInputAria(input: HTMLInputElement): void {\n this.#ariaValueNow.return(input);\n this.#ariaValueMin.return(input);\n this.#ariaValueMax.return(input);\n }\n\n /** Disables a step button at its bound, returning focus to the input first. */\n #updateButtons(value: number): void {\n for (const button of this.incrementTargets) this.#toggleButton(button, value < this.maxValue);\n for (const button of this.decrementTargets) this.#toggleButton(button, value > this.minValue);\n }\n\n /**\n * Enables or disables a step button at its bound, never disabling one while it\n * holds focus. Owns only the `disabled` it sets itself via a marker\n * (`data-number-input-disabled`, like `conditional-fields`/`submit-once`), so an\n * author-disabled button (e.g. the whole control disabled) is never re-enabled.\n */\n #toggleButton(button: HTMLButtonElement, enabled: boolean): void {\n if (enabled) {\n if (button.hasAttribute(OWNED_DISABLED)) {\n button.disabled = false;\n button.removeAttribute(OWNED_DISABLED);\n }\n return;\n }\n if (button.disabled) return; // already disabled (possibly by the author) — leave it\n if (document.activeElement === button && this.hasInputTarget) this.inputTarget.focus();\n button.disabled = true;\n button.setAttribute(OWNED_DISABLED, \"\");\n }\n\n /** Returns `disabled` only when its explicit marker still identifies controller ownership. */\n #releaseButtonState(button: HTMLButtonElement): void {\n if (!button.hasAttribute(OWNED_DISABLED)) return;\n button.disabled = false;\n button.removeAttribute(OWNED_DISABLED);\n }\n\n /** The current numeric value, falling back to a finite min (else 0) when blank. */\n #currentValue(): number {\n const parsed = Number(this.inputTarget.value);\n if (Number.isFinite(parsed) && this.inputTarget.value.trim() !== \"\") return parsed;\n return Number.isFinite(this.minValue) ? this.minValue : 0;\n }\n\n /** Clamps to `[min, max]` and snaps to the step grid anchored at a finite min (else 0). */\n #normalize(raw: number): number {\n return snapSteppedValue(raw, this.#steppedRange);\n }\n\n /** Shared range configuration; finite endpoints remain allowed off the grid. */\n get #steppedRange() {\n return { min: this.minValue, max: this.maxValue, step: this.stepValue };\n }\n /** Returns borrowed range ARIA before Turbo snapshots the page. */\n #rewindForCache(): void {\n this.#ariaValueNow.returnAll();\n this.#ariaValueMin.returnAll();\n this.#ariaValueMax.returnAll();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/arrow_step.ts","../../src/utils/attribute_lease.ts","../../src/utils/before_cache_reset.ts","../../src/utils/composition_tracker.ts","../../src/utils/field_mirror.ts","../../src/utils/half_width.ts","../../src/utils/microtask_coalescer.ts","../../src/utils/safe_timeout.ts","../../src/utils/stepped_value.ts","../../src/controllers/number_input_controller.ts"],"names":["position"],"mappings":"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","file":"number_input_controller.js","sourcesContent":["import { isRtl } from \"./logical_scroll\";\n\n/**\n * Turns an arrow key into a **logical** step: `+1` for \"next\", `-1` for\n * \"previous\", `0` when the key names neither.\n *\n * APG defines the horizontal pair as *next / previous* and says a vertical\n * arrangement swaps in Down/Up for the same meaning — so the pair is one axis's\n * spelling of an order, and the order reverses with the writing direction. Only\n * the horizontal pair reverses. Down/Up name an axis the writing direction does\n * not mirror, and returning them unchanged is the point: many controllers fold\n * both pairs into one branch, where swapping the branches under RTL would flip\n * the vertical axis too — a bug that reads as \"the arrows work\" until someone\n * presses Down.\n *\n * **Direction is read from the element the caller passes, which should be the\n * container that lays the items out** — not the focused child. A child may carry\n * its own `dir` (an LTR input inside an RTL form is ordinary authoring), and\n * probing per handler makes two handlers disagree at the boundary between them.\n *\n * This decides direction only. Whether the axis is even active (an\n * `orientation=\"horizontal\"` widget ignoring Down/Up), how far the step lands,\n * and what wrapping does all stay with the caller.\n *\n * **It encodes the list-order convention: `ArrowDown` is *next*.** Widgets that\n * pair the arrows by *value* instead — `ArrowUp` meaning \"more\", as a rating or a\n * slider does — must not use this, or their vertical axis inverts. Reverse the\n * horizontal pair on its own there.\n *\n * @example\n * ```ts\n * const step = logicalArrowStep(event.key, this.element);\n * if (step === 0) return;\n * this.#roving.setActive(rovingMove(current, length, step, \"wrap\"), { focus: true });\n * ```\n */\nexport function logicalArrowStep(key: string, element: Element): 1 | -1 | 0 {\n if (key === \"ArrowDown\") return 1;\n if (key === \"ArrowUp\") return -1;\n if (key !== \"ArrowRight\" && key !== \"ArrowLeft\") return 0;\n const forward = isRtl(element) ? \"ArrowLeft\" : \"ArrowRight\";\n return key === forward ? 1 : -1;\n}\n\n/**\n * Rewrites `key` so an existing LTR-shaped branch keeps working under RTL:\n * `ArrowRight` and `ArrowLeft` trade places, everything else passes through.\n *\n * The alternative — negating a delta — silently breaks handlers whose two\n * horizontal branches are **not mirror images**. A grid that clamps one edge but\n * not the other, or a segmented field guarding `index > 0` on one side and\n * `index < length - 1` on the other, ends up applying the wrong guard to the\n * wrong direction. Swapping the key leaves each branch, guards and all, exactly\n * where its author put it.\n *\n * Same rule as {@link logicalArrowStep} about which element to read: pass the\n * container that lays the items out, not the focused child.\n *\n * @example\n * ```ts\n * switch (logicalArrowKey(event.key, this.element)) {\n * case \"ArrowLeft\": // \"previous\" — whatever direction that is on screen\n * ```\n */\nexport function logicalArrowKey(key: string, element: Element): string {\n if (key !== \"ArrowRight\" && key !== \"ArrowLeft\") return key;\n if (!isRtl(element)) return key;\n return key === \"ArrowRight\" ? \"ArrowLeft\" : \"ArrowRight\";\n}\n\n/** Modifiers a widget may claim on an arrow key, named for the `allow` list. */\nexport type ArrowModifier = \"alt\" | \"ctrl\" | \"meta\" | \"shift\";\n\n/**\n * True when an arrow key arrived carrying a modifier the widget must leave to\n * the browser: return without calling `preventDefault()` and without moving any\n * state.\n *\n * A bare arrow belongs to the widget; a chorded one usually does not.\n * `Alt`/`Meta` plus a horizontal arrow is history back/forward on every desktop\n * browser, and a widget that swallows it makes the shortcut work or not\n * depending on where focus happens to sit — a coin-flip the user cannot see.\n *\n * `allow` is for the combinations APG assigns to a pattern **and the widget\n * actually implements** — today only Combobox's optional `Alt+Down`/`Alt+Up`.\n * Listing one the widget does not implement defeats the point: the chord then\n * runs the plain-arrow branch, which is exactly what this guard exists to stop.\n * Non-arrow keys return `false`, so chorded letters and\n * `Control+Home`/`Control+End` are untouched.\n *\n * @example\n * ```ts\n * if (isReservedArrowChord(event)) return;\n * ```\n */\nexport function isReservedArrowChord(\n event: KeyboardEvent,\n allow: readonly ArrowModifier[] = [],\n): boolean {\n if (!event.key.startsWith(\"Arrow\")) return false;\n return (\n (event.altKey && !allow.includes(\"alt\")) ||\n (event.ctrlKey && !allow.includes(\"ctrl\")) ||\n (event.metaKey && !allow.includes(\"meta\")) ||\n (event.shiftKey && !allow.includes(\"shift\"))\n );\n}\n\n/**\n * True when a press arrived carrying any modifier, for the keys the browser and\n * the OS own outright: return without calling `preventDefault()` and without\n * moving any state.\n *\n * `Control+Home` and `Control+End` jump the document to its ends, and a widget\n * that swallows them makes the shortcut work or not depending on where focus\n * happens to sit. {@link isReservedArrowChord} answers the same question for the\n * arrows, but returns `false` for every other key so that this one can decide.\n *\n * There is no `allow` list here on purpose. APG assigns no modifier chord to\n * `Home`/`End`, so a widget that wanted one would be claiming a combination the\n * pattern never gave it.\n *\n * @example\n * ```ts\n * case \"Home\":\n * case \"End\":\n * if (hasModifierChord(event)) return;\n * ```\n */\nexport function hasModifierChord(event: KeyboardEvent): boolean {\n return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;\n}\n","/** One temporarily controlled attribute value and the authored value it displaced. */\ninterface AttributeLeaseRecord {\n readonly original: string | null;\n written: string | null;\n}\n\n/**\n * Temporarily controls one attribute across a changing set of elements.\n *\n * The first write remembers the authored value, including the distinction between\n * an absent attribute and an authored empty string. Returning a lease restores that\n * value only while the attribute still matches the controller's last write. If a\n * consumer changed it in the meantime, the consumer owns the new value and teardown\n * leaves it alone.\n *\n * A `null` write deliberately removes the attribute while retaining the lease. This\n * is useful for derived ARIA whose valid absence is itself controller state, such as\n * an unbounded `aria-valuemin` or a blank spinbutton's `aria-valuenow`.\n *\n * The lease has no lifecycle of its own, so it never subscribes to document events.\n * A consumer that must not let a derived value become the authored baseline of a\n * cached page returns its leases from its own `turbo:before-cache` rewind, where the\n * matching `disconnect()` also releases the subscription.\n */\nexport class AttributeLease<T extends Element = Element> {\n readonly #attribute: string;\n readonly #records = new Map<T, AttributeLeaseRecord>();\n\n /** @param attribute - The attribute whose temporary values this lease owns. */\n constructor(attribute: string) {\n this.#attribute = attribute;\n }\n\n /** Writes or removes the leased attribute while preserving its authored value. */\n write(element: T, value: string | null): void {\n const existing = this.#records.get(element);\n if (existing) {\n existing.written = value;\n } else {\n this.#records.set(element, {\n original: element.getAttribute(this.#attribute),\n written: value,\n });\n }\n\n this.#reflect(element, value);\n }\n\n /** Returns one lease without overwriting a value subsequently authored by a consumer. */\n return(element: T): void {\n const record = this.#records.get(element);\n if (!record) return;\n this.#records.delete(element);\n const stillOwned = element.getAttribute(this.#attribute) === record.written;\n if (stillOwned) this.#reflect(element, record.original);\n }\n\n /** Reflects only a real value transition, avoiding self-triggered mutation work. */\n #reflect(element: T, value: string | null): void {\n if (element.getAttribute(this.#attribute) === value) return;\n if (value === null) element.removeAttribute(this.#attribute);\n else element.setAttribute(this.#attribute, value);\n }\n\n /** Returns every outstanding lease using the same ownership check as {@link return}. */\n returnAll(): void {\n for (const element of Array.from(this.#records.keys())) this.return(element);\n }\n}\n","/**\n * Runs a controller's \"return to the initial state\" pass just before Turbo\n * caches the page.\n *\n * **`disconnect()` cannot do this job, for two independent reasons.** Turbo\n * queues the clone from this event rather than taking it here, and the body swap\n * that runs the controller's `disconnect()` is queued separately — so which of\n * the two lands first is not something a controller can rely on, and a rewind\n * written in `disconnect()` may reach only the DOM being thrown away. In the\n * other direction, `disconnect()` also fires on an in-page move (Stimulus tears\n * down and reconnects the same element), where rewinding would wipe a\n * legitimately in-progress interaction — a spinner mid-load would vanish. One\n * timing is unreliable, the other is too eager; `turbo:before-cache` is the only\n * point that is exactly \"the page is about to be frozen\".\n *\n * Scope is the subscription only: registering on `activate()`, unregistering on\n * `deactivate()`, and one shared document listener no matter how many instances\n * are live. *What* to return to its initial state — which `data-state`, which\n * `hidden`, which `aria-busy` — stays in the controller, because no two\n * consumers answer it the same way.\n *\n * **Rewind state, not appearance.** The pass writes attributes the controller\n * itself owns; the visual result of those attributes is the consumer's CSS, and\n * a library that reached for style or class names would be guessing at markup\n * it does not own.\n *\n * Both entry points are idempotent, so the lifecycle hooks can call them\n * unconditionally: a second `activate()` does not double-subscribe and does not\n * make the callback run twice, and `deactivate()` on an instance that never\n * subscribed is a no-op.\n *\n * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and only the contract belongs on the members.\n *\n * @example\n * ```ts\n * readonly #beforeCache = new BeforeCacheReset(() => this.#rewind());\n *\n * connect() { this.#beforeCache.activate(); }\n * disconnect() { this.#beforeCache.deactivate(); }\n * ```\n */\nexport class BeforeCacheReset {\n /** Every subscribed instance, iterated by the one shared document listener. */\n static readonly #subscribers = new Set<BeforeCacheReset>();\n\n /** The shared listener; installed while at least one instance is subscribed. */\n static readonly #onBeforeCache = (): void => {\n for (const subscriber of BeforeCacheReset.#subscribers) subscriber.#rewind();\n };\n\n readonly #rewind: () => void;\n\n /** @param rewind - the pass that returns this controller's state to its initial form. */\n constructor(rewind: () => void) {\n this.#rewind = rewind;\n }\n\n /** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */\n activate(): void {\n const first = BeforeCacheReset.#subscribers.size === 0;\n BeforeCacheReset.#subscribers.add(this);\n if (first) {\n document.addEventListener(\"turbo:before-cache\", BeforeCacheReset.#onBeforeCache);\n }\n }\n\n /** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */\n deactivate(): void {\n BeforeCacheReset.#subscribers.delete(this);\n if (BeforeCacheReset.#subscribers.size > 0) return;\n document.removeEventListener(\"turbo:before-cache\", BeforeCacheReset.#onBeforeCache);\n }\n}\n","/** Minimal event shape carrying the platform's per-event composition signal. */\nexport interface CompositionSignal {\n readonly isComposing?: boolean;\n}\n\n/** Minimal `input` shape needed to tell a composition echo from a fresh edit. */\nexport interface ConfirmedInputSignal {\n readonly target: EventTarget | null;\n readonly inputType?: string;\n}\n\n/**\n * The `inputType` values an engine reports on an `input` that belongs to a\n * composition rather than to an edit the user just made.\n */\nconst COMPOSITION_INPUT_TYPES = new Set([\n \"insertCompositionText\",\n \"insertFromComposition\",\n \"deleteCompositionText\",\n \"deleteByComposition\",\n]);\n\n/** Hooks that keep component-specific work outside {@link CompositionTracker}. */\nexport interface CompositionTrackerOptions {\n /** Runs after lifecycle state is set for a `compositionstart` event. */\n readonly onStart?: (event: Event) => void;\n /** Runs after lifecycle state is cleared for a `compositionend` event. */\n readonly onEnd?: (event: Event) => void;\n}\n\n/**\n * Owns IME composition listeners and transient state for one or more event targets.\n *\n * Some browsers omit `KeyboardEvent.isComposing` on the keydown that confirms a\n * conversion. Tracking `compositionstart` through `compositionend` supplies the\n * missing lifecycle signal without relying on deprecated numeric key codes.\n * Component policy stays with the consumer: filtering, validation, and submission\n * belong in the optional {@link CompositionTrackerOptions.onEnd} callback.\n *\n * The tracker also owns the other half of that lifecycle: **the `input` some\n * engines send after `compositionend` to echo the text just confirmed.** A\n * consumer that ran `onEnd` and then handled the echo would commit the same\n * confirmation twice, so {@link CompositionTracker.consumesConfirmedInput} tells\n * the echo apart and the consumer returns early on it.\n *\n * **What the echo is not, is the harder half.** Not every engine sends one —\n * Chromium ends a confirmation at `compositionend` with no `input` after it — so\n * a window that waits for the echo and only closes on the next key would swallow\n * the first edit that arrives without one: dictation, autofill, a drop,\n * `insertText`. Two things keep that from happening. The window closes on the\n * next anything — a key, a new composition, any `input`, losing the field — and\n * an `input` that reports an `inputType` outside {@link COMPOSITION_INPUT_TYPES}\n * is an edit, so it is never folded. Only an engine that reports no `inputType`\n * at all is left relying on the window alone.\n *\n * @example\n * ```ts\n * #composition = new CompositionTracker({ onEnd: () => this.filter() });\n *\n * connect(): void {\n * this.#composition.observe(this.inputTarget);\n * }\n *\n * disconnect(): void {\n * this.#composition.disconnect();\n * }\n *\n * onInput(event: InputEvent): void {\n * if (this.#composition.consumesConfirmedInput(event)) return;\n * if (this.#composition.isComposing(event)) return;\n * }\n *\n * onKeydown(event: KeyboardEvent): void {\n * if (this.#composition.isComposing(event)) return;\n * }\n * ```\n */\nexport class CompositionTracker {\n readonly #observedTargets = new Set<EventTarget>();\n readonly #activeTargets = new Set<EventTarget>();\n readonly #onStart: ((event: Event) => void) | undefined;\n readonly #onEnd: ((event: Event) => void) | undefined;\n /** The field whose confirming `input` is still owed, while the window is open. */\n #confirmedTarget: EventTarget | null = null;\n\n constructor(options: CompositionTrackerOptions = {}) {\n this.#onStart = options.onStart;\n this.#onEnd = options.onEnd;\n }\n\n /** Starts lifecycle tracking for `target`; repeated calls are idempotent. */\n observe(target: EventTarget): void {\n if (this.#observedTargets.has(target)) return;\n target.addEventListener(\"compositionstart\", this.#handleStart);\n target.addEventListener(\"compositionend\", this.#handleEnd);\n target.addEventListener(\"keydown\", this.#handleKeydown);\n this.#observedTargets.add(target);\n }\n\n /** Stops tracking one target and clears any active composition it owned. */\n unobserve(target: EventTarget): void {\n if (!this.#observedTargets.delete(target)) return;\n target.removeEventListener(\"compositionstart\", this.#handleStart);\n target.removeEventListener(\"compositionend\", this.#handleEnd);\n target.removeEventListener(\"keydown\", this.#handleKeydown);\n this.#activeTargets.delete(target);\n if (this.#confirmedTarget === target) this.#confirmedTarget = null;\n }\n\n /** Releases every listener and clears state so reconnect starts cleanly. */\n disconnect(): void {\n for (const target of this.#observedTargets) {\n target.removeEventListener(\"compositionstart\", this.#handleStart);\n target.removeEventListener(\"compositionend\", this.#handleEnd);\n target.removeEventListener(\"keydown\", this.#handleKeydown);\n }\n this.#observedTargets.clear();\n this.#activeTargets.clear();\n this.#confirmedTarget = null;\n }\n\n /** True when lifecycle tracking or the current event reports composition. */\n isComposing(event?: CompositionSignal): boolean {\n return this.#activeTargets.size > 0 || event?.isComposing === true;\n }\n\n /**\n * Whether `event` is the `input` echoing the composition just confirmed.\n *\n * Asking closes the window either way, so one confirmation is folded at most\n * once and a consumer asks once per `input`.\n */\n consumesConfirmedInput(event: ConfirmedInputSignal): boolean {\n const confirmed = this.#confirmedTarget;\n this.#confirmedTarget = null;\n if (confirmed === null || confirmed !== event.target) return false;\n // An engine that reports no kind at all leaves `inputType` empty, and the\n // window is then the only signal there is.\n const inputType = event.inputType;\n return !inputType || COMPOSITION_INPUT_TYPES.has(inputType);\n }\n\n readonly #handleStart = (event: Event): void => {\n this.#confirmedTarget = null;\n if (event.currentTarget) this.#activeTargets.add(event.currentTarget);\n this.#onStart?.(event);\n };\n\n readonly #handleEnd = (event: Event): void => {\n if (event.currentTarget) this.#activeTargets.delete(event.currentTarget);\n this.#confirmedTarget = event.target;\n this.#onEnd?.(event);\n };\n\n /** A key on an observed field opens an edit of its own, so no echo is owed. */\n readonly #handleKeydown = (): void => {\n this.#confirmedTarget = null;\n };\n}\n","/**\n * The hidden form fields a widget keeps in step with the state it owns.\n *\n * A widget whose value lives in ARIA attributes submits nothing on its own, so\n * it mirrors that value into `<input type=\"hidden\">` — one field for a single\n * value, a container of generated fields for a set. The mirror is one-way: the\n * widget's own state stays the source of truth and the field is what the form\n * reads.\n *\n * The write and the report are separate calls because they answer to different\n * rules. A mirror is refreshed on connect, after a morph, and whenever the\n * widget repairs itself — none of which a form should treat as an edit. Only\n * the user's own commit reports one, which is why {@link commitField} is never\n * folded into the writes.\n */\n\n/** How a widget's generated fields identify themselves to the form. */\nexport interface GeneratedFieldOptions {\n /** The `name` every generated input submits under. */\n readonly name: string;\n /** The `form` id, for a widget that sits outside the form it submits to. */\n readonly form?: string;\n}\n\n/**\n * Mirrors one value into a hidden field.\n *\n * @returns Whether the field's value moved.\n *\n * @example\n * ```ts\n * if (writeField(this.fieldTarget, String(value)) && userDriven) {\n * commitField(this.fieldTarget);\n * }\n * ```\n */\nexport function writeField(field: HTMLInputElement, value: string): boolean {\n if (field.value === value) return false;\n field.value = value;\n return true;\n}\n\n/**\n * Mirrors a set of values into `container` as generated hidden fields, in the\n * order given.\n *\n * A container that already submits exactly this set is left untouched, children\n * and all — the mirror is refreshed far more often than it moves (every connect,\n * morph and repair), and a rebuild that changes nothing is still a DOM mutation\n * a consumer's observer and a `:empty` rule can see.\n *\n * @returns Whether the submitted set moved — its values, their order, the\n * `name`, or the `form`.\n */\nexport function writeFields(\n container: HTMLElement,\n values: readonly string[],\n { name, form = \"\" }: GeneratedFieldOptions,\n): boolean {\n const current = [...container.children];\n const submits =\n current.length === values.length &&\n current.every(\n (child, index) =>\n child instanceof HTMLInputElement &&\n child.type === \"hidden\" &&\n child.value === values[index] &&\n child.name === name &&\n (child.getAttribute(\"form\") ?? \"\") === form,\n );\n if (submits) return false;\n\n container.replaceChildren(\n ...values.map((value) => {\n const input = document.createElement(\"input\");\n input.type = \"hidden\";\n input.name = name;\n input.value = value;\n if (form !== \"\") input.setAttribute(\"form\", form);\n return input;\n }),\n );\n return true;\n}\n\n/**\n * Reports a committed value from `target` the way a native form control does:\n * one bubbling, non-cancelable `change`.\n *\n * Call it only for a move the user made. Form-level behaviors listen for this —\n * validation re-checks, auto-submit — and a mirror refreshed by a morph or a\n * repair is not something the user submitted.\n *\n * `target` is the element that carries the value: the hidden field for a single\n * mirrored value, the container for a generated set (whose own inputs are\n * replaced on every write), or a widget's own native control where that is what\n * the form reads.\n */\nexport function commitField(target: HTMLElement): void {\n target.dispatchEvent(new Event(\"change\", { bubbles: true }));\n}\n\n/**\n * Reports an edit a widget made on the user's behalf, the way the browser\n * reports its own: one bubbling `input`, then one bubbling `change`.\n *\n * This is for a widget that owns the stepping or picking its *own* native\n * control would otherwise do — writing `control.value` from script fires\n * nothing, so a form listening for either event never hears the edit. Use\n * {@link commitField} instead where the element is a mirror of state held\n * elsewhere: a hidden field has no native edit to imitate, so it reports the\n * commit alone.\n *\n * Call it only for a move the user made, and only where the widget really did\n * the writing — an edit the browser already reported would be doubled.\n */\nexport function commitEdit(control: HTMLElement): void {\n control.dispatchEvent(new Event(\"input\", { bubbles: true }));\n control.dispatchEvent(new Event(\"change\", { bubbles: true }));\n}\n","/** The distance between a full-width ASCII code point and its half-width twin. */\nconst FULL_WIDTH_SHIFT = 0xfee0;\n\n/**\n * The half-width form of `char`, or `char` itself when it has none.\n *\n * An IME confirming in full-width mode produces U+FF01–U+FF5E (and U+3000 for\n * the space) where the page means ASCII — the digits of a phone number, the\n * letters of a code, the separators between them. Reading those as their ASCII\n * equivalents keeps confirmed text usable instead of discarding it. Characters\n * outside that block, including kana and astral code points, are returned\n * unchanged, so this never rewrites text a consumer meant to keep as typed.\n */\nexport function halfWidthChar(char: string): string {\n if (char >= \"!\" && char <= \"~\") {\n return String.fromCharCode(char.charCodeAt(0) - FULL_WIDTH_SHIFT);\n }\n return char === \" \" ? \" \" : char;\n}\n\n/** Every full-width ASCII character in `text` rewritten by {@link halfWidthChar}. */\nexport function toHalfWidth(text: string): string {\n let out = \"\";\n for (const char of text) out += halfWidthChar(char);\n return out;\n}\n","/**\n * Collapses many Stimulus lifecycle callbacks from one DOM mutation into a\n * single pass.\n *\n * Stimulus fires `<name>TargetConnected` / `Disconnected` once per element and\n * `<name>ValueChanged` once per changed attribute. Replacing a list of N options\n * or morphing several render Values therefore delivers N callbacks — but the\n * useful unit of work is \"reconcile against the resulting declarative input\",\n * once, after the batch has settled. Every controller with reconcilable targets\n * or render Values needs the same shape: a `queued` flag plus `queueMicrotask`.\n *\n * **A microtask is the right horizon, and the reason is specific.** Stimulus\n * drives these callbacks from a `MutationObserver`, whose own callback already\n * runs as a microtask with the whole batch in hand; scheduling one more lands\n * after the last sibling callback of that batch and still before paint or any\n * event handler. A timer would be later than it needs to be, and reconciling\n * synchronously would run once per element against a half-applied DOM.\n *\n * **The two guards are not the same guard.** Scheduling is refused before the\n * controller connects, and running is refused after it disconnects:\n *\n * - **Before `connect()`** — Stimulus delivers initial target and Value callbacks\n * ahead of `connect()`. Reconciling there would compute output against a\n * controller whose own state has not been initialised, and `connect()` is\n * about to do a full pass anyway.\n * - **After `disconnect()`** — Stimulus fires a callback for **every** target\n * during teardown, and a microtask queued just before it would otherwise run\n * against a detached tree. {@link MicrotaskCoalescer.cancel} exists for the\n * teardown path to drop the pending pass outright.\n *\n * Both guards are part of one contract here rather than something each consumer\n * has to remember separately.\n *\n * Scope is the scheduling only. *What* to reconcile — keep the surviving active\n * option, fall back to the next / previous / first visible one, rebuild derived\n * chips or hidden fields — stays in the controller, because no two consumers\n * answer it the same way.\n *\n * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and only the contract belongs on the members.\n *\n * @example\n * ```ts\n * readonly #reconcile = new MicrotaskCoalescer(() => this.#reconcileOptions());\n *\n * connect() { this.#reconcile.activate(); }\n * disconnect() { this.#reconcile.cancel(); }\n *\n * optionTargetConnected() { this.#reconcile.schedule(); }\n * optionTargetDisconnected() { this.#reconcile.schedule(); }\n * ```\n */\nexport class MicrotaskCoalescer {\n readonly #run: () => void;\n #queued = false;\n #active = false;\n #generation = 0;\n\n /** @param run - the single reconciliation pass, invoked at most once per batch. */\n constructor(run: () => void) {\n this.#run = run;\n }\n\n /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */\n activate(): void {\n this.#active = true;\n }\n\n /** Closes the window and drops any pending pass; call from `disconnect()`. */\n cancel(): void {\n this.#active = false;\n this.#queued = false;\n this.#generation += 1;\n }\n\n /** Requests one pass after the batch settles. Idempotent; inert outside the window. */\n schedule(): void {\n if (!this.#active || this.#queued) return;\n this.#queued = true;\n const generation = this.#generation;\n queueMicrotask(() => {\n // A cancelled callback must not consume a pass queued after reconnect.\n if (generation !== this.#generation || !this.#queued || !this.#active) return;\n this.#queued = false;\n this.#run();\n });\n }\n}\n","/**\n * 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","/** Configuration for a value constrained to a stepped numeric range. */\nexport interface SteppedRange {\n /** Inclusive lower endpoint. May be infinite for an unbounded control. */\n readonly min: number;\n /** Inclusive upper endpoint. May be infinite for an unbounded control. */\n readonly max: number;\n /** Requested step. Non-finite and non-positive values fall back to `1`. */\n readonly step: number;\n /** Step-grid origin. Defaults to a finite `min`, otherwise `0`. */\n readonly base?: number;\n}\n\n/** The positive finite step used at runtime and by keyboard stepping. */\nexport function effectiveStep(step: number): number {\n return Number.isFinite(step) && step > 0 ? step : 1;\n}\n\n/**\n * Clamps a raw value and selects its nearest allowed value.\n *\n * The allowed set is the step grid plus each finite range endpoint. Keeping the\n * endpoints in that set makes an authored `aria-valuemin`/`aria-valuemax`\n * truthful even when the upper endpoint is not aligned to the grid. A value\n * exactly between two allowed values resolves upward.\n */\nexport function snapSteppedValue(raw: number, range: SteppedRange): number {\n if (!(range.min <= range.max)) return finiteFallback(range.min, range.max);\n\n const input = Number.isNaN(raw) ? finiteFallback(range.min, range.max) : raw;\n const clamped = Math.min(range.max, Math.max(range.min, input));\n const step = effectiveStep(range.step);\n const base = stepBase(range);\n const candidates: number[] = [];\n\n if (Number.isFinite(range.min)) candidates.push(range.min);\n if (Number.isFinite(range.max)) candidates.push(range.max);\n\n const gridPosition = (clamped - base) / step;\n if (Number.isFinite(gridPosition)) {\n addGridCandidate(candidates, Math.floor(gridPosition), range, base, step);\n addGridCandidate(candidates, Math.ceil(gridPosition), range, base, step);\n }\n\n if (candidates.length === 0) return clamped;\n let nearest = candidates[0] as number;\n let nearestDistance = Math.abs(clamped - nearest);\n for (const candidate of candidates.slice(1)) {\n const distance = Math.abs(clamped - candidate);\n if (\n distance < nearestDistance ||\n (nearlyEqual(distance, nearestDistance) && candidate > nearest)\n ) {\n nearest = candidate;\n nearestDistance = distance;\n }\n }\n return nearest;\n}\n\n/**\n * Moves through the ordered allowed-value set by an integer number of steps.\n *\n * This differs from adding `step * count` and snapping afterward at an\n * off-grid endpoint: with `max=94, step=10`, one decrement from `94` must land\n * on `90`, and one increment from `90` must reach the advertised maximum `94`.\n */\nexport function stepSteppedValue(current: number, count: number, range: SteppedRange): number {\n const value = snapSteppedValue(current, range);\n const distance = Math.abs(Math.trunc(count));\n if (!Number.isFinite(distance) || distance === 0) return value;\n const direction = Math.sign(count);\n const adjacent = adjacentSteppedValue(value, direction, range);\n\n // The first move accounts for a possibly off-grid endpoint. From there a\n // direct grid jump is equivalent to repeated adjacency, and avoids work that\n // grows with Page-step magnitudes or programmatic counts.\n const raw = adjacent + direction * (distance - 1) * effectiveStep(range.step);\n return snapSteppedValue(raw, range);\n}\n\n/** Returns the next member of the ordered endpoint-plus-grid set. */\nfunction adjacentSteppedValue(current: number, direction: number, range: SteppedRange): number {\n const step = effectiveStep(range.step);\n const base = stepBase(range);\n const candidates: number[] = [];\n\n if (direction > 0) {\n const position = (current - base) / step;\n if (Number.isFinite(position)) {\n let gridIndex = Math.floor(position) + 1;\n let candidate = cleanGridValue(base + gridIndex * step, base, step);\n if (candidate < current || nearlyEqual(candidate, current)) {\n gridIndex += 1;\n candidate = cleanGridValue(base + gridIndex * step, base, step);\n }\n if (\n candidate > current &&\n !nearlyEqual(candidate, current) &&\n within(candidate, range.min, range.max)\n ) {\n candidates.push(clamp(candidate, range));\n }\n }\n return clamp(Math.min(...candidates), range);\n }\n\n const position = (current - base) / step;\n if (Number.isFinite(position)) {\n let gridIndex = Math.ceil(position) - 1;\n let candidate = cleanGridValue(base + gridIndex * step, base, step);\n if (candidate > current || nearlyEqual(candidate, current)) {\n gridIndex -= 1;\n candidate = cleanGridValue(base + gridIndex * step, base, step);\n }\n if (\n candidate < current &&\n !nearlyEqual(candidate, current) &&\n within(candidate, range.min, range.max)\n ) {\n candidates.push(clamp(candidate, range));\n }\n }\n return clamp(Math.max(...candidates), range);\n}\n\n/** Adds one grid value when it falls inside the inclusive range. */\nfunction addGridCandidate(\n candidates: number[],\n index: number,\n range: SteppedRange,\n base: number,\n step: number,\n): void {\n const candidate = cleanGridValue(base + index * step, base, step);\n if (within(candidate, range.min, range.max)) candidates.push(clamp(candidate, range));\n}\n\n/** A finite authored base wins; otherwise the finite lower bound or zero does. */\nfunction stepBase(range: SteppedRange): number {\n if (range.base !== undefined && Number.isFinite(range.base)) return range.base;\n return Number.isFinite(range.min) ? range.min : 0;\n}\n\n/** Removes binary noise without discarding significant integer-place digits. */\nfunction cleanGridValue(value: number, base: number, step: number): number {\n const precision = Math.max(decimalPlaces(base), decimalPlaces(step));\n // `toFixed` accepts at most 100 fractional digits. Below that limit it rounds\n // according to the authored grid precision rather than an arbitrary total\n // significant-digit count, which keeps unit steps distinct at large bases.\n return precision <= 100 ? Number(value.toFixed(precision)) : value;\n}\n\n/** Number of fractional decimal places represented by a finite JS number. */\nfunction decimalPlaces(value: number): number {\n const [coefficient = \"\", exponentText] = Math.abs(value).toString().toLowerCase().split(\"e\");\n const fractionLength = coefficient.split(\".\")[1]?.length ?? 0;\n const exponent = exponentText === undefined ? 0 : Number(exponentText);\n return Math.max(0, fractionLength - exponent);\n}\n\n/** Inclusive range check tolerant only of arithmetic noise at an endpoint. */\nfunction within(value: number, min: number, max: number): boolean {\n return (value > min || nearlyEqual(value, min)) && (value < max || nearlyEqual(value, max));\n}\n\n/** Clamps a candidate whose only overshoot may be floating-point noise. */\nfunction clamp(value: number, range: SteppedRange): number {\n return Math.min(range.max, Math.max(range.min, value));\n}\n\n/** Relative comparison used only to collapse arithmetic noise, not real steps. */\nfunction nearlyEqual(left: number, right: number): boolean {\n const scale = Math.max(Math.abs(left), Math.abs(right));\n return (\n Number.isFinite(left) &&\n Number.isFinite(right) &&\n Math.abs(left - right) <= Number.EPSILON * scale\n );\n}\n\n/** Stable fallback for malformed ranges or `NaN` input. */\nfunction finiteFallback(min: number, max: number): number {\n if (Number.isFinite(min)) return min;\n if (Number.isFinite(max)) return max;\n return 0;\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { isReservedArrowChord } from \"../utils/arrow_step\";\nimport { AttributeLease } from \"../utils/attribute_lease\";\nimport { BeforeCacheReset } from \"../utils/before_cache_reset\";\nimport { CompositionTracker } from \"../utils/composition_tracker\";\nimport { commitEdit } from \"../utils/field_mirror\";\nimport { toHalfWidth } from \"../utils/half_width\";\nimport { MicrotaskCoalescer } from \"../utils/microtask_coalescer\";\nimport { SafeInterval, SafeTimeout } from \"../utils/safe_timeout\";\nimport { snapSteppedValue, stepSteppedValue } from \"../utils/stepped_value\";\n\n/** Marks a `disabled` state derived and therefore removable by this controller. */\nconst OWNED_DISABLED = \"data-number-input-disabled\";\n\n/**\n * Headless, accessible number / spin-button behavior.\n *\n * Markup contract (identifier: `stimeo--number-input`):\n * <div data-controller=\"stimeo--number-input\"\n * data-stimeo--number-input-min-value=\"0\"\n * data-stimeo--number-input-max-value=\"100\"\n * data-stimeo--number-input-step-value=\"1\">\n * <button type=\"button\" aria-label=\"Decrease\" tabindex=\"-1\"\n * data-stimeo--number-input-target=\"decrement\"\n * data-action=\"click->stimeo--number-input#decrement\">−</button>\n * <input type=\"number\" min=\"0\" max=\"100\" step=\"1\" value=\"0\"\n * data-stimeo--number-input-target=\"input\"\n * data-action=\"change->stimeo--number-input#onInput\n * keydown->stimeo--number-input#onKeydown\" />\n * <button type=\"button\" aria-label=\"Increase\" tabindex=\"-1\"\n * data-stimeo--number-input-target=\"increment\"\n * data-action=\"click->stimeo--number-input#increment\">+</button>\n * </div>\n *\n * Implements the WAI-ARIA APG **Spinbutton** pattern. The arrow/step logic is\n * fully owned by the controller (not delegated to the browser's native number\n * stepping) so the behavior is identical for a native `<input type=\"number\">` and\n * a custom `role=\"spinbutton\"` host. Owning the stepping also means owning its\n * report: a step the user takes through a button, an arrow key or a press-and-hold\n * is written from script, which fires nothing, so the input reports it the way the\n * browser reports its own — one bubbling `input`, then one bubbling `change`, ahead\n * of this controller's `change`. Typing is bound to the native `change`, which the\n * browser has already reported, and is left alone. The two reports answer to\n * different questions: the native pair follows the *field's text*, the way a\n * browser reports its own control, so a step away from an entry the widget never\n * accepted is reported even though the settled number did not move; `change`\n * follows the *number*, and means the user settled on one.\n *\n * `change` dispatches `{ value: number }`; `reconcile` dispatches `{ value: number }`.\n *\n * @remarks\n * Behavior only — the consumer styles the field and buttons. The input is the\n * sole Tab stop; the buttons are `tabindex=\"-1\"` and keep focus on the input\n * (pointerdown is suppressed), so stepping never moves focus away.\n *\n * Behavior provided:\n * - `ArrowUp`/`ArrowDown` step by `step`; `PageUp`/`PageDown` by `pageStep`\n * (default `step × 10`); `Home`/`End` jump to a finite `min`/`max`.\n * - Increment/decrement buttons step too, and are `disabled` at the bounds (focus\n * is returned to the input before a focused button is disabled).\n * - **Press-and-hold auto-repeat**: holding a step button steps once, then after a\n * short delay repeats until release, the bound is reached, or the element\n * disconnects. The `click` binding stays the single-step path (a quick click, a\n * synthesized/programmatic click, or assistive activation), so a normal click\n * never double-steps — the trailing click after a hold is swallowed.\n * - Typed input is clamped and snapped to the step grid on `change`;\n * `stimeo--number-input:change` is dispatched once for each changed committed\n * numeric value with `{ value: number }` detail. When a change the page makes —\n * a runtime `min`/`max`/`step` that pulls the committed value with it, or an\n * input swapped in holding another number — moves the number shown, that same\n * detail arrives as `stimeo--number-input:reconcile` instead, so a consumer can\n * tell its own edit from the page's. Both are measured from the number shown\n * last, which a blank field leaves empty and a missing input leaves as it was.\n * The same instance keeps that number when it connects again — after an\n * in-page move, say — and takes a new one without a report from an input that\n * is present, while a new instance starts with none. Neither event fires on\n * connect. A runtime change that arrives while the input is composing (IME)\n * leaves the uncommitted text alone and is applied once the composition ends.\n * - A text-type spinbutton's text is read through the half-width mapping, so\n * full-width digits and signs an IME confirms (`34`, `-5`) count as the number\n * they show, and the field is written back in half-width.\n * - `step` must be finite and positive; invalid runtime input falls back to `1`,\n * while finite range endpoints remain reachable off the grid.\n */\nexport class NumberInputController extends Controller<HTMLElement> {\n static override targets = [\"input\", \"increment\", \"decrement\"];\n static override values = {\n min: { type: Number, default: Number.NEGATIVE_INFINITY },\n max: { type: Number, default: Number.POSITIVE_INFINITY },\n step: { type: Number, default: 1 },\n pageStep: { type: Number, default: 0 },\n };\n static actions = [\"decrement\", \"increment\", \"onInput\", \"onKeydown\"] as const;\n static events = [\"change\", \"reconcile\"] as const;\n\n declare readonly inputTarget: HTMLInputElement;\n declare readonly hasInputTarget: boolean;\n declare readonly incrementTarget: HTMLButtonElement;\n declare readonly incrementTargets: HTMLButtonElement[];\n declare readonly hasIncrementTarget: boolean;\n declare readonly decrementTarget: HTMLButtonElement;\n declare readonly decrementTargets: HTMLButtonElement[];\n declare readonly hasDecrementTarget: boolean;\n declare minValue: number;\n declare maxValue: number;\n declare stepValue: number;\n declare pageStepValue: number;\n\n /** Delay (ms) a button must be held before auto-repeat starts. */\n static readonly #HOLD_DELAY_MS = 400;\n /** Interval (ms) between auto-repeat steps once a hold has started. */\n static readonly #HOLD_REPEAT_MS = 80;\n /**\n * Window (ms) after a hold ends during which the trailing synthetic `click` is\n * swallowed. A safety net for the (rare) case where that click never arrives\n * (e.g. the pointer was released off the button): the flag self-clears instead\n * of poisoning the next legitimate click.\n */\n static readonly #SUPPRESS_RESET_MS = 250;\n\n /** Aborts global pointer guards on disconnect so none outlive the element. */\n #globalGuards: AbortController | null = null;\n /** Per-target guards allow an old button to be released without touching its replacement. */\n readonly #buttonGuards = new Map<HTMLButtonElement, AbortController>();\n /**\n * Tracks IME lifecycle on the current input, including confirming keys without a\n * signal, and runs a reconciliation the composition held back once it ends.\n */\n readonly #composition = new CompositionTracker({\n onEnd: () => {\n if (this.#reconcileHeld) this.#repaint.schedule();\n },\n });\n /** A reconciliation that arrived while the input was composing. */\n #reconcileHeld = false;\n /** Restores authored custom-spinbutton ARIA when a target leaves or the controller stops. */\n readonly #beforeCache = new BeforeCacheReset(() => this.#rewindForCache());\n readonly #ariaValueNow = new AttributeLease<HTMLInputElement>(\"aria-valuenow\");\n readonly #ariaValueMin = new AttributeLease<HTMLInputElement>(\"aria-valuemin\");\n readonly #ariaValueMax = new AttributeLease<HTMLInputElement>(\"aria-valuemax\");\n /**\n * The number shown last — reconciled or committed by the user — which the next\n * move is measured from; `null` while the field is blank, and before this\n * instance has shown one. A disconnect keeps it for the same instance\n * connecting again.\n */\n #lastValue: number | null = null;\n /**\n * Whether `connect()` has run and `disconnect()` has not since. The pass\n * `connect()` runs takes the baseline without reporting it.\n */\n #connected = false;\n /** Timers for the hold delay and the suppress-reset safety net. */\n readonly #holdTimeouts = new SafeTimeout();\n /** The running auto-repeat interval (one at a time). */\n readonly #holdIntervals = new SafeInterval();\n /** True while a hold is armed/running, making `#stopHold` idempotent. */\n #holdActive = false;\n /** True once a hold actually produced a repeated step (vs. a quick click). */\n #repeatedDuringHold = false;\n /** True when the next `click` is the trailing one after a hold and must be ignored. */\n #suppressNextClick = false;\n /** Button and pointer that own the current hold, so other instances/pointers cannot end it. */\n #holdButton: HTMLButtonElement | null = null;\n #holdPointerId: number | null = null;\n /** Collapses runtime range/step changes into one input reconciliation. */\n readonly #repaint = new MicrotaskCoalescer(() => this.#reconcile());\n\n /** Normalizes any initial value and wires the focus/hold pointer guards. */\n override connect(): void {\n this.#repaint.activate();\n this.#beforeCache.activate();\n this.#globalGuards = new AbortController();\n const { signal } = this.#globalGuards;\n for (const button of this.incrementTargets) this.#wireButton(button, 1);\n for (const button of this.decrementTargets) this.#wireButton(button, -1);\n // A pointer released or focus lost anywhere stops a running hold, even when\n // the button stopped receiving its own pointerup (e.g. it became disabled at\n // the bound, or the pointer was released off it).\n window.addEventListener(\"pointerup\", this.#onPointerEnd, { signal });\n window.addEventListener(\"pointercancel\", this.#onPointerEnd, { signal });\n window.addEventListener(\"blur\", this.#onWindowBlur, { signal });\n this.#reconcile();\n this.#connected = true;\n }\n\n /**\n * Releases listeners, derived attributes, timers, and the hold state. The number\n * shown last stays: the pass the next `connect()` runs takes a new one without a\n * report from an input that is present, and with none it stays the number an\n * input arriving later is compared with.\n */\n override disconnect(): void {\n this.#connected = false;\n this.#repaint.cancel();\n this.#composition.disconnect();\n this.#beforeCache.deactivate();\n this.#globalGuards?.abort();\n this.#globalGuards = null;\n const buttons = new Set([\n ...this.#buttonGuards.keys(),\n ...this.incrementTargets,\n ...this.decrementTargets,\n ]);\n for (const guard of this.#buttonGuards.values()) guard.abort();\n this.#buttonGuards.clear();\n for (const button of buttons) this.#releaseButtonState(button);\n this.#stopHold(false);\n this.#repeatedDuringHold = false;\n this.#suppressNextClick = false;\n this.#holdTimeouts.clearAll();\n this.#holdIntervals.clearAll();\n this.#ariaValueNow.returnAll();\n this.#ariaValueMin.returnAll();\n this.#ariaValueMax.returnAll();\n }\n\n /** Reconciles the value against a minimum changed by application code or a Turbo morph. */\n minValueChanged(): void {\n this.#repaint.schedule();\n }\n\n /** Reconciles the value against a maximum changed by application code or a Turbo morph. */\n maxValueChanged(): void {\n this.#repaint.schedule();\n }\n\n /** Reconciles the value against a step changed by application code or a Turbo morph. */\n stepValueChanged(): void {\n this.#repaint.schedule();\n }\n\n /** Tracks and reconciles an input inserted or replaced at runtime. */\n inputTargetConnected(input: HTMLInputElement): void {\n this.#composition.observe(input);\n this.#repaint.schedule();\n }\n\n /**\n * Releases ARIA and composition state owned by an input that left the controller.\n * The number it showed stays the baseline, so an input swapped in holding another\n * number is reported as `reconcile`.\n */\n inputTargetDisconnected(input: HTMLInputElement): void {\n this.#composition.unobserve(input);\n this.#releaseInputAria(input);\n this.#stopHold(false);\n this.#repaint.schedule();\n }\n\n /** Wires focus preservation and hold behavior on a runtime increment target. */\n incrementTargetConnected(button: HTMLButtonElement): void {\n this.#wireButton(button, 1);\n this.#repaint.schedule();\n }\n\n /** Releases only the increment target that actually disconnected. */\n incrementTargetDisconnected(button: HTMLButtonElement): void {\n this.#unwireButton(button);\n this.#repaint.schedule();\n }\n\n /** Wires focus preservation and hold behavior on a runtime decrement target. */\n decrementTargetConnected(button: HTMLButtonElement): void {\n this.#wireButton(button, -1);\n this.#repaint.schedule();\n }\n\n /** Releases only the decrement target that actually disconnected. */\n decrementTargetDisconnected(button: HTMLButtonElement): void {\n this.#unwireButton(button);\n this.#repaint.schedule();\n }\n\n /** Increases by one step. Bound via `data-action` (click). */\n increment(): void {\n if (this.#consumeSuppressedClick()) return;\n if (!this.hasInputTarget) return;\n this.#commitStep(1);\n this.inputTarget.focus();\n }\n\n /** Decreases by one step. Bound via `data-action` (click). */\n decrement(): void {\n if (this.#consumeSuppressedClick()) return;\n if (!this.hasInputTarget) return;\n this.#commitStep(-1);\n this.inputTarget.focus();\n }\n\n /** Clamps and snaps a typed value. Bound via `data-action` (change). */\n onInput(): void {\n if (!this.hasInputTarget) return;\n if (this.inputTarget.value.trim() === \"\") {\n this.#lastValue = null;\n this.#reflectEmpty();\n return;\n }\n this.#commit(this.#currentValue());\n }\n\n /** Keyboard stepping per the APG spinbutton model. */\n onKeydown(event: KeyboardEvent): void {\n if (!this.hasInputTarget) return;\n if (this.#composition.isComposing(event)) return;\n if (isReservedArrowChord(event)) return;\n let next: number | null = null;\n switch (event.key) {\n case \"ArrowUp\":\n next = stepSteppedValue(this.#currentValue(), 1, this.#steppedRange);\n break;\n case \"ArrowDown\":\n next = stepSteppedValue(this.#currentValue(), -1, this.#steppedRange);\n break;\n case \"PageUp\":\n next =\n this.pageStepValue > 0\n ? this.#currentValue() + this.pageStepValue\n : stepSteppedValue(this.#currentValue(), 10, this.#steppedRange);\n break;\n case \"PageDown\":\n next =\n this.pageStepValue > 0\n ? this.#currentValue() - this.pageStepValue\n : stepSteppedValue(this.#currentValue(), -10, this.#steppedRange);\n break;\n case \"Home\":\n if (!Number.isFinite(this.minValue)) return;\n next = this.minValue;\n break;\n case \"End\":\n if (!Number.isFinite(this.maxValue)) return;\n next = this.maxValue;\n break;\n default:\n return;\n }\n event.preventDefault();\n // `preventDefault` stops the browser's own stepping, so the edit below is\n // entirely this widget's and nothing else will report it.\n this.#commit(next, true);\n }\n\n /**\n * Wires a step button: `pointerdown` keeps focus on the input and arms the\n * hold; leaving the button while held stops it (the global listeners cover\n * release/cancel/blur).\n */\n #wireButton(button: HTMLButtonElement, direction: number): void {\n if (this.#buttonGuards.has(button)) return;\n const guard = new AbortController();\n this.#buttonGuards.set(button, guard);\n button.addEventListener(\"pointerdown\", (event) => this.#armHold(event, button, direction), {\n signal: guard.signal,\n });\n button.addEventListener(\n \"pointerleave\",\n (event) => {\n if (this.#holdButton === button && this.#ownsPointer(event)) this.#stopHold();\n },\n { signal: guard.signal },\n );\n }\n\n /** Aborts one target's listeners and returns any state derived onto that button. */\n #unwireButton(button: HTMLButtonElement): void {\n this.#buttonGuards.get(button)?.abort();\n this.#buttonGuards.delete(button);\n if (this.#holdButton === button) this.#stopHold(false);\n this.#releaseButtonState(button);\n }\n\n /** Starts a hold: focus the input, then schedule the first repeat after a delay. */\n #armHold(event: Event, button: HTMLButtonElement, direction: number): void {\n // Ignore secondary buttons (right/middle) when the event exposes one.\n const pointerButton = (event as PointerEvent).button;\n if (typeof pointerButton === \"number\" && pointerButton !== 0) return;\n if (button.disabled || !this.hasInputTarget) return;\n event.preventDefault(); // keep focus on the input rather than the button\n this.inputTarget.focus();\n\n this.#stopHold(false);\n this.#holdActive = true;\n this.#holdButton = button;\n this.#holdPointerId = this.#pointerId(event);\n this.#repeatedDuringHold = false;\n this.#suppressNextClick = false;\n this.#holdTimeouts.set(() => {\n if (!this.#commitStep(direction)) {\n this.#stopHold();\n return;\n }\n this.#repeatedDuringHold = true;\n this.#holdIntervals.set(() => {\n if (!this.#commitStep(direction)) this.#stopHold();\n }, NumberInputController.#HOLD_REPEAT_MS);\n }, NumberInputController.#HOLD_DELAY_MS);\n }\n\n /**\n * Stops the current hold (idempotent). When the hold actually repeated, the\n * trailing synthetic `click` must be ignored, so it is suppressed until the\n * click consumes it or a short safety-net timeout clears it.\n */\n #stopHold(suppressTrailingClick = true): void {\n if (!this.#holdActive) return;\n this.#holdActive = false;\n this.#holdButton = null;\n this.#holdPointerId = null;\n this.#holdTimeouts.clearAll();\n this.#holdIntervals.clearAll();\n if (suppressTrailingClick && this.#repeatedDuringHold) {\n this.#suppressNextClick = true;\n this.#holdTimeouts.set(() => {\n this.#suppressNextClick = false;\n }, NumberInputController.#SUPPRESS_RESET_MS);\n } else if (!suppressTrailingClick) {\n this.#suppressNextClick = false;\n }\n }\n\n /** Ends a hold only for its initiating pointer (legacy/synthetic events remain accepted). */\n readonly #onPointerEnd = (event: Event): void => {\n if (this.#ownsPointer(event)) this.#stopHold();\n };\n\n /** Window deactivation is global and always ends the active hold. */\n readonly #onWindowBlur = (): void => {\n this.#stopHold();\n };\n\n /** Extracts pointer identity when the runtime event exposes it. */\n #pointerId(event: Event): number | null {\n return (event as Partial<PointerEvent>).pointerId ?? null;\n }\n\n /** Whether `event` belongs to the active pointer, with a synthetic-event fallback. */\n #ownsPointer(event: Event): boolean {\n const pointerId = this.#pointerId(event);\n return (\n this.#holdActive &&\n (this.#holdPointerId === null || pointerId === null || pointerId === this.#holdPointerId)\n );\n }\n\n /** Consumes a pending trailing-click suppression; returns true if the click was swallowed. */\n #consumeSuppressedClick(): boolean {\n if (!this.#suppressNextClick) return false;\n this.#suppressNextClick = false;\n this.#holdTimeouts.clearAll();\n return true;\n }\n\n /**\n * Normalizes `raw`, reflects it, and dispatches `{ value: number }` only when\n * the committed numeric value differs from the last reconciled value.\n *\n * @returns Whether the value changed (drives the auto-repeat's bound stop).\n */\n #commit(raw: number, report = false): boolean {\n const value = this.#normalize(raw);\n const changed = this.#lastValue === null || value !== this.#lastValue;\n // The two reports answer to different questions. The browser reports what its\n // control shows, so the native pair follows the field's own text — a step away\n // from an entry the widget never accepted moves that text even when the\n // settled number does not. `change` means the user settled on a number, so it\n // follows the number.\n const shown = this.inputTarget.value;\n this.#write(value);\n // Settled before either report, so the `change` below — which re-enters\n // through this widget's own markup contract — finds nothing left to commit.\n this.#lastValue = value;\n if (report && shown !== this.inputTarget.value) commitEdit(this.inputTarget);\n if (changed) this.dispatch(\"change\", { detail: { value } });\n return changed;\n }\n\n /** Commits an adjacent endpoint/grid value and reports whether it moved. */\n #commitStep(count: number): boolean {\n if (!this.hasInputTarget) return false;\n return this.#commit(stepSteppedValue(this.#currentValue(), count, this.#steppedRange), true);\n }\n\n /**\n * Reflects the current value after a range, step or input change, reporting a\n * number that moved from the one shown last as `reconcile`. While the input is\n * composing, its text is the IME's: the pass waits for the composition to end\n * instead of reading and rewriting it. With no input there is nothing to show,\n * and the number shown last stays the baseline for the input that comes next.\n *\n * @stimeoRenderRoot\n */\n #reconcile(): void {\n if (!this.hasInputTarget) return;\n if (this.#composition.isComposing()) {\n this.#reconcileHeld = true;\n return;\n }\n this.#reconcileHeld = false;\n const previous = this.#lastValue;\n if (this.inputTarget.value.trim() !== \"\") {\n const value = this.#normalize(this.#currentValue());\n this.#write(value);\n this.#lastValue = value;\n } else {\n this.#lastValue = null;\n this.#reflectEmpty();\n }\n this.#reportMove(previous);\n }\n\n /**\n * Reports the number shown as `reconcile` when a change the page made moved it\n * from `previous`. The page moved it, so it is reported apart from `change`,\n * which stays reserved for user edits. A blank field reports nothing, the way a\n * user blanking it does: both events carry a number.\n */\n #reportMove(previous: number | null): void {\n const value = this.#lastValue;\n if (!this.#connected || value === null || value === previous) return;\n this.dispatch(\"reconcile\", { detail: { value } });\n }\n\n /** Reflects `value` on the input (and ARIA for non-native hosts) and the buttons. */\n #write(value: number): void {\n const input = this.inputTarget;\n input.value = String(value);\n this.#syncInputAria(input, value);\n this.#updateButtons(value);\n }\n\n /** Reflects a blank value without inventing a numeric `aria-valuenow`. */\n #reflectEmpty(): void {\n this.#syncInputAria(this.inputTarget, null);\n this.#updateButtons(this.#currentValue());\n }\n\n /** Synchronizes controller-derived ARIA while preserving authored teardown values. */\n #syncInputAria(input: HTMLInputElement, value: number | null): void {\n if (input.getAttribute(\"role\") !== \"spinbutton\") {\n this.#releaseInputAria(input);\n return;\n }\n this.#ariaValueNow.write(input, value === null ? null : String(value));\n this.#ariaValueMin.write(input, Number.isFinite(this.minValue) ? String(this.minValue) : null);\n this.#ariaValueMax.write(input, Number.isFinite(this.maxValue) ? String(this.maxValue) : null);\n }\n\n /** Returns all custom-spinbutton ARIA leased on `input`. */\n #releaseInputAria(input: HTMLInputElement): void {\n this.#ariaValueNow.return(input);\n this.#ariaValueMin.return(input);\n this.#ariaValueMax.return(input);\n }\n\n /** Disables a step button at its bound, returning focus to the input first. */\n #updateButtons(value: number): void {\n for (const button of this.incrementTargets) this.#toggleButton(button, value < this.maxValue);\n for (const button of this.decrementTargets) this.#toggleButton(button, value > this.minValue);\n }\n\n /**\n * Enables or disables a step button at its bound, never disabling one while it\n * holds focus. Owns only the `disabled` it sets itself via a marker\n * (`data-number-input-disabled`, like `conditional-fields`/`submit-once`), so an\n * author-disabled button (e.g. the whole control disabled) is never re-enabled.\n */\n #toggleButton(button: HTMLButtonElement, enabled: boolean): void {\n if (enabled) {\n if (button.hasAttribute(OWNED_DISABLED)) {\n button.disabled = false;\n button.removeAttribute(OWNED_DISABLED);\n }\n return;\n }\n if (button.disabled) return; // already disabled (possibly by the author) — leave it\n if (document.activeElement === button && this.hasInputTarget) this.inputTarget.focus();\n button.disabled = true;\n button.setAttribute(OWNED_DISABLED, \"\");\n }\n\n /** Returns `disabled` only when its explicit marker still identifies controller ownership. */\n #releaseButtonState(button: HTMLButtonElement): void {\n if (!button.hasAttribute(OWNED_DISABLED)) return;\n button.disabled = false;\n button.removeAttribute(OWNED_DISABLED);\n }\n\n /**\n * The current numeric value, falling back to a finite min (else 0) when blank.\n * Every read of the field's number comes through here, and the text is read\n * through the shared half-width mapping: a text-type spinbutton holds whatever an\n * IME confirmed, full-width digits and signs included (`34`, `-5`), while a\n * native number field's value is already half-width or empty.\n */\n #currentValue(): number {\n const text = toHalfWidth(this.inputTarget.value);\n const parsed = Number(text);\n if (Number.isFinite(parsed) && text.trim() !== \"\") return parsed;\n return Number.isFinite(this.minValue) ? this.minValue : 0;\n }\n\n /** Clamps to `[min, max]` and snaps to the step grid anchored at a finite min (else 0). */\n #normalize(raw: number): number {\n return snapSteppedValue(raw, this.#steppedRange);\n }\n\n /** Shared range configuration; finite endpoints remain allowed off the grid. */\n get #steppedRange() {\n return { min: this.minValue, max: this.maxValue, step: this.stepValue };\n }\n /** Returns borrowed range ARIA before Turbo snapshots the page. */\n #rewindForCache(): void {\n this.#ariaValueNow.returnAll();\n this.#ariaValueMin.returnAll();\n this.#ariaValueMax.returnAll();\n }\n}\n"]}
|
|
@@ -81,7 +81,20 @@ var ListenerSet = class {
|
|
|
81
81
|
}
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
+
// src/utils/transient_hooks.ts
|
|
85
|
+
var TransientHooks = class {
|
|
86
|
+
#attributes;
|
|
87
|
+
constructor(options) {
|
|
88
|
+
this.#attributes = options.attributes;
|
|
89
|
+
}
|
|
90
|
+
/** Drops the declared hooks from `element`; what it carries besides stays. */
|
|
91
|
+
reset(element) {
|
|
92
|
+
for (const attribute of this.#attributes) element.removeAttribute(attribute);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
84
96
|
// src/controllers/optimistic_controller.ts
|
|
97
|
+
var TRANSIENT = new TransientHooks({ attributes: ["data-optimistic"] });
|
|
85
98
|
var HIDDEN_MARKER = "data-optimistic-toggled";
|
|
86
99
|
var BUSY_MARKER = "data-optimistic-busy";
|
|
87
100
|
var AUTHORED_ABSENT = "absent";
|
|
@@ -189,7 +202,7 @@ var OptimisticController = class extends Controller {
|
|
|
189
202
|
* received.
|
|
190
203
|
*/
|
|
191
204
|
#revert() {
|
|
192
|
-
this.element
|
|
205
|
+
TRANSIENT.reset(this.element);
|
|
193
206
|
this.#restore(this.element, "aria-busy", BUSY_MARKER, "true");
|
|
194
207
|
const hidden = new Set(this.hideTargets);
|
|
195
208
|
for (const target of /* @__PURE__ */ new Set([...this.showTargets, ...this.hideTargets])) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/detach_gate.ts","../../src/utils/listener_set.ts","../../src/controllers/optimistic_controller.ts"],"names":[],"mappings":";;;;;AA2EO,IAAM,UAAA,GAAN,MAAM,WAAA,CAAW;AAAA;AAAA,EAEtB,QAAA,GAAW,KAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASX,IAAI,OAAA,GAAmB;AACrB,IAAA,OAAO,IAAA,CAAK,QAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,WAAW,IAAA,EAA+B;AAC/C,IAAA,IAAI,CAAC,IAAA,CAAK,OAAA,CAAQ,WAAA,EAAa,OAAO,IAAA;AACtC,IAAA,MAAM,MAAA,GAAA,CAAU,KAAK,OAAA,CAAQ,YAAA,CAAa,iBAAiB,CAAA,IAAK,EAAA,EAAI,MAAM,KAAK,CAAA;AAC/E,IAAA,OAAO,CAAC,MAAA,CAAO,QAAA,CAAS,IAAA,CAAK,UAAU,CAAA;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,YAAA,CAAa,MAAsB,QAAA,EAA4B;AAC7D,IAAA,IAAI,WAAA,CAAW,UAAA,CAAW,IAAI,CAAA,EAAG;AAG/B,MAAA,IAAA,CAAK,QAAA,GAAW,KAAA;AAChB,MAAA,QAAA,EAAS;AACT,MAAA;AAAA,IACF;AACA,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAChB,IAAA,cAAA,CAAe,MAAM;AACnB,MAAA,IAAI,CAAC,KAAK,QAAA,EAAU;AACpB,MAAA,IAAA,CAAK,QAAA,GAAW,KAAA;AAChB,MAAA,QAAA,EAAS;AAAA,IACX,CAAC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAA,GAAe;AACb,IAAA,IAAA,CAAK,QAAA,GAAW,KAAA;AAAA,EAClB;AACF,CAAA;;;AC/EO,IAAM,cAAN,MAAkB;AAAA;AAAA,EAEvB,MAAA,GAAS,IAAI,eAAA,EAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS7B,GAAA,CACE,MAAA,EACA,IAAA,EACA,OAAA,EACA,OAAA,EACM;AACN,IAAA,MAAA,CAAO,gBAAA,CAAiB,IAAA,EAAM,OAAA,EAAS,EAAE,GAAG,SAAS,MAAA,EAAQ,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,CAAA;AAAA,EACnF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAA,GAAgB;AACd,IAAA,IAAA,CAAK,OAAO,KAAA,EAAM;AAClB,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,eAAA,EAAgB;AAAA,EACpC;AACF,CAAA;;;AC7EA,IAAM,aAAA,GAAgB,yBAAA;AAGtB,IAAM,WAAA,GAAc,sBAAA;AAGpB,IAAM,eAAA,GAAkB,QAAA;AAGxB,IAAM,cAAA,GAAiB,QAAA;AAOvB,IAAM,uBAAuB,EAAC;AAG9B,SAAS,aAAa,KAAA,EAAuB;AAC3C,EAAA,MAAM,SAAU,KAAA,CAAoD,MAAA;AACpE,EAAA,OAAO,QAAQ,cAAA,IAAkB,oBAAA;AACnC;AAkDO,IAAM,oBAAA,GAAN,cAAmC,UAAA,CAAwB;AAAA,EAChE,OAAgB,OAAA,GAAU,CAAC,MAAA,EAAQ,MAAM,CAAA;AAAA,EACzC,OAAO,MAAA,GAAS,CAAC,QAAA,EAAU,UAAU,CAAA;AAAA;AAAA,EAMrC,QAAA,GAAoB,IAAA;AAAA,EAEX,KAAA,GAAQ,IAAI,UAAA,EAAW;AAAA,EACvB,UAAA,GAAa,IAAI,WAAA,EAAY;AAAA;AAAA,EAGtC,UAAA,GAAa,KAAA;AAAA,EAEJ,OAAA,GAAgB;AAKvB,IAAA,MAAM,WAAW,CAAC,IAAA,CAAK,UAAA,IAAc,CAAC,KAAK,KAAA,CAAM,OAAA;AACjD,IAAA,IAAA,CAAK,MAAM,MAAA,EAAO;AAClB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,IAAA,IAAI,YAAY,IAAA,CAAK,OAAA,CAAQ,aAAa,iBAAiB,CAAA,OAAQ,OAAA,EAAQ;AAC3E,IAAA,IAAA,CAAK,WAAW,GAAA,CAAI,IAAA,CAAK,OAAA,EAAS,oBAAA,EAAsB,KAAK,cAAc,CAAA;AAC3E,IAAA,IAAA,CAAK,WAAW,GAAA,CAAI,IAAA,CAAK,OAAA,EAAS,kBAAA,EAAoB,KAAK,YAAY,CAAA;AAAA,EACzE;AAAA,EAES,UAAA,GAAmB;AAC1B,IAAA,IAAA,CAAK,UAAA,GAAa,KAAA;AAClB,IAAA,IAAA,CAAK,WAAW,OAAA,EAAQ;AAExB,IAAA,IAAA,CAAK,KAAA,CAAM,YAAA,CAAa,IAAA,EAAM,MAAM;AAClC,MAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAAA,IAClB,CAAC,CAAA;AAAA,EACH;AAAA,EAES,cAAA,GAAiB,CAAC,KAAA,KAAuB;AAChD,IAAA,IAAA,CAAK,QAAA,GAAW,aAAa,KAAK,CAAA;AAIlC,IAAA,IAAI,IAAA,CAAK,mBAAmB,eAAA,IAAmB,CAAC,KAAK,OAAA,CAAQ,YAAA,CAAa,WAAW,CAAA,EAAG;AACtF,MAAA,IAAA,CAAK,OAAA,CAAQ,YAAA,CAAa,WAAA,EAAa,eAAe,CAAA;AAAA,IACxD;AACA,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,OAAA,EAAS,WAAA,EAAa,QAAQ,WAAW,CAAA;AAC1D,IAAA,IAAA,CAAK,OAAA,CAAQ,YAAA,CAAa,iBAAA,EAAmB,MAAM,CAAA;AACnD,IAAA,KAAA,MAAW,UAAU,IAAA,CAAK,WAAA,EAAa,IAAA,CAAK,UAAA,CAAW,QAAQ,KAAK,CAAA;AACpE,IAAA,KAAA,MAAW,UAAU,IAAA,CAAK,WAAA,EAAa,IAAA,CAAK,UAAA,CAAW,QAAQ,IAAI,CAAA;AAAA,EACrE,CAAA;AAAA,EAES,YAAA,GAAe,CAAC,KAAA,KAAuB;AAG9C,IAAA,IAAI,YAAA,CAAa,KAAK,CAAA,KAAM,IAAA,CAAK,QAAA,EAAU;AAC3C,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAEhB,IAAA,MAAM,OAAA,GAAW,KAAA,CAA6C,MAAA,EAAQ,OAAA,KAAY,IAAA;AAClF,IAAA,IAAI,OAAA,EAAS;AAGX,MAAA,IAAA,CAAK,OAAA,CAAQ,gBAAgB,iBAAiB,CAAA;AAC9C,MAAA,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,OAAA,EAAS,WAAA,EAAa,aAAa,MAAM,CAAA;AAC5D,MAAA,KAAA,MAAW,MAAA,IAAU,CAAC,GAAG,IAAA,CAAK,aAAa,GAAG,IAAA,CAAK,WAAW,CAAA,EAAG;AAC/D,QAAA,MAAA,CAAO,gBAAgB,aAAa,CAAA;AAAA,MACtC;AACA,MAAA,IAAA,CAAK,SAAS,QAAQ,CAAA;AAAA,IACxB,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,OAAA,EAAQ;AACb,MAAA,IAAA,CAAK,SAAS,UAAU,CAAA;AAAA,IAC1B;AAAA,EACF,CAAA;AAAA;AAAA,EAGA,UAAA,CAAW,QAAqB,MAAA,EAAuB;AACrD,IAAA,IAAI,MAAA,CAAO,YAAA,CAAa,QAAQ,CAAA,KAAM,MAAA,EAAQ;AAC9C,IAAA,IAAA,CAAK,OAAO,MAAA,EAAQ,QAAA,EAAU,MAAA,GAAS,EAAA,GAAK,MAAM,aAAa,CAAA;AAAA,EACjE;AAAA;AAAA,EAGA,MAAA,CAAO,OAAA,EAAkB,SAAA,EAAmB,KAAA,EAAsB,MAAA,EAAsB;AACtF,IAAA,IAAI,CAAC,OAAA,CAAQ,YAAA,CAAa,MAAM,CAAA,EAAG;AACjC,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,YAAA,CAAa,SAAS,CAAA;AAC/C,MAAA,OAAA,CAAQ,YAAA;AAAA,QACN,MAAA;AAAA,QACA,aAAa,IAAA,GAAO,eAAA,GAAkB,CAAA,EAAG,cAAc,GAAG,QAAQ,CAAA;AAAA,OACpE;AAAA,IACF;AACA,IAAA,IAAI,KAAA,KAAU,IAAA,EAAM,OAAA,CAAQ,eAAA,CAAgB,SAAS,CAAA;AAAA,SAChD,OAAA,CAAQ,YAAA,CAAa,SAAA,EAAW,KAAK,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,QAAA,CAAS,OAAA,EAAkB,SAAA,EAAmB,MAAA,EAAgB,OAAA,EAA8B;AAC1F,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,YAAA,CAAa,MAAM,CAAA;AAC5C,IAAA,IAAI,aAAa,IAAA,EAAM;AACvB,IAAA,OAAA,CAAQ,gBAAgB,MAAM,CAAA;AAC9B,IAAA,IAAI,OAAA,CAAQ,YAAA,CAAa,SAAS,CAAA,KAAM,OAAA,EAAS;AACjD,IAAA,IAAI,QAAA,CAAS,UAAA,CAAW,cAAc,CAAA,EAAG;AACvC,MAAA,OAAA,CAAQ,aAAa,SAAA,EAAW,QAAA,CAAS,KAAA,CAAM,cAAA,CAAe,MAAM,CAAC,CAAA;AACrE,MAAA;AAAA,IACF;AACA,IAAA,OAAA,CAAQ,gBAAgB,SAAS,CAAA;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,OAAA,GAAgB;AACd,IAAA,IAAA,CAAK,OAAA,CAAQ,gBAAgB,iBAAiB,CAAA;AAC9C,IAAA,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,OAAA,EAAS,WAAA,EAAa,aAAa,MAAM,CAAA;AAC5D,IAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,IAAA,CAAK,WAAW,CAAA;AACvC,IAAA,KAAA,MAAW,MAAA,oBAAU,IAAI,GAAA,CAAI,CAAC,GAAG,IAAA,CAAK,WAAA,EAAa,GAAG,IAAA,CAAK,WAAW,CAAC,CAAA,EAAG;AACxE,MAAA,IAAA,CAAK,QAAA,CAAS,QAAQ,QAAA,EAAU,aAAA,EAAe,OAAO,GAAA,CAAI,MAAM,CAAA,GAAI,EAAA,GAAK,IAAI,CAAA;AAAA,IAC/E;AAAA,EACF;AACF","file":"optimistic_controller.js","sourcesContent":["/**\n * Shared \"in-page move vs real detach\" discriminator for `disconnect()`.\n *\n * A mid-session `disconnect()` whose element is still in the document is\n * ambiguous. It can be:\n *\n * - an **in-page move** — a consumer (sortable, a teleport) re-inserted the\n * element; Stimulus reconnects the SAME instance in the same mutation batch\n * and the controller's runtime state must survive;\n * - a **detach that keeps the element** — `data-controller` no longer lists the\n * identifier (a Turbo 8 morph), or the element moved outside the observed\n * root (a scoped `Application.start(root)`, a shadow root); no reconnect will\n * ever come and the controller must tear down or it leaks its sessions,\n * document listeners, and moved nodes.\n *\n * Neither available signal decides this alone. A synchronous `data-controller`\n * token check catches the morph case but misses an observed-root exit (the\n * token stays, the reconnect never comes). Deferring teardown one microtask and\n * letting `connect()` cancel it catches the root exit, but leans on Stimulus\n * reconnecting moved elements within the same mutation batch — internal\n * behavior, not a public contract. {@link DetachGate} composes both: the token\n * check is the synchronous fast path (no probe, no reliance on batching), the\n * microtask probe is the fallback for the genuinely ambiguous remainder.\n *\n * Policy stays with the consumer; this util owns the discrimination only:\n *\n * - **Probe** ({@link disconnected}) when the teardown must eventually happen —\n * live drag sessions, document listeners, teleported *content*. Call\n * {@link cancel} from `connect()` (a reconnect means in-page move) and from\n * the head of any direct teardown path (see below).\n * - **Keep** (bare {@link DetachGate.isDetached}) when undoing the ambiguous\n * case would fight the controller's own effect: a controller that moves its\n * OWN element (portal's no-`content` form) exits a scoped observed root as\n * its normal job — a probe-driven teardown would restore the element into\n * the root, reconnect, re-teleport, and disconnect again, forever. Such\n * controllers keep their state on an ambiguous disconnect and tear down only\n * on {@link DetachGate.isDetached}.\n *\n * Event convention for probe consumers: teardown into a dead (detached) tree is\n * silent — consumers restore from their own `connect()`; a detach that keeps the\n * tree alive must end an in-flight session with a `cancel` event, or composing\n * consumers strand their session bookkeeping.\n */\n\n/** The slice of a Stimulus controller the gate inspects. */\nexport interface DetachGateHost {\n readonly element: Element;\n readonly identifier: string;\n}\n\n/**\n * Per-controller-instance gate deciding whether a `disconnect()` is a real\n * detach. Hold one per controller (`#gate = new DetachGate()`) — Stimulus\n * reuses the instance across reconnects, so the pending probe state carries\n * over exactly as the protocol needs.\n *\n * @example\n * ```ts\n * #gate = new DetachGate();\n *\n * connect(): void {\n * this.#gate.cancel(); // a reconnect: the element moved in-page\n * // …\n * }\n *\n * disconnect(): void {\n * this.#gate.disconnected(this, () => this.#teardown());\n * }\n *\n * #teardown(): void {\n * this.#gate.cancel(); // disarm a still-queued probe (double-run guard)\n * // …\n * }\n * ```\n */\nexport class DetachGate {\n /** Set while a probe is queued, waiting for a reconnect to cancel it. */\n #pending = false;\n\n /**\n * True while a probe is queued — the last disconnect was ambiguous and no\n * reconnect has cancelled it yet. Read it from `connect()` to tell the\n * reconnect half of an in-page move from a first connect: a controller whose\n * initialisation restarts a measurement (a min-duration floor, an elapsed\n * counter) must skip it for the move, where nothing actually restarted.\n */\n get pending(): boolean {\n return this.#pending;\n }\n\n /**\n * True when the disconnect is definitely a real detach — the element left\n * the document, or `data-controller` no longer lists the identifier. False\n * means ambiguous (in-page move or observed-root exit), NOT \"alive\".\n */\n static isDetached(host: DetachGateHost): boolean {\n if (!host.element.isConnected) return true;\n const tokens = (host.element.getAttribute(\"data-controller\") ?? \"\").split(/\\s+/);\n return !tokens.includes(host.identifier);\n }\n\n /**\n * Call from `disconnect()`: runs `teardown` synchronously on a definite\n * detach (fast path), otherwise defers it one microtask — a reconnect\n * ({@link cancel} from `connect()`) keeps the state, no reconnect runs it.\n * One microtask is the whole probe window: Stimulus reconnects a moved\n * element within the same mutation batch, before the checkpoint drains.\n */\n disconnected(host: DetachGateHost, teardown: () => void): void {\n if (DetachGate.isDetached(host)) {\n // Also disarms a probe a previous ambiguous disconnect left queued\n // (defer, element removed, disconnect again) — exactly one teardown.\n this.#pending = false;\n teardown();\n return;\n }\n this.#pending = true;\n queueMicrotask(() => {\n if (!this.#pending) return;\n this.#pending = false;\n teardown();\n });\n }\n\n /**\n * Disarms a pending probe. Call from `connect()` (the reconnect that proves\n * an in-page move) and from the head of any teardown path not routed through\n * {@link disconnected} (disabled-toggle, Escape), so an orphaned probe can\n * never run the teardown a second time.\n */\n cancel(): void {\n this.#pending = false;\n }\n}\n","/**\n * Owns every listener a controller attaches for one connected lifetime and\n * releases them together, so an event name is spelled once and the release\n * cannot drift from the registration.\n *\n * Detaching by hand repeats three things at the release site — the type, the\n * handler reference and the capture flag — and any one of them drifting leaves\n * a listener alive on an element the controller no longer drives. Aborting the\n * signal a registration was created with releases it whatever it was made of,\n * so the release site names nothing.\n *\n * One set per lifetime, held as a field and never rebuilt. {@link\n * ListenerSet.dispose} is the only thing that closes a generation, and it opens\n * the next one in the same step, so the lifecycle hooks can call `add` and\n * `dispose` in any order any number of times, and no `add` ever meets an\n * already aborted signal — a DOM that registers a listener on such a signal\n * would keep it forever. Rebuilding the set instead would leak:\n * `addEventListener` matches an existing registration on target, type, callback\n * and capture alone, so re-adding the same handler while the first registration\n * is live is discarded, and that live registration keeps the signal it was\n * attached with — a set built by a second `connect()` can never release what it\n * appears to own.\n *\n * Scope is the registration only. Timers, observers, leases and the order they\n * are unwound in stay with the controller, so `dispose()` belongs exactly where\n * the `removeEventListener` block stood. 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 * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and only the contract belongs on the members.\n *\n * @example\n * ```ts\n * readonly #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","import { Controller } from \"@hotwired/stimulus\";\nimport { DetachGate } from \"../utils/detach_gate\";\nimport { ListenerSet } from \"../utils/listener_set\";\n\n/** Records that this controller wrote `hidden`, and the authored value to put back. */\nconst HIDDEN_MARKER = \"data-optimistic-toggled\";\n\n/** The same record for `aria-busy` on the controller element. */\nconst BUSY_MARKER = \"data-optimistic-busy\";\n\n/** Marker value standing for \"the attribute was not there\". */\nconst AUTHORED_ABSENT = \"absent\";\n\n/** Marker prefix carrying the authored attribute value that follows it. */\nconst AUTHORED_VALUE = \"value:\";\n\n/**\n * Stands in for the submission a synthetic lifecycle event does not name. Turbo\n * puts a `formSubmission` on both events, so this only ever pairs hand-dispatched\n * events with each other — one shared instance, so a start and its end match.\n */\nconst SYNTHETIC_SUBMISSION = {};\n\n/** The submission a Turbo lifecycle event belongs to. */\nfunction submissionOf(event: Event): unknown {\n const detail = (event as CustomEvent<{ formSubmission?: unknown }>).detail;\n return detail?.formSubmission ?? SYNTHETIC_SUBMISSION;\n}\n\n/**\n * Headless **optimistic UI** for Turbo form submissions — the one server-bound\n * behavior that needs no Action Cable: it wraps a Turbo form\n * and applies a *declared* optimistic state the moment the submission starts,\n * keeps it when the server confirms, and **rolls it back** when the submission\n * fails. The pattern: flip the like button instantly; the success response (a\n * Turbo Stream) replaces the fragment with the server truth anyway, so the\n * client only needs the instant flip and the failure rollback. Core (zero\n * dependencies — it rides Turbo's own `turbo:submit-*` events).\n *\n * Markup contract (identifier: `stimeo--optimistic`):\n * <form data-controller=\"stimeo--optimistic\" method=\"post\" action=\"/likes\">\n * <button type=\"submit\" aria-label=\"Like\">\n * <span data-stimeo--optimistic-target=\"hide\">♡</span>\n * <span hidden data-stimeo--optimistic-target=\"show\">♥</span>\n * </button>\n * </form>\n *\n * On `turbo:submit-start`, every `show` target is unhidden and every `hide`\n * target hidden, the element gains `data-optimistic=\"true\"` + `aria-busy=\"true\"`,\n * and the submission named by the event takes ownership of that state. On\n * `turbo:submit-end`: the terminal of any *other* submission is ignored; the\n * owner's terminal clears the pending hook and, on success, dispatches `commit`\n * (the toggled state stays — the server response owns the final DOM), or on\n * failure restores the authored markup and dispatches `rollback`.\n *\n * **Every write records what it displaced.** A target this controller hides or\n * reveals carries the authored `hidden` value in `data-optimistic-toggled`, and\n * the element carries the authored `aria-busy` in `data-optimistic-busy`, so a\n * rollback puts back exactly what the author wrote — not the inverse of whatever\n * the attribute says at that moment. The record lives in the DOM rather than in\n * memory because a Turbo cache snapshot has to carry it: `connect()` is what\n * rewinds a restored page.\n *\n * @remarks\n * Behavior only — what \"optimistic\" looks like is the author's markup (the\n * show/hide pair) and CSS (`[data-optimistic=\"true\"]`). Pairs with\n * `stimeo--submit-once` (double-submit guard) and `stimeo--live-counter`\n * (optimistic numbers). The listeners are delegated on the element, so a form\n * nested under it (or swapped for a new one) keeps working, and the submission\n * ownership is what keeps a sibling form's terminal from resolving this state.\n * With several submissions in flight the newest start owns the optimistic state,\n * so a submission whose terminal never arrives — its form replaced mid-flight —\n * cannot strand the element. `connect()` is idempotent: a Turbo cache snapshot\n * taken mid-submit is rewound, since no submission can be in flight across a\n * restore, while an in-page move (where the submission *does* survive) keeps it.\n * On a real detach the listeners go with the element.\n */\nexport class OptimisticController extends Controller<HTMLElement> {\n static override targets = [\"show\", \"hide\"];\n static events = [\"commit\", \"rollback\"] as const;\n\n declare readonly showTargets: HTMLElement[];\n declare readonly hideTargets: HTMLElement[];\n\n /** The submission that owns the applied optimistic state, if any. */\n #pending: unknown = null;\n\n readonly #gate = new DetachGate();\n readonly #listeners = new ListenerSet();\n\n /** True between `connect()` and `disconnect()`, so a repeat connect rewinds nothing. */\n #connected = false;\n\n override connect(): void {\n // Only a page this instance did not apply the state to can be holding a stale\n // one: a restored snapshot. An in-page move and a repeat connect both leave a\n // live submission behind, so neither may rewind — the gate names the first and\n // the connected flag the second.\n const restored = !this.#connected && !this.#gate.pending;\n this.#gate.cancel();\n this.#connected = true;\n if (restored && this.element.hasAttribute(\"data-optimistic\")) this.#revert();\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 override disconnect(): void {\n this.#connected = false;\n this.#listeners.dispose();\n // Ownership outlives an in-page move: only a real detach forgets it.\n this.#gate.disconnected(this, () => {\n this.#pending = null;\n });\n }\n\n readonly #onSubmitStart = (event: Event): void => {\n this.#pending = submissionOf(event);\n // Turbo owns `aria-busy` on a form and marks it before this event, so what is\n // there now is Turbo's, not the author's. Recording it would put Turbo's value\n // back on a page restored mid-submit, where no terminal ever arrives to clear it.\n if (this.element instanceof HTMLFormElement && !this.element.hasAttribute(BUSY_MARKER)) {\n this.element.setAttribute(BUSY_MARKER, AUTHORED_ABSENT);\n }\n this.#write(this.element, \"aria-busy\", \"true\", BUSY_MARKER);\n this.element.setAttribute(\"data-optimistic\", \"true\");\n for (const target of this.showTargets) this.#setHidden(target, false);\n for (const target of this.hideTargets) this.#setHidden(target, true);\n };\n\n readonly #onSubmitEnd = (event: Event): void => {\n // A terminal always names a submission, so an element holding none (nothing\n // started, or this one already resolved) falls out here too.\n if (submissionOf(event) !== this.#pending) return;\n this.#pending = null;\n\n const success = (event as CustomEvent<{ success?: boolean }>).detail?.success === true;\n if (success) {\n // Keep the toggled faces (the server response owns the final DOM) but drop\n // their records, so a later failure cannot revert a confirmed state.\n this.element.removeAttribute(\"data-optimistic\");\n this.#restore(this.element, \"aria-busy\", BUSY_MARKER, \"true\");\n for (const target of [...this.showTargets, ...this.hideTargets]) {\n target.removeAttribute(HIDDEN_MARKER);\n }\n this.dispatch(\"commit\");\n } else {\n this.#revert();\n this.dispatch(\"rollback\");\n }\n };\n\n /** Hides or reveals a target, recording the authored `hidden` on first write. */\n #setHidden(target: HTMLElement, hidden: boolean): void {\n if (target.hasAttribute(\"hidden\") === hidden) return;\n this.#write(target, \"hidden\", hidden ? \"\" : null, HIDDEN_MARKER);\n }\n\n /** Writes an attribute, recording what it displaced under `marker` once. */\n #write(element: Element, attribute: string, value: string | null, marker: string): void {\n if (!element.hasAttribute(marker)) {\n const authored = element.getAttribute(attribute);\n element.setAttribute(\n marker,\n authored === null ? AUTHORED_ABSENT : `${AUTHORED_VALUE}${authored}`,\n );\n }\n if (value === null) element.removeAttribute(attribute);\n else element.setAttribute(attribute, value);\n }\n\n /**\n * Puts back the value `marker` recorded and drops the record.\n *\n * `written` is what this controller last put in the attribute. Anything else\n * there now belongs to whoever wrote it — Turbo owns `aria-busy` on a form and\n * clears it before the terminal arrives, and a consumer may have moved a face\n * mid-flight — so the record is dropped without touching the attribute. A\n * marker this build did not write names no authored value, so the attribute\n * goes back to being absent.\n */\n #restore(element: Element, attribute: string, marker: string, written: string | null): void {\n const recorded = element.getAttribute(marker);\n if (recorded === null) return;\n element.removeAttribute(marker);\n if (element.getAttribute(attribute) !== written) return;\n if (recorded.startsWith(AUTHORED_VALUE)) {\n element.setAttribute(attribute, recorded.slice(AUTHORED_VALUE.length));\n return;\n }\n element.removeAttribute(attribute);\n }\n\n /**\n * Puts back exactly the markup this controller displaced (record-owned only).\n *\n * A face can be registered on both sides, and the hide pass runs last, so what\n * this controller wrote there is `hidden` — matching it against the reveal's\n * absent value instead would read as someone else's write and leave the face\n * hidden. Each target is therefore restored once, against the write it last\n * received.\n */\n #revert(): void {\n this.element.removeAttribute(\"data-optimistic\");\n this.#restore(this.element, \"aria-busy\", BUSY_MARKER, \"true\");\n const hidden = new Set(this.hideTargets);\n for (const target of new Set([...this.showTargets, ...this.hideTargets])) {\n this.#restore(target, \"hidden\", HIDDEN_MARKER, hidden.has(target) ? \"\" : null);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/detach_gate.ts","../../src/utils/listener_set.ts","../../src/utils/transient_hooks.ts","../../src/controllers/optimistic_controller.ts"],"names":[],"mappings":"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","file":"optimistic_controller.js","sourcesContent":["/**\n * Shared \"in-page move vs real detach\" discriminator for `disconnect()`.\n *\n * A mid-session `disconnect()` whose element is still in the document is\n * ambiguous. It can be:\n *\n * - an **in-page move** — a consumer (sortable, a teleport) re-inserted the\n * element; Stimulus reconnects the SAME instance in the same mutation batch\n * and the controller's runtime state must survive;\n * - a **detach that keeps the element** — `data-controller` no longer lists the\n * identifier (a Turbo 8 morph), or the element moved outside the observed\n * root (a scoped `Application.start(root)`, a shadow root); no reconnect will\n * ever come and the controller must tear down or it leaks its sessions,\n * document listeners, and moved nodes.\n *\n * Neither available signal decides this alone. A synchronous `data-controller`\n * token check catches the morph case but misses an observed-root exit (the\n * token stays, the reconnect never comes). Deferring teardown one microtask and\n * letting `connect()` cancel it catches the root exit, but leans on Stimulus\n * reconnecting moved elements within the same mutation batch — internal\n * behavior, not a public contract. {@link DetachGate} composes both: the token\n * check is the synchronous fast path (no probe, no reliance on batching), the\n * microtask probe is the fallback for the genuinely ambiguous remainder.\n *\n * Policy stays with the consumer; this util owns the discrimination only:\n *\n * - **Probe** ({@link disconnected}) when the teardown must eventually happen —\n * live drag sessions, document listeners, teleported *content*. Call\n * {@link cancel} from `connect()` (a reconnect means in-page move) and from\n * the head of any direct teardown path (see below).\n * - **Keep** (bare {@link DetachGate.isDetached}) when undoing the ambiguous\n * case would fight the controller's own effect: a controller that moves its\n * OWN element (portal's no-`content` form) exits a scoped observed root as\n * its normal job — a probe-driven teardown would restore the element into\n * the root, reconnect, re-teleport, and disconnect again, forever. Such\n * controllers keep their state on an ambiguous disconnect and tear down only\n * on {@link DetachGate.isDetached}.\n *\n * Event convention for probe consumers: teardown into a dead (detached) tree is\n * silent — consumers restore from their own `connect()`; a detach that keeps the\n * tree alive must end an in-flight session with a `cancel` event, or composing\n * consumers strand their session bookkeeping.\n */\n\n/** The slice of a Stimulus controller the gate inspects. */\nexport interface DetachGateHost {\n readonly element: Element;\n readonly identifier: string;\n}\n\n/**\n * Per-controller-instance gate deciding whether a `disconnect()` is a real\n * detach. Hold one per controller (`#gate = new DetachGate()`) — Stimulus\n * reuses the instance across reconnects, so the pending probe state carries\n * over exactly as the protocol needs.\n *\n * @example\n * ```ts\n * #gate = new DetachGate();\n *\n * connect(): void {\n * this.#gate.cancel(); // a reconnect: the element moved in-page\n * // …\n * }\n *\n * disconnect(): void {\n * this.#gate.disconnected(this, () => this.#teardown());\n * }\n *\n * #teardown(): void {\n * this.#gate.cancel(); // disarm a still-queued probe (double-run guard)\n * // …\n * }\n * ```\n */\nexport class DetachGate {\n /** Set while a probe is queued, waiting for a reconnect to cancel it. */\n #pending = false;\n\n /**\n * True while a probe is queued — the last disconnect was ambiguous and no\n * reconnect has cancelled it yet. Read it from `connect()` to tell the\n * reconnect half of an in-page move from a first connect: a controller whose\n * initialisation restarts a measurement (a min-duration floor, an elapsed\n * counter) must skip it for the move, where nothing actually restarted.\n */\n get pending(): boolean {\n return this.#pending;\n }\n\n /**\n * True when the disconnect is definitely a real detach — the element left\n * the document, or `data-controller` no longer lists the identifier. False\n * means ambiguous (in-page move or observed-root exit), NOT \"alive\".\n */\n static isDetached(host: DetachGateHost): boolean {\n if (!host.element.isConnected) return true;\n const tokens = (host.element.getAttribute(\"data-controller\") ?? \"\").split(/\\s+/);\n return !tokens.includes(host.identifier);\n }\n\n /**\n * Call from `disconnect()`: runs `teardown` synchronously on a definite\n * detach (fast path), otherwise defers it one microtask — a reconnect\n * ({@link cancel} from `connect()`) keeps the state, no reconnect runs it.\n * One microtask is the whole probe window: Stimulus reconnects a moved\n * element within the same mutation batch, before the checkpoint drains.\n */\n disconnected(host: DetachGateHost, teardown: () => void): void {\n if (DetachGate.isDetached(host)) {\n // Also disarms a probe a previous ambiguous disconnect left queued\n // (defer, element removed, disconnect again) — exactly one teardown.\n this.#pending = false;\n teardown();\n return;\n }\n this.#pending = true;\n queueMicrotask(() => {\n if (!this.#pending) return;\n this.#pending = false;\n teardown();\n });\n }\n\n /**\n * Disarms a pending probe. Call from `connect()` (the reconnect that proves\n * an in-page move) and from the head of any teardown path not routed through\n * {@link disconnected} (disabled-toggle, Escape), so an orphaned probe can\n * never run the teardown a second time.\n */\n cancel(): void {\n this.#pending = false;\n }\n}\n","/**\n * Owns every listener a controller attaches for one connected lifetime and\n * releases them together, so an event name is spelled once and the release\n * cannot drift from the registration.\n *\n * Detaching by hand repeats three things at the release site — the type, the\n * handler reference and the capture flag — and any one of them drifting leaves\n * a listener alive on an element the controller no longer drives. Aborting the\n * signal a registration was created with releases it whatever it was made of,\n * so the release site names nothing.\n *\n * One set per lifetime, held as a field and never rebuilt. {@link\n * ListenerSet.dispose} is the only thing that closes a generation, and it opens\n * the next one in the same step, so the lifecycle hooks can call `add` and\n * `dispose` in any order any number of times, and no `add` ever meets an\n * already aborted signal — a DOM that registers a listener on such a signal\n * would keep it forever. Rebuilding the set instead would leak:\n * `addEventListener` matches an existing registration on target, type, callback\n * and capture alone, so re-adding the same handler while the first registration\n * is live is discarded, and that live registration keeps the signal it was\n * attached with — a set built by a second `connect()` can never release what it\n * appears to own.\n *\n * Scope is the registration only. Timers, observers, leases and the order they\n * are unwound in stay with the controller, so `dispose()` belongs exactly where\n * the `removeEventListener` block stood. 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 * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and only the contract belongs on the members.\n *\n * @example\n * ```ts\n * readonly #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 * Drops the transient state hooks a connection inherited, so the cycle it opens\n * starts from a DOM that claims nothing.\n *\n * A transient hook is an attribute a controller writes to say \"this is happening\n * right now\" — a drag in progress, a form with unsaved edits, a refused\n * subscription. Its lifetime belongs to the interaction, not to the element, and\n * the two come apart whenever the element outlives the instance that wrote it: a\n * Turbo cache snapshot taken mid-interaction restores the hook with the markup,\n * and an element re-inserted elsewhere in the page carries it along. Nothing\n * takes such a hook off, because the instance that owned it is gone; consumer\n * CSS then keeps claiming a state no one is in.\n *\n * **`turbo:before-cache` cannot do this job**, which is why the pass belongs in\n * `connect()`: that event fires for a navigation only, so an in-page move — the\n * other half of the population — never reaches it, and a hook that arrived with\n * a moved element would survive. `connect()` covers both, because both end in a\n * connection.\n *\n * Scope is the **drop** only. What the fresh cycle should say instead is the\n * consumer's: a value re-read from the live DOM, a roster refilled by the\n * stream, a measurement retaken from the current scroll position, a slot emptied\n * of the last result. Those answers differ per consumer and none of them is a\n * hook removal, so the pass stops where every consumer agrees.\n *\n * A declaration names one group of hooks and carries no per-instance state, so\n * it belongs at module scope, shared by every instance the page connects. A\n * consumer writes as many as it has groups — hooks it drops under different\n * conditions cannot share one. The exception is a hook whose name is only known\n * at runtime, such as one built from the registered identifier: that\n * declaration is a field, because the name is.\n *\n * **A drop is not always right.** The population splits on one question: can the\n * state a hook describes outlive the instance that wrote it? Where it cannot — a\n * drag ends with the pointer, a subscription dies with its socket — dropping on\n * every connection is correct. Where it can — a submission in flight is not\n * interrupted by an in-page move — only a connection that follows a real restore\n * may drop, and the consumer answers \"did a session survive?\" with the signal it\n * already holds for its own teardown (a live-session field, a `DetachGate`\n * probe). The drop then sits behind that answer, as the second example shows.\n *\n * @example Unconditional — the state cannot outlive the instance.\n * ```ts\n * const TRANSIENT = new TransientHooks({ attributes: [\"data-sortable-dragging\"] });\n *\n * connect(): void {\n * TRANSIENT.reset(this.element);\n * }\n * ```\n *\n * @example Gated — an in-page move must not drop a live state.\n * ```ts\n * const TRANSIENT = new TransientHooks({ attributes: [\"data-optimistic\"] });\n *\n * connect(): void {\n * const restored = !this.#connected && !this.#gate.pending;\n * this.#gate.cancel();\n * this.#connected = true;\n * if (restored) TRANSIENT.reset(this.element);\n * }\n * ```\n */\n\n/** What a consumer declares once about the hooks its interaction owns. */\nexport interface TransientHooksOptions {\n /**\n * The attribute names a connection may find written by an earlier one. Hooks\n * a consumer drops under different conditions belong to different\n * declarations, so each one can be reset on its own.\n */\n readonly attributes: readonly string[];\n}\n\n/**\n * One consumer's declaration of the hooks its interaction owns, dropped from an\n * element by {@link TransientHooks.reset}.\n *\n * Every member comment inside this class body is inlined into each consumer\n * bundle (`tsup` builds with `splitting: false`), so rationale belongs in this\n * file's own block, which `dist` drops, and only the contract belongs inside.\n */\nexport class TransientHooks {\n readonly #attributes: readonly string[];\n\n constructor(options: TransientHooksOptions) {\n this.#attributes = options.attributes;\n }\n\n /** Drops the declared hooks from `element`; what it carries besides stays. */\n reset(element: Element): void {\n for (const attribute of this.#attributes) element.removeAttribute(attribute);\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { DetachGate } from \"../utils/detach_gate\";\nimport { ListenerSet } from \"../utils/listener_set\";\nimport { TransientHooks } from \"../utils/transient_hooks\";\n\n/** The hook a connection may find written by an earlier, now-gone one. */\nconst TRANSIENT = new TransientHooks({ attributes: [\"data-optimistic\"] });\n\n/** Records that this controller wrote `hidden`, and the authored value to put back. */\nconst HIDDEN_MARKER = \"data-optimistic-toggled\";\n\n/** The same record for `aria-busy` on the controller element. */\nconst BUSY_MARKER = \"data-optimistic-busy\";\n\n/** Marker value standing for \"the attribute was not there\". */\nconst AUTHORED_ABSENT = \"absent\";\n\n/** Marker prefix carrying the authored attribute value that follows it. */\nconst AUTHORED_VALUE = \"value:\";\n\n/**\n * Stands in for the submission a synthetic lifecycle event does not name. Turbo\n * puts a `formSubmission` on both events, so this only ever pairs hand-dispatched\n * events with each other — one shared instance, so a start and its end match.\n */\nconst SYNTHETIC_SUBMISSION = {};\n\n/** The submission a Turbo lifecycle event belongs to. */\nfunction submissionOf(event: Event): unknown {\n const detail = (event as CustomEvent<{ formSubmission?: unknown }>).detail;\n return detail?.formSubmission ?? SYNTHETIC_SUBMISSION;\n}\n\n/**\n * Headless **optimistic UI** for Turbo form submissions — the one server-bound\n * behavior that needs no Action Cable: it wraps a Turbo form\n * and applies a *declared* optimistic state the moment the submission starts,\n * keeps it when the server confirms, and **rolls it back** when the submission\n * fails. The pattern: flip the like button instantly; the success response (a\n * Turbo Stream) replaces the fragment with the server truth anyway, so the\n * client only needs the instant flip and the failure rollback. Core (zero\n * dependencies — it rides Turbo's own `turbo:submit-*` events).\n *\n * Markup contract (identifier: `stimeo--optimistic`):\n * <form data-controller=\"stimeo--optimistic\" method=\"post\" action=\"/likes\">\n * <button type=\"submit\" aria-label=\"Like\">\n * <span data-stimeo--optimistic-target=\"hide\">♡</span>\n * <span hidden data-stimeo--optimistic-target=\"show\">♥</span>\n * </button>\n * </form>\n *\n * On `turbo:submit-start`, every `show` target is unhidden and every `hide`\n * target hidden, the element gains `data-optimistic=\"true\"` + `aria-busy=\"true\"`,\n * and the submission named by the event takes ownership of that state. On\n * `turbo:submit-end`: the terminal of any *other* submission is ignored; the\n * owner's terminal clears the pending hook and, on success, dispatches `commit`\n * (the toggled state stays — the server response owns the final DOM), or on\n * failure restores the authored markup and dispatches `rollback`.\n *\n * **Every write records what it displaced.** A target this controller hides or\n * reveals carries the authored `hidden` value in `data-optimistic-toggled`, and\n * the element carries the authored `aria-busy` in `data-optimistic-busy`, so a\n * rollback puts back exactly what the author wrote — not the inverse of whatever\n * the attribute says at that moment. The record lives in the DOM rather than in\n * memory because a Turbo cache snapshot has to carry it: `connect()` is what\n * rewinds a restored page.\n *\n * @remarks\n * Behavior only — what \"optimistic\" looks like is the author's markup (the\n * show/hide pair) and CSS (`[data-optimistic=\"true\"]`). Pairs with\n * `stimeo--submit-once` (double-submit guard) and `stimeo--live-counter`\n * (optimistic numbers). The listeners are delegated on the element, so a form\n * nested under it (or swapped for a new one) keeps working, and the submission\n * ownership is what keeps a sibling form's terminal from resolving this state.\n * With several submissions in flight the newest start owns the optimistic state,\n * so a submission whose terminal never arrives — its form replaced mid-flight —\n * cannot strand the element. `connect()` is idempotent: a Turbo cache snapshot\n * taken mid-submit is rewound, since no submission can be in flight across a\n * restore, while an in-page move (where the submission *does* survive) keeps it.\n * On a real detach the listeners go with the element.\n */\nexport class OptimisticController extends Controller<HTMLElement> {\n static override targets = [\"show\", \"hide\"];\n static events = [\"commit\", \"rollback\"] as const;\n\n declare readonly showTargets: HTMLElement[];\n declare readonly hideTargets: HTMLElement[];\n\n /** The submission that owns the applied optimistic state, if any. */\n #pending: unknown = null;\n\n readonly #gate = new DetachGate();\n readonly #listeners = new ListenerSet();\n\n /** True between `connect()` and `disconnect()`, so a repeat connect rewinds nothing. */\n #connected = false;\n\n override connect(): void {\n // Only a page this instance did not apply the state to can be holding a stale\n // one: a restored snapshot. An in-page move and a repeat connect both leave a\n // live submission behind, so neither may rewind — the gate names the first and\n // the connected flag the second.\n const restored = !this.#connected && !this.#gate.pending;\n this.#gate.cancel();\n this.#connected = true;\n if (restored && this.element.hasAttribute(\"data-optimistic\")) this.#revert();\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 override disconnect(): void {\n this.#connected = false;\n this.#listeners.dispose();\n // Ownership outlives an in-page move: only a real detach forgets it.\n this.#gate.disconnected(this, () => {\n this.#pending = null;\n });\n }\n\n readonly #onSubmitStart = (event: Event): void => {\n this.#pending = submissionOf(event);\n // Turbo owns `aria-busy` on a form and marks it before this event, so what is\n // there now is Turbo's, not the author's. Recording it would put Turbo's value\n // back on a page restored mid-submit, where no terminal ever arrives to clear it.\n if (this.element instanceof HTMLFormElement && !this.element.hasAttribute(BUSY_MARKER)) {\n this.element.setAttribute(BUSY_MARKER, AUTHORED_ABSENT);\n }\n this.#write(this.element, \"aria-busy\", \"true\", BUSY_MARKER);\n this.element.setAttribute(\"data-optimistic\", \"true\");\n for (const target of this.showTargets) this.#setHidden(target, false);\n for (const target of this.hideTargets) this.#setHidden(target, true);\n };\n\n readonly #onSubmitEnd = (event: Event): void => {\n // A terminal always names a submission, so an element holding none (nothing\n // started, or this one already resolved) falls out here too.\n if (submissionOf(event) !== this.#pending) return;\n this.#pending = null;\n\n const success = (event as CustomEvent<{ success?: boolean }>).detail?.success === true;\n if (success) {\n // Keep the toggled faces (the server response owns the final DOM) but drop\n // their records, so a later failure cannot revert a confirmed state.\n this.element.removeAttribute(\"data-optimistic\");\n this.#restore(this.element, \"aria-busy\", BUSY_MARKER, \"true\");\n for (const target of [...this.showTargets, ...this.hideTargets]) {\n target.removeAttribute(HIDDEN_MARKER);\n }\n this.dispatch(\"commit\");\n } else {\n this.#revert();\n this.dispatch(\"rollback\");\n }\n };\n\n /** Hides or reveals a target, recording the authored `hidden` on first write. */\n #setHidden(target: HTMLElement, hidden: boolean): void {\n if (target.hasAttribute(\"hidden\") === hidden) return;\n this.#write(target, \"hidden\", hidden ? \"\" : null, HIDDEN_MARKER);\n }\n\n /** Writes an attribute, recording what it displaced under `marker` once. */\n #write(element: Element, attribute: string, value: string | null, marker: string): void {\n if (!element.hasAttribute(marker)) {\n const authored = element.getAttribute(attribute);\n element.setAttribute(\n marker,\n authored === null ? AUTHORED_ABSENT : `${AUTHORED_VALUE}${authored}`,\n );\n }\n if (value === null) element.removeAttribute(attribute);\n else element.setAttribute(attribute, value);\n }\n\n /**\n * Puts back the value `marker` recorded and drops the record.\n *\n * `written` is what this controller last put in the attribute. Anything else\n * there now belongs to whoever wrote it — Turbo owns `aria-busy` on a form and\n * clears it before the terminal arrives, and a consumer may have moved a face\n * mid-flight — so the record is dropped without touching the attribute. A\n * marker this build did not write names no authored value, so the attribute\n * goes back to being absent.\n */\n #restore(element: Element, attribute: string, marker: string, written: string | null): void {\n const recorded = element.getAttribute(marker);\n if (recorded === null) return;\n element.removeAttribute(marker);\n if (element.getAttribute(attribute) !== written) return;\n if (recorded.startsWith(AUTHORED_VALUE)) {\n element.setAttribute(attribute, recorded.slice(AUTHORED_VALUE.length));\n return;\n }\n element.removeAttribute(attribute);\n }\n\n /**\n * Puts back exactly the markup this controller displaced (record-owned only).\n *\n * A face can be registered on both sides, and the hide pass runs last, so what\n * this controller wrote there is `hidden` — matching it against the reveal's\n * absent value instead would read as someone else's write and leave the face\n * hidden. Each target is therefore restored once, against the write it last\n * received.\n */\n #revert(): void {\n TRANSIENT.reset(this.element);\n this.#restore(this.element, \"aria-busy\", BUSY_MARKER, \"true\");\n const hidden = new Set(this.hideTargets);\n for (const target of new Set([...this.showTargets, ...this.hideTargets])) {\n this.#restore(target, \"hidden\", HIDDEN_MARKER, hidden.has(target) ? \"\" : null);\n }\n }\n}\n"]}
|