stimeo-ui 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +209 -0
- package/README.md +128 -1
- package/dist/cable/index.d.ts +79 -23
- package/dist/cable/index.js +392 -417
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.d.ts +34 -5
- package/dist/controllers/accordion_controller.js +228 -28
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +20 -9
- package/dist/controllers/alert_dialog_controller.js +862 -221
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +38 -7
- package/dist/controllers/announcer_controller.js +135 -191
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/aspect_ratio_controller.js +0 -10
- package/dist/controllers/aspect_ratio_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.d.ts +30 -6
- package/dist/controllers/auto_submit_controller.js +295 -110
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +172 -106
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.d.ts +10 -7
- package/dist/controllers/breadcrumb_controller.js +0 -129
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +22 -4
- package/dist/controllers/bulk_select_controller.js +264 -53
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +109 -28
- package/dist/controllers/calendar_controller.js +346 -180
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +99 -27
- package/dist/controllers/carousel_controller.js +587 -300
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +18 -2
- package/dist/controllers/character_counter_controller.js +271 -143
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.d.ts +7 -2
- package/dist/controllers/checkbox_controller.js +77 -75
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +44 -7
- package/dist/controllers/clipboard_controller.js +187 -108
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +41 -5
- package/dist/controllers/collapsible_controller.js +374 -108
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +47 -14
- package/dist/controllers/color_picker_controller.js +183 -127
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +33 -6
- package/dist/controllers/combobox_controller.js +426 -109
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +45 -14
- package/dist/controllers/command_palette_controller.js +994 -350
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.d.ts +15 -2
- package/dist/controllers/conditional_fields_controller.js +212 -125
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.d.ts +49 -12
- package/dist/controllers/confirm_controller.js +939 -245
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +36 -3
- package/dist/controllers/context_menu_controller.js +275 -127
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/count_up_controller.d.ts +13 -1
- package/dist/controllers/count_up_controller.js +73 -21
- package/dist/controllers/count_up_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +75 -2
- package/dist/controllers/countdown_controller.js +235 -97
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +93 -15
- package/dist/controllers/currency_input_controller.js +241 -118
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +78 -22
- package/dist/controllers/data_grid_controller.js +229 -135
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +64 -11
- package/dist/controllers/date_range_picker_controller.js +359 -165
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +22 -3
- package/dist/controllers/dialog_controller.js +887 -225
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +24 -7
- package/dist/controllers/direct_upload_controller.js +80 -142
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.d.ts +2 -2
- package/dist/controllers/dirty_form_controller.js +26 -47
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.d.ts +19 -0
- package/dist/controllers/dismissible_controller.js +175 -20
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +52 -16
- package/dist/controllers/drawer_controller.js +963 -336
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +39 -3
- package/dist/controllers/dropdown_controller.js +342 -85
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.d.ts +12 -1
- package/dist/controllers/editable_controller.js +182 -58
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.js +67 -53
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +60 -18
- package/dist/controllers/file_dropzone_controller.js +426 -229
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.d.ts +10 -4
- package/dist/controllers/filter_controller.js +78 -36
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +57 -14
- package/dist/controllers/flash_controller.js +328 -211
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +12 -3
- package/dist/controllers/focus_controller.js +654 -239
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +153 -136
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +29 -96
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +41 -9
- package/dist/controllers/frame_loading_controller.js +263 -198
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +22 -2
- package/dist/controllers/highlight_controller.js +119 -70
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +65 -4
- package/dist/controllers/hover_card_controller.js +335 -118
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +58 -4
- package/dist/controllers/idle_controller.js +316 -65
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +5 -2
- package/dist/controllers/input_mask_controller.js +116 -61
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.d.ts +32 -13
- package/dist/controllers/intersection_controller.js +147 -103
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.d.ts +16 -0
- package/dist/controllers/lazy_frame_controller.js +9 -56
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +84 -10
- package/dist/controllers/listbox_controller.js +413 -153
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.d.ts +1 -1
- package/dist/controllers/local_time_controller.js +54 -64
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.d.ts +11 -3
- package/dist/controllers/masonry_controller.js +70 -95
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +40 -9
- package/dist/controllers/menu_controller.js +254 -167
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +15 -4
- package/dist/controllers/menubar_controller.js +206 -331
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.d.ts +20 -0
- package/dist/controllers/meter_controller.js +159 -71
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +50 -7
- package/dist/controllers/multi_select_controller.js +797 -388
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +19 -1
- package/dist/controllers/navigation_menu_controller.js +156 -231
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.d.ts +10 -0
- package/dist/controllers/nested_form_controller.js +373 -174
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.d.ts +23 -0
- package/dist/controllers/network_status_controller.js +241 -45
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +62 -10
- package/dist/controllers/number_input_controller.js +387 -227
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.js +28 -75
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +38 -7
- package/dist/controllers/otp_controller.js +344 -196
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +13 -12
- package/dist/controllers/overflow_indicator_controller.js +167 -93
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +66 -9
- package/dist/controllers/overflow_menu_controller.js +408 -277
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +52 -15
- package/dist/controllers/pagination_controller.js +342 -120
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +60 -9
- package/dist/controllers/password_reveal_controller.js +424 -99
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +14 -5
- package/dist/controllers/password_strength_controller.js +99 -153
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.d.ts +11 -4
- package/dist/controllers/persist_controller.js +252 -96
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +19 -7
- package/dist/controllers/pointer_drag_controller.js +263 -164
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +46 -4
- package/dist/controllers/popover_controller.js +267 -94
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +44 -4
- package/dist/controllers/portal_controller.js +209 -72
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.d.ts +10 -4
- package/dist/controllers/preview_guard_controller.js +243 -112
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +11 -0
- package/dist/controllers/progress_controller.js +132 -59
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +19 -8
- package/dist/controllers/radio_group_controller.js +138 -133
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +92 -4
- package/dist/controllers/range_slider_controller.js +245 -112
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +33 -6
- package/dist/controllers/rating_controller.js +429 -194
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +26 -2
- package/dist/controllers/read_more_controller.js +185 -47
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/reading_progress_controller.d.ts +1 -1
- package/dist/controllers/reading_progress_controller.js +147 -135
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.d.ts +18 -4
- package/dist/controllers/relative_time_controller.js +145 -110
- package/dist/controllers/relative_time_controller.js.map +1 -1
- package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
- package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
- package/dist/controllers/reset_on_restore_controller.js.map +1 -0
- package/dist/controllers/resizable_controller.d.ts +44 -4
- package/dist/controllers/resizable_controller.js +294 -118
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.js +39 -84
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.d.ts +7 -3
- package/dist/controllers/scroll_area_controller.js +428 -221
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.js +0 -53
- package/dist/controllers/scroll_restore_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
- package/dist/controllers/scroll_visibility_controller.js +395 -146
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +9 -3
- package/dist/controllers/scrollspy_controller.js +181 -236
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +59 -17
- package/dist/controllers/separator_controller.js +288 -132
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +81 -7
- package/dist/controllers/sidebar_controller.js +1071 -375
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.d.ts +23 -0
- package/dist/controllers/skeleton_controller.js +248 -92
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +72 -8
- package/dist/controllers/slider_controller.js +211 -96
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.d.ts +21 -3
- package/dist/controllers/smart_sticky_header_controller.js +163 -54
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.js +13 -110
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.d.ts +39 -4
- package/dist/controllers/spinner_controller.js +277 -161
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +24 -17
- package/dist/controllers/step_indicator_controller.js +107 -53
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +43 -9
- package/dist/controllers/stepper_controller.js +171 -48
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +25 -0
- package/dist/controllers/stick_to_bottom_controller.js +310 -116
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/sticky_observer_controller.d.ts +11 -0
- package/dist/controllers/sticky_observer_controller.js +214 -49
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +22 -6
- package/dist/controllers/submit_once_controller.js +294 -192
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +40 -7
- package/dist/controllers/switch_controller.js +107 -43
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +13 -1
- package/dist/controllers/tabs_controller.js +50 -25
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +27 -3
- package/dist/controllers/tags_input_controller.js +290 -149
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.d.ts +10 -0
- package/dist/controllers/textarea_autosize_controller.js +71 -58
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +37 -8
- package/dist/controllers/theme_controller.js +148 -80
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +18 -1
- package/dist/controllers/time_picker_controller.js +162 -81
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +85 -23
- package/dist/controllers/toast_controller.js +365 -193
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +60 -9
- package/dist/controllers/toggle_group_controller.js +194 -99
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +92 -90
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +52 -2
- package/dist/controllers/tooltip_controller.js +185 -125
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.d.ts +22 -4
- package/dist/controllers/transition_controller.js +123 -115
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +59 -18
- package/dist/controllers/tree_view_controller.js +248 -290
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +33 -3
- package/dist/index.js +8649 -8572
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +95 -9
- package/dist/inspector/cli.js +343 -77
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +390 -96
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +45 -45
- package/dist/inspector/manifest.json +1308 -155
- package/dist/positioning/index.d.ts +8 -0
- package/dist/positioning/index.js +148 -45
- package/dist/positioning/index.js.map +1 -1
- package/package.json +7 -7
- package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
- package/dist/controllers/reset_before_cache_controller.js.map +0 -1
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
function sharedRegistry(key, create) {
|
|
4
|
+
const symbol = Symbol.for(key);
|
|
5
|
+
const scope = globalThis;
|
|
6
|
+
const existing = scope[symbol];
|
|
7
|
+
if (existing !== void 0) return existing;
|
|
8
|
+
const registry = create();
|
|
9
|
+
Object.defineProperty(globalThis, symbol, { value: registry });
|
|
10
|
+
return registry;
|
|
11
|
+
}
|
|
4
12
|
|
|
5
|
-
|
|
6
|
-
var counter = 0;
|
|
13
|
+
var ids = sharedRegistry("stimeo-ui.aria-ids.registry.v1", () => ({ counter: 0 }));
|
|
7
14
|
function uniqueId(prefix = "stimeo") {
|
|
8
15
|
let candidate;
|
|
9
16
|
do {
|
|
10
|
-
counter += 1;
|
|
11
|
-
candidate = `${prefix}-${counter}`;
|
|
17
|
+
ids.counter += 1;
|
|
18
|
+
candidate = `${prefix}-${ids.counter}`;
|
|
12
19
|
} while (typeof document !== "undefined" && document.getElementById(candidate) !== null);
|
|
13
20
|
return candidate;
|
|
14
21
|
}
|
|
@@ -19,12 +26,10 @@ function ensureId(element, prefix = "stimeo") {
|
|
|
19
26
|
return id;
|
|
20
27
|
}
|
|
21
28
|
|
|
22
|
-
// src/utils/logical_scroll.ts
|
|
23
29
|
function isRtl(element) {
|
|
24
30
|
return window.getComputedStyle(element).direction === "rtl";
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
// src/utils/arrow_step.ts
|
|
28
33
|
function logicalArrowKey(key, element) {
|
|
29
34
|
if (key !== "ArrowRight" && key !== "ArrowLeft") return key;
|
|
30
35
|
if (!isRtl(element)) return key;
|
|
@@ -35,124 +40,209 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
35
40
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
function isAttributeValue(value) {
|
|
44
|
+
return value === null || typeof value === "string";
|
|
45
|
+
}
|
|
46
|
+
function readLeaseRecord(element, record) {
|
|
47
|
+
try {
|
|
48
|
+
const parsed = JSON.parse(element.getAttribute(record) ?? "null");
|
|
49
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
50
|
+
const [original, written, ...rest] = parsed;
|
|
51
|
+
const linked = rest[0] !== false;
|
|
52
|
+
const beneath = linked ? rest : rest.slice(1);
|
|
53
|
+
if (!beneath.every((under) => typeof under === "string")) return void 0;
|
|
54
|
+
return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
|
|
55
|
+
} catch {
|
|
56
|
+
return void 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
var AttributeLease = class _AttributeLease {
|
|
60
|
+
static #registry = sharedRegistry(
|
|
61
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
62
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
63
|
+
);
|
|
40
64
|
#attribute;
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
65
|
+
#record;
|
|
66
|
+
#base;
|
|
67
|
+
#held = /* @__PURE__ */ new Set();
|
|
68
|
+
constructor(attribute, owner, options = {}) {
|
|
44
69
|
this.#attribute = attribute;
|
|
70
|
+
this.#record = `data-${owner}-${attribute}-lease`;
|
|
71
|
+
this.#base = options.base === true;
|
|
72
|
+
const names = _AttributeLease.#registry.names;
|
|
73
|
+
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
45
74
|
}
|
|
46
|
-
/** Writes or removes the leased attribute while preserving its authored value. */
|
|
47
75
|
write(element, value) {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
this.#reflect(element, value);
|
|
76
|
+
const stack = this.#stack(element);
|
|
77
|
+
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
78
|
+
this.#held.add(element);
|
|
79
|
+
const shows = !this.#covered(element, stack, hold);
|
|
80
|
+
hold.written = value;
|
|
81
|
+
this.#mark(element, stack);
|
|
82
|
+
if (shows) this.#reflect(element, value);
|
|
58
83
|
}
|
|
59
|
-
/** Returns one lease without overwriting a value subsequently authored by a consumer. */
|
|
60
84
|
return(element) {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
this.#
|
|
64
|
-
const
|
|
65
|
-
if (
|
|
85
|
+
const held = this.#held.delete(element);
|
|
86
|
+
const known = _AttributeLease.#registry.stacks.get(element)?.has(this.#attribute);
|
|
87
|
+
const stack = held || known || element.hasAttribute(this.#record) ? this.#stack(element) : [];
|
|
88
|
+
const hold = this.#take(stack);
|
|
89
|
+
if (!hold) return false;
|
|
90
|
+
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
91
|
+
const owns = carries && !this.#covered(element, stack, hold);
|
|
92
|
+
const index = stack.indexOf(hold);
|
|
93
|
+
stack.splice(index, 1);
|
|
94
|
+
const above = stack[index];
|
|
95
|
+
if (above) above.linked &&= hold.linked;
|
|
96
|
+
this.#mark(element, stack);
|
|
97
|
+
if (!owns) return true;
|
|
98
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
99
|
+
this.#reflect(element, below ? below.written : hold.original);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
returnAll() {
|
|
103
|
+
for (const element of Array.from(this.#held)) this.return(element);
|
|
104
|
+
}
|
|
105
|
+
#stack(element) {
|
|
106
|
+
const stacks = _AttributeLease.#registry.stacks;
|
|
107
|
+
const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
108
|
+
stacks.set(element, byAttribute);
|
|
109
|
+
const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
|
|
110
|
+
byAttribute.set(this.#attribute, stack);
|
|
111
|
+
return stack;
|
|
112
|
+
}
|
|
113
|
+
#seed(element) {
|
|
114
|
+
const found = [];
|
|
115
|
+
for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
|
|
116
|
+
const read = readLeaseRecord(element, record);
|
|
117
|
+
if (read) found.push({ ...read, record });
|
|
118
|
+
}
|
|
119
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
120
|
+
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
121
|
+
lease: null,
|
|
122
|
+
record,
|
|
123
|
+
written,
|
|
124
|
+
original,
|
|
125
|
+
linked: linked && index > 0
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
#take(stack) {
|
|
129
|
+
const hold = stack.find((held) => held.lease === this) ?? stack.find((held) => held.lease === null && held.record === this.#record);
|
|
130
|
+
if (hold) hold.lease = this;
|
|
131
|
+
return hold;
|
|
132
|
+
}
|
|
133
|
+
#acquire(element, stack) {
|
|
134
|
+
const current = element.getAttribute(this.#attribute);
|
|
135
|
+
const top = stack.at(-1);
|
|
136
|
+
const bottom = stack[0];
|
|
137
|
+
const over = top?.written === current ? top : void 0;
|
|
138
|
+
const from = this.#base ? bottom : over;
|
|
139
|
+
const hold = {
|
|
140
|
+
lease: this,
|
|
141
|
+
record: this.#record,
|
|
142
|
+
written: current,
|
|
143
|
+
original: from ? from.original : current,
|
|
144
|
+
linked: !this.#base && over !== void 0
|
|
145
|
+
};
|
|
146
|
+
if (this.#base && bottom) {
|
|
147
|
+
bottom.linked = true;
|
|
148
|
+
stack.unshift(hold);
|
|
149
|
+
} else stack.push(hold);
|
|
150
|
+
return hold;
|
|
151
|
+
}
|
|
152
|
+
#covered(element, stack, hold) {
|
|
153
|
+
const current = element.getAttribute(this.#attribute);
|
|
154
|
+
return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
|
|
155
|
+
}
|
|
156
|
+
#mark(element, stack) {
|
|
157
|
+
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
158
|
+
stack.forEach((hold, index) => {
|
|
159
|
+
const names = stack.slice(0, index).map((under) => under.record);
|
|
160
|
+
const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
|
|
161
|
+
const marker = hold.linked || beneath.length === 0 ? [] : [false];
|
|
162
|
+
records.set(
|
|
163
|
+
hold.record,
|
|
164
|
+
hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
for (const [name, recorded] of records) {
|
|
168
|
+
if (element.getAttribute(name) === recorded) continue;
|
|
169
|
+
if (recorded === null) element.removeAttribute(name);
|
|
170
|
+
else element.setAttribute(name, recorded);
|
|
171
|
+
}
|
|
66
172
|
}
|
|
67
|
-
/** Reflects only a real value transition, avoiding self-triggered mutation work. */
|
|
68
173
|
#reflect(element, value) {
|
|
69
174
|
if (element.getAttribute(this.#attribute) === value) return;
|
|
70
175
|
if (value === null) element.removeAttribute(this.#attribute);
|
|
71
176
|
else element.setAttribute(this.#attribute, value);
|
|
72
177
|
}
|
|
73
|
-
/** Returns every outstanding lease using the same ownership check as {@link return}. */
|
|
74
|
-
returnAll() {
|
|
75
|
-
for (const element of Array.from(this.#records.keys())) this.return(element);
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// src/utils/before_cache_reset.ts
|
|
80
|
-
var BeforeCacheReset = class _BeforeCacheReset {
|
|
81
|
-
/** Every subscribed instance, iterated by the one shared document listener. */
|
|
82
|
-
static #subscribers = /* @__PURE__ */ new Set();
|
|
83
|
-
/** The shared listener; installed while at least one instance is subscribed. */
|
|
84
|
-
static #onBeforeCache = () => {
|
|
85
|
-
for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
|
|
86
|
-
};
|
|
87
|
-
#rewind;
|
|
88
|
-
/** @param rewind - the pass that returns this controller's state to its initial form. */
|
|
89
|
-
constructor(rewind) {
|
|
90
|
-
this.#rewind = rewind;
|
|
91
|
-
}
|
|
92
|
-
/** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
|
|
93
|
-
activate() {
|
|
94
|
-
const first = _BeforeCacheReset.#subscribers.size === 0;
|
|
95
|
-
_BeforeCacheReset.#subscribers.add(this);
|
|
96
|
-
if (first) {
|
|
97
|
-
document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
/** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
|
|
101
|
-
deactivate() {
|
|
102
|
-
_BeforeCacheReset.#subscribers.delete(this);
|
|
103
|
-
if (_BeforeCacheReset.#subscribers.size > 0) return;
|
|
104
|
-
document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
105
|
-
}
|
|
106
178
|
};
|
|
107
179
|
|
|
108
|
-
|
|
180
|
+
var COMPOSITION_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
181
|
+
"insertCompositionText",
|
|
182
|
+
"insertFromComposition",
|
|
183
|
+
"deleteCompositionText",
|
|
184
|
+
"deleteByComposition"
|
|
185
|
+
]);
|
|
109
186
|
var CompositionTracker = class {
|
|
110
187
|
#observedTargets = /* @__PURE__ */ new Set();
|
|
111
188
|
#activeTargets = /* @__PURE__ */ new Set();
|
|
112
189
|
#onStart;
|
|
113
190
|
#onEnd;
|
|
191
|
+
#confirmedTarget = null;
|
|
114
192
|
constructor(options = {}) {
|
|
115
193
|
this.#onStart = options.onStart;
|
|
116
194
|
this.#onEnd = options.onEnd;
|
|
117
195
|
}
|
|
118
|
-
/** Starts lifecycle tracking for `target`; repeated calls are idempotent. */
|
|
119
196
|
observe(target) {
|
|
120
197
|
if (this.#observedTargets.has(target)) return;
|
|
121
198
|
target.addEventListener("compositionstart", this.#handleStart);
|
|
122
199
|
target.addEventListener("compositionend", this.#handleEnd);
|
|
200
|
+
target.addEventListener("keydown", this.#handleKeydown);
|
|
123
201
|
this.#observedTargets.add(target);
|
|
124
202
|
}
|
|
125
|
-
/** Stops tracking one target and clears any active composition it owned. */
|
|
126
203
|
unobserve(target) {
|
|
127
204
|
if (!this.#observedTargets.delete(target)) return;
|
|
128
205
|
target.removeEventListener("compositionstart", this.#handleStart);
|
|
129
206
|
target.removeEventListener("compositionend", this.#handleEnd);
|
|
207
|
+
target.removeEventListener("keydown", this.#handleKeydown);
|
|
130
208
|
this.#activeTargets.delete(target);
|
|
209
|
+
if (this.#confirmedTarget === target) this.#confirmedTarget = null;
|
|
131
210
|
}
|
|
132
|
-
/** Releases every listener and clears state so reconnect starts cleanly. */
|
|
133
211
|
disconnect() {
|
|
134
212
|
for (const target of this.#observedTargets) {
|
|
135
213
|
target.removeEventListener("compositionstart", this.#handleStart);
|
|
136
214
|
target.removeEventListener("compositionend", this.#handleEnd);
|
|
215
|
+
target.removeEventListener("keydown", this.#handleKeydown);
|
|
137
216
|
}
|
|
138
217
|
this.#observedTargets.clear();
|
|
139
218
|
this.#activeTargets.clear();
|
|
219
|
+
this.#confirmedTarget = null;
|
|
140
220
|
}
|
|
141
|
-
/** True when lifecycle tracking or the current event reports composition. */
|
|
142
221
|
isComposing(event) {
|
|
143
222
|
return this.#activeTargets.size > 0 || event?.isComposing === true;
|
|
144
223
|
}
|
|
224
|
+
consumesConfirmedInput(event) {
|
|
225
|
+
const confirmed = this.#confirmedTarget;
|
|
226
|
+
this.#confirmedTarget = null;
|
|
227
|
+
if (confirmed === null || confirmed !== event.target) return false;
|
|
228
|
+
const inputType = event.inputType;
|
|
229
|
+
return !inputType || COMPOSITION_INPUT_TYPES.has(inputType);
|
|
230
|
+
}
|
|
145
231
|
#handleStart = (event) => {
|
|
232
|
+
this.#confirmedTarget = null;
|
|
146
233
|
if (event.currentTarget) this.#activeTargets.add(event.currentTarget);
|
|
147
234
|
this.#onStart?.(event);
|
|
148
235
|
};
|
|
149
236
|
#handleEnd = (event) => {
|
|
150
237
|
if (event.currentTarget) this.#activeTargets.delete(event.currentTarget);
|
|
238
|
+
this.#confirmedTarget = event.target;
|
|
151
239
|
this.#onEnd?.(event);
|
|
152
240
|
};
|
|
241
|
+
#handleKeydown = () => {
|
|
242
|
+
this.#confirmedTarget = null;
|
|
243
|
+
};
|
|
153
244
|
};
|
|
154
245
|
|
|
155
|
-
// src/utils/declared_value.ts
|
|
156
246
|
function parseDeclared(raw, parse, fallback) {
|
|
157
247
|
try {
|
|
158
248
|
return parse(raw);
|
|
@@ -171,7 +261,46 @@ function compileRegExp(source, anchor = "none") {
|
|
|
171
261
|
);
|
|
172
262
|
}
|
|
173
263
|
|
|
174
|
-
|
|
264
|
+
var FormResetWatcher = class {
|
|
265
|
+
#owns;
|
|
266
|
+
#onReset;
|
|
267
|
+
#frames = /* @__PURE__ */ new Set();
|
|
268
|
+
#generation = 0;
|
|
269
|
+
constructor(owns, onReset) {
|
|
270
|
+
this.#owns = owns;
|
|
271
|
+
this.#onReset = onReset;
|
|
272
|
+
}
|
|
273
|
+
observe() {
|
|
274
|
+
document.addEventListener("reset", this.#handleReset, true);
|
|
275
|
+
}
|
|
276
|
+
disconnect() {
|
|
277
|
+
document.removeEventListener("reset", this.#handleReset, true);
|
|
278
|
+
this.#generation += 1;
|
|
279
|
+
for (const frame of this.#frames) cancelAnimationFrame(frame);
|
|
280
|
+
this.#frames.clear();
|
|
281
|
+
}
|
|
282
|
+
#handleReset = (event) => {
|
|
283
|
+
const form = event.target;
|
|
284
|
+
if (!(form instanceof HTMLFormElement) || !this.#owns(form)) return;
|
|
285
|
+
const generation = this.#generation;
|
|
286
|
+
queueMicrotask(() => {
|
|
287
|
+
if (generation !== this.#generation) return;
|
|
288
|
+
if (event.eventPhase === Event.NONE) {
|
|
289
|
+
this.#settle(event);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const frame = requestAnimationFrame(() => {
|
|
293
|
+
this.#frames.delete(frame);
|
|
294
|
+
this.#settle(event);
|
|
295
|
+
});
|
|
296
|
+
this.#frames.add(frame);
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
#settle(event) {
|
|
300
|
+
if (!event.defaultPrevented) this.#onReset();
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
175
304
|
var FULL_WIDTH_SHIFT = 65248;
|
|
176
305
|
function halfWidthChar(char) {
|
|
177
306
|
if (char >= "\uFF01" && char <= "\uFF5E") {
|
|
@@ -185,27 +314,22 @@ function toHalfWidth(text) {
|
|
|
185
314
|
return out;
|
|
186
315
|
}
|
|
187
316
|
|
|
188
|
-
// src/utils/microtask_coalescer.ts
|
|
189
317
|
var MicrotaskCoalescer = class {
|
|
190
318
|
#run;
|
|
191
319
|
#queued = false;
|
|
192
320
|
#active = false;
|
|
193
321
|
#generation = 0;
|
|
194
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
195
322
|
constructor(run) {
|
|
196
323
|
this.#run = run;
|
|
197
324
|
}
|
|
198
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
199
325
|
activate() {
|
|
200
326
|
this.#active = true;
|
|
201
327
|
}
|
|
202
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
203
328
|
cancel() {
|
|
204
329
|
this.#active = false;
|
|
205
330
|
this.#queued = false;
|
|
206
331
|
this.#generation += 1;
|
|
207
332
|
}
|
|
208
|
-
/** Requests one pass after the batch settles. Idempotent; inert outside the window. */
|
|
209
333
|
schedule() {
|
|
210
334
|
if (!this.#active || this.#queued) return;
|
|
211
335
|
this.#queued = true;
|
|
@@ -218,7 +342,38 @@ var MicrotaskCoalescer = class {
|
|
|
218
342
|
}
|
|
219
343
|
};
|
|
220
344
|
|
|
221
|
-
|
|
345
|
+
var MoveCounter = class {
|
|
346
|
+
#sequence = 0;
|
|
347
|
+
record() {
|
|
348
|
+
this.#sequence += 1;
|
|
349
|
+
return this.#sequence;
|
|
350
|
+
}
|
|
351
|
+
isLatest(token) {
|
|
352
|
+
return token === this.#sequence;
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
var LEASE_MARKERS = {
|
|
357
|
+
"aria-invalid": "data-otp-invalid-lease",
|
|
358
|
+
"aria-errormessage": "data-otp-errormessage-lease",
|
|
359
|
+
"aria-describedby": "data-otp-describedby-lease"
|
|
360
|
+
};
|
|
361
|
+
function isErrorLease(value) {
|
|
362
|
+
const lease = value;
|
|
363
|
+
return typeof lease?.written === "string" && (lease.authored === null || typeof lease.authored === "string");
|
|
364
|
+
}
|
|
365
|
+
function readLease(field, marker) {
|
|
366
|
+
try {
|
|
367
|
+
const parsed = JSON.parse(field.getAttribute(marker) ?? "null");
|
|
368
|
+
return isErrorLease(parsed) ? parsed : null;
|
|
369
|
+
} catch {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
function describedByWith(described, errorId, earlier) {
|
|
374
|
+
const tokens = (described ?? "").split(/\s+/).filter((token) => token.length > 0 && token !== errorId && token !== earlier);
|
|
375
|
+
return [...tokens, errorId].join(" ");
|
|
376
|
+
}
|
|
222
377
|
var DEFAULT_PATTERN = "[0-9]";
|
|
223
378
|
var DEFAULT_MATCHER = new RegExp(`^(?:${DEFAULT_PATTERN})$`);
|
|
224
379
|
function hasModifier(event) {
|
|
@@ -228,51 +383,53 @@ function statesDiffer(left, right) {
|
|
|
228
383
|
return left.value !== right.value || left.state !== right.state;
|
|
229
384
|
}
|
|
230
385
|
var OtpController = class extends Controller {
|
|
386
|
+
#moves = new MoveCounter();
|
|
231
387
|
static targets = ["field", "value", "error"];
|
|
232
388
|
static values = {
|
|
233
389
|
pattern: { type: String, default: DEFAULT_PATTERN }
|
|
234
390
|
};
|
|
235
391
|
static actions = ["onInput", "onKeydown", "onPaste", "onPointerDown", "clear"];
|
|
236
392
|
static events = ["change", "complete", "invalid", "reconcile"];
|
|
237
|
-
/** Validated matcher; the hot path never compiles a raw declaration. */
|
|
238
393
|
#pattern = DEFAULT_MATCHER;
|
|
239
|
-
/** Source of {@link #pattern}, reported in `invalid` so consumers can word it. */
|
|
240
394
|
#patternSource = DEFAULT_PATTERN;
|
|
241
|
-
/** Public state carried by the last dispatch; keeps a no-op sync silent. */
|
|
242
395
|
#published = null;
|
|
243
|
-
/** Field whose confirming `input` after `compositionend` is already handled. */
|
|
244
|
-
#confirmedField = null;
|
|
245
|
-
/** True between connect and disconnect, so pre-connect Value changes stay silent. */
|
|
246
396
|
#connected = false;
|
|
247
|
-
/** Digit each field last committed, restored when rejected input replaced it. */
|
|
248
397
|
#committed = /* @__PURE__ */ new WeakMap();
|
|
249
|
-
|
|
398
|
+
#composing = null;
|
|
399
|
+
#dropHeld = false;
|
|
250
400
|
#reconcile = new MicrotaskCoalescer(() => this.#reconcileFields());
|
|
251
|
-
#
|
|
252
|
-
#
|
|
253
|
-
#
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
401
|
+
#errorHidden = new AttributeLease("hidden", this.identifier);
|
|
402
|
+
#state = new AttributeLease("data-state", this.identifier);
|
|
403
|
+
#formReset = new FormResetWatcher(
|
|
404
|
+
(form) => this.#ownedBy(form),
|
|
405
|
+
() => {
|
|
406
|
+
this.#adopt();
|
|
407
|
+
this.#reconcile.schedule();
|
|
408
|
+
}
|
|
409
|
+
);
|
|
259
410
|
#composition = new CompositionTracker({
|
|
260
|
-
onStart: () => {
|
|
261
|
-
|
|
411
|
+
onStart: (event) => {
|
|
412
|
+
const field = this.#fieldFrom(event);
|
|
413
|
+
if (field) this.#composing = { field, before: field.value };
|
|
262
414
|
},
|
|
263
415
|
onEnd: (event) => {
|
|
416
|
+
const composing = this.#composing;
|
|
417
|
+
this.#composing = null;
|
|
264
418
|
const input = this.#fieldFrom(event);
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
this.#
|
|
419
|
+
const data = event.data ?? "";
|
|
420
|
+
const text = input && data.length <= input.value.length ? input.value : data;
|
|
421
|
+
const commit = input !== null && (composing?.field !== input || text !== composing.before);
|
|
422
|
+
if (this.#dropHeld) {
|
|
423
|
+
if (input && commit) input.value = this.#committed.get(input) ?? "";
|
|
424
|
+
this.#reconcileFields();
|
|
425
|
+
}
|
|
426
|
+
if (input && commit) this.#accept(input, text);
|
|
269
427
|
}
|
|
270
428
|
});
|
|
271
429
|
connect() {
|
|
272
430
|
this.#connected = true;
|
|
273
431
|
for (const field of this.fieldTargets) this.#bind(field);
|
|
274
|
-
|
|
275
|
-
this.#beforeCache.activate();
|
|
432
|
+
this.#formReset.observe();
|
|
276
433
|
this.#reconcile.activate();
|
|
277
434
|
this.#adopt();
|
|
278
435
|
this.#sync();
|
|
@@ -281,37 +438,47 @@ var OtpController = class extends Controller {
|
|
|
281
438
|
this.#connected = false;
|
|
282
439
|
for (const field of this.fieldTargets) this.#unbind(field);
|
|
283
440
|
this.#composition.disconnect();
|
|
284
|
-
|
|
285
|
-
this.#beforeCache.deactivate();
|
|
441
|
+
this.#formReset.disconnect();
|
|
286
442
|
this.#reconcile.cancel();
|
|
443
|
+
this.#dropHeld = false;
|
|
287
444
|
this.#clearError();
|
|
288
445
|
this.#state.return(this.element);
|
|
289
446
|
}
|
|
290
|
-
/**
|
|
291
|
-
* Stimulus lifecycle callback when a new field target enters the DOM.
|
|
292
|
-
* Wires the new field and folds the wider digit count into one reconciliation,
|
|
293
|
-
* which stays inert until `connect()` opens the window.
|
|
294
|
-
*/
|
|
295
447
|
fieldTargetConnected(element) {
|
|
296
448
|
this.#bind(element);
|
|
297
449
|
this.#adoptField(element);
|
|
298
450
|
this.#reconcile.schedule();
|
|
299
451
|
}
|
|
300
|
-
/** Releases a dropped field's listeners and leases, then reconciles the rest. */
|
|
301
452
|
fieldTargetDisconnected(element) {
|
|
302
453
|
this.#unbind(element);
|
|
303
|
-
this.#
|
|
454
|
+
this.#returnLeases(element);
|
|
455
|
+
this.#reconcile.schedule();
|
|
456
|
+
}
|
|
457
|
+
valueTargetConnected() {
|
|
458
|
+
this.#reconcile.schedule();
|
|
459
|
+
}
|
|
460
|
+
valueTargetDisconnected() {
|
|
304
461
|
this.#reconcile.schedule();
|
|
305
462
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
463
|
+
errorTargetConnected() {
|
|
464
|
+
this.#followError();
|
|
465
|
+
}
|
|
466
|
+
errorTargetDisconnected(element) {
|
|
467
|
+
if (!this.errorTargets.includes(element)) this.#errorHidden.return(element);
|
|
468
|
+
this.#followError();
|
|
469
|
+
}
|
|
310
470
|
patternValueChanged() {
|
|
311
471
|
const compiled = compileRegExp(this.patternValue, "exact");
|
|
312
472
|
this.#patternSource = compiled ? this.patternValue : DEFAULT_PATTERN;
|
|
313
473
|
this.#pattern = compiled ?? DEFAULT_MATCHER;
|
|
314
474
|
if (!this.#connected) return;
|
|
475
|
+
if (this.#composing !== null) {
|
|
476
|
+
this.#dropHeld = true;
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
if (this.#dropRejected()) this.#reconcile.schedule();
|
|
480
|
+
}
|
|
481
|
+
#dropRejected() {
|
|
315
482
|
let dropped = false;
|
|
316
483
|
for (const field of this.fieldTargets) {
|
|
317
484
|
if (field.value === "" || !this.#isWritable(field)) continue;
|
|
@@ -319,24 +486,19 @@ var OtpController = class extends Controller {
|
|
|
319
486
|
this.#writeField(field, "");
|
|
320
487
|
dropped = true;
|
|
321
488
|
}
|
|
322
|
-
|
|
489
|
+
return dropped;
|
|
323
490
|
}
|
|
324
|
-
/** Handles keystroke inputs, distributes autofilled text, and advances focus. */
|
|
325
491
|
onInput(event) {
|
|
326
492
|
const input = this.#fieldFrom(event);
|
|
327
493
|
if (!input) return;
|
|
328
|
-
|
|
329
|
-
this.#confirmedField = null;
|
|
330
|
-
if (confirmed === input) return;
|
|
494
|
+
if (this.#composition.consumesConfirmedInput(event)) return;
|
|
331
495
|
if (this.#composition.isComposing(event)) return;
|
|
332
496
|
this.#accept(input);
|
|
333
497
|
}
|
|
334
|
-
/** Handles Backspace clearing, arrows, and home/end navigation. */
|
|
335
498
|
onKeydown(event) {
|
|
336
499
|
if (isReservedArrowChord(event)) return;
|
|
337
500
|
const input = this.#fieldFrom(event);
|
|
338
501
|
if (!input) return;
|
|
339
|
-
this.#confirmedField = null;
|
|
340
502
|
if (this.#composition.isComposing(event)) return;
|
|
341
503
|
const index = this.fieldTargets.indexOf(input);
|
|
342
504
|
const fields = this.fieldTargets;
|
|
@@ -395,19 +557,12 @@ var OtpController = class extends Controller {
|
|
|
395
557
|
}
|
|
396
558
|
}
|
|
397
559
|
}
|
|
398
|
-
/** Divides pasted string characters across the available input fields. */
|
|
399
560
|
onPaste(event) {
|
|
400
561
|
const input = this.#fieldFrom(event);
|
|
401
562
|
if (!input) return;
|
|
402
563
|
event.preventDefault();
|
|
403
|
-
this.#confirmedField = null;
|
|
404
564
|
this.#distribute(input, toHalfWidth(event.clipboardData?.getData("text") ?? ""));
|
|
405
565
|
}
|
|
406
|
-
/**
|
|
407
|
-
* Redirects a pointer landing on an empty field to the earliest empty one, so
|
|
408
|
-
* a passcode is entered in order. Filled fields stay directly reachable for
|
|
409
|
-
* correction, and keyboard focus is left alone.
|
|
410
|
-
*/
|
|
411
566
|
onPointerDown(event) {
|
|
412
567
|
const input = this.#fieldFrom(event);
|
|
413
568
|
if (input?.value !== "") return;
|
|
@@ -416,7 +571,6 @@ var OtpController = class extends Controller {
|
|
|
416
571
|
event.preventDefault();
|
|
417
572
|
first.focus();
|
|
418
573
|
}
|
|
419
|
-
/** Empties every writable field and restarts entry at the first of them. */
|
|
420
574
|
clear() {
|
|
421
575
|
for (const field of this.fieldTargets) {
|
|
422
576
|
if (this.#isWritable(field)) this.#writeField(field, "");
|
|
@@ -431,17 +585,6 @@ var OtpController = class extends Controller {
|
|
|
431
585
|
input.select();
|
|
432
586
|
}
|
|
433
587
|
};
|
|
434
|
-
/** Reconciles derived state after a non-cancelled reset restores the fields. */
|
|
435
|
-
#onReset = (event) => {
|
|
436
|
-
const form = event.target;
|
|
437
|
-
if (!(form instanceof HTMLFormElement) || !this.#ownedBy(form)) return;
|
|
438
|
-
queueMicrotask(() => {
|
|
439
|
-
if (event.defaultPrevented) return;
|
|
440
|
-
this.#adopt();
|
|
441
|
-
this.#syncAndDispatch();
|
|
442
|
-
});
|
|
443
|
-
};
|
|
444
|
-
/** Whether a form owns at least one field or the hidden combined value. */
|
|
445
588
|
#ownedBy(form) {
|
|
446
589
|
if (this.fieldTargets.some((field) => field.form === form)) return true;
|
|
447
590
|
return this.hasValueTarget && this.valueTarget.form === form;
|
|
@@ -453,43 +596,44 @@ var OtpController = class extends Controller {
|
|
|
453
596
|
#unbind(field) {
|
|
454
597
|
field.removeEventListener("focus", this.#onFieldFocus);
|
|
455
598
|
this.#composition.unobserve(field);
|
|
456
|
-
if (this.#
|
|
599
|
+
if (this.#composing?.field === field) this.#composing = null;
|
|
457
600
|
}
|
|
458
|
-
/** Reads every field back so a restored or reset group starts consistent. */
|
|
459
601
|
#adopt() {
|
|
460
602
|
for (const field of this.fieldTargets) this.#adoptField(field);
|
|
461
603
|
this.#clearError();
|
|
462
|
-
if (this.hasErrorTarget) this.errorTarget.setAttribute("hidden", "");
|
|
463
604
|
}
|
|
464
|
-
|
|
605
|
+
#lease(field, attribute, value) {
|
|
606
|
+
const marker = LEASE_MARKERS[attribute];
|
|
607
|
+
const held = readLease(field, marker);
|
|
608
|
+
const authored = held ? held.authored : field.getAttribute(attribute);
|
|
609
|
+
field.setAttribute(marker, JSON.stringify({ authored, written: value }));
|
|
610
|
+
field.setAttribute(attribute, value);
|
|
611
|
+
}
|
|
612
|
+
#returnLeases(field) {
|
|
613
|
+
for (const [attribute, marker] of Object.entries(LEASE_MARKERS)) {
|
|
614
|
+
const held = readLease(field, marker);
|
|
615
|
+
if (!held) continue;
|
|
616
|
+
field.removeAttribute(marker);
|
|
617
|
+
if (field.getAttribute(attribute) !== held.written) continue;
|
|
618
|
+
if (held.authored === null) field.removeAttribute(attribute);
|
|
619
|
+
else field.setAttribute(attribute, held.authored);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
465
622
|
#adoptField(field) {
|
|
466
623
|
this.#committed.set(field, field.value);
|
|
467
624
|
this.#markFilled(field, field.value);
|
|
468
625
|
}
|
|
469
|
-
/**
|
|
470
|
-
* Absorbs a batch of field additions or removals as one state transition.
|
|
471
|
-
*
|
|
472
|
-
* The page, not the user, moved the state here, so it is reported as
|
|
473
|
-
* `reconcile`: automation listening for `change` must not read a re-render as
|
|
474
|
-
* an edit, and a passcode that happens to end up full must not fire the
|
|
475
|
-
* `complete` that submits it.
|
|
476
|
-
*
|
|
477
|
-
* Completeness moves on its own when the field count changes: dropping a
|
|
478
|
-
* trailing empty field completes a passcode whose combined value never moved,
|
|
479
|
-
* and adding one un-completes it. Comparing the whole derived state, not the
|
|
480
|
-
* string it contains, is what makes those transitions reportable.
|
|
481
|
-
*/
|
|
482
626
|
#reconcileFields() {
|
|
627
|
+
if (this.#dropHeld && this.#composing === null) {
|
|
628
|
+
this.#dropHeld = false;
|
|
629
|
+
this.#dropRejected();
|
|
630
|
+
}
|
|
483
631
|
const previous = this.#published;
|
|
484
632
|
const current = this.#sync();
|
|
485
633
|
if (previous && !statesDiffer(previous, current)) return;
|
|
634
|
+
this.#moves.record();
|
|
486
635
|
this.dispatch("reconcile", { detail: { value: current.value } });
|
|
487
636
|
}
|
|
488
|
-
/**
|
|
489
|
-
* Validates the text an entry point received and distributes what it accepts.
|
|
490
|
-
* `text` defaults to the field's own value; a confirmation passes the string it
|
|
491
|
-
* committed, which `maxlength` would otherwise have truncated.
|
|
492
|
-
*/
|
|
493
637
|
#accept(input, text = input.value) {
|
|
494
638
|
const raw = toHalfWidth(text);
|
|
495
639
|
if (raw === "") {
|
|
@@ -500,10 +644,6 @@ var OtpController = class extends Controller {
|
|
|
500
644
|
}
|
|
501
645
|
this.#distribute(input, raw);
|
|
502
646
|
}
|
|
503
|
-
/**
|
|
504
|
-
* Fills `text`'s accepted characters into the writable fields at and after the
|
|
505
|
-
* entry point, then leaves focus on the field after the last one filled.
|
|
506
|
-
*/
|
|
507
647
|
#distribute(from, text) {
|
|
508
648
|
const accepted = Array.from(text).filter((char) => this.#pattern.test(char));
|
|
509
649
|
let reached = false;
|
|
@@ -528,11 +668,9 @@ var OtpController = class extends Controller {
|
|
|
528
668
|
this.#clearError();
|
|
529
669
|
this.#syncAndDispatch();
|
|
530
670
|
}
|
|
531
|
-
/** Restores the digit a field committed before rejected input replaced it. */
|
|
532
671
|
#restore(field) {
|
|
533
672
|
this.#writeField(field, this.#committed.get(field) ?? "");
|
|
534
673
|
}
|
|
535
|
-
/** Commits one field's value and the derived hook that reports it as entered. */
|
|
536
674
|
#writeField(field, value) {
|
|
537
675
|
field.value = value;
|
|
538
676
|
this.#committed.set(field, value);
|
|
@@ -561,27 +699,23 @@ var OtpController = class extends Controller {
|
|
|
561
699
|
#focusableBefore(index) {
|
|
562
700
|
return this.#before(index).find((field) => this.#isFocusable(field)) ?? null;
|
|
563
701
|
}
|
|
564
|
-
/** Fields before `index`, nearest first; empty at the first field. */
|
|
565
702
|
#before(index) {
|
|
566
703
|
return this.fieldTargets.slice(0, Math.max(index, 0)).reverse();
|
|
567
704
|
}
|
|
568
|
-
/** The event's field target, or `null` when the wiring points somewhere else. */
|
|
569
705
|
#fieldFrom(event) {
|
|
570
706
|
const input = event.currentTarget;
|
|
571
707
|
return this.fieldTargets.find((field) => field === input) ?? null;
|
|
572
708
|
}
|
|
573
709
|
#combinedValue() {
|
|
574
|
-
return this.fieldTargets.map((field) => field
|
|
710
|
+
return this.fieldTargets.map((field) => this.#publishedText(field)).join("");
|
|
575
711
|
}
|
|
576
|
-
/** Every field carries a character, and there is at least one field. */
|
|
577
712
|
#isComplete() {
|
|
578
713
|
const fields = this.fieldTargets;
|
|
579
|
-
return fields.length > 0 && fields.every((field) => field.
|
|
714
|
+
return fields.length > 0 && fields.every((field) => this.#publishedText(field).length > 0);
|
|
715
|
+
}
|
|
716
|
+
#publishedText(field) {
|
|
717
|
+
return field === this.#composing?.field ? this.#committed.get(field) ?? "" : field.value;
|
|
580
718
|
}
|
|
581
|
-
/**
|
|
582
|
-
* Mirrors the combined value into the form and the root's readable state, and
|
|
583
|
-
* records what was published so the next pass can compare against it.
|
|
584
|
-
*/
|
|
585
719
|
#sync() {
|
|
586
720
|
const combined = this.#combinedValue();
|
|
587
721
|
if (this.hasValueTarget) {
|
|
@@ -601,42 +735,56 @@ var OtpController = class extends Controller {
|
|
|
601
735
|
const previous = this.#published;
|
|
602
736
|
const { value: combined } = this.#sync();
|
|
603
737
|
if (previous?.value === combined) return;
|
|
738
|
+
const token = this.#moves.record();
|
|
604
739
|
this.dispatch("change", { detail: { value: combined } });
|
|
740
|
+
if (!this.#moves.isLatest(token)) return;
|
|
605
741
|
if (this.#isComplete()) {
|
|
606
742
|
this.dispatch("complete", { detail: { value: combined } });
|
|
607
743
|
}
|
|
608
744
|
}
|
|
609
|
-
/** Surfaces rejected input on every field and on the optional error target. */
|
|
610
745
|
#showError() {
|
|
611
746
|
const errorId = this.hasErrorTarget ? ensureId(this.errorTarget, "stimeo--otp-error") : null;
|
|
612
747
|
for (const field of this.fieldTargets) {
|
|
613
|
-
this.#
|
|
614
|
-
if (
|
|
615
|
-
this.#ariaErrorMessage.write(field, errorId);
|
|
616
|
-
this.#ariaDescribedBy.write(field, this.#describedByWith(field, errorId));
|
|
748
|
+
this.#lease(field, "aria-invalid", "true");
|
|
749
|
+
if (errorId) this.#pointAtError(field, errorId);
|
|
617
750
|
}
|
|
618
751
|
if (this.hasErrorTarget) this.#errorHidden.write(this.errorTarget, null);
|
|
619
752
|
this.dispatch("invalid", { detail: { pattern: this.#patternSource } });
|
|
620
753
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
this.#
|
|
624
|
-
|
|
625
|
-
this.#
|
|
626
|
-
|
|
754
|
+
#pointAtError(field, errorId) {
|
|
755
|
+
const earlier = readLease(field, LEASE_MARKERS["aria-errormessage"])?.written ?? null;
|
|
756
|
+
this.#lease(field, "aria-errormessage", errorId);
|
|
757
|
+
const described = field.getAttribute("aria-describedby");
|
|
758
|
+
this.#lease(field, "aria-describedby", describedByWith(described, errorId, earlier));
|
|
759
|
+
}
|
|
760
|
+
#followError() {
|
|
761
|
+
if (!this.#connected) return;
|
|
762
|
+
const reported = this.fieldTargets.filter(
|
|
763
|
+
(field) => readLease(field, LEASE_MARKERS["aria-invalid"]) !== null
|
|
764
|
+
);
|
|
765
|
+
if (reported.length === 0) {
|
|
766
|
+
this.#hideErrors();
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
if (!this.hasErrorTarget) return;
|
|
770
|
+
const errorId = ensureId(this.errorTarget, "stimeo--otp-error");
|
|
771
|
+
for (const field of reported) this.#pointAtError(field, errorId);
|
|
772
|
+
this.#errorHidden.write(this.errorTarget, null);
|
|
627
773
|
}
|
|
628
|
-
#
|
|
629
|
-
this.#
|
|
630
|
-
this.#
|
|
631
|
-
|
|
774
|
+
#clearError() {
|
|
775
|
+
for (const field of this.fieldTargets) this.#returnLeases(field);
|
|
776
|
+
if (!this.#connected) {
|
|
777
|
+
this.#errorHidden.returnAll();
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
this.#hideErrors();
|
|
632
781
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
const
|
|
636
|
-
|
|
782
|
+
#hideErrors() {
|
|
783
|
+
const [first, ...others] = this.errorTargets;
|
|
784
|
+
for (const other of others) this.#errorHidden.return(other);
|
|
785
|
+
if (first) this.#errorHidden.write(first, "");
|
|
637
786
|
}
|
|
638
787
|
};
|
|
639
788
|
|
|
640
789
|
export { OtpController };
|
|
641
|
-
//# sourceMappingURL=otp_controller.js.map
|
|
642
790
|
//# sourceMappingURL=otp_controller.js.map
|