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
|
@@ -13,6 +13,76 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
13
13
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
// src/utils/attribute_lease.ts
|
|
17
|
+
var AttributeLease = class {
|
|
18
|
+
#attribute;
|
|
19
|
+
#records = /* @__PURE__ */ new Map();
|
|
20
|
+
/** @param attribute - The attribute whose temporary values this lease owns. */
|
|
21
|
+
constructor(attribute) {
|
|
22
|
+
this.#attribute = attribute;
|
|
23
|
+
}
|
|
24
|
+
/** Writes or removes the leased attribute while preserving its authored value. */
|
|
25
|
+
write(element, value) {
|
|
26
|
+
const existing = this.#records.get(element);
|
|
27
|
+
if (existing) {
|
|
28
|
+
existing.written = value;
|
|
29
|
+
} else {
|
|
30
|
+
this.#records.set(element, {
|
|
31
|
+
original: element.getAttribute(this.#attribute),
|
|
32
|
+
written: value
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
this.#reflect(element, value);
|
|
36
|
+
}
|
|
37
|
+
/** Returns one lease without overwriting a value subsequently authored by a consumer. */
|
|
38
|
+
return(element) {
|
|
39
|
+
const record = this.#records.get(element);
|
|
40
|
+
if (!record) return;
|
|
41
|
+
this.#records.delete(element);
|
|
42
|
+
const stillOwned = element.getAttribute(this.#attribute) === record.written;
|
|
43
|
+
if (stillOwned) this.#reflect(element, record.original);
|
|
44
|
+
}
|
|
45
|
+
/** Reflects only a real value transition, avoiding self-triggered mutation work. */
|
|
46
|
+
#reflect(element, value) {
|
|
47
|
+
if (element.getAttribute(this.#attribute) === value) return;
|
|
48
|
+
if (value === null) element.removeAttribute(this.#attribute);
|
|
49
|
+
else element.setAttribute(this.#attribute, value);
|
|
50
|
+
}
|
|
51
|
+
/** Returns every outstanding lease using the same ownership check as {@link return}. */
|
|
52
|
+
returnAll() {
|
|
53
|
+
for (const element of Array.from(this.#records.keys())) this.return(element);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/utils/before_cache_reset.ts
|
|
58
|
+
var BeforeCacheReset = class _BeforeCacheReset {
|
|
59
|
+
/** Every subscribed instance, iterated by the one shared document listener. */
|
|
60
|
+
static #subscribers = /* @__PURE__ */ new Set();
|
|
61
|
+
/** The shared listener; installed while at least one instance is subscribed. */
|
|
62
|
+
static #onBeforeCache = () => {
|
|
63
|
+
for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
|
|
64
|
+
};
|
|
65
|
+
#rewind;
|
|
66
|
+
/** @param rewind - the pass that returns this controller's state to its initial form. */
|
|
67
|
+
constructor(rewind) {
|
|
68
|
+
this.#rewind = rewind;
|
|
69
|
+
}
|
|
70
|
+
/** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
|
|
71
|
+
activate() {
|
|
72
|
+
const first = _BeforeCacheReset.#subscribers.size === 0;
|
|
73
|
+
_BeforeCacheReset.#subscribers.add(this);
|
|
74
|
+
if (first) {
|
|
75
|
+
document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
|
|
79
|
+
deactivate() {
|
|
80
|
+
_BeforeCacheReset.#subscribers.delete(this);
|
|
81
|
+
if (_BeforeCacheReset.#subscribers.size > 0) return;
|
|
82
|
+
document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
16
86
|
// src/utils/microtask_coalescer.ts
|
|
17
87
|
var MicrotaskCoalescer = class {
|
|
18
88
|
#run;
|
|
@@ -68,10 +138,12 @@ var RovingTabindex = class {
|
|
|
68
138
|
* "nothing is currently tabbable".
|
|
69
139
|
*
|
|
70
140
|
* @param index - Position of the item to make tabbable.
|
|
71
|
-
* @param options - Pass `{ focus: true }` to also move DOM focus to that item
|
|
141
|
+
* @param options - Pass `{ focus: true }` to also move DOM focus to that item,
|
|
142
|
+
* and `items` to reuse an event-scoped collection snapshot.
|
|
72
143
|
*/
|
|
73
|
-
setActive(index,
|
|
74
|
-
const
|
|
144
|
+
setActive(index, options = {}) {
|
|
145
|
+
const { focus = false } = options;
|
|
146
|
+
const items = options.items ?? this.#getItems();
|
|
75
147
|
items.forEach((item, i) => {
|
|
76
148
|
item.tabIndex = i === index ? 0 : -1;
|
|
77
149
|
});
|
|
@@ -79,167 +151,282 @@ var RovingTabindex = class {
|
|
|
79
151
|
}
|
|
80
152
|
};
|
|
81
153
|
|
|
154
|
+
// src/utils/tabindex_loan.ts
|
|
155
|
+
var TabindexLoan = class {
|
|
156
|
+
#value;
|
|
157
|
+
#lent = /* @__PURE__ */ new Set();
|
|
158
|
+
/** Returns live loans before Turbo can copy them into its page snapshot. */
|
|
159
|
+
#beforeCache = new BeforeCacheReset(() => this.returnAll());
|
|
160
|
+
/**
|
|
161
|
+
* @param value - the `tabindex` to lend. `"-1"` (the default) is
|
|
162
|
+
* programmatically focusable but not a Tab stop; `"0"` is a real Tab stop,
|
|
163
|
+
* which a scroll region with no focusable content of its own needs.
|
|
164
|
+
*/
|
|
165
|
+
constructor(value = "-1") {
|
|
166
|
+
this.#value = value;
|
|
167
|
+
}
|
|
168
|
+
/** Lends `element` the value; no-ops when it already carries a `tabindex`. */
|
|
169
|
+
lend(element) {
|
|
170
|
+
if (element.hasAttribute("tabindex")) return;
|
|
171
|
+
element.setAttribute("tabindex", this.#value);
|
|
172
|
+
this.#lent.add(element);
|
|
173
|
+
this.#beforeCache.activate();
|
|
174
|
+
}
|
|
175
|
+
/** Takes back every loan whose value is still the one that was lent. */
|
|
176
|
+
returnAll() {
|
|
177
|
+
for (const element of this.#lent) {
|
|
178
|
+
if (element.getAttribute("tabindex") === this.#value) element.removeAttribute("tabindex");
|
|
179
|
+
}
|
|
180
|
+
this.#lent.clear();
|
|
181
|
+
this.#beforeCache.deactivate();
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
82
185
|
// src/controllers/rating_controller.ts
|
|
83
186
|
var RatingController = class extends Controller {
|
|
84
187
|
static targets = ["symbol", "field"];
|
|
85
188
|
static values = {
|
|
86
189
|
value: { type: Number, default: 0 },
|
|
87
|
-
max: { type: Number, default: 5 },
|
|
88
190
|
clearable: { type: Boolean, default: true },
|
|
89
191
|
readonly: { type: Boolean, default: false }
|
|
90
192
|
};
|
|
91
193
|
static actions = ["endPreview", "onKeydown", "preview", "select"];
|
|
92
|
-
static events = ["change"];
|
|
194
|
+
static events = ["change", "reconcile"];
|
|
93
195
|
#roving = new RovingTabindex(() => this.symbolTargets);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
this.#apply(this.#clamp(this.valueValue), { focus: false });
|
|
105
|
-
});
|
|
196
|
+
#rootRole = new AttributeLease("role");
|
|
197
|
+
#symbolRole = new AttributeLease("role");
|
|
198
|
+
#symbolAriaHidden = new AttributeLease("aria-hidden");
|
|
199
|
+
#rootTabindex = new TabindexLoan();
|
|
200
|
+
#repaint = new MicrotaskCoalescer(() => this.#reconcileScale());
|
|
201
|
+
#beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
|
|
202
|
+
#connected = false;
|
|
203
|
+
#rescuedFocus = false;
|
|
204
|
+
/** Reflects declarative state without announcing an initial user change. */
|
|
106
205
|
connect() {
|
|
107
206
|
this.#repaint.activate();
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
this.#apply(this.#clamp(this.valueValue), { focus: false });
|
|
207
|
+
this.#beforeCache.activate();
|
|
208
|
+
this.#apply(this.#normalize(this.valueValue), { focus: false });
|
|
209
|
+
this.#connected = true;
|
|
113
210
|
}
|
|
114
|
-
/**
|
|
211
|
+
/** Drops a queued reconciliation and hands every borrowed attribute back. */
|
|
115
212
|
disconnect() {
|
|
213
|
+
this.#connected = false;
|
|
116
214
|
this.#repaint.cancel();
|
|
215
|
+
this.#beforeCache.deactivate();
|
|
216
|
+
this.#releaseReadonly();
|
|
217
|
+
}
|
|
218
|
+
/** Removes a runtime-added symbol's authored Tab stop before the batch repaint. */
|
|
219
|
+
symbolTargetConnected(symbol) {
|
|
220
|
+
if (this.#connected === false) return;
|
|
221
|
+
symbol.tabIndex = -1;
|
|
222
|
+
this.#repaint.schedule();
|
|
223
|
+
}
|
|
224
|
+
/** Releases readonly ownership and reconciles the remaining DOM-ordered scale. */
|
|
225
|
+
symbolTargetDisconnected(symbol) {
|
|
226
|
+
this.#symbolRole.return(symbol);
|
|
227
|
+
this.#symbolAriaHidden.return(symbol);
|
|
228
|
+
this.#repaint.schedule();
|
|
229
|
+
}
|
|
230
|
+
/** Reflects into a hidden field added or replaced after connection. */
|
|
231
|
+
fieldTargetConnected() {
|
|
232
|
+
this.#repaint.schedule();
|
|
233
|
+
}
|
|
234
|
+
/** Reconciles after a hidden field is removed or replaced. */
|
|
235
|
+
fieldTargetDisconnected() {
|
|
236
|
+
this.#repaint.schedule();
|
|
117
237
|
}
|
|
118
|
-
/** Repaints when application code
|
|
238
|
+
/** Repaints when application code or a Turbo morph changes `value`. */
|
|
119
239
|
valueValueChanged() {
|
|
120
240
|
this.#repaint.schedule();
|
|
121
241
|
}
|
|
122
|
-
/**
|
|
242
|
+
/** Repaints when application code changes whether value 0 is permitted. */
|
|
243
|
+
clearableValueChanged() {
|
|
244
|
+
this.#repaint.schedule();
|
|
245
|
+
}
|
|
246
|
+
/** Repaints when application code enters or leaves the readonly snapshot. */
|
|
247
|
+
readonlyValueChanged() {
|
|
248
|
+
this.#repaint.schedule();
|
|
249
|
+
}
|
|
250
|
+
/** Selects or clears the clicked symbol. Bound via `data-action` (click). */
|
|
123
251
|
select(event) {
|
|
124
252
|
if (this.readonlyValue) return;
|
|
125
|
-
const
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
253
|
+
const ordinal = this.#symbolOrdinal(event.currentTarget);
|
|
254
|
+
if (ordinal === null) return;
|
|
255
|
+
const current = this.#normalize(this.valueValue);
|
|
256
|
+
this.#commit(this.clearableValue && ordinal === current ? 0 : ordinal, {
|
|
257
|
+
focus: ordinal === current
|
|
258
|
+
});
|
|
131
259
|
}
|
|
132
|
-
/** Previews a fill range on hover
|
|
260
|
+
/** Previews a fill range on hover or focus without committing it. */
|
|
133
261
|
preview(event) {
|
|
134
262
|
if (this.readonlyValue) return;
|
|
135
|
-
this.#
|
|
263
|
+
const ordinal = this.#symbolOrdinal(event.currentTarget);
|
|
264
|
+
if (ordinal !== null) this.#setFillRange(ordinal);
|
|
136
265
|
}
|
|
137
|
-
/** Restores the fill range
|
|
266
|
+
/** Restores the fill range after hover or focus leaves a symbol. */
|
|
138
267
|
endPreview() {
|
|
139
268
|
if (this.readonlyValue) return;
|
|
140
|
-
this.#setFillRange(this.valueValue);
|
|
269
|
+
this.#setFillRange(this.#normalize(this.valueValue));
|
|
141
270
|
}
|
|
142
|
-
/** Arrow/Home/End/Space keyboard control, clamped
|
|
271
|
+
/** Arrow/Home/End/Space/Delete keyboard control, clamped without wrapping. */
|
|
143
272
|
onKeydown(event) {
|
|
144
|
-
if (event.defaultPrevented) return;
|
|
145
|
-
|
|
146
|
-
if (this.readonlyValue) return;
|
|
273
|
+
if (event.defaultPrevented || isReservedArrowChord(event) || this.readonlyValue) return;
|
|
274
|
+
const current = this.#normalize(this.valueValue);
|
|
147
275
|
let next = null;
|
|
148
276
|
const rtl = isRtl(this.element);
|
|
149
277
|
switch (event.key) {
|
|
150
278
|
case "ArrowRight":
|
|
151
279
|
case "ArrowUp":
|
|
152
|
-
next =
|
|
280
|
+
next = current + (event.key === "ArrowRight" && rtl ? -1 : 1);
|
|
153
281
|
break;
|
|
154
282
|
case "ArrowLeft":
|
|
155
283
|
case "ArrowDown":
|
|
156
|
-
next =
|
|
284
|
+
next = current - (event.key === "ArrowLeft" && rtl ? -1 : 1);
|
|
157
285
|
break;
|
|
158
286
|
case "Home":
|
|
159
287
|
next = this.#minValue;
|
|
160
288
|
break;
|
|
161
289
|
case "End":
|
|
162
|
-
next = this.
|
|
290
|
+
next = this.symbolTargets.length;
|
|
163
291
|
break;
|
|
164
292
|
case " ":
|
|
165
293
|
case "Enter":
|
|
166
|
-
next = this.#
|
|
294
|
+
next = this.#symbolOrdinal(event.currentTarget);
|
|
295
|
+
break;
|
|
296
|
+
case "Delete":
|
|
297
|
+
case "Backspace":
|
|
298
|
+
if (!this.clearableValue) return;
|
|
299
|
+
next = 0;
|
|
167
300
|
break;
|
|
168
301
|
default:
|
|
169
302
|
return;
|
|
170
303
|
}
|
|
304
|
+
if (next === null) return;
|
|
171
305
|
event.preventDefault();
|
|
172
|
-
this.#
|
|
306
|
+
this.#commit(next, { focus: true });
|
|
173
307
|
}
|
|
174
308
|
/**
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* initialization mirrors state without emitting an event.
|
|
309
|
+
* Repaints one settled target/Value mutation batch and reports only a value
|
|
310
|
+
* this controller had to normalize.
|
|
178
311
|
*
|
|
179
312
|
* @stimeoRenderRoot
|
|
180
313
|
*/
|
|
181
|
-
#
|
|
314
|
+
#reconcileScale() {
|
|
315
|
+
const requested = this.valueValue;
|
|
316
|
+
const value = this.#normalize(requested);
|
|
317
|
+
this.#apply(value, { focus: false });
|
|
318
|
+
if (!Object.is(value, requested)) {
|
|
319
|
+
this.dispatch("reconcile", { detail: { value } });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/** Applies one user operation and emits only when its committed value changes. */
|
|
323
|
+
#commit(raw, { focus }) {
|
|
324
|
+
const previous = this.#normalize(this.valueValue);
|
|
325
|
+
const value = this.#normalize(raw);
|
|
182
326
|
this.#apply(value, { focus });
|
|
183
|
-
this.dispatch("change", { detail: { value } });
|
|
327
|
+
if (value !== previous) this.dispatch("change", { detail: { value } });
|
|
184
328
|
}
|
|
185
|
-
/**
|
|
186
|
-
* Stores `value`, syncs `aria-checked` and the roving Tab stop, the hidden
|
|
187
|
-
* field, and the fill range — without dispatching `change`. Idempotent and
|
|
188
|
-
* safe on connect (and across Turbo morphing).
|
|
189
|
-
*/
|
|
329
|
+
/** Synchronizes value, ARIA, roving focus, form state, and the visual fill hook. */
|
|
190
330
|
#apply(value, { focus }) {
|
|
191
|
-
this.valueValue = value;
|
|
192
|
-
this.symbolTargets.forEach((symbol) => {
|
|
193
|
-
symbol.setAttribute(
|
|
194
|
-
"aria-checked",
|
|
195
|
-
value > 0 && this.#symbolValue(symbol) === value ? "true" : "false"
|
|
196
|
-
);
|
|
331
|
+
if (!Object.is(this.valueValue, value)) this.valueValue = value;
|
|
332
|
+
this.symbolTargets.forEach((symbol, index) => {
|
|
333
|
+
symbol.setAttribute("aria-checked", value > 0 && index + 1 === value ? "true" : "false");
|
|
197
334
|
});
|
|
198
|
-
this
|
|
335
|
+
if (this.readonlyValue) {
|
|
336
|
+
this.#applyReadonly();
|
|
337
|
+
} else {
|
|
338
|
+
const returning = this.#releaseReadonly();
|
|
339
|
+
this.#roving.setActive(value > 0 ? value - 1 : 0, { focus: focus || returning });
|
|
340
|
+
}
|
|
199
341
|
if (this.hasFieldTarget) this.fieldTarget.value = String(value);
|
|
200
342
|
this.#setFillRange(value);
|
|
201
343
|
}
|
|
202
|
-
/** Marks
|
|
344
|
+
/** Marks the first `range` symbols with the consumer-owned fill hook. */
|
|
203
345
|
#setFillRange(range) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
} else {
|
|
208
|
-
symbol.removeAttribute("data-rating-hover");
|
|
209
|
-
}
|
|
210
|
-
}
|
|
346
|
+
this.symbolTargets.forEach((symbol, index) => {
|
|
347
|
+
symbol.toggleAttribute("data-rating-hover", range > 0 && index < range);
|
|
348
|
+
});
|
|
211
349
|
}
|
|
212
|
-
/**
|
|
350
|
+
/**
|
|
351
|
+
* Temporarily turns the radiogroup into a non-interactive image snapshot.
|
|
352
|
+
*
|
|
353
|
+
* Each lease is returned before it is taken again, so a value the consumer
|
|
354
|
+
* wrote while readonly becomes the value the lease restores on release. The
|
|
355
|
+
* return is a no-op on an attribute still holding this controller's own
|
|
356
|
+
* write, which is the ordinary case.
|
|
357
|
+
*/
|
|
213
358
|
#applyReadonly() {
|
|
214
|
-
|
|
215
|
-
this.
|
|
216
|
-
this.element
|
|
359
|
+
this.#rescueFocus();
|
|
360
|
+
this.#rootRole.return(this.element);
|
|
361
|
+
this.#rootRole.write(this.element, "img");
|
|
217
362
|
for (const symbol of this.symbolTargets) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
symbol
|
|
363
|
+
this.#symbolRole.return(symbol);
|
|
364
|
+
this.#symbolRole.write(symbol, null);
|
|
365
|
+
this.#symbolAriaHidden.return(symbol);
|
|
366
|
+
this.#symbolAriaHidden.write(symbol, "true");
|
|
221
367
|
}
|
|
222
|
-
|
|
223
|
-
this.#setFillRange(value);
|
|
368
|
+
this.#roving.setActive(-1);
|
|
224
369
|
}
|
|
225
370
|
/**
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
371
|
+
* Hands every readonly borrowing back before Turbo clones the page.
|
|
372
|
+
*
|
|
373
|
+
* The snapshot is taken while the controller is still connected, so an
|
|
374
|
+
* element left as `role="img"` with hidden symbols is what a restored page
|
|
375
|
+
* connects against — and that markup would be read as the authored one,
|
|
376
|
+
* leaving no way back to the radiogroup. Rewinding first keeps the cached
|
|
377
|
+
* copy identical to what the consumer wrote.
|
|
230
378
|
*/
|
|
231
|
-
#
|
|
232
|
-
|
|
233
|
-
|
|
379
|
+
#rewindForCache() {
|
|
380
|
+
this.#releaseReadonly();
|
|
381
|
+
const value = this.#normalize(this.valueValue);
|
|
382
|
+
this.#roving.setActive(value > 0 ? value - 1 : 0);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Lands focus on the root before the symbols leave the accessibility tree.
|
|
386
|
+
*
|
|
387
|
+
* A symbol holding focus when readonly begins would keep it while losing its
|
|
388
|
+
* role and gaining `aria-hidden`, stranding the user on a node no longer in
|
|
389
|
+
* the tree. The root is the one element that survives the transition named:
|
|
390
|
+
* it carries the consumer's accessible name under `role="img"`.
|
|
391
|
+
*/
|
|
392
|
+
#rescueFocus() {
|
|
393
|
+
const active = document.activeElement;
|
|
394
|
+
if (!(active instanceof HTMLElement) || active === this.element) return;
|
|
395
|
+
if (!this.element.contains(active)) return;
|
|
396
|
+
this.#rootTabindex.lend(this.element);
|
|
397
|
+
this.element.focus();
|
|
398
|
+
this.#rescuedFocus = true;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Restores authored roles and visibility after leaving readonly mode.
|
|
402
|
+
*
|
|
403
|
+
* @returns whether focus is standing on the root because {@link #rescueFocus}
|
|
404
|
+
* put it there, and therefore belongs back on the Tab stop.
|
|
405
|
+
*/
|
|
406
|
+
#releaseReadonly() {
|
|
407
|
+
const returning = this.#rescuedFocus && document.activeElement === this.element;
|
|
408
|
+
this.#rescuedFocus = false;
|
|
409
|
+
this.#rootRole.return(this.element);
|
|
410
|
+
this.#symbolRole.returnAll();
|
|
411
|
+
this.#symbolAriaHidden.returnAll();
|
|
412
|
+
this.#rootTabindex.returnAll();
|
|
413
|
+
return returning;
|
|
414
|
+
}
|
|
415
|
+
/** Normalizes a raw value to an integer ordinal in the live DOM range. */
|
|
416
|
+
#normalize(raw) {
|
|
417
|
+
const maximum = this.symbolTargets.length;
|
|
418
|
+
const ordinal = Number.isFinite(raw) ? Math.round(raw) : this.#minValue;
|
|
419
|
+
return Math.min(maximum, Math.max(this.#minValue, ordinal));
|
|
234
420
|
}
|
|
235
421
|
/** Lowest selectable value: 0 when clearable, otherwise 1. */
|
|
236
422
|
get #minValue() {
|
|
237
423
|
return this.clearableValue ? 0 : 1;
|
|
238
424
|
}
|
|
239
|
-
/**
|
|
240
|
-
#
|
|
241
|
-
const
|
|
242
|
-
|
|
425
|
+
/** Returns a target's 1-based position, or null when the action host is invalid. */
|
|
426
|
+
#symbolOrdinal(target) {
|
|
427
|
+
const targets = this.symbolTargets;
|
|
428
|
+
const index = targets.indexOf(target);
|
|
429
|
+
return index < 0 ? null : index + 1;
|
|
243
430
|
}
|
|
244
431
|
};
|
|
245
432
|
|