stimeo-ui 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/README.md +8 -1
- package/dist/cable/index.d.ts +79 -23
- package/dist/cable/index.js +363 -436
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.d.ts +4 -2
- package/dist/controllers/accordion_controller.js +166 -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 +862 -221
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +35 -5
- package/dist/controllers/announcer_controller.js +135 -198
- 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 +271 -127
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +171 -105
- 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 +18 -0
- package/dist/controllers/bulk_select_controller.js +262 -52
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +24 -5
- package/dist/controllers/calendar_controller.js +175 -209
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +34 -7
- package/dist/controllers/carousel_controller.js +461 -399
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +17 -1
- package/dist/controllers/character_counter_controller.js +246 -156
- 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 +32 -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 +168 -147
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +16 -4
- package/dist/controllers/collapsible_controller.js +301 -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 +149 -139
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +23 -5
- package/dist/controllers/combobox_controller.js +360 -133
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +45 -14
- package/dist/controllers/command_palette_controller.js +970 -358
- 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 +166 -124
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.d.ts +49 -12
- package/dist/controllers/confirm_controller.js +939 -248
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +31 -4
- package/dist/controllers/context_menu_controller.js +256 -128
- 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 +45 -1
- package/dist/controllers/countdown_controller.js +183 -148
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +21 -2
- package/dist/controllers/currency_input_controller.js +134 -158
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +15 -4
- package/dist/controllers/data_grid_controller.js +114 -186
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +30 -5
- package/dist/controllers/date_range_picker_controller.js +288 -206
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +13 -1
- package/dist/controllers/dialog_controller.js +858 -223
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +24 -7
- package/dist/controllers/direct_upload_controller.js +73 -153
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.js +14 -48
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.d.ts +19 -0
- package/dist/controllers/dismissible_controller.js +175 -21
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +39 -11
- package/dist/controllers/drawer_controller.js +933 -341
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +31 -1
- package/dist/controllers/dropdown_controller.js +312 -82
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.d.ts +12 -1
- package/dist/controllers/editable_controller.js +158 -68
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.js +67 -53
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +60 -18
- package/dist/controllers/file_dropzone_controller.js +311 -207
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.js +67 -39
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +31 -8
- package/dist/controllers/flash_controller.js +172 -314
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +12 -3
- package/dist/controllers/focus_controller.js +654 -240
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +147 -132
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +13 -86
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +41 -9
- package/dist/controllers/frame_loading_controller.js +264 -213
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +22 -2
- package/dist/controllers/highlight_controller.js +107 -75
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +56 -3
- package/dist/controllers/hover_card_controller.js +309 -118
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +42 -1
- package/dist/controllers/idle_controller.js +267 -101
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.js +71 -72
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.d.ts +32 -13
- package/dist/controllers/intersection_controller.js +147 -106
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.d.ts +16 -0
- package/dist/controllers/lazy_frame_controller.js +9 -65
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +34 -2
- package/dist/controllers/listbox_controller.js +306 -204
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.d.ts +1 -1
- package/dist/controllers/local_time_controller.js +54 -64
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.d.ts +11 -3
- package/dist/controllers/masonry_controller.js +70 -93
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +32 -7
- package/dist/controllers/menu_controller.js +214 -156
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +4 -4
- package/dist/controllers/menubar_controller.js +170 -329
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.d.ts +20 -0
- package/dist/controllers/meter_controller.js +151 -67
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +32 -4
- package/dist/controllers/multi_select_controller.js +593 -358
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +8 -1
- package/dist/controllers/navigation_menu_controller.js +124 -232
- 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 +352 -182
- 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 +242 -54
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +26 -2
- package/dist/controllers/number_input_controller.js +320 -263
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.js +16 -76
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +20 -4
- package/dist/controllers/otp_controller.js +198 -193
- 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 +116 -113
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +48 -11
- package/dist/controllers/overflow_menu_controller.js +343 -283
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +35 -6
- package/dist/controllers/pagination_controller.js +295 -119
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +45 -8
- package/dist/controllers/password_reveal_controller.js +389 -121
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +14 -5
- package/dist/controllers/password_strength_controller.js +99 -153
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.d.ts +11 -4
- package/dist/controllers/persist_controller.js +237 -103
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +19 -7
- package/dist/controllers/pointer_drag_controller.js +185 -165
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +38 -2
- package/dist/controllers/popover_controller.js +236 -97
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +44 -4
- package/dist/controllers/portal_controller.js +209 -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 +232 -116
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +11 -0
- package/dist/controllers/progress_controller.js +125 -56
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +14 -5
- package/dist/controllers/radio_group_controller.js +117 -137
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +43 -3
- package/dist/controllers/range_slider_controller.js +192 -105
- 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 +418 -207
- 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 +134 -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 +147 -135
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.d.ts +18 -4
- package/dist/controllers/relative_time_controller.js +145 -110
- package/dist/controllers/relative_time_controller.js.map +1 -1
- package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
- package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
- package/dist/controllers/reset_on_restore_controller.js.map +1 -0
- package/dist/controllers/resizable_controller.d.ts +28 -2
- package/dist/controllers/resizable_controller.js +255 -139
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.js +39 -84
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.d.ts +7 -3
- package/dist/controllers/scroll_area_controller.js +428 -225
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.js +0 -53
- package/dist/controllers/scroll_restore_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
- package/dist/controllers/scroll_visibility_controller.js +395 -145
- 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 +181 -242
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +19 -4
- package/dist/controllers/separator_controller.js +267 -140
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +64 -7
- package/dist/controllers/sidebar_controller.js +1023 -386
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.d.ts +23 -0
- package/dist/controllers/skeleton_controller.js +248 -97
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +27 -1
- package/dist/controllers/slider_controller.js +159 -79
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.d.ts +21 -3
- package/dist/controllers/smart_sticky_header_controller.js +162 -63
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.js +0 -112
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.d.ts +39 -4
- package/dist/controllers/spinner_controller.js +277 -169
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +5 -0
- package/dist/controllers/step_indicator_controller.js +99 -46
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +12 -3
- package/dist/controllers/stepper_controller.js +114 -73
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +6 -0
- package/dist/controllers/stick_to_bottom_controller.js +262 -138
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/sticky_observer_controller.d.ts +11 -0
- package/dist/controllers/sticky_observer_controller.js +214 -49
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +23 -8
- package/dist/controllers/submit_once_controller.js +294 -205
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +12 -1
- package/dist/controllers/switch_controller.js +33 -60
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +2 -2
- package/dist/controllers/tabs_controller.js +35 -29
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +12 -0
- package/dist/controllers/tags_input_controller.js +136 -145
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.d.ts +10 -0
- package/dist/controllers/textarea_autosize_controller.js +71 -58
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +21 -9
- package/dist/controllers/theme_controller.js +130 -112
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +18 -1
- package/dist/controllers/time_picker_controller.js +146 -80
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +36 -11
- package/dist/controllers/toast_controller.js +186 -297
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +12 -3
- package/dist/controllers/toggle_group_controller.js +95 -136
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +68 -98
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +45 -3
- 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 +21 -11
- package/dist/controllers/tree_view_controller.js +150 -345
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +6370 -9346
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +23 -3
- package/dist/inspector/cli.js +92 -37
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +92 -56
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +8 -8
- package/dist/inspector/manifest.json +787 -100
- package/dist/positioning/index.d.ts +8 -0
- package/dist/positioning/index.js +148 -47
- package/dist/positioning/index.js.map +1 -1
- package/package.json +6 -6
- package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
- package/dist/controllers/reset_before_cache_controller.js.map +0 -1
|
@@ -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.
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
// src/controllers/countdown_controller.ts
|
|
4
|
-
|
|
5
|
-
// src/utils/announce.ts
|
|
6
3
|
function announce(message, options = {}) {
|
|
7
4
|
const text = message.trim();
|
|
8
5
|
if (text.length === 0) return;
|
|
@@ -19,27 +16,33 @@ function fillTemplate(template, values) {
|
|
|
19
16
|
});
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
var ListenerSet = class {
|
|
20
|
+
#abort = new AbortController();
|
|
21
|
+
add(target, type, handler, options) {
|
|
22
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
23
|
+
}
|
|
24
|
+
dispose() {
|
|
25
|
+
this.#abort.abort();
|
|
26
|
+
this.#abort = new AbortController();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
23
30
|
var MicrotaskCoalescer = class {
|
|
24
31
|
#run;
|
|
25
32
|
#queued = false;
|
|
26
33
|
#active = false;
|
|
27
34
|
#generation = 0;
|
|
28
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
29
35
|
constructor(run) {
|
|
30
36
|
this.#run = run;
|
|
31
37
|
}
|
|
32
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
33
38
|
activate() {
|
|
34
39
|
this.#active = true;
|
|
35
40
|
}
|
|
36
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
37
41
|
cancel() {
|
|
38
42
|
this.#active = false;
|
|
39
43
|
this.#queued = false;
|
|
40
44
|
this.#generation += 1;
|
|
41
45
|
}
|
|
42
|
-
/** Requests one pass after the batch settles. Idempotent; inert outside the window. */
|
|
43
46
|
schedule() {
|
|
44
47
|
if (!this.#active || this.#queued) return;
|
|
45
48
|
this.#queued = true;
|
|
@@ -52,38 +55,96 @@ var MicrotaskCoalescer = class {
|
|
|
52
55
|
}
|
|
53
56
|
};
|
|
54
57
|
|
|
55
|
-
|
|
58
|
+
var MorphRenderWatcher = class {
|
|
59
|
+
#listeners = new ListenerSet();
|
|
60
|
+
#pass;
|
|
61
|
+
#element = null;
|
|
62
|
+
#onMorph = () => this.#pass.schedule();
|
|
63
|
+
constructor(run) {
|
|
64
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
65
|
+
}
|
|
66
|
+
observe(element) {
|
|
67
|
+
if (this.#element === element) return;
|
|
68
|
+
this.disconnect();
|
|
69
|
+
this.#element = element;
|
|
70
|
+
this.#pass.activate();
|
|
71
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
72
|
+
}
|
|
73
|
+
schedule() {
|
|
74
|
+
this.#pass.schedule();
|
|
75
|
+
}
|
|
76
|
+
disconnect() {
|
|
77
|
+
this.#listeners.dispose();
|
|
78
|
+
this.#pass.cancel();
|
|
79
|
+
this.#element = null;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
84
|
+
|
|
85
|
+
var NUMBER_BOUNDS = {
|
|
86
|
+
positiveTimer: { finite: true, exclusiveMin: 0, max: MAX_TIMER_DELAY_MS }
|
|
87
|
+
};
|
|
88
|
+
function matchesNumberBounds(value, bounds) {
|
|
89
|
+
if (!Number.isFinite(value)) {
|
|
90
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
91
|
+
if (direction === null) return false;
|
|
92
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
93
|
+
}
|
|
94
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
95
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
96
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
97
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
98
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function readNumber(raw, fallback, bounds) {
|
|
103
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
var NumberValueReader = class {
|
|
107
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
108
|
+
read(owner, name, raw, fallback, bounds) {
|
|
109
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
110
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
111
|
+
this.#lastRejected.delete(name);
|
|
112
|
+
return resolved;
|
|
113
|
+
}
|
|
114
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
115
|
+
const literal = owner.element.getAttribute(attribute);
|
|
116
|
+
if (literal === null) {
|
|
117
|
+
this.#lastRejected.delete(name);
|
|
118
|
+
return resolved;
|
|
119
|
+
}
|
|
120
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
121
|
+
this.#lastRejected.set(name, literal);
|
|
122
|
+
console.warn(
|
|
123
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return resolved;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
56
130
|
var TimerRegistry = class {
|
|
57
|
-
/** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
|
|
58
131
|
ids = /* @__PURE__ */ new Set();
|
|
59
|
-
/**
|
|
60
|
-
* Cancels a single tracked timer.
|
|
61
|
-
*
|
|
62
|
-
* No-ops if the id is unknown (already cleared, fired, or never owned by this
|
|
63
|
-
* registry), so callers can clear defensively without guarding.
|
|
64
|
-
*/
|
|
65
132
|
clear(id) {
|
|
66
133
|
if (this.ids.delete(id)) {
|
|
67
134
|
this.cancel(id);
|
|
68
135
|
}
|
|
69
136
|
}
|
|
70
|
-
/**
|
|
71
|
-
* Cancels every tracked timer. Call this from a controller's `disconnect()`
|
|
72
|
-
* to guarantee no timer outlives the element.
|
|
73
|
-
*/
|
|
74
137
|
clearAll() {
|
|
75
138
|
for (const id of this.ids) {
|
|
76
139
|
this.cancel(id);
|
|
77
140
|
}
|
|
78
141
|
this.ids.clear();
|
|
79
142
|
}
|
|
80
|
-
/** Number of timers currently tracked (pending). */
|
|
81
143
|
get size() {
|
|
82
144
|
return this.ids.size;
|
|
83
145
|
}
|
|
84
146
|
};
|
|
85
147
|
var SafeInterval = class extends TimerRegistry {
|
|
86
|
-
/** Schedules a repeating `callback` every `delay` ms and returns the timer id. */
|
|
87
148
|
set(callback, delay) {
|
|
88
149
|
const id = this.schedule(callback, delay);
|
|
89
150
|
this.ids.add(id);
|
|
@@ -97,11 +158,10 @@ var SafeInterval = class extends TimerRegistry {
|
|
|
97
158
|
}
|
|
98
159
|
};
|
|
99
160
|
|
|
100
|
-
// src/controllers/countdown_controller.ts
|
|
101
161
|
var SECOND_MS = 1e3;
|
|
102
162
|
var OWNED_STATUS = "owns-status";
|
|
103
|
-
var CountdownController = class extends Controller {
|
|
104
|
-
|
|
163
|
+
var CountdownController = class _CountdownController extends Controller {
|
|
164
|
+
#numbers = new NumberValueReader();
|
|
105
165
|
get #ownedStatus() {
|
|
106
166
|
return `data-${this.identifier}-${OWNED_STATUS}`;
|
|
107
167
|
}
|
|
@@ -114,114 +174,122 @@ var CountdownController = class extends Controller {
|
|
|
114
174
|
completeLabel: { type: String, default: "" },
|
|
115
175
|
announceText: { type: String, default: "" }
|
|
116
176
|
};
|
|
177
|
+
static valueConstraints = {
|
|
178
|
+
interval: NUMBER_BOUNDS.positiveTimer
|
|
179
|
+
};
|
|
117
180
|
static actions = ["pause", "reset", "resume", "start"];
|
|
118
181
|
static events = ["complete", "tick"];
|
|
119
182
|
#intervals = new SafeInterval();
|
|
120
183
|
#intervalId = null;
|
|
121
|
-
|
|
122
|
-
#resync = new
|
|
123
|
-
/**
|
|
124
|
-
* Whether `connect()` has run for this connection. Stimulus delivers Value
|
|
125
|
-
* callbacks ahead of it, while the run state is still the markup's own and not
|
|
126
|
-
* yet settled; `connect()` brings the status up to date once it is.
|
|
127
|
-
*/
|
|
184
|
+
#armedPeriod = 0;
|
|
185
|
+
#resync = new MorphRenderWatcher(() => this.#resyncToValues());
|
|
128
186
|
#connected = false;
|
|
129
|
-
/** Epoch-ms anchor: the deadline (down) or the count-up origin (up). */
|
|
130
187
|
#reference = 0;
|
|
131
|
-
/** Amount (ms) captured at pause, so resume can restore the same display. */
|
|
132
188
|
#pausedAmount = 0;
|
|
133
|
-
/**
|
|
134
|
-
* The amount the slots are currently showing, floored to the second they render.
|
|
135
|
-
* It lags {@link #currentAmount} by up to one tick, and it — not the live reading —
|
|
136
|
-
* is what a pause has to preserve: storing the fraction behind the display instead
|
|
137
|
-
* makes the first tick after a resume step by two units.
|
|
138
|
-
*/
|
|
139
189
|
#renderedAmount = 0;
|
|
190
|
+
#phase = "paused";
|
|
191
|
+
#valuesDirty = false;
|
|
192
|
+
#statusWrites = /* @__PURE__ */ new WeakMap();
|
|
140
193
|
connect() {
|
|
141
194
|
this.#connected = true;
|
|
142
|
-
this.#resync.
|
|
195
|
+
this.#resync.observe(this.element);
|
|
196
|
+
this.#valuesDirty = false;
|
|
143
197
|
this.#initReference();
|
|
144
198
|
const amount = this.#currentAmount();
|
|
145
199
|
this.#render(amount);
|
|
146
200
|
this.#pausedAmount = this.#renderedAmount;
|
|
147
201
|
const authored = this.element.getAttribute("data-state");
|
|
202
|
+
this.#phase = authored ?? "paused";
|
|
148
203
|
const settled = authored === "complete" && this.#isSettled(amount);
|
|
149
204
|
if (authored === null) {
|
|
150
205
|
if (this.autostartValue && this.#isValidDeadline) this.start();
|
|
151
|
-
else this
|
|
206
|
+
else this.#setPhase("paused");
|
|
152
207
|
} else if (!settled) {
|
|
153
|
-
this
|
|
208
|
+
this.#setPhase("paused");
|
|
154
209
|
if (authored === "running") this.start();
|
|
155
210
|
}
|
|
156
211
|
this.#syncStatus();
|
|
157
212
|
}
|
|
158
213
|
disconnect() {
|
|
159
214
|
this.#connected = false;
|
|
160
|
-
this.#resync.
|
|
215
|
+
this.#resync.disconnect();
|
|
161
216
|
this.#intervals.clearAll();
|
|
162
217
|
this.#intervalId = null;
|
|
163
218
|
}
|
|
164
|
-
|
|
219
|
+
daysTargetConnected() {
|
|
220
|
+
this.#resync.schedule();
|
|
221
|
+
}
|
|
222
|
+
daysTargetDisconnected() {
|
|
223
|
+
this.#resync.schedule();
|
|
224
|
+
}
|
|
225
|
+
hoursTargetConnected() {
|
|
226
|
+
this.#resync.schedule();
|
|
227
|
+
}
|
|
228
|
+
hoursTargetDisconnected() {
|
|
229
|
+
this.#resync.schedule();
|
|
230
|
+
}
|
|
231
|
+
minutesTargetConnected() {
|
|
232
|
+
this.#resync.schedule();
|
|
233
|
+
}
|
|
234
|
+
minutesTargetDisconnected() {
|
|
235
|
+
this.#resync.schedule();
|
|
236
|
+
}
|
|
237
|
+
secondsTargetConnected() {
|
|
238
|
+
this.#resync.schedule();
|
|
239
|
+
}
|
|
240
|
+
secondsTargetDisconnected() {
|
|
241
|
+
this.#resync.schedule();
|
|
242
|
+
}
|
|
243
|
+
statusTargetConnected() {
|
|
244
|
+
this.#resync.schedule();
|
|
245
|
+
}
|
|
246
|
+
statusTargetDisconnected(status) {
|
|
247
|
+
if (!this.statusTargets.includes(status) && this.#ownsStatus(status)) {
|
|
248
|
+
status.removeAttribute(this.#ownedStatus);
|
|
249
|
+
this.#statusWrites.delete(status);
|
|
250
|
+
}
|
|
251
|
+
this.#resync.schedule();
|
|
252
|
+
}
|
|
165
253
|
deadlineValueChanged() {
|
|
254
|
+
this.#valuesDirty = true;
|
|
166
255
|
this.#resync.schedule();
|
|
167
256
|
}
|
|
168
|
-
/** Re-derives the display when a morph flips the counting direction in place. */
|
|
169
257
|
directionValueChanged() {
|
|
258
|
+
this.#valuesDirty = true;
|
|
170
259
|
this.#resync.schedule();
|
|
171
260
|
}
|
|
172
|
-
/**
|
|
173
|
-
* Follows a completion label swapped in place by a morph.
|
|
174
|
-
*
|
|
175
|
-
* Render only: `complete` is neither dispatched nor announced again. The status
|
|
176
|
-
* changes only after the countdown has completed, and only while it still shows
|
|
177
|
-
* the text this controller wrote there and nothing else, so text or an element
|
|
178
|
-
* the consumer put in the slot stays. A change delivered ahead of `connect()` is
|
|
179
|
-
* left to `connect()`.
|
|
180
|
-
*/
|
|
181
261
|
completeLabelValueChanged() {
|
|
182
262
|
if (this.#connected) this.#syncStatus();
|
|
183
263
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
*
|
|
190
|
-
* @stimeoRenderRoot
|
|
191
|
-
*/
|
|
264
|
+
intervalValueChanged() {
|
|
265
|
+
if (this.#intervalId === null || this.#armedPeriod === this.#safeInterval) return;
|
|
266
|
+
this.#teardownInterval();
|
|
267
|
+
this.#armTick();
|
|
268
|
+
}
|
|
192
269
|
#syncStatus() {
|
|
193
270
|
if (!this.hasStatusTarget || !this.#ownsStatus(this.statusTarget)) return;
|
|
194
271
|
if (this.#state === "complete") this.#writeStatus(this.statusTarget, this.completeLabelValue);
|
|
195
272
|
else this.#releaseStatus(this.statusTarget);
|
|
196
273
|
}
|
|
197
|
-
/**
|
|
198
|
-
* Points the anchor at the current `deadline` / `direction` and repaints.
|
|
199
|
-
*
|
|
200
|
-
* Render only: it starts no interval and emits no event, so a morph cannot make a
|
|
201
|
-
* paused timer run or replay a milestone. A running one needs no restart either —
|
|
202
|
-
* every tick reads the anchor, so moving it is enough. While paused the stored
|
|
203
|
-
* amount follows the new reading, or resume would continue from the old deadline.
|
|
204
|
-
* A completion the new reading no longer settles (a deadline moved forward, or a
|
|
205
|
-
* count turned `up`) is handed back paused, as `connect()` hands back a restored
|
|
206
|
-
* one, so `resume()` counts again; the completion text this controller wrote goes
|
|
207
|
-
* with it, from a status slot that still shows it and nothing else.
|
|
208
|
-
*
|
|
209
|
-
* @stimeoRenderRoot
|
|
210
|
-
*/
|
|
211
274
|
#resyncToValues() {
|
|
275
|
+
this.#setPhase(this.#state);
|
|
276
|
+
if (!this.#valuesDirty) {
|
|
277
|
+
this.#render(this.#renderedAmount);
|
|
278
|
+
this.#syncStatus();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
this.#valuesDirty = false;
|
|
212
282
|
this.#initReference();
|
|
213
283
|
const amount = this.#currentAmount();
|
|
214
284
|
this.#render(amount);
|
|
215
|
-
if (this.#state === "
|
|
216
|
-
this
|
|
217
|
-
this.#
|
|
285
|
+
if (this.#state === "running" && !this.#isValidDeadline) {
|
|
286
|
+
this.#teardownInterval();
|
|
287
|
+
this.#setPhase("paused");
|
|
218
288
|
}
|
|
289
|
+
if (this.#state === "complete" && !this.#isSettled(amount)) this.#setPhase("paused");
|
|
219
290
|
if (this.#state !== "running") this.#pausedAmount = this.#renderedAmount;
|
|
291
|
+
this.#syncStatus();
|
|
220
292
|
}
|
|
221
|
-
/**
|
|
222
|
-
* Starts (or restarts after pause) ticking toward the deadline. Counting on from a
|
|
223
|
-
* completion takes this controller's completion text back out of the status.
|
|
224
|
-
*/
|
|
225
293
|
start() {
|
|
226
294
|
if (this.#state === "running" || !this.#isValidDeadline) return;
|
|
227
295
|
if (this.#isDown && this.#currentAmount() <= 0) {
|
|
@@ -230,36 +298,25 @@ var CountdownController = class extends Controller {
|
|
|
230
298
|
}
|
|
231
299
|
this.#runInterval();
|
|
232
300
|
}
|
|
233
|
-
/** Pauses ticking, preserving the currently displayed amount. */
|
|
234
301
|
pause() {
|
|
235
302
|
if (this.#state !== "running") return;
|
|
236
303
|
this.#pausedAmount = this.#renderedAmount;
|
|
237
304
|
this.#teardownInterval();
|
|
238
|
-
this
|
|
305
|
+
this.#setPhase("paused");
|
|
239
306
|
}
|
|
240
|
-
/** Resumes from a pause, continuing from the preserved amount. */
|
|
241
307
|
resume() {
|
|
242
308
|
if (this.#state !== "paused" || !this.#isValidDeadline) return;
|
|
243
309
|
const now = Date.now();
|
|
244
310
|
this.#reference = this.#isDown ? now + this.#pausedAmount : now - this.#pausedAmount;
|
|
245
311
|
this.start();
|
|
246
312
|
}
|
|
247
|
-
/**
|
|
248
|
-
* Re-syncs to the deadline and clears any pause offset, **preserving the current
|
|
249
|
-
* run state**: a running timer keeps counting down from the reset amount, while a
|
|
250
|
-
* paused (or completed) one resets the displayed amount but stays paused until the
|
|
251
|
-
* user resumes — it never silently restarts. The run state is read from the DOM,
|
|
252
|
-
* not re-derived from the declarative `autostart` Value (which governs only markup
|
|
253
|
-
* that states no run state at all); re-deriving it would override a user's pause —
|
|
254
|
-
* the DOM, not a re-run of declarative config, is the source of truth.
|
|
255
|
-
*/
|
|
256
313
|
reset() {
|
|
257
314
|
const wasRunning = this.#state === "running";
|
|
258
315
|
this.#teardownInterval();
|
|
259
316
|
this.#initReference();
|
|
260
317
|
const amount = this.#currentAmount();
|
|
261
318
|
this.#render(amount);
|
|
262
|
-
this
|
|
319
|
+
this.#setPhase("paused");
|
|
263
320
|
this.#syncStatus();
|
|
264
321
|
if (wasRunning && this.#isValidDeadline) {
|
|
265
322
|
this.#pausedAmount = 0;
|
|
@@ -268,29 +325,21 @@ var CountdownController = class extends Controller {
|
|
|
268
325
|
this.#pausedAmount = this.#renderedAmount;
|
|
269
326
|
}
|
|
270
327
|
}
|
|
271
|
-
/**
|
|
272
|
-
* Schedules the repeating tick and marks the timer running. This is the one way
|
|
273
|
-
* into `running`, and a running timer keeps none of this controller's completion
|
|
274
|
-
* text in the status, so the status is settled here as on every other way out of
|
|
275
|
-
* `complete`: `start()` leaves a completion directly when `direction` flips to
|
|
276
|
-
* `up` and `start()` runs in the same task, ahead of the direction callback.
|
|
277
|
-
*
|
|
278
|
-
* @stimeoRuntimeOnly `interval` is the period of the one timer this call arms; the running state
|
|
279
|
-
* it writes does not depend on a Value.
|
|
280
|
-
*/
|
|
281
328
|
#runInterval() {
|
|
282
|
-
this
|
|
329
|
+
this.#setPhase("running");
|
|
283
330
|
this.#syncStatus();
|
|
284
|
-
this.#
|
|
331
|
+
this.#armTick();
|
|
332
|
+
}
|
|
333
|
+
#armTick() {
|
|
334
|
+
this.#armedPeriod = this.#safeInterval;
|
|
335
|
+
this.#intervalId = this.#intervals.set(() => this.#tick(), this.#armedPeriod);
|
|
285
336
|
}
|
|
286
|
-
/** Cancels the repeating tick, if any. */
|
|
287
337
|
#teardownInterval() {
|
|
288
338
|
if (this.#intervalId !== null) {
|
|
289
339
|
this.#intervals.clear(this.#intervalId);
|
|
290
340
|
this.#intervalId = null;
|
|
291
341
|
}
|
|
292
342
|
}
|
|
293
|
-
/** Recomputes, renders, emits `tick`, and completes when a countdown hits 0. */
|
|
294
343
|
#tick() {
|
|
295
344
|
const amount = this.#currentAmount();
|
|
296
345
|
this.#render(amount);
|
|
@@ -301,26 +350,14 @@ var CountdownController = class extends Controller {
|
|
|
301
350
|
this.#complete();
|
|
302
351
|
}
|
|
303
352
|
}
|
|
304
|
-
/**
|
|
305
|
-
* Stops at zero, marks completion, writes the completion label, emits `complete`,
|
|
306
|
-
* and announces it.
|
|
307
|
-
*
|
|
308
|
-
* @stimeoRenderRoot
|
|
309
|
-
*/
|
|
310
353
|
#complete() {
|
|
311
354
|
this.#teardownInterval();
|
|
312
355
|
this.#render(0);
|
|
313
|
-
this
|
|
356
|
+
this.#setPhase("complete");
|
|
314
357
|
this.#claimStatus();
|
|
315
358
|
this.dispatch("complete", { detail: {} });
|
|
316
359
|
this.#announceCompletion();
|
|
317
360
|
}
|
|
318
|
-
/**
|
|
319
|
-
* Writes the label in force into the status slot as this controller's text. An
|
|
320
|
-
* empty label puts no text there: it claims a slot that is already empty — no
|
|
321
|
-
* text and no element — empties one that is still this controller's, and leaves
|
|
322
|
-
* any other slot, the consumer's, alone.
|
|
323
|
-
*/
|
|
324
361
|
#claimStatus() {
|
|
325
362
|
if (!this.hasStatusTarget) return;
|
|
326
363
|
const status = this.statusTarget;
|
|
@@ -328,54 +365,42 @@ var CountdownController = class extends Controller {
|
|
|
328
365
|
if (label === "" && !this.#isEmpty(status) && !this.#ownsStatus(status)) return;
|
|
329
366
|
this.#writeStatus(status, label);
|
|
330
367
|
}
|
|
331
|
-
/**
|
|
332
|
-
* Whether the status slot shows the text this controller wrote there and nothing
|
|
333
|
-
* else: the text matches the marker, and the slot holds no element. Writing the
|
|
334
|
-
* slot's text replaces every child node, so an element the consumer puts in the
|
|
335
|
-
* slot makes it theirs even while the text still matches. An empty marker
|
|
336
|
-
* matches the slot whenever it is empty.
|
|
337
|
-
*/
|
|
338
368
|
#ownsStatus(status) {
|
|
369
|
+
const held = this.#statusWrites.get(status);
|
|
370
|
+
if (!status.hasAttribute(this.#ownedStatus) && held && status.firstElementChild === null && (status.textContent === held.text || status.textContent === held.original))
|
|
371
|
+
return true;
|
|
339
372
|
return status.getAttribute(this.#ownedStatus) === status.textContent && status.firstElementChild === null;
|
|
340
373
|
}
|
|
341
|
-
/** Whether the status slot holds nothing at all: no text and no element. */
|
|
342
374
|
#isEmpty(status) {
|
|
343
375
|
return status.textContent === "" && status.firstElementChild === null;
|
|
344
376
|
}
|
|
345
|
-
/** Puts `text` into the status slot and records it as this controller's. */
|
|
346
377
|
#writeStatus(status, text) {
|
|
378
|
+
const original = this.#statusWrites.get(status)?.original ?? status.textContent ?? "";
|
|
379
|
+
this.#statusWrites.set(status, { original, text });
|
|
347
380
|
if (status.textContent !== text) status.textContent = text;
|
|
348
|
-
status.
|
|
381
|
+
if (status.getAttribute(this.#ownedStatus) !== text)
|
|
382
|
+
status.setAttribute(this.#ownedStatus, text);
|
|
349
383
|
}
|
|
350
|
-
/** Takes this controller's text out of the status slot, marker and all. */
|
|
351
384
|
#releaseStatus(status) {
|
|
352
385
|
status.textContent = "";
|
|
353
386
|
status.removeAttribute(this.#ownedStatus);
|
|
387
|
+
this.#statusWrites.delete(status);
|
|
354
388
|
}
|
|
355
|
-
/**
|
|
356
|
-
* Reads the completion out through the shared announcer.
|
|
357
|
-
*
|
|
358
|
-
* @stimeoRuntimeOnly `announceText` words the one announcement a completion makes.
|
|
359
|
-
*/
|
|
360
389
|
#announceCompletion() {
|
|
361
390
|
announce(fillTemplate(this.announceTextValue, {}));
|
|
362
391
|
}
|
|
363
|
-
/** Sets the time anchor from the `deadline` value. */
|
|
364
392
|
#initReference() {
|
|
365
393
|
this.#reference = Date.parse(this.deadlineValue);
|
|
366
394
|
}
|
|
367
|
-
/** Whether a reading of `amount` leaves nothing to count: only a countdown at zero. */
|
|
368
395
|
#isSettled(amount) {
|
|
369
396
|
return this.#isDown && amount <= 0;
|
|
370
397
|
}
|
|
371
|
-
/** Remaining (down) or elapsed (up) ms, never negative. */
|
|
372
398
|
#currentAmount() {
|
|
373
399
|
if (!this.#isValidDeadline) return 0;
|
|
374
400
|
const now = Date.now();
|
|
375
401
|
const raw = this.#isDown ? this.#reference - now : now - this.#reference;
|
|
376
402
|
return Math.max(0, raw);
|
|
377
403
|
}
|
|
378
|
-
/** Writes the amount into the day/hour/minute/second slots. */
|
|
379
404
|
#render(amount) {
|
|
380
405
|
const totalSeconds = Math.floor(amount / SECOND_MS);
|
|
381
406
|
this.#renderedAmount = totalSeconds * SECOND_MS;
|
|
@@ -388,7 +413,6 @@ var CountdownController = class extends Controller {
|
|
|
388
413
|
if (this.hasMinutesTarget) this.minutesTarget.textContent = this.#pad(minutes);
|
|
389
414
|
if (this.hasSecondsTarget) this.secondsTarget.textContent = this.#pad(seconds);
|
|
390
415
|
}
|
|
391
|
-
/** Zero-pads a unit to two digits. */
|
|
392
416
|
#pad(unit) {
|
|
393
417
|
return String(unit).padStart(2, "0");
|
|
394
418
|
}
|
|
@@ -398,12 +422,23 @@ var CountdownController = class extends Controller {
|
|
|
398
422
|
get #isValidDeadline() {
|
|
399
423
|
return !Number.isNaN(this.#reference);
|
|
400
424
|
}
|
|
401
|
-
|
|
425
|
+
#setPhase(phase) {
|
|
426
|
+
this.#phase = phase;
|
|
427
|
+
this.element.setAttribute("data-state", phase);
|
|
428
|
+
}
|
|
402
429
|
get #state() {
|
|
403
|
-
return this.element.getAttribute("data-state") ??
|
|
430
|
+
return this.element.getAttribute("data-state") ?? this.#phase;
|
|
431
|
+
}
|
|
432
|
+
get #safeInterval() {
|
|
433
|
+
return this.#numbers.read(
|
|
434
|
+
this,
|
|
435
|
+
"interval",
|
|
436
|
+
this.intervalValue,
|
|
437
|
+
_CountdownController.values.interval.default,
|
|
438
|
+
_CountdownController.valueConstraints.interval
|
|
439
|
+
);
|
|
404
440
|
}
|
|
405
441
|
};
|
|
406
442
|
|
|
407
443
|
export { CountdownController };
|
|
408
|
-
//# sourceMappingURL=countdown_controller.js.map
|
|
409
444
|
//# sourceMappingURL=countdown_controller.js.map
|