stimeo-ui 0.16.0 → 0.18.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 +136 -0
- package/README.md +27 -11
- package/dist/cable/index.d.ts +88 -31
- package/dist/cable/index.js +384 -449
- package/dist/cable/index.js.map +1 -1
- package/dist/coerce-JtZVNG5h.d.ts +20 -0
- package/dist/controllers/accordion_controller.d.ts +4 -2
- package/dist/controllers/accordion_controller.js +174 -55
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +12 -2
- package/dist/controllers/alert_dialog_controller.js +903 -235
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +37 -5
- package/dist/controllers/announcer_controller.js +238 -205
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/aspect_ratio_controller.js +0 -10
- package/dist/controllers/aspect_ratio_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.d.ts +30 -6
- package/dist/controllers/auto_submit_controller.js +414 -145
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +185 -107
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.js +0 -130
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +21 -3
- package/dist/controllers/bulk_select_controller.js +282 -58
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +38 -16
- package/dist/controllers/calendar_controller.js +228 -260
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +43 -12
- package/dist/controllers/carousel_controller.js +536 -436
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +44 -7
- package/dist/controllers/character_counter_controller.js +493 -207
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.d.ts +7 -2
- package/dist/controllers/checkbox_controller.js +38 -74
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +16 -5
- package/dist/controllers/clipboard_controller.js +261 -149
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +17 -5
- package/dist/controllers/collapsible_controller.js +304 -120
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +18 -6
- package/dist/controllers/color_picker_controller.js +178 -146
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +24 -6
- package/dist/controllers/combobox_controller.js +522 -156
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +51 -20
- package/dist/controllers/command_palette_controller.js +1156 -395
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.d.ts +15 -2
- package/dist/controllers/conditional_fields_controller.js +300 -171
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.d.ts +49 -12
- package/dist/controllers/confirm_controller.js +980 -262
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +80 -18
- package/dist/controllers/context_menu_controller.js +346 -153
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/count_up_controller.d.ts +13 -1
- package/dist/controllers/count_up_controller.js +73 -21
- package/dist/controllers/count_up_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +47 -3
- package/dist/controllers/countdown_controller.js +281 -158
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +93 -10
- package/dist/controllers/currency_input_controller.js +575 -225
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +25 -12
- package/dist/controllers/data_grid_controller.js +248 -214
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +42 -14
- package/dist/controllers/date_range_picker_controller.js +338 -235
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +17 -6
- package/dist/controllers/dialog_controller.js +899 -237
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +25 -8
- package/dist/controllers/direct_upload_controller.js +97 -167
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.d.ts +6 -1
- package/dist/controllers/dirty_form_controller.js +72 -69
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.d.ts +19 -0
- package/dist/controllers/dismissible_controller.js +196 -24
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +39 -11
- package/dist/controllers/drawer_controller.js +974 -355
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +31 -1
- package/dist/controllers/dropdown_controller.js +339 -96
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.d.ts +12 -1
- package/dist/controllers/editable_controller.js +315 -88
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.js +79 -58
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +63 -18
- package/dist/controllers/file_dropzone_controller.js +322 -211
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.js +77 -42
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +35 -12
- package/dist/controllers/flash_controller.js +238 -347
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +15 -6
- package/dist/controllers/focus_controller.js +679 -240
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +156 -132
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.d.ts +3 -3
- package/dist/controllers/form_validation_controller.js +31 -97
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +47 -14
- package/dist/controllers/frame_loading_controller.js +275 -217
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +27 -6
- package/dist/controllers/highlight_controller.js +107 -75
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +60 -4
- package/dist/controllers/hover_card_controller.js +327 -130
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +48 -6
- package/dist/controllers/idle_controller.js +271 -101
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +30 -13
- package/dist/controllers/input_mask_controller.js +483 -97
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.d.ts +32 -13
- package/dist/controllers/intersection_controller.js +150 -106
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.d.ts +19 -2
- package/dist/controllers/lazy_frame_controller.js +24 -68
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +44 -9
- package/dist/controllers/listbox_controller.js +523 -240
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.d.ts +9 -7
- package/dist/controllers/local_time_controller.js +102 -77
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.d.ts +11 -3
- package/dist/controllers/masonry_controller.js +80 -96
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +55 -11
- package/dist/controllers/menu_controller.js +302 -188
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +51 -9
- package/dist/controllers/menubar_controller.js +232 -345
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.d.ts +33 -20
- package/dist/controllers/meter_controller.js +417 -87
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +48 -18
- package/dist/controllers/multi_select_controller.js +785 -393
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +27 -6
- package/dist/controllers/navigation_menu_controller.js +190 -250
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.d.ts +10 -0
- package/dist/controllers/nested_form_controller.js +396 -192
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.d.ts +23 -0
- package/dist/controllers/network_status_controller.js +268 -62
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +50 -8
- package/dist/controllers/number_input_controller.js +610 -303
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.d.ts +8 -5
- package/dist/controllers/optimistic_controller.js +45 -87
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +55 -8
- package/dist/controllers/otp_controller.js +658 -255
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +5 -0
- package/dist/controllers/overflow_indicator_controller.js +122 -113
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +58 -20
- package/dist/controllers/overflow_menu_controller.js +470 -295
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +37 -7
- package/dist/controllers/pagination_controller.js +332 -123
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +46 -8
- package/dist/controllers/password_reveal_controller.js +392 -121
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +15 -6
- package/dist/controllers/password_strength_controller.js +111 -158
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.d.ts +11 -4
- package/dist/controllers/persist_controller.js +165 -111
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +35 -12
- package/dist/controllers/pointer_drag_controller.js +434 -188
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +41 -5
- package/dist/controllers/popover_controller.js +269 -113
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +44 -4
- package/dist/controllers/portal_controller.js +215 -76
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.d.ts +10 -4
- package/dist/controllers/preview_guard_controller.js +243 -116
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +20 -19
- package/dist/controllers/progress_controller.js +391 -78
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +21 -9
- package/dist/controllers/radio_group_controller.js +278 -211
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +47 -6
- package/dist/controllers/range_slider_controller.js +220 -113
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +20 -4
- package/dist/controllers/rating_controller.js +458 -211
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +2 -0
- package/dist/controllers/read_more_controller.js +138 -63
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/reading_progress_controller.d.ts +1 -1
- package/dist/controllers/reading_progress_controller.js +166 -137
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.d.ts +20 -6
- package/dist/controllers/relative_time_controller.js +211 -112
- package/dist/controllers/relative_time_controller.js.map +1 -1
- package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
- package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +103 -38
- package/dist/controllers/reset_on_restore_controller.js.map +1 -0
- package/dist/controllers/resizable_controller.d.ts +31 -2
- package/dist/controllers/resizable_controller.js +299 -150
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.d.ts +3 -3
- package/dist/controllers/roving_controller.js +49 -88
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.d.ts +7 -3
- package/dist/controllers/scroll_area_controller.js +490 -246
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.d.ts +3 -3
- package/dist/controllers/scroll_restore_controller.js +14 -55
- package/dist/controllers/scroll_restore_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
- package/dist/controllers/scroll_visibility_controller.js +432 -149
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +6 -1
- package/dist/controllers/scrollspy_controller.js +193 -248
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +20 -5
- package/dist/controllers/separator_controller.js +285 -142
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +65 -8
- package/dist/controllers/sidebar_controller.js +1069 -404
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.d.ts +23 -0
- package/dist/controllers/skeleton_controller.js +259 -101
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +29 -2
- package/dist/controllers/slider_controller.js +187 -86
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.d.ts +22 -4
- package/dist/controllers/smart_sticky_header_controller.js +173 -66
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.d.ts +4 -4
- package/dist/controllers/sortable_controller.js +26 -125
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.d.ts +39 -4
- package/dist/controllers/spinner_controller.js +288 -173
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +9 -4
- package/dist/controllers/step_indicator_controller.js +105 -46
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +14 -6
- package/dist/controllers/stepper_controller.js +120 -73
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +9 -1
- package/dist/controllers/stick_to_bottom_controller.js +275 -140
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/sticky_observer_controller.d.ts +11 -0
- package/dist/controllers/sticky_observer_controller.js +220 -49
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +24 -8
- package/dist/controllers/submit_once_controller.js +376 -227
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +18 -4
- package/dist/controllers/switch_controller.js +201 -84
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +9 -6
- package/dist/controllers/tabs_controller.js +41 -30
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +16 -3
- package/dist/controllers/tags_input_controller.js +293 -173
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.d.ts +11 -1
- package/dist/controllers/textarea_autosize_controller.js +108 -74
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +24 -11
- package/dist/controllers/theme_controller.js +140 -116
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +55 -5
- package/dist/controllers/time_picker_controller.js +331 -127
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +40 -16
- package/dist/controllers/toast_controller.js +244 -324
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +14 -6
- package/dist/controllers/toggle_group_controller.js +252 -191
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.d.ts +8 -6
- package/dist/controllers/toolbar_controller.js +219 -120
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +49 -4
- package/dist/controllers/tooltip_controller.js +158 -124
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.d.ts +22 -4
- package/dist/controllers/transition_controller.js +123 -119
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +22 -12
- package/dist/controllers/tree_view_controller.js +283 -369
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.js +8573 -10410
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +38 -14
- package/dist/inspector/cli.js +108 -40
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +145 -64
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +20 -20
- package/dist/inspector/manifest.json +802 -101
- package/dist/positioning/index.d.ts +15 -7
- package/dist/positioning/index.js +154 -47
- package/dist/positioning/index.js.map +1 -1
- package/package.json +11 -10
- package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
- package/dist/controllers/reset_before_cache_controller.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/authored_integer.ts","../../src/utils/reduced_motion.ts","../../src/controllers/count_up_controller.ts"],"names":[],"mappings":"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","file":"count_up_controller.js","sourcesContent":["/** Digits plus the characters that may separate them inside one number. */\nconst TOKEN_BODY = /\\d[\\d.,'’’ _]*\\d|\\d/;\n/** A `-` opens a token only against one of these on its left. */\nconst SIGN_OPENER = /[\\s([]/;\n\n/**\n * Reads the integer that a human-formatted string displays.\n *\n * Takes the first numeric token, drops group separators, and truncates any\n * fractional part toward zero — so `\"1,200 users\"`, `\"$1,299.99\"` and\n * `\"1.299,99\"` read 1200, 1299 and 1299 without asking the platform which\n * locale is in play. A separator counts as grouping only where the digits that\n * follow it form a run of exactly three and the leading run could head a\n * grouped number; anything else is the decimal point, and the digits after it\n * are the fraction.\n *\n * `-` counts as a sign only where it opens the token — at the start of the\n * string, or after whitespace or an opening bracket — so a hyphen inside a\n * label (`\"Sign-ups: 1,200\"`, `\"Top-10 users\"`) never becomes one.\n *\n * `\"1.200\"` is genuinely ambiguous (1200 grouped, 1.2 with a decimal point) and\n * reads as 1200: display numbers group far more often than they carry a\n * fraction in exactly three digits.\n *\n * Returns `null` when the string holds no digits at all. What that means is the\n * caller's to decide — starting nothing, or falling back to a base value.\n */\nexport function authoredInteger(text: string): number | null {\n const firstDigit = /\\d/.exec(text);\n if (firstDigit === null) return null;\n\n const at = firstDigit.index;\n const signed =\n at > 0 && text[at - 1] === \"-\" && (at === 1 || SIGN_OPENER.test(text[at - 2] as string));\n\n const body = TOKEN_BODY.exec(text.slice(at)) as RegExpExecArray;\n const runs = body[0].split(/\\D+/);\n const separators = body[0].match(/\\D+/g) ?? [];\n\n // The leading run heads a grouped number only when it is short enough to be a\n // group itself and carries no leading zero; \"0.500\" and \"012,3\" are decimals.\n const lead = runs[0] as string;\n const heads = lead.length <= 3 && !lead.startsWith(\"0\");\n let digits = lead;\n for (let i = 1; heads && i < runs.length; i += 1) {\n if (separators[i - 1]?.length !== 1 || runs[i]?.length !== 3) break;\n digits += runs[i];\n }\n\n const magnitude = Number.parseInt(digits, 10);\n return signed ? -magnitude || 0 : magnitude;\n}\n","/**\n * Shared `prefers-reduced-motion` lookup for the motion-aware controllers.\n *\n * This one-liner keeps the media query string and the environment guard\n * single-sourced across them. The preference is intentionally re-read on every\n * call — the controllers check it at each animation/scroll start (WCAG 2.2\n * **2.3.3**), so flipping the OS setting takes effect immediately without any\n * listener or cache bookkeeping here.\n */\n\n/**\n * Whether the user currently requests reduced motion.\n *\n * @returns `true` when `(prefers-reduced-motion: reduce)` matches; `false`\n * otherwise, including environments without `window.matchMedia` (treated as\n * \"no preference\").\n */\nexport function prefersReducedMotion(): boolean {\n return (\n typeof window.matchMedia === \"function\" &&\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n );\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { authoredInteger } from \"../utils/authored_integer\";\nimport { prefersReducedMotion } from \"../utils/reduced_motion\";\n\n/** The animation length used when `duration` falls outside its domain. */\nconst DEFAULT_DURATION = 1200;\n\n/**\n * Headless **count-up**: animates a number from `from` up to the value already\n * in the DOM — typically started the moment the element scrolls into view by\n * composing with the `stimeo--intersection` primitive (the classic \"animate\n * when visible\"). Distinct from `stimeo--countdown` (time-based). Core\n * (zero dependencies).\n *\n * Markup contract (identifier: `stimeo--count-up`):\n * <span data-controller=\"stimeo--intersection stimeo--count-up\"\n * data-stimeo--intersection-once-value=\"true\"\n * data-action=\"stimeo--intersection:enter->stimeo--count-up#start\">1200</span>\n *\n * The **final number stays authored in the markup** (SEO / no-JS / SR read the\n * real value); `start` animates the displayed text from `from` to it over\n * `duration` ms with an ease-out curve, then restores the exact authored text.\n * With `once` (default) later starts are ignored (`data-count-up-done` records\n * a finished run across Turbo cache restores).\n *\n * Only the text node holding the number is animated, so sibling markup — a unit\n * in a `<small>`, a label in a `<b>` — is left where the author put it.\n *\n * `end` dispatches `{ value }`.\n *\n * @remarks\n * Behavior only — no formatting is imposed: the authored text is read for the\n * integer it displays and restored verbatim at the end; intermediate frames\n * render plain integers. Accessibility: when the user prefers reduced motion the\n * animation is skipped entirely (the value just stays final — WCAG 2.3.3).\n * During a run the ticking number is wrapped in a `role=\"img\"` element named\n * with the authored text, which is where a name is allowed to live — the host\n * keeps whatever semantics it had, so a `<dd>` stays a definition. That wrapper\n * doubles as the interrupted-run record `connect()` restores from after a Turbo\n * cache snapshot taken mid-animation. The animation frame is canceled on\n * `disconnect()` (Turbo navigation included) and the authored text restored.\n */\nexport class CountUpController extends Controller<HTMLElement> {\n static override values = {\n duration: { type: Number, default: DEFAULT_DURATION },\n from: { type: Number, default: 0 },\n once: { type: Boolean, default: true },\n };\n static actions = [\"start\"] as const;\n static events = [\"end\"] as const;\n\n declare durationValue: number;\n declare fromValue: number;\n declare onceValue: boolean;\n\n #frame: number | null = null;\n\n /** The animation length, with a declaration outside its domain read as the default. */\n get #duration(): number {\n return Number.isFinite(this.durationValue) && this.durationValue > 0\n ? this.durationValue\n : DEFAULT_DURATION;\n }\n\n /** The starting value, with a declaration that is not a finite number read as zero. */\n get #from(): number {\n return Number.isFinite(this.fromValue) ? this.fromValue : 0;\n }\n\n override connect(): void {\n // Turbo snapshots the page BEFORE the body swap, so a cached page can hold\n // a mid-animation frame (disconnect()'s settle runs too late for it). The\n // wrapper this controller owns flags the interrupted run and carries the\n // authored text in the name it published.\n const ticker = this.#ownedTicker();\n if (ticker === null) return;\n const authored = ticker.getAttribute(\"aria-label\");\n this.#unwrap(ticker, authored);\n if (authored !== null) this.element.setAttribute(\"data-count-up-done\", \"true\");\n }\n\n override disconnect(): void {\n // A run cannot survive the element: settle instantly so the cached\n // snapshot holds the real value, never a mid-animation frame.\n if (this.#frame !== null) this.#settle();\n }\n\n /**\n * Starts the animation (typically from `stimeo--intersection:enter` via\n * `data-action`). No-ops while running, and after a finished run when `once`.\n */\n start(): void {\n if (this.#frame !== null) return;\n if (this.onceValue && this.element.hasAttribute(\"data-count-up-done\")) return;\n\n const node = this.#numericNode();\n if (node === null) return;\n const authored = node.data;\n const target = authoredInteger(authored) as number;\n\n // Reduced motion: no ticking, just the final value (WCAG 2.3.3).\n if (prefersReducedMotion()) {\n this.element.setAttribute(\"data-count-up-done\", \"true\");\n this.dispatch(\"end\", { detail: { value: target } });\n return;\n }\n\n // A name may not live on every host — a `<span>` prohibits one, and a role\n // that permits naming would cost a `<dd>` its own semantics — so the ticking\n // number gets a wrapper that permits naming, and the name carries the\n // authored text AT should hear instead of the ticks.\n const ticker = this.#wrap(node, authored);\n const from = this.#from;\n const started = performance.now();\n const step = (now: number): void => {\n const t = Math.min((now - started) / this.#duration, 1);\n const eased = 1 - (1 - t) ** 3; // ease-out cubic\n ticker.textContent = String(Math.round(from + (target - from) * eased));\n if (t < 1) {\n this.#frame = requestAnimationFrame(step);\n } else {\n this.#settle();\n this.dispatch(\"end\", { detail: { value: target } });\n }\n };\n this.#frame = requestAnimationFrame(step);\n }\n\n /** The first text node that displays a number, or null when the host has none. */\n #numericNode(): Text | null {\n const walker = document.createTreeWalker(this.element, NodeFilter.SHOW_TEXT);\n let node = walker.nextNode() as Text | null;\n while (node !== null && authoredInteger(node.data) === null) {\n node = walker.nextNode() as Text | null;\n }\n return node;\n }\n\n /** The wrapper this controller published, if one outlived its run. */\n #ownedTicker(): HTMLElement | null {\n return this.element.querySelector<HTMLElement>(\"[data-count-up-label]\");\n }\n\n /** Publishes the ticking number inside a named wrapper, replacing `node`. */\n #wrap(node: Text, authored: string): HTMLElement {\n const ticker = document.createElement(\"span\");\n ticker.setAttribute(\"data-count-up-label\", \"true\");\n ticker.setAttribute(\"role\", \"img\");\n ticker.setAttribute(\"aria-label\", authored);\n node.replaceWith(ticker);\n ticker.append(node);\n return ticker;\n }\n\n /** Puts `text` back where the wrapper stood, leaving the rest of the host alone. */\n #unwrap(ticker: HTMLElement, text: string | null): void {\n ticker.replaceWith(document.createTextNode(text ?? ticker.textContent ?? \"\"));\n }\n\n /** Ends the run: cancels the frame and restores the authored presentation. */\n #settle(): void {\n if (this.#frame !== null) cancelAnimationFrame(this.#frame);\n this.#frame = null;\n const ticker = this.#ownedTicker();\n if (ticker !== null) this.#unwrap(ticker, ticker.getAttribute(\"aria-label\"));\n this.element.setAttribute(\"data-count-up-done\", \"true\");\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/authored_integer.ts","../../src/utils/number_bounds.ts","../../src/utils/coerce.ts","../../src/utils/number_value.ts","../../src/utils/reduced_motion.ts","../../src/controllers/count_up_controller.ts"],"names":[],"mappings":"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","file":"count_up_controller.js","sourcesContent":["/** Digits plus the characters that may separate them inside one number. */\nconst TOKEN_BODY = /\\d[\\d.,'’’ _]*\\d|\\d/;\n/** A `-` opens a token only against one of these on its left. */\nconst SIGN_OPENER = /[\\s([]/;\n\n/**\n * Reads the integer that a human-formatted string displays.\n *\n * Takes the first numeric token, drops group separators, and truncates any\n * fractional part toward zero — so `\"1,200 users\"`, `\"$1,299.99\"` and\n * `\"1.299,99\"` read 1200, 1299 and 1299 without asking the platform which\n * locale is in play. A separator counts as grouping only where the digits that\n * follow it form a run of exactly three and the leading run could head a\n * grouped number; anything else is the decimal point, and the digits after it\n * are the fraction.\n *\n * `-` counts as a sign only where it opens the token — at the start of the\n * string, or after whitespace or an opening bracket — so a hyphen inside a\n * label (`\"Sign-ups: 1,200\"`, `\"Top-10 users\"`) never becomes one.\n *\n * `\"1.200\"` is genuinely ambiguous (1200 grouped, 1.2 with a decimal point) and\n * reads as 1200: display numbers group far more often than they carry a\n * fraction in exactly three digits.\n *\n * Returns `null` when the string holds no digits at all. What that means is the\n * caller's to decide — starting nothing, or falling back to a base value.\n */\nexport function authoredInteger(text: string): number | null {\n const firstDigit = /\\d/.exec(text);\n if (firstDigit === null) return null;\n\n const at = firstDigit.index;\n const signed =\n at > 0 && text[at - 1] === \"-\" && (at === 1 || SIGN_OPENER.test(text[at - 2] as string));\n\n const body = TOKEN_BODY.exec(text.slice(at)) as RegExpExecArray;\n const runs = body[0].split(/\\D+/);\n const separators = body[0].match(/\\D+/g) ?? [];\n\n // The leading run heads a grouped number only when it is short enough to be a\n // group itself and carries no leading zero; \"0.500\" and \"012,3\" are decimals.\n const lead = runs[0] as string;\n const heads = lead.length <= 3 && !lead.startsWith(\"0\");\n let digits = lead;\n for (let i = 1; heads && i < runs.length; i += 1) {\n if (separators[i - 1]?.length !== 1 || runs[i]?.length !== 3) break;\n digits += runs[i];\n }\n\n const magnitude = Number.parseInt(digits, 10);\n return signed ? -magnitude || 0 : magnitude;\n}\n","import { MAX_TIMER_DELAY_MS } from \"./timer_bounds\";\n\n/** Semantic constraints on an already decoded number, independent of the DOM. */\nexport interface NumberBounds {\n /** Finite numbers are required, including when this field is omitted. */\n readonly finite?: true;\n /** Inclusive lower endpoint. */\n readonly min?: number;\n /** Inclusive upper endpoint. */\n readonly max?: number;\n /** Exclusive lower endpoint. */\n readonly exclusiveMin?: number;\n /** Require a number without a fractional part. */\n readonly integer?: boolean;\n /** Exact decoded numbers accepted by a discrete contract. */\n readonly allowedValues?: readonly number[];\n /** JSON-safe exceptions to finite-number checking for unbounded endpoints. */\n readonly allowInfinity?: \"negative\" | \"positive\" | \"both\";\n}\n\n/** One constraint for each Number Value, including shorthand declarations. */\nexport type NumberValueConstraints<Values> = {\n readonly [Key in keyof Values as Values[Key] extends\n | NumberConstructor\n | { type: NumberConstructor }\n ? Key\n : never]: NumberBounds;\n};\n\n/** Reusable semantic domains; fractional inputs remain fractional unless excluded. */\nexport const NUMBER_BOUNDS = {\n finite: { finite: true },\n nonNegative: { finite: true, min: 0 },\n positive: { finite: true, exclusiveMin: 0 },\n nonNegativeInteger: { finite: true, min: 0, integer: true },\n positiveInteger: { finite: true, exclusiveMin: 0, integer: true },\n lowerBound: { finite: true, allowInfinity: \"negative\" },\n upperBound: { finite: true, allowInfinity: \"positive\" },\n timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS },\n positiveTimer: { finite: true, exclusiveMin: 0, max: MAX_TIMER_DELAY_MS },\n} as const satisfies Record<string, NumberBounds>;\n\n/**\n * Checks all declared bounds without rounding, clamping, or coercing the input.\n * Infinity exceptions relax only finite checking; every other bound still applies.\n */\nexport function matchesNumberBounds(value: number, bounds: NumberBounds): boolean {\n if (!Number.isFinite(value)) {\n const direction = value === Infinity ? \"positive\" : value === -Infinity ? \"negative\" : null;\n if (direction === null) return false;\n if (bounds.allowInfinity !== \"both\" && bounds.allowInfinity !== direction) return false;\n }\n if (bounds.min !== undefined && value < bounds.min) return false;\n if (bounds.max !== undefined && value > bounds.max) return false;\n if (bounds.exclusiveMin !== undefined && value <= bounds.exclusiveMin) return false;\n if (bounds.integer && !Number.isInteger(value)) return false;\n if (bounds.allowedValues !== undefined && !bounds.allowedValues.includes(value)) return false;\n return true;\n}\n\n/** Decodes a Number Value literal; action params use their own JSON decoder. */\nexport function decodeNumberValue(raw: string): number {\n return Number(raw.replace(/_/g, \"\"));\n}\n","/**\n * Numeric coercion for controllers that accept a number through a Value, an action\n * param, or an event detail.\n *\n * Stimulus decodes action params as JSON when possible and leaves other text\n * untouched. CustomEvent detail can also supply a number or numeric string.\n * The reader accepts those two types while rejecting blank and non-finite input.\n */\n\nimport { matchesNumberBounds, type NumberBounds } from \"./number_bounds\";\n\n/** Numeric update input, accepting decoded action params before CustomEvent detail. */\nexport type NumericValueEvent = Event & {\n params?: { amount?: unknown };\n detail?: { value?: unknown };\n};\n\n/** Reads the nullish action-param/detail precedence without falling back from invalid input. */\nexport function eventNumber(event: NumericValueEvent): number | null {\n return toFiniteNumber(event.params?.amount ?? event.detail?.value);\n}\n\n/**\n * Reads a finite number or nonblank numeric string; other input returns `null`.\n * Blank strings cannot silently reset a value to zero.\n */\nexport function toFiniteNumber(raw: unknown): number | null {\n if (typeof raw !== \"number\" && typeof raw !== \"string\") return null;\n if (typeof raw === \"string\" && raw.trim().length === 0) return null;\n const value = Number(raw);\n return Number.isFinite(value) ? value : null;\n}\n\n/**\n * Returns an accepted decoded Number Value or the caller's fallback unchanged.\n * The caller owns the fallback contract; this reader does not parse or write a Value.\n */\nexport function readNumber(raw: number, fallback: number, bounds: NumberBounds): number {\n return matchesNumberBounds(raw, bounds) ? raw : fallback;\n}\n","import { readNumber } from \"./coerce\";\nimport { matchesNumberBounds, type NumberBounds } from \"./number_bounds\";\n\n/** The instance identity carried by a Number Value read. */\nexport interface NumberValueOwner {\n readonly identifier: string;\n readonly element: Element;\n}\n\n/** Reads current declarations at the point of use without rewriting their attributes. */\nexport class NumberValueReader {\n /** The last rejected literal per Value; no history grows for an unchanged instance. */\n readonly #lastRejected = new Map<string, string>();\n\n /** Resolves the declared number using its own fallback and shared class contract. */\n read(\n owner: NumberValueOwner,\n name: string,\n raw: number,\n fallback: number,\n bounds: NumberBounds,\n ): number {\n const resolved = readNumber(raw, fallback, bounds);\n if (matchesNumberBounds(raw, bounds)) {\n this.#lastRejected.delete(name);\n return resolved;\n }\n const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;\n const literal = owner.element.getAttribute(attribute);\n if (literal === null) {\n this.#lastRejected.delete(name);\n return resolved;\n }\n if (this.#lastRejected.get(name) !== literal) {\n this.#lastRejected.set(name, literal);\n console.warn(\n `Stimeo UI: \"${owner.identifier}\" has an invalid number Value \"${name}\" declaration ${JSON.stringify(literal)}; using ${fallback}.`,\n );\n }\n return resolved;\n }\n}\n","/**\n * Shared `prefers-reduced-motion` lookup for the motion-aware controllers.\n *\n * This one-liner keeps the media query string and the environment guard\n * single-sourced across them. The preference is intentionally re-read on every\n * call — the controllers check it at each animation/scroll start (WCAG 2.2\n * **2.3.3**), so flipping the OS setting takes effect immediately without any\n * listener or cache bookkeeping here.\n */\n\n/**\n * Whether the user currently requests reduced motion.\n *\n * @returns `true` when `(prefers-reduced-motion: reduce)` matches; `false`\n * otherwise, including environments without `window.matchMedia` (treated as\n * \"no preference\").\n */\nexport function prefersReducedMotion(): boolean {\n return (\n typeof window.matchMedia === \"function\" &&\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n );\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { authoredInteger } from \"../utils/authored_integer\";\nimport { NUMBER_BOUNDS, type NumberValueConstraints } from \"../utils/number_bounds\";\nimport { NumberValueReader } from \"../utils/number_value\";\nimport { prefersReducedMotion } from \"../utils/reduced_motion\";\n\n/** The animation length used when `duration` falls outside its domain. */\nconst DEFAULT_DURATION = 1200;\n\n/**\n * Headless **count-up**: animates a number from `from` up to the value already\n * in the DOM — typically started the moment the element scrolls into view by\n * composing with the `stimeo--intersection` primitive (the classic \"animate\n * when visible\"). Distinct from `stimeo--countdown` (time-based). Core\n * (zero dependencies).\n *\n * Markup contract (identifier: `stimeo--count-up`):\n * <span data-controller=\"stimeo--intersection stimeo--count-up\"\n * data-stimeo--intersection-once-value=\"true\"\n * data-action=\"stimeo--intersection:enter->stimeo--count-up#start\">1200</span>\n *\n * The **final number stays authored in the markup** (SEO / no-JS / SR read the\n * real value); `start` animates the displayed text from `from` to it over\n * `duration` ms with an ease-out curve, then restores the exact authored text.\n * With `once` (default) later starts are ignored (`data-count-up-done` records\n * a finished run across Turbo cache restores). `from` and `duration` are read as\n * a run starts: a running animation keeps the values it began with, the next run\n * reads the new ones, and a change on its own starts nothing.\n *\n * Only the text node holding the number is animated, so sibling markup — a unit\n * in a `<small>`, a label in a `<b>` — is left where the author put it.\n *\n * `end` dispatches `{ value }`.\n *\n * @remarks\n * Behavior only — no formatting is imposed: the authored text is read for the\n * integer it displays and restored verbatim at the end; intermediate frames\n * render plain integers. Accessibility: when the user prefers reduced motion the\n * animation is skipped entirely (the value just stays final — WCAG 2.3.3).\n * During a run the ticking number is wrapped in a `role=\"img\"` element named\n * with the authored text, which is where a name is allowed to live — the host\n * keeps whatever semantics it had, so a `<dd>` stays a definition. That wrapper\n * doubles as the interrupted-run record `connect()` restores from after a Turbo\n * cache snapshot taken mid-animation. The animation frame is canceled on\n * `disconnect()` (Turbo navigation included) and the authored text restored.\n */\nexport class CountUpController extends Controller<HTMLElement> {\n /** Numeric read boundaries share one reader for this controller instance. */\n readonly #numbers = new NumberValueReader();\n\n static override values = {\n duration: { type: Number, default: DEFAULT_DURATION },\n from: { type: Number, default: 0 },\n once: { type: Boolean, default: true },\n };\n\n static valueConstraints = {\n duration: NUMBER_BOUNDS.positive,\n from: NUMBER_BOUNDS.finite,\n } satisfies NumberValueConstraints<typeof CountUpController.values>;\n static actions = [\"start\"] as const;\n static events = [\"end\"] as const;\n\n declare durationValue: number;\n declare fromValue: number;\n declare onceValue: boolean;\n\n #frame: number | null = null;\n\n /** The animation length, with a declaration outside its domain read as the default. */\n get #duration(): number {\n return this.#safeDuration;\n }\n\n /** The starting value, with a declaration that is not a finite number read as zero. */\n get #from(): number {\n return this.#safeFrom;\n }\n\n override connect(): void {\n // Turbo snapshots the page BEFORE the body swap, so a cached page can hold\n // a mid-animation frame (disconnect()'s settle runs too late for it). The\n // wrapper this controller owns flags the interrupted run and carries the\n // authored text in the name it published.\n const ticker = this.#ownedTicker();\n if (ticker === null) return;\n const authored = ticker.getAttribute(\"aria-label\");\n this.#unwrap(ticker, authored);\n if (authored !== null) this.element.setAttribute(\"data-count-up-done\", \"true\");\n }\n\n override disconnect(): void {\n // Cancel the live run and restore the element's authored text.\n if (this.#frame !== null) this.#settle();\n }\n\n /**\n * Starts the animation (typically from `stimeo--intersection:enter` via\n * `data-action`). No-ops while running, and after a finished run when `once`.\n * The run keeps the `from` and `duration` read here to its last frame.\n */\n start(): void {\n if (this.#frame !== null) return;\n if (this.onceValue && this.element.hasAttribute(\"data-count-up-done\")) return;\n\n const node = this.#numericNode();\n if (node === null) return;\n const authored = node.data;\n const target = authoredInteger(authored) as number;\n\n // Reduced motion: no ticking, just the final value (WCAG 2.3.3).\n if (prefersReducedMotion()) {\n this.element.setAttribute(\"data-count-up-done\", \"true\");\n this.dispatch(\"end\", { detail: { value: target } });\n return;\n }\n\n // A name may not live on every host — a `<span>` prohibits one, and a role\n // that permits naming would cost a `<dd>` its own semantics — so the ticking\n // number gets a wrapper that permits naming, and the name carries the\n // authored text AT should hear instead of the ticks.\n const ticker = this.#wrap(node, authored);\n const from = this.#from;\n const duration = this.#duration;\n const started = performance.now();\n const step = (now: number): void => {\n const t = Math.min((now - started) / duration, 1);\n const eased = 1 - (1 - t) ** 3; // ease-out cubic\n ticker.textContent = String(Math.round(from + (target - from) * eased));\n if (t < 1) {\n this.#frame = requestAnimationFrame(step);\n } else {\n this.#settle();\n this.dispatch(\"end\", { detail: { value: target } });\n }\n };\n this.#frame = requestAnimationFrame(step);\n }\n\n /** The first text node that displays a number, or null when the host has none. */\n #numericNode(): Text | null {\n const walker = document.createTreeWalker(this.element, NodeFilter.SHOW_TEXT);\n let node = walker.nextNode() as Text | null;\n while (node !== null && authoredInteger(node.data) === null) {\n node = walker.nextNode() as Text | null;\n }\n return node;\n }\n\n /** The wrapper this controller published, if one outlived its run. */\n #ownedTicker(): HTMLElement | null {\n return this.element.querySelector<HTMLElement>(\"[data-count-up-label]\");\n }\n\n /** Publishes the ticking number inside a named wrapper, replacing `node`. */\n #wrap(node: Text, authored: string): HTMLElement {\n const ticker = document.createElement(\"span\");\n ticker.setAttribute(\"data-count-up-label\", \"true\");\n ticker.setAttribute(\"role\", \"img\");\n ticker.setAttribute(\"aria-label\", authored);\n node.replaceWith(ticker);\n ticker.append(node);\n return ticker;\n }\n\n /** Puts `text` back where the wrapper stood, leaving the rest of the host alone. */\n #unwrap(ticker: HTMLElement, text: string | null): void {\n ticker.replaceWith(document.createTextNode(text ?? ticker.textContent ?? \"\"));\n }\n\n /** Ends the run: cancels the frame and restores the authored presentation. */\n #settle(): void {\n if (this.#frame !== null) cancelAnimationFrame(this.#frame);\n this.#frame = null;\n const ticker = this.#ownedTicker();\n if (ticker !== null) this.#unwrap(ticker, ticker.getAttribute(\"aria-label\"));\n this.element.setAttribute(\"data-count-up-done\", \"true\");\n }\n /** Current `duration` declaration resolved against its numeric contract. */\n get #safeDuration(): number {\n return this.#numbers.read(\n this,\n \"duration\",\n this.durationValue,\n CountUpController.values.duration.default,\n CountUpController.valueConstraints.duration,\n );\n }\n\n /** Current `from` declaration resolved against its numeric contract. */\n get #safeFrom(): number {\n return this.#numbers.read(\n this,\n \"from\",\n this.fromValue,\n CountUpController.values.from.default,\n CountUpController.valueConstraints.from,\n );\n }\n}\n"]}
|
|
@@ -25,7 +25,7 @@ import { Controller } from '@hotwired/stimulus';
|
|
|
25
25
|
* same thing twice.
|
|
26
26
|
*
|
|
27
27
|
* The slot is shared with the consumer. Completion writes the label in force and
|
|
28
|
-
*
|
|
28
|
+
* records `["text", original, written]` in `data-<identifier>-owns-status`; while
|
|
29
29
|
* the slot still shows that text and nothing else, the controller keeps it on the
|
|
30
30
|
* current `completeLabel` and takes it back once the timer leaves the complete
|
|
31
31
|
* state — `reset`, `start()` counting on from it, or a completion the new reading
|
|
@@ -34,7 +34,7 @@ import { Controller } from '@hotwired/stimulus';
|
|
|
34
34
|
* again.
|
|
35
35
|
* An empty label puts no text there, so it never displaces what the slot already
|
|
36
36
|
* holds; completing with one claims an empty slot — no text and no element — with
|
|
37
|
-
*
|
|
37
|
+
* a record of the empty string, which owns the slot whenever it is empty. Text the consumer put
|
|
38
38
|
* there stays theirs while it is there, and so does a slot they put an element in,
|
|
39
39
|
* whatever its text: the label is neither followed into it nor taken back out of
|
|
40
40
|
* it, and its marker stays, until the element is gone. Only a completion with a
|
|
@@ -53,7 +53,10 @@ import { Controller } from '@hotwired/stimulus';
|
|
|
53
53
|
* source of truth — `autostart` only decides the state of a timer whose markup does
|
|
54
54
|
* not carry one. The interval is owned by `SafeInterval` and torn down on
|
|
55
55
|
* `disconnect()` (Turbo navigation included); nothing else needs carrying across,
|
|
56
|
-
* because every reading is re-derived from `deadline` and the wall clock.
|
|
56
|
+
* because every reading is re-derived from `deadline` and the wall clock. A change to
|
|
57
|
+
* `interval` while the timer runs re-arms the tick at the new period, keeping the run
|
|
58
|
+
* state, the reading and the completion; a paused or completed timer picks it up when
|
|
59
|
+
* it next runs.
|
|
57
60
|
*/
|
|
58
61
|
declare class CountdownController extends Controller<HTMLElement> {
|
|
59
62
|
#private;
|
|
@@ -84,6 +87,13 @@ declare class CountdownController extends Controller<HTMLElement> {
|
|
|
84
87
|
default: string;
|
|
85
88
|
};
|
|
86
89
|
};
|
|
90
|
+
static valueConstraints: {
|
|
91
|
+
interval: {
|
|
92
|
+
readonly finite: true;
|
|
93
|
+
readonly exclusiveMin: 0;
|
|
94
|
+
readonly max: 2147483647;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
87
97
|
static actions: readonly ["pause", "reset", "resume", "start"];
|
|
88
98
|
static events: readonly ["complete", "tick"];
|
|
89
99
|
readonly daysTarget: HTMLElement;
|
|
@@ -91,6 +101,7 @@ declare class CountdownController extends Controller<HTMLElement> {
|
|
|
91
101
|
readonly minutesTarget: HTMLElement;
|
|
92
102
|
readonly secondsTarget: HTMLElement;
|
|
93
103
|
readonly statusTarget: HTMLElement;
|
|
104
|
+
readonly statusTargets: HTMLElement[];
|
|
94
105
|
readonly hasDaysTarget: boolean;
|
|
95
106
|
readonly hasHoursTarget: boolean;
|
|
96
107
|
readonly hasMinutesTarget: boolean;
|
|
@@ -104,6 +115,30 @@ declare class CountdownController extends Controller<HTMLElement> {
|
|
|
104
115
|
announceTextValue: string;
|
|
105
116
|
connect(): void;
|
|
106
117
|
disconnect(): void;
|
|
118
|
+
/** Writes the reading on screen into a days slot that arrives at runtime. */
|
|
119
|
+
daysTargetConnected(): void;
|
|
120
|
+
/** Writes the reading on screen into the days slot that stays after another leaves. */
|
|
121
|
+
daysTargetDisconnected(): void;
|
|
122
|
+
/** Writes the reading on screen into an hours slot that arrives at runtime. */
|
|
123
|
+
hoursTargetConnected(): void;
|
|
124
|
+
/** Writes the reading on screen into the hours slot that stays after another leaves. */
|
|
125
|
+
hoursTargetDisconnected(): void;
|
|
126
|
+
/** Writes the reading on screen into a minutes slot that arrives at runtime. */
|
|
127
|
+
minutesTargetConnected(): void;
|
|
128
|
+
/** Writes the reading on screen into the minutes slot that stays after another leaves. */
|
|
129
|
+
minutesTargetDisconnected(): void;
|
|
130
|
+
/** Writes the reading on screen into a seconds slot that arrives at runtime. */
|
|
131
|
+
secondsTargetConnected(): void;
|
|
132
|
+
/** Writes the reading on screen into the seconds slot that stays after another leaves. */
|
|
133
|
+
secondsTargetDisconnected(): void;
|
|
134
|
+
/** Brings a status that arrives at runtime, when it shows this controller's text, in line. */
|
|
135
|
+
statusTargetConnected(): void;
|
|
136
|
+
/**
|
|
137
|
+
* Takes the claim off a status that no longer resolves, even after `disconnect()`, when
|
|
138
|
+
* it still shows this controller's text alone; the text stays. Brings the status that
|
|
139
|
+
* stays in line.
|
|
140
|
+
*/
|
|
141
|
+
statusTargetDisconnected(status: HTMLElement): void;
|
|
107
142
|
/** Re-derives the display when a morph swaps the deadline in place. */
|
|
108
143
|
deadlineValueChanged(): void;
|
|
109
144
|
/** Re-derives the display when a morph flips the counting direction in place. */
|
|
@@ -118,6 +153,15 @@ declare class CountdownController extends Controller<HTMLElement> {
|
|
|
118
153
|
* left to `connect()`.
|
|
119
154
|
*/
|
|
120
155
|
completeLabelValueChanged(): void;
|
|
156
|
+
/**
|
|
157
|
+
* Follows a tick period changed at runtime. A running countdown swaps its one interval
|
|
158
|
+
* for one at the new period, starting from now; its run state, reading, deadline and
|
|
159
|
+
* completion are untouched, and nothing ticks or repaints until the next period ends. A
|
|
160
|
+
* paused or completed countdown starts nothing and uses the new period when it next
|
|
161
|
+
* runs, and a declaration that resolves to the period already running changes nothing.
|
|
162
|
+
* Stimulus also calls this ahead of `connect()`, when no interval runs.
|
|
163
|
+
*/
|
|
164
|
+
intervalValueChanged(): void;
|
|
121
165
|
/**
|
|
122
166
|
* Starts (or restarts after pause) ticking toward the deadline. Counting on from a
|
|
123
167
|
* completion takes this controller's completion text back out of the status.
|