stimeo-ui 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +178 -0
- package/dist/controllers/alert_dialog_controller.d.ts +2 -0
- package/dist/controllers/alert_dialog_controller.js +75 -4
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/aspect_ratio_controller.d.ts +1 -3
- package/dist/controllers/aspect_ratio_controller.js +19 -11
- package/dist/controllers/aspect_ratio_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.d.ts +2 -0
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.d.ts +36 -15
- package/dist/controllers/avatar_controller.js +237 -40
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.d.ts +2 -0
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +2 -0
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +2 -0
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +12 -3
- package/dist/controllers/carousel_controller.js +85 -9
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +52 -20
- package/dist/controllers/character_counter_controller.js +338 -63
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.d.ts +34 -5
- package/dist/controllers/checkbox_controller.js +136 -25
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +10 -1
- package/dist/controllers/color_picker_controller.js +35 -9
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +2 -0
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +2 -0
- package/dist/controllers/command_palette_controller.js +75 -4
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.d.ts +42 -14
- package/dist/controllers/conditional_fields_controller.js +345 -51
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.d.ts +2 -0
- package/dist/controllers/confirm_controller.js +75 -4
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/count_up_controller.d.ts +2 -0
- package/dist/controllers/count_up_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +4 -0
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +2 -0
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +3 -0
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +23 -4
- package/dist/controllers/date_range_picker_controller.js +157 -30
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.js +75 -4
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +56 -24
- package/dist/controllers/direct_upload_controller.js +201 -45
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.d.ts +14 -6
- package/dist/controllers/dirty_form_controller.js +192 -29
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.d.ts +2 -0
- package/dist/controllers/dismissible_controller.js +83 -18
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.js +75 -4
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.d.ts +2 -0
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +9 -1
- package/dist/controllers/file_dropzone_controller.js +26 -3
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.d.ts +2 -0
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +4 -0
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +4 -3
- package/dist/controllers/focus_controller.js +75 -4
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.d.ts +50 -10
- package/dist/controllers/form_field_controller.js +280 -62
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.d.ts +10 -8
- package/dist/controllers/form_validation_controller.js +208 -83
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +2 -0
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +2 -0
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +2 -2
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +5 -3
- package/dist/controllers/idle_controller.js +27 -5
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +2 -0
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.d.ts +2 -0
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +2 -0
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.d.ts +2 -0
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.d.ts +2 -0
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.js +5 -3
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.d.ts +2 -0
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +48 -12
- package/dist/controllers/multi_select_controller.js +460 -151
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.d.ts +2 -0
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.d.ts +2 -0
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +26 -6
- package/dist/controllers/number_input_controller.js +317 -51
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +3 -0
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +2 -0
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +2 -0
- package/dist/controllers/overflow_menu_controller.js +6 -1
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +2 -0
- package/dist/controllers/pagination_controller.js +35 -1
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +2 -0
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +5 -3
- package/dist/controllers/password_strength_controller.js +20 -2
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.d.ts +33 -19
- package/dist/controllers/persist_controller.js +449 -120
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +3 -0
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +2 -2
- package/dist/controllers/popover_controller.js +77 -4
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +3 -2
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.d.ts +2 -0
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +3 -0
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +44 -15
- package/dist/controllers/radio_group_controller.js +540 -56
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +38 -31
- package/dist/controllers/rating_controller.js +276 -89
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/reading_progress_controller.d.ts +2 -0
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/resizable_controller.d.ts +2 -0
- package/dist/controllers/resizable_controller.js +33 -0
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.d.ts +6 -0
- package/dist/controllers/roving_controller.js +60 -5
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.d.ts +25 -11
- package/dist/controllers/scroll_area_controller.js +557 -125
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.d.ts +2 -0
- package/dist/controllers/scroll_visibility_controller.js +33 -0
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +2 -0
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +54 -11
- package/dist/controllers/separator_controller.js +354 -38
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.js +83 -10
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.d.ts +2 -0
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +2 -0
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.d.ts +2 -0
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.d.ts +2 -0
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +2 -0
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +2 -0
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +2 -0
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/sticky_observer_controller.d.ts +2 -0
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +94 -38
- package/dist/controllers/submit_once_controller.js +399 -121
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +2 -0
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +43 -11
- package/dist/controllers/tags_input_controller.js +356 -120
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.d.ts +2 -0
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +2 -0
- package/dist/controllers/theme_controller.js +8 -6
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +44 -13
- package/dist/controllers/time_picker_controller.js +296 -107
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +4 -0
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +41 -13
- package/dist/controllers/toggle_group_controller.js +378 -55
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +5 -3
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +2 -2
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.d.ts +4 -3
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +2 -0
- package/dist/controllers/tree_view_controller.js +7 -4
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.js +4963 -1581
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +123 -6
- package/dist/inspector/cli.js +212 -18
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +274 -50
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +15 -15
- package/dist/inspector/manifest.json +602 -80
- package/dist/positioning/index.d.ts +4 -2
- package/dist/positioning/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -8,6 +8,123 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
8
8
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
// src/utils/attribute_lease.ts
|
|
12
|
+
var AttributeLease = class {
|
|
13
|
+
#attribute;
|
|
14
|
+
#records = /* @__PURE__ */ new Map();
|
|
15
|
+
/** @param attribute - The attribute whose temporary values this lease owns. */
|
|
16
|
+
constructor(attribute) {
|
|
17
|
+
this.#attribute = attribute;
|
|
18
|
+
}
|
|
19
|
+
/** Writes or removes the leased attribute while preserving its authored value. */
|
|
20
|
+
write(element, value) {
|
|
21
|
+
const existing = this.#records.get(element);
|
|
22
|
+
if (existing) {
|
|
23
|
+
existing.written = value;
|
|
24
|
+
} else {
|
|
25
|
+
this.#records.set(element, {
|
|
26
|
+
original: element.getAttribute(this.#attribute),
|
|
27
|
+
written: value
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
this.#reflect(element, value);
|
|
31
|
+
}
|
|
32
|
+
/** Returns one lease without overwriting a value subsequently authored by a consumer. */
|
|
33
|
+
return(element) {
|
|
34
|
+
const record = this.#records.get(element);
|
|
35
|
+
if (!record) return;
|
|
36
|
+
this.#records.delete(element);
|
|
37
|
+
const stillOwned = element.getAttribute(this.#attribute) === record.written;
|
|
38
|
+
if (stillOwned) this.#reflect(element, record.original);
|
|
39
|
+
}
|
|
40
|
+
/** Reflects only a real value transition, avoiding self-triggered mutation work. */
|
|
41
|
+
#reflect(element, value) {
|
|
42
|
+
if (element.getAttribute(this.#attribute) === value) return;
|
|
43
|
+
if (value === null) element.removeAttribute(this.#attribute);
|
|
44
|
+
else element.setAttribute(this.#attribute, value);
|
|
45
|
+
}
|
|
46
|
+
/** Returns every outstanding lease using the same ownership check as {@link return}. */
|
|
47
|
+
returnAll() {
|
|
48
|
+
for (const element of Array.from(this.#records.keys())) this.return(element);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// src/utils/before_cache_reset.ts
|
|
53
|
+
var BeforeCacheReset = class _BeforeCacheReset {
|
|
54
|
+
/** Every subscribed instance, iterated by the one shared document listener. */
|
|
55
|
+
static #subscribers = /* @__PURE__ */ new Set();
|
|
56
|
+
/** The shared listener; installed while at least one instance is subscribed. */
|
|
57
|
+
static #onBeforeCache = () => {
|
|
58
|
+
for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
|
|
59
|
+
};
|
|
60
|
+
#rewind;
|
|
61
|
+
/** @param rewind - the pass that returns this controller's state to its initial form. */
|
|
62
|
+
constructor(rewind) {
|
|
63
|
+
this.#rewind = rewind;
|
|
64
|
+
}
|
|
65
|
+
/** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
|
|
66
|
+
activate() {
|
|
67
|
+
const first = _BeforeCacheReset.#subscribers.size === 0;
|
|
68
|
+
_BeforeCacheReset.#subscribers.add(this);
|
|
69
|
+
if (first) {
|
|
70
|
+
document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
|
|
74
|
+
deactivate() {
|
|
75
|
+
_BeforeCacheReset.#subscribers.delete(this);
|
|
76
|
+
if (_BeforeCacheReset.#subscribers.size > 0) return;
|
|
77
|
+
document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// src/utils/composition_tracker.ts
|
|
82
|
+
var CompositionTracker = class {
|
|
83
|
+
#observedTargets = /* @__PURE__ */ new Set();
|
|
84
|
+
#activeTargets = /* @__PURE__ */ new Set();
|
|
85
|
+
#onStart;
|
|
86
|
+
#onEnd;
|
|
87
|
+
constructor(options = {}) {
|
|
88
|
+
this.#onStart = options.onStart;
|
|
89
|
+
this.#onEnd = options.onEnd;
|
|
90
|
+
}
|
|
91
|
+
/** Starts lifecycle tracking for `target`; repeated calls are idempotent. */
|
|
92
|
+
observe(target) {
|
|
93
|
+
if (this.#observedTargets.has(target)) return;
|
|
94
|
+
target.addEventListener("compositionstart", this.#handleStart);
|
|
95
|
+
target.addEventListener("compositionend", this.#handleEnd);
|
|
96
|
+
this.#observedTargets.add(target);
|
|
97
|
+
}
|
|
98
|
+
/** Stops tracking one target and clears any active composition it owned. */
|
|
99
|
+
unobserve(target) {
|
|
100
|
+
if (!this.#observedTargets.delete(target)) return;
|
|
101
|
+
target.removeEventListener("compositionstart", this.#handleStart);
|
|
102
|
+
target.removeEventListener("compositionend", this.#handleEnd);
|
|
103
|
+
this.#activeTargets.delete(target);
|
|
104
|
+
}
|
|
105
|
+
/** Releases every listener and clears state so reconnect starts cleanly. */
|
|
106
|
+
disconnect() {
|
|
107
|
+
for (const target of this.#observedTargets) {
|
|
108
|
+
target.removeEventListener("compositionstart", this.#handleStart);
|
|
109
|
+
target.removeEventListener("compositionend", this.#handleEnd);
|
|
110
|
+
}
|
|
111
|
+
this.#observedTargets.clear();
|
|
112
|
+
this.#activeTargets.clear();
|
|
113
|
+
}
|
|
114
|
+
/** True when lifecycle tracking or the current event reports composition. */
|
|
115
|
+
isComposing(event) {
|
|
116
|
+
return this.#activeTargets.size > 0 || event?.isComposing === true;
|
|
117
|
+
}
|
|
118
|
+
#handleStart = (event) => {
|
|
119
|
+
if (event.currentTarget) this.#activeTargets.add(event.currentTarget);
|
|
120
|
+
this.#onStart?.(event);
|
|
121
|
+
};
|
|
122
|
+
#handleEnd = (event) => {
|
|
123
|
+
if (event.currentTarget) this.#activeTargets.delete(event.currentTarget);
|
|
124
|
+
this.#onEnd?.(event);
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
|
|
11
128
|
// src/utils/microtask_coalescer.ts
|
|
12
129
|
var MicrotaskCoalescer = class {
|
|
13
130
|
#run;
|
|
@@ -215,6 +332,7 @@ function finiteFallback(min, max) {
|
|
|
215
332
|
}
|
|
216
333
|
|
|
217
334
|
// src/controllers/number_input_controller.ts
|
|
335
|
+
var OWNED_DISABLED = "data-number-input-disabled";
|
|
218
336
|
var NumberInputController = class _NumberInputController extends Controller {
|
|
219
337
|
static targets = ["input", "increment", "decrement"];
|
|
220
338
|
static values = {
|
|
@@ -224,7 +342,7 @@ var NumberInputController = class _NumberInputController extends Controller {
|
|
|
224
342
|
pageStep: { type: Number, default: 0 }
|
|
225
343
|
};
|
|
226
344
|
static actions = ["decrement", "increment", "onInput", "onKeydown"];
|
|
227
|
-
static events = ["change"];
|
|
345
|
+
static events = ["change", "reconcile"];
|
|
228
346
|
/** Delay (ms) a button must be held before auto-repeat starts. */
|
|
229
347
|
static #HOLD_DELAY_MS = 400;
|
|
230
348
|
/** Interval (ms) between auto-repeat steps once a hold has started. */
|
|
@@ -236,8 +354,19 @@ var NumberInputController = class _NumberInputController extends Controller {
|
|
|
236
354
|
* of poisoning the next legitimate click.
|
|
237
355
|
*/
|
|
238
356
|
static #SUPPRESS_RESET_MS = 250;
|
|
239
|
-
/** Aborts
|
|
240
|
-
#
|
|
357
|
+
/** Aborts global pointer guards on disconnect so none outlive the element. */
|
|
358
|
+
#globalGuards = null;
|
|
359
|
+
/** Per-target guards allow an old button to be released without touching its replacement. */
|
|
360
|
+
#buttonGuards = /* @__PURE__ */ new Map();
|
|
361
|
+
/** Tracks IME lifecycle on the current input, including confirming keys without a signal. */
|
|
362
|
+
#composition = new CompositionTracker();
|
|
363
|
+
/** Restores authored custom-spinbutton ARIA when a target leaves or the controller stops. */
|
|
364
|
+
#beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
|
|
365
|
+
#ariaValueNow = new AttributeLease("aria-valuenow");
|
|
366
|
+
#ariaValueMin = new AttributeLease("aria-valuemin");
|
|
367
|
+
#ariaValueMax = new AttributeLease("aria-valuemax");
|
|
368
|
+
/** Last reconciled or user-committed numeric value. */
|
|
369
|
+
#lastValue = null;
|
|
241
370
|
/** Timers for the hold delay and the suppress-reset safety net. */
|
|
242
371
|
#holdTimeouts = new SafeTimeout();
|
|
243
372
|
/** The running auto-repeat interval (one at a time). */
|
|
@@ -248,63 +377,122 @@ var NumberInputController = class _NumberInputController extends Controller {
|
|
|
248
377
|
#repeatedDuringHold = false;
|
|
249
378
|
/** True when the next `click` is the trailing one after a hold and must be ignored. */
|
|
250
379
|
#suppressNextClick = false;
|
|
251
|
-
/**
|
|
380
|
+
/** Button and pointer that own the current hold, so other instances/pointers cannot end it. */
|
|
381
|
+
#holdButton = null;
|
|
382
|
+
#holdPointerId = null;
|
|
383
|
+
/** Collapses runtime range/step changes into one input reconciliation. */
|
|
252
384
|
#repaint = new MicrotaskCoalescer(() => this.#reconcile());
|
|
253
385
|
/** Normalizes any initial value and wires the focus/hold pointer guards. */
|
|
254
386
|
connect() {
|
|
255
387
|
this.#repaint.activate();
|
|
256
|
-
|
|
388
|
+
this.#beforeCache.activate();
|
|
389
|
+
this.#globalGuards = new AbortController();
|
|
390
|
+
const { signal } = this.#globalGuards;
|
|
391
|
+
for (const button of this.incrementTargets) this.#wireButton(button, 1);
|
|
392
|
+
for (const button of this.decrementTargets) this.#wireButton(button, -1);
|
|
393
|
+
window.addEventListener("pointerup", this.#onPointerEnd, { signal });
|
|
394
|
+
window.addEventListener("pointercancel", this.#onPointerEnd, { signal });
|
|
395
|
+
window.addEventListener("blur", this.#onWindowBlur, { signal });
|
|
257
396
|
this.#reconcile();
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
if (this.hasIncrementTarget) this.#wireButton(this.incrementTarget, 1, signal);
|
|
261
|
-
if (this.hasDecrementTarget) this.#wireButton(this.decrementTarget, -1, signal);
|
|
262
|
-
window.addEventListener("pointerup", this.#stopHold, { signal });
|
|
263
|
-
window.addEventListener("pointercancel", this.#stopHold, { signal });
|
|
264
|
-
window.addEventListener("blur", this.#stopHold, { signal });
|
|
265
|
-
}
|
|
266
|
-
/** Releases the pointer guards and tears down every pending timer and hold state. */
|
|
397
|
+
}
|
|
398
|
+
/** Releases listeners, derived attributes, timers, and transient value state. */
|
|
267
399
|
disconnect() {
|
|
268
400
|
this.#repaint.cancel();
|
|
269
|
-
this.#
|
|
270
|
-
this.#
|
|
271
|
-
this.#
|
|
401
|
+
this.#composition.disconnect();
|
|
402
|
+
this.#beforeCache.deactivate();
|
|
403
|
+
this.#globalGuards?.abort();
|
|
404
|
+
this.#globalGuards = null;
|
|
405
|
+
const buttons = /* @__PURE__ */ new Set([
|
|
406
|
+
...this.#buttonGuards.keys(),
|
|
407
|
+
...this.incrementTargets,
|
|
408
|
+
...this.decrementTargets
|
|
409
|
+
]);
|
|
410
|
+
for (const guard of this.#buttonGuards.values()) guard.abort();
|
|
411
|
+
this.#buttonGuards.clear();
|
|
412
|
+
for (const button of buttons) this.#releaseButtonState(button);
|
|
413
|
+
this.#stopHold(false);
|
|
272
414
|
this.#repeatedDuringHold = false;
|
|
273
415
|
this.#suppressNextClick = false;
|
|
274
416
|
this.#holdTimeouts.clearAll();
|
|
275
417
|
this.#holdIntervals.clearAll();
|
|
418
|
+
this.#ariaValueNow.returnAll();
|
|
419
|
+
this.#ariaValueMin.returnAll();
|
|
420
|
+
this.#ariaValueMax.returnAll();
|
|
421
|
+
this.#lastValue = null;
|
|
276
422
|
}
|
|
277
|
-
/**
|
|
423
|
+
/** Reconciles the value against a minimum changed by application code or a Turbo morph. */
|
|
278
424
|
minValueChanged() {
|
|
279
425
|
this.#repaint.schedule();
|
|
280
426
|
}
|
|
281
|
-
/**
|
|
427
|
+
/** Reconciles the value against a maximum changed by application code or a Turbo morph. */
|
|
282
428
|
maxValueChanged() {
|
|
283
429
|
this.#repaint.schedule();
|
|
284
430
|
}
|
|
285
|
-
/**
|
|
431
|
+
/** Reconciles the value against a step changed by application code or a Turbo morph. */
|
|
286
432
|
stepValueChanged() {
|
|
287
433
|
this.#repaint.schedule();
|
|
288
434
|
}
|
|
435
|
+
/** Tracks and reconciles an input inserted or replaced at runtime. */
|
|
436
|
+
inputTargetConnected(input) {
|
|
437
|
+
this.#composition.observe(input);
|
|
438
|
+
this.#repaint.schedule();
|
|
439
|
+
}
|
|
440
|
+
/** Releases ARIA and composition state owned by an input that left the controller. */
|
|
441
|
+
inputTargetDisconnected(input) {
|
|
442
|
+
this.#composition.unobserve(input);
|
|
443
|
+
this.#releaseInputAria(input);
|
|
444
|
+
this.#stopHold(false);
|
|
445
|
+
this.#lastValue = null;
|
|
446
|
+
this.#repaint.schedule();
|
|
447
|
+
}
|
|
448
|
+
/** Wires focus preservation and hold behavior on a runtime increment target. */
|
|
449
|
+
incrementTargetConnected(button) {
|
|
450
|
+
this.#wireButton(button, 1);
|
|
451
|
+
this.#repaint.schedule();
|
|
452
|
+
}
|
|
453
|
+
/** Releases only the increment target that actually disconnected. */
|
|
454
|
+
incrementTargetDisconnected(button) {
|
|
455
|
+
this.#unwireButton(button);
|
|
456
|
+
this.#repaint.schedule();
|
|
457
|
+
}
|
|
458
|
+
/** Wires focus preservation and hold behavior on a runtime decrement target. */
|
|
459
|
+
decrementTargetConnected(button) {
|
|
460
|
+
this.#wireButton(button, -1);
|
|
461
|
+
this.#repaint.schedule();
|
|
462
|
+
}
|
|
463
|
+
/** Releases only the decrement target that actually disconnected. */
|
|
464
|
+
decrementTargetDisconnected(button) {
|
|
465
|
+
this.#unwireButton(button);
|
|
466
|
+
this.#repaint.schedule();
|
|
467
|
+
}
|
|
289
468
|
/** Increases by one step. Bound via `data-action` (click). */
|
|
290
469
|
increment() {
|
|
291
470
|
if (this.#consumeSuppressedClick()) return;
|
|
471
|
+
if (!this.hasInputTarget) return;
|
|
292
472
|
this.#commitStep(1);
|
|
293
473
|
this.inputTarget.focus();
|
|
294
474
|
}
|
|
295
475
|
/** Decreases by one step. Bound via `data-action` (click). */
|
|
296
476
|
decrement() {
|
|
297
477
|
if (this.#consumeSuppressedClick()) return;
|
|
478
|
+
if (!this.hasInputTarget) return;
|
|
298
479
|
this.#commitStep(-1);
|
|
299
480
|
this.inputTarget.focus();
|
|
300
481
|
}
|
|
301
482
|
/** Clamps and snaps a typed value. Bound via `data-action` (change). */
|
|
302
483
|
onInput() {
|
|
303
|
-
if (this.
|
|
484
|
+
if (!this.hasInputTarget) return;
|
|
485
|
+
if (this.inputTarget.value.trim() === "") {
|
|
486
|
+
this.#lastValue = null;
|
|
487
|
+
this.#reflectEmpty();
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
304
490
|
this.#commit(this.#currentValue());
|
|
305
491
|
}
|
|
306
492
|
/** Keyboard stepping per the APG spinbutton model. */
|
|
307
493
|
onKeydown(event) {
|
|
494
|
+
if (!this.hasInputTarget) return;
|
|
495
|
+
if (this.#composition.isComposing(event)) return;
|
|
308
496
|
if (isReservedArrowChord(event)) return;
|
|
309
497
|
let next = null;
|
|
310
498
|
switch (event.key) {
|
|
@@ -339,21 +527,39 @@ var NumberInputController = class _NumberInputController extends Controller {
|
|
|
339
527
|
* hold; leaving the button while held stops it (the global listeners cover
|
|
340
528
|
* release/cancel/blur).
|
|
341
529
|
*/
|
|
342
|
-
#wireButton(button, direction
|
|
530
|
+
#wireButton(button, direction) {
|
|
531
|
+
if (this.#buttonGuards.has(button)) return;
|
|
532
|
+
const guard = new AbortController();
|
|
533
|
+
this.#buttonGuards.set(button, guard);
|
|
343
534
|
button.addEventListener("pointerdown", (event) => this.#armHold(event, button, direction), {
|
|
344
|
-
signal
|
|
535
|
+
signal: guard.signal
|
|
345
536
|
});
|
|
346
|
-
button.addEventListener(
|
|
537
|
+
button.addEventListener(
|
|
538
|
+
"pointerleave",
|
|
539
|
+
(event) => {
|
|
540
|
+
if (this.#holdButton === button && this.#ownsPointer(event)) this.#stopHold();
|
|
541
|
+
},
|
|
542
|
+
{ signal: guard.signal }
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
/** Aborts one target's listeners and returns any state derived onto that button. */
|
|
546
|
+
#unwireButton(button) {
|
|
547
|
+
this.#buttonGuards.get(button)?.abort();
|
|
548
|
+
this.#buttonGuards.delete(button);
|
|
549
|
+
if (this.#holdButton === button) this.#stopHold(false);
|
|
550
|
+
this.#releaseButtonState(button);
|
|
347
551
|
}
|
|
348
552
|
/** Starts a hold: focus the input, then schedule the first repeat after a delay. */
|
|
349
553
|
#armHold(event, button, direction) {
|
|
350
554
|
const pointerButton = event.button;
|
|
351
555
|
if (typeof pointerButton === "number" && pointerButton !== 0) return;
|
|
352
|
-
if (button.disabled) return;
|
|
556
|
+
if (button.disabled || !this.hasInputTarget) return;
|
|
353
557
|
event.preventDefault();
|
|
354
558
|
this.inputTarget.focus();
|
|
355
|
-
this.#stopHold();
|
|
559
|
+
this.#stopHold(false);
|
|
356
560
|
this.#holdActive = true;
|
|
561
|
+
this.#holdButton = button;
|
|
562
|
+
this.#holdPointerId = this.#pointerId(event);
|
|
357
563
|
this.#repeatedDuringHold = false;
|
|
358
564
|
this.#suppressNextClick = false;
|
|
359
565
|
this.#holdTimeouts.set(() => {
|
|
@@ -372,18 +578,39 @@ var NumberInputController = class _NumberInputController extends Controller {
|
|
|
372
578
|
* trailing synthetic `click` must be ignored, so it is suppressed until the
|
|
373
579
|
* click consumes it or a short safety-net timeout clears it.
|
|
374
580
|
*/
|
|
375
|
-
#stopHold =
|
|
581
|
+
#stopHold(suppressTrailingClick = true) {
|
|
376
582
|
if (!this.#holdActive) return;
|
|
377
583
|
this.#holdActive = false;
|
|
584
|
+
this.#holdButton = null;
|
|
585
|
+
this.#holdPointerId = null;
|
|
378
586
|
this.#holdTimeouts.clearAll();
|
|
379
587
|
this.#holdIntervals.clearAll();
|
|
380
|
-
if (this.#repeatedDuringHold) {
|
|
588
|
+
if (suppressTrailingClick && this.#repeatedDuringHold) {
|
|
381
589
|
this.#suppressNextClick = true;
|
|
382
590
|
this.#holdTimeouts.set(() => {
|
|
383
591
|
this.#suppressNextClick = false;
|
|
384
592
|
}, _NumberInputController.#SUPPRESS_RESET_MS);
|
|
593
|
+
} else if (!suppressTrailingClick) {
|
|
594
|
+
this.#suppressNextClick = false;
|
|
385
595
|
}
|
|
596
|
+
}
|
|
597
|
+
/** Ends a hold only for its initiating pointer (legacy/synthetic events remain accepted). */
|
|
598
|
+
#onPointerEnd = (event) => {
|
|
599
|
+
if (this.#ownsPointer(event)) this.#stopHold();
|
|
600
|
+
};
|
|
601
|
+
/** Window deactivation is global and always ends the active hold. */
|
|
602
|
+
#onWindowBlur = () => {
|
|
603
|
+
this.#stopHold();
|
|
386
604
|
};
|
|
605
|
+
/** Extracts pointer identity when the runtime event exposes it. */
|
|
606
|
+
#pointerId(event) {
|
|
607
|
+
return event.pointerId ?? null;
|
|
608
|
+
}
|
|
609
|
+
/** Whether `event` belongs to the active pointer, with a synthetic-event fallback. */
|
|
610
|
+
#ownsPointer(event) {
|
|
611
|
+
const pointerId = this.#pointerId(event);
|
|
612
|
+
return this.#holdActive && (this.#holdPointerId === null || pointerId === null || pointerId === this.#holdPointerId);
|
|
613
|
+
}
|
|
387
614
|
/** Consumes a pending trailing-click suppression; returns true if the click was swallowed. */
|
|
388
615
|
#consumeSuppressedClick() {
|
|
389
616
|
if (!this.#suppressNextClick) return false;
|
|
@@ -392,53 +619,80 @@ var NumberInputController = class _NumberInputController extends Controller {
|
|
|
392
619
|
return true;
|
|
393
620
|
}
|
|
394
621
|
/**
|
|
395
|
-
* Normalizes `raw`, reflects it, and dispatches `
|
|
396
|
-
*
|
|
622
|
+
* Normalizes `raw`, reflects it, and dispatches `{ value: number }` only when
|
|
623
|
+
* the committed numeric value differs from the last reconciled value.
|
|
397
624
|
*
|
|
398
625
|
* @returns Whether the value changed (drives the auto-repeat's bound stop).
|
|
399
626
|
*/
|
|
400
627
|
#commit(raw) {
|
|
401
628
|
const value = this.#normalize(raw);
|
|
402
|
-
const changed = this
|
|
629
|
+
const changed = this.#lastValue === null || value !== this.#lastValue;
|
|
403
630
|
this.#write(value);
|
|
631
|
+
this.#lastValue = value;
|
|
404
632
|
if (changed) this.dispatch("change", { detail: { value } });
|
|
405
633
|
return changed;
|
|
406
634
|
}
|
|
407
635
|
/** Commits an adjacent endpoint/grid value and reports whether it moved. */
|
|
408
636
|
#commitStep(count) {
|
|
637
|
+
if (!this.hasInputTarget) return false;
|
|
409
638
|
return this.#commit(stepSteppedValue(this.#currentValue(), count, this.#steppedRange));
|
|
410
639
|
}
|
|
411
640
|
/**
|
|
412
|
-
*
|
|
641
|
+
* Reflects the current value after a range or step morph, reporting a moved
|
|
642
|
+
* value as `reconcile`.
|
|
413
643
|
*
|
|
414
644
|
* @stimeoRenderRoot
|
|
415
645
|
*/
|
|
416
646
|
#reconcile() {
|
|
417
|
-
if (!this.hasInputTarget)
|
|
647
|
+
if (!this.hasInputTarget) {
|
|
648
|
+
this.#lastValue = null;
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
418
651
|
if (this.inputTarget.value.trim() !== "") {
|
|
419
|
-
this.#
|
|
652
|
+
const previous = this.#lastValue;
|
|
653
|
+
const value = this.#normalize(this.#currentValue());
|
|
654
|
+
this.#write(value);
|
|
655
|
+
this.#lastValue = value;
|
|
656
|
+
if (previous !== null && value !== previous) {
|
|
657
|
+
this.dispatch("reconcile", { detail: { value } });
|
|
658
|
+
}
|
|
420
659
|
} else {
|
|
421
|
-
this.#
|
|
660
|
+
this.#lastValue = null;
|
|
661
|
+
this.#reflectEmpty();
|
|
422
662
|
}
|
|
423
663
|
}
|
|
424
664
|
/** Reflects `value` on the input (and ARIA for non-native hosts) and the buttons. */
|
|
425
665
|
#write(value) {
|
|
426
|
-
this.inputTarget
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
if (Number.isFinite(this.minValue)) {
|
|
430
|
-
this.inputTarget.setAttribute("aria-valuemin", String(this.minValue));
|
|
431
|
-
}
|
|
432
|
-
if (Number.isFinite(this.maxValue)) {
|
|
433
|
-
this.inputTarget.setAttribute("aria-valuemax", String(this.maxValue));
|
|
434
|
-
}
|
|
435
|
-
}
|
|
666
|
+
const input = this.inputTarget;
|
|
667
|
+
input.value = String(value);
|
|
668
|
+
this.#syncInputAria(input, value);
|
|
436
669
|
this.#updateButtons(value);
|
|
437
670
|
}
|
|
671
|
+
/** Reflects a blank value without inventing a numeric `aria-valuenow`. */
|
|
672
|
+
#reflectEmpty() {
|
|
673
|
+
this.#syncInputAria(this.inputTarget, null);
|
|
674
|
+
this.#updateButtons(this.#currentValue());
|
|
675
|
+
}
|
|
676
|
+
/** Synchronizes controller-derived ARIA while preserving authored teardown values. */
|
|
677
|
+
#syncInputAria(input, value) {
|
|
678
|
+
if (input.getAttribute("role") !== "spinbutton") {
|
|
679
|
+
this.#releaseInputAria(input);
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
this.#ariaValueNow.write(input, value === null ? null : String(value));
|
|
683
|
+
this.#ariaValueMin.write(input, Number.isFinite(this.minValue) ? String(this.minValue) : null);
|
|
684
|
+
this.#ariaValueMax.write(input, Number.isFinite(this.maxValue) ? String(this.maxValue) : null);
|
|
685
|
+
}
|
|
686
|
+
/** Returns all custom-spinbutton ARIA leased on `input`. */
|
|
687
|
+
#releaseInputAria(input) {
|
|
688
|
+
this.#ariaValueNow.return(input);
|
|
689
|
+
this.#ariaValueMin.return(input);
|
|
690
|
+
this.#ariaValueMax.return(input);
|
|
691
|
+
}
|
|
438
692
|
/** Disables a step button at its bound, returning focus to the input first. */
|
|
439
693
|
#updateButtons(value) {
|
|
440
|
-
|
|
441
|
-
|
|
694
|
+
for (const button of this.incrementTargets) this.#toggleButton(button, value < this.maxValue);
|
|
695
|
+
for (const button of this.decrementTargets) this.#toggleButton(button, value > this.minValue);
|
|
442
696
|
}
|
|
443
697
|
/**
|
|
444
698
|
* Enables or disables a step button at its bound, never disabling one while it
|
|
@@ -448,16 +702,22 @@ var NumberInputController = class _NumberInputController extends Controller {
|
|
|
448
702
|
*/
|
|
449
703
|
#toggleButton(button, enabled) {
|
|
450
704
|
if (enabled) {
|
|
451
|
-
if (button.hasAttribute(
|
|
705
|
+
if (button.hasAttribute(OWNED_DISABLED)) {
|
|
452
706
|
button.disabled = false;
|
|
453
|
-
button.removeAttribute(
|
|
707
|
+
button.removeAttribute(OWNED_DISABLED);
|
|
454
708
|
}
|
|
455
709
|
return;
|
|
456
710
|
}
|
|
457
711
|
if (button.disabled) return;
|
|
458
|
-
if (document.activeElement === button) this.inputTarget.focus();
|
|
712
|
+
if (document.activeElement === button && this.hasInputTarget) this.inputTarget.focus();
|
|
459
713
|
button.disabled = true;
|
|
460
|
-
button.setAttribute(
|
|
714
|
+
button.setAttribute(OWNED_DISABLED, "");
|
|
715
|
+
}
|
|
716
|
+
/** Returns `disabled` only when its explicit marker still identifies controller ownership. */
|
|
717
|
+
#releaseButtonState(button) {
|
|
718
|
+
if (!button.hasAttribute(OWNED_DISABLED)) return;
|
|
719
|
+
button.disabled = false;
|
|
720
|
+
button.removeAttribute(OWNED_DISABLED);
|
|
461
721
|
}
|
|
462
722
|
/** The current numeric value, falling back to a finite min (else 0) when blank. */
|
|
463
723
|
#currentValue() {
|
|
@@ -473,6 +733,12 @@ var NumberInputController = class _NumberInputController extends Controller {
|
|
|
473
733
|
get #steppedRange() {
|
|
474
734
|
return { min: this.minValue, max: this.maxValue, step: this.stepValue };
|
|
475
735
|
}
|
|
736
|
+
/** Returns borrowed range ARIA before Turbo snapshots the page. */
|
|
737
|
+
#rewindForCache() {
|
|
738
|
+
this.#ariaValueNow.returnAll();
|
|
739
|
+
this.#ariaValueMin.returnAll();
|
|
740
|
+
this.#ariaValueMax.returnAll();
|
|
741
|
+
}
|
|
476
742
|
};
|
|
477
743
|
|
|
478
744
|
export { NumberInputController };
|