stimeo-ui 0.15.0 → 0.16.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/README.md +120 -0
- package/dist/cable/index.js +63 -15
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.d.ts +30 -3
- package/dist/controllers/accordion_controller.js +98 -9
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +8 -7
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +3 -2
- package/dist/controllers/announcer_controller.js +8 -1
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.js +43 -2
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +1 -1
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.d.ts +10 -7
- package/dist/controllers/breadcrumb_controller.js +9 -8
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +4 -4
- package/dist/controllers/bulk_select_controller.js +7 -6
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +85 -23
- package/dist/controllers/calendar_controller.js +323 -123
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +67 -22
- package/dist/controllers/carousel_controller.js +263 -38
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +1 -1
- package/dist/controllers/character_counter_controller.js +40 -2
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.js +54 -10
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +28 -2
- package/dist/controllers/clipboard_controller.js +63 -5
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +25 -1
- package/dist/controllers/collapsible_controller.js +99 -14
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +29 -8
- package/dist/controllers/color_picker_controller.js +80 -34
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +10 -1
- package/dist/controllers/combobox_controller.js +106 -16
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +3 -3
- package/dist/controllers/command_palette_controller.js +35 -3
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.js +56 -11
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.js +3 -0
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +6 -0
- package/dist/controllers/context_menu_controller.js +32 -12
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +30 -1
- package/dist/controllers/countdown_controller.js +129 -26
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +74 -15
- package/dist/controllers/currency_input_controller.js +205 -58
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +63 -18
- package/dist/controllers/data_grid_controller.js +195 -29
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +34 -6
- package/dist/controllers/date_range_picker_controller.js +139 -27
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +10 -3
- package/dist/controllers/dialog_controller.js +35 -8
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.js +22 -4
- 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 +14 -1
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.js +1 -0
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +18 -10
- package/dist/controllers/drawer_controller.js +54 -19
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +9 -3
- package/dist/controllers/dropdown_controller.js +36 -9
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.js +34 -0
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.js +144 -51
- 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 +20 -6
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +26 -6
- package/dist/controllers/flash_controller.js +305 -46
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.js +1 -0
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +7 -5
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +19 -13
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.js +16 -2
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.js +19 -2
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +10 -2
- package/dist/controllers/hover_card_controller.js +40 -14
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +16 -3
- package/dist/controllers/idle_controller.js +90 -5
- 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 +65 -9
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.js +3 -0
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.js +11 -2
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +50 -8
- package/dist/controllers/listbox_controller.js +203 -45
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.js +0 -2
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +9 -3
- package/dist/controllers/menu_controller.js +45 -16
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +11 -0
- package/dist/controllers/menubar_controller.js +58 -24
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.js +9 -5
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +18 -3
- package/dist/controllers/multi_select_controller.js +278 -104
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +11 -0
- package/dist/controllers/navigation_menu_controller.js +48 -15
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.js +37 -8
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.js +9 -1
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +36 -8
- package/dist/controllers/number_input_controller.js +124 -21
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.js +14 -1
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +22 -7
- package/dist/controllers/otp_controller.js +198 -55
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +8 -12
- package/dist/controllers/overflow_indicator_controller.js +84 -13
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +26 -6
- package/dist/controllers/overflow_menu_controller.js +112 -41
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +19 -11
- package/dist/controllers/pagination_controller.js +74 -28
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +15 -1
- package/dist/controllers/password_reveal_controller.js +59 -2
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/persist_controller.js +30 -8
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.js +131 -52
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +9 -3
- package/dist/controllers/popover_controller.js +45 -11
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +1 -1
- package/dist/controllers/portal_controller.js +6 -2
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.js +16 -1
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.js +8 -4
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +6 -4
- package/dist/controllers/radio_group_controller.js +42 -17
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +49 -1
- package/dist/controllers/range_slider_controller.js +88 -42
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +14 -3
- package/dist/controllers/rating_controller.js +39 -15
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +24 -2
- package/dist/controllers/read_more_controller.js +69 -2
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/resizable_controller.d.ts +16 -2
- package/dist/controllers/resizable_controller.js +82 -22
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.js +5 -1
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.js +0 -1
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +3 -2
- package/dist/controllers/scrollspy_controller.js +6 -0
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +41 -14
- package/dist/controllers/separator_controller.js +66 -37
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +20 -3
- package/dist/controllers/sidebar_controller.js +77 -18
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.js +6 -1
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +45 -7
- package/dist/controllers/slider_controller.js +82 -47
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.js +11 -1
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.js +17 -2
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.js +10 -2
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +19 -17
- package/dist/controllers/step_indicator_controller.js +18 -17
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +34 -9
- package/dist/controllers/stepper_controller.js +101 -19
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +19 -0
- package/dist/controllers/stick_to_bottom_controller.js +75 -5
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +3 -2
- package/dist/controllers/submit_once_controller.js +16 -3
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +29 -7
- package/dist/controllers/switch_controller.js +101 -10
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +12 -0
- package/dist/controllers/tabs_controller.js +21 -2
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +15 -3
- package/dist/controllers/tags_input_controller.js +209 -59
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +20 -3
- package/dist/controllers/theme_controller.js +64 -14
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.js +23 -8
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +52 -15
- package/dist/controllers/toast_controller.js +337 -54
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +49 -7
- package/dist/controllers/toggle_group_controller.js +159 -23
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +32 -0
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +8 -0
- package/dist/controllers/tooltip_controller.js +39 -13
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.js +4 -0
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +39 -8
- package/dist/controllers/tree_view_controller.js +169 -16
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +4588 -1535
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +72 -6
- package/dist/inspector/cli.js +262 -51
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +309 -51
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +40 -40
- package/dist/inspector/manifest.json +525 -59
- package/dist/positioning/index.js +2 -0
- package/dist/positioning/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -52,6 +52,33 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
52
52
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
// src/utils/element_part.ts
|
|
56
|
+
function matchingPart(root, selector) {
|
|
57
|
+
return root.matches(selector) ? root : root.querySelector(selector);
|
|
58
|
+
}
|
|
59
|
+
function writeLabel(slot, text) {
|
|
60
|
+
if (slot.childElementCount === 0) {
|
|
61
|
+
slot.textContent = text;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
let first = null;
|
|
65
|
+
for (const node of Array.from(slot.childNodes)) {
|
|
66
|
+
if (node.nodeType !== Node.TEXT_NODE) continue;
|
|
67
|
+
if (first) node.remove();
|
|
68
|
+
else first = node;
|
|
69
|
+
}
|
|
70
|
+
if (first) first.nodeValue = text;
|
|
71
|
+
else slot.prepend(text);
|
|
72
|
+
}
|
|
73
|
+
function readLabel(slot) {
|
|
74
|
+
if (!slot) return "";
|
|
75
|
+
let text = "";
|
|
76
|
+
for (const node of Array.from(slot.childNodes)) {
|
|
77
|
+
if (node.nodeType === Node.TEXT_NODE) text += node.nodeValue ?? "";
|
|
78
|
+
}
|
|
79
|
+
return text.trim();
|
|
80
|
+
}
|
|
81
|
+
|
|
55
82
|
// src/utils/roving_tabindex.ts
|
|
56
83
|
var RovingTabindex = class {
|
|
57
84
|
/** Returns the current ordered item elements; called on every operation. */
|
|
@@ -99,7 +126,7 @@ var ChipRow = class {
|
|
|
99
126
|
constructor(options) {
|
|
100
127
|
this.#directionElement = options.directionElement;
|
|
101
128
|
this.#getItems = options.getItems;
|
|
102
|
-
this.#getButton = options.getButton ?? ((item) => item
|
|
129
|
+
this.#getButton = options.getButton ?? ((item) => matchingPart(item, "button"));
|
|
103
130
|
this.#onRemove = options.onRemove;
|
|
104
131
|
this.#focusAfterEnd = options.focusAfterEnd;
|
|
105
132
|
}
|
|
@@ -224,11 +251,19 @@ var ChipRow = class {
|
|
|
224
251
|
};
|
|
225
252
|
|
|
226
253
|
// src/utils/composition_tracker.ts
|
|
254
|
+
var COMPOSITION_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
255
|
+
"insertCompositionText",
|
|
256
|
+
"insertFromComposition",
|
|
257
|
+
"deleteCompositionText",
|
|
258
|
+
"deleteByComposition"
|
|
259
|
+
]);
|
|
227
260
|
var CompositionTracker = class {
|
|
228
261
|
#observedTargets = /* @__PURE__ */ new Set();
|
|
229
262
|
#activeTargets = /* @__PURE__ */ new Set();
|
|
230
263
|
#onStart;
|
|
231
264
|
#onEnd;
|
|
265
|
+
/** The field whose confirming `input` is still owed, while the window is open. */
|
|
266
|
+
#confirmedTarget = null;
|
|
232
267
|
constructor(options = {}) {
|
|
233
268
|
this.#onStart = options.onStart;
|
|
234
269
|
this.#onEnd = options.onEnd;
|
|
@@ -238,6 +273,7 @@ var CompositionTracker = class {
|
|
|
238
273
|
if (this.#observedTargets.has(target)) return;
|
|
239
274
|
target.addEventListener("compositionstart", this.#handleStart);
|
|
240
275
|
target.addEventListener("compositionend", this.#handleEnd);
|
|
276
|
+
target.addEventListener("keydown", this.#handleKeydown);
|
|
241
277
|
this.#observedTargets.add(target);
|
|
242
278
|
}
|
|
243
279
|
/** Stops tracking one target and clears any active composition it owned. */
|
|
@@ -245,31 +281,77 @@ var CompositionTracker = class {
|
|
|
245
281
|
if (!this.#observedTargets.delete(target)) return;
|
|
246
282
|
target.removeEventListener("compositionstart", this.#handleStart);
|
|
247
283
|
target.removeEventListener("compositionend", this.#handleEnd);
|
|
284
|
+
target.removeEventListener("keydown", this.#handleKeydown);
|
|
248
285
|
this.#activeTargets.delete(target);
|
|
286
|
+
if (this.#confirmedTarget === target) this.#confirmedTarget = null;
|
|
249
287
|
}
|
|
250
288
|
/** Releases every listener and clears state so reconnect starts cleanly. */
|
|
251
289
|
disconnect() {
|
|
252
290
|
for (const target of this.#observedTargets) {
|
|
253
291
|
target.removeEventListener("compositionstart", this.#handleStart);
|
|
254
292
|
target.removeEventListener("compositionend", this.#handleEnd);
|
|
293
|
+
target.removeEventListener("keydown", this.#handleKeydown);
|
|
255
294
|
}
|
|
256
295
|
this.#observedTargets.clear();
|
|
257
296
|
this.#activeTargets.clear();
|
|
297
|
+
this.#confirmedTarget = null;
|
|
258
298
|
}
|
|
259
299
|
/** True when lifecycle tracking or the current event reports composition. */
|
|
260
300
|
isComposing(event) {
|
|
261
301
|
return this.#activeTargets.size > 0 || event?.isComposing === true;
|
|
262
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* Whether `event` is the `input` echoing the composition just confirmed.
|
|
305
|
+
*
|
|
306
|
+
* Asking closes the window either way, so one confirmation is folded at most
|
|
307
|
+
* once and a consumer asks once per `input`.
|
|
308
|
+
*/
|
|
309
|
+
consumesConfirmedInput(event) {
|
|
310
|
+
const confirmed = this.#confirmedTarget;
|
|
311
|
+
this.#confirmedTarget = null;
|
|
312
|
+
if (confirmed === null || confirmed !== event.target) return false;
|
|
313
|
+
const inputType = event.inputType;
|
|
314
|
+
return !inputType || COMPOSITION_INPUT_TYPES.has(inputType);
|
|
315
|
+
}
|
|
263
316
|
#handleStart = (event) => {
|
|
317
|
+
this.#confirmedTarget = null;
|
|
264
318
|
if (event.currentTarget) this.#activeTargets.add(event.currentTarget);
|
|
265
319
|
this.#onStart?.(event);
|
|
266
320
|
};
|
|
267
321
|
#handleEnd = (event) => {
|
|
268
322
|
if (event.currentTarget) this.#activeTargets.delete(event.currentTarget);
|
|
323
|
+
this.#confirmedTarget = event.target;
|
|
269
324
|
this.#onEnd?.(event);
|
|
270
325
|
};
|
|
326
|
+
/** A key on an observed field opens an edit of its own, so no echo is owed. */
|
|
327
|
+
#handleKeydown = () => {
|
|
328
|
+
this.#confirmedTarget = null;
|
|
329
|
+
};
|
|
271
330
|
};
|
|
272
331
|
|
|
332
|
+
// src/utils/field_mirror.ts
|
|
333
|
+
function writeFields(container, values, { name, form = "" }) {
|
|
334
|
+
const current = [...container.children];
|
|
335
|
+
const submits = current.length === values.length && current.every(
|
|
336
|
+
(child, index) => child instanceof HTMLInputElement && child.type === "hidden" && child.value === values[index] && child.name === name && (child.getAttribute("form") ?? "") === form
|
|
337
|
+
);
|
|
338
|
+
if (submits) return false;
|
|
339
|
+
container.replaceChildren(
|
|
340
|
+
...values.map((value) => {
|
|
341
|
+
const input = document.createElement("input");
|
|
342
|
+
input.type = "hidden";
|
|
343
|
+
input.name = name;
|
|
344
|
+
input.value = value;
|
|
345
|
+
if (form !== "") input.setAttribute("form", form);
|
|
346
|
+
return input;
|
|
347
|
+
})
|
|
348
|
+
);
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
function commitField(target) {
|
|
352
|
+
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
353
|
+
}
|
|
354
|
+
|
|
273
355
|
// src/utils/microtask_coalescer.ts
|
|
274
356
|
var MicrotaskCoalescer = class {
|
|
275
357
|
#run;
|
|
@@ -315,6 +397,47 @@ function scrollOptionIntoView(list, option) {
|
|
|
315
397
|
}
|
|
316
398
|
}
|
|
317
399
|
|
|
400
|
+
// src/utils/state_regions.ts
|
|
401
|
+
var StateRegions = class {
|
|
402
|
+
#whenTrue;
|
|
403
|
+
#whenFalse;
|
|
404
|
+
#taken = /* @__PURE__ */ new WeakSet();
|
|
405
|
+
constructor(options) {
|
|
406
|
+
this.#whenTrue = options.whenTrue;
|
|
407
|
+
this.#whenFalse = options.whenFalse ?? null;
|
|
408
|
+
}
|
|
409
|
+
/** Shows the regions inside `host` that belong to `isTrue` and hides the others. */
|
|
410
|
+
reflect(host, isTrue) {
|
|
411
|
+
const shown = this.#inside(host, this.#whenTrue());
|
|
412
|
+
if (!this.#whenFalse) {
|
|
413
|
+
for (const region of shown) this.#write(region, !isTrue);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
const hiddenSide = this.#inside(host, this.#whenFalse());
|
|
417
|
+
if (shown.length === 0 || hiddenSide.length === 0) {
|
|
418
|
+
for (const region of shown) this.#give(region);
|
|
419
|
+
for (const region of hiddenSide) this.#give(region);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
for (const region of shown) this.#write(region, !isTrue);
|
|
423
|
+
for (const region of hiddenSide) this.#write(region, isTrue);
|
|
424
|
+
}
|
|
425
|
+
/** Writes `hidden` where it moves, noting which regions it takes out of view. */
|
|
426
|
+
#write(region, hidden) {
|
|
427
|
+
if (hidden) this.#taken.add(region);
|
|
428
|
+
else this.#taken.delete(region);
|
|
429
|
+
if (region.hidden !== hidden) region.hidden = hidden;
|
|
430
|
+
}
|
|
431
|
+
/** Returns a region this instance hid; one it never hid keeps what it carries. */
|
|
432
|
+
#give(region) {
|
|
433
|
+
if (this.#taken.delete(region)) region.hidden = false;
|
|
434
|
+
}
|
|
435
|
+
/** The declared regions that sit within `host`, which may be the host itself. */
|
|
436
|
+
#inside(host, regions) {
|
|
437
|
+
return regions.filter((region) => host.contains(region));
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
|
|
318
441
|
// src/utils/before_cache_reset.ts
|
|
319
442
|
var BeforeCacheReset = class _BeforeCacheReset {
|
|
320
443
|
/** Every subscribed instance, iterated by the one shared document listener. */
|
|
@@ -375,6 +498,85 @@ var TabindexLoan = class {
|
|
|
375
498
|
}
|
|
376
499
|
};
|
|
377
500
|
|
|
501
|
+
// src/utils/target_selector.ts
|
|
502
|
+
function targetSelector(identifier, name) {
|
|
503
|
+
return `[data-${identifier}-target~="${name}"]`;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// src/utils/template_row.ts
|
|
507
|
+
function cloneTemplateRoot(template) {
|
|
508
|
+
const root = template.content.firstElementChild;
|
|
509
|
+
return root instanceof HTMLElement ? root.cloneNode(true) : null;
|
|
510
|
+
}
|
|
511
|
+
var TemplateRow = class {
|
|
512
|
+
#options;
|
|
513
|
+
#warned = false;
|
|
514
|
+
constructor(options) {
|
|
515
|
+
this.#options = options;
|
|
516
|
+
}
|
|
517
|
+
/** The attribute selector for one declared part, in this controller's namespace. */
|
|
518
|
+
selector(name) {
|
|
519
|
+
return targetSelector(this.#options.identifier, name);
|
|
520
|
+
}
|
|
521
|
+
/** Re-arms the once-per-connection diagnostic. */
|
|
522
|
+
connect() {
|
|
523
|
+
this.#warned = false;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Names one missing part on the console, at most once per connection, and
|
|
527
|
+
* returns `null` so a caller can hand it straight back.
|
|
528
|
+
*/
|
|
529
|
+
report(missing) {
|
|
530
|
+
return this.#say(`lacks ${missing}`);
|
|
531
|
+
}
|
|
532
|
+
/** Writes one diagnostic per connection, saying what the template got wrong. */
|
|
533
|
+
#say(problem) {
|
|
534
|
+
if (this.#warned) return null;
|
|
535
|
+
this.#warned = true;
|
|
536
|
+
const { identifier, outcome, noun } = this.#options;
|
|
537
|
+
console.warn(`Stimeo UI: "${identifier}" ${outcome} because its ${noun} ${problem}.`);
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Clones the row and resolves its parts, or names what the template got wrong
|
|
542
|
+
* and returns `null`. `values` fills the button's authored `aria-label`.
|
|
543
|
+
*/
|
|
544
|
+
instantiate(template, values) {
|
|
545
|
+
const { root: rootName, required, optional, button: buttonName } = this.#options;
|
|
546
|
+
const root = cloneTemplateRoot(template);
|
|
547
|
+
if (!root?.matches(this.selector(rootName))) {
|
|
548
|
+
return this.report(`${this.#article(rootName)} "${rootName}" root`);
|
|
549
|
+
}
|
|
550
|
+
const count = template.content.children.length;
|
|
551
|
+
if (count !== 1) {
|
|
552
|
+
return this.#say(`holds ${count} elements, and the row has to be its only one`);
|
|
553
|
+
}
|
|
554
|
+
const slots = {};
|
|
555
|
+
for (const name2 of required) {
|
|
556
|
+
const part = this.#resolve(root, name2);
|
|
557
|
+
if (!part) return this.report(`${this.#article(name2)} "${name2}" target`);
|
|
558
|
+
slots[name2] = part;
|
|
559
|
+
}
|
|
560
|
+
for (const name2 of optional ?? []) slots[name2] = this.#resolve(root, name2);
|
|
561
|
+
const named = this.#resolve(root, buttonName);
|
|
562
|
+
if (!(named instanceof HTMLButtonElement)) {
|
|
563
|
+
return this.report(`${this.#article(buttonName)} "${buttonName}" target <button>`);
|
|
564
|
+
}
|
|
565
|
+
const button = named;
|
|
566
|
+
const name = button.getAttribute("aria-label")?.trim() ?? "";
|
|
567
|
+
if (name === "") return this.report(`a non-empty aria-label on its "${buttonName}" target`);
|
|
568
|
+
button.setAttribute("aria-label", fillTemplate(name, values));
|
|
569
|
+
return { root, slots, button };
|
|
570
|
+
}
|
|
571
|
+
/** The row may be its own part; otherwise the first descendant carrying the target. */
|
|
572
|
+
#resolve(root, name) {
|
|
573
|
+
return matchingPart(root, this.selector(name));
|
|
574
|
+
}
|
|
575
|
+
#article(name) {
|
|
576
|
+
return /^[aeiou]/i.test(name) ? "an" : "a";
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
|
|
378
580
|
// src/controllers/multi_select_controller.ts
|
|
379
581
|
var MultiSelectController = class extends Controller {
|
|
380
582
|
static targets = [
|
|
@@ -386,7 +588,8 @@ var MultiSelectController = class extends Controller {
|
|
|
386
588
|
"tagTemplate",
|
|
387
589
|
"label",
|
|
388
590
|
"remove",
|
|
389
|
-
"fields"
|
|
591
|
+
"fields",
|
|
592
|
+
"empty"
|
|
390
593
|
];
|
|
391
594
|
static values = {
|
|
392
595
|
max: { type: Number, default: 0 },
|
|
@@ -405,36 +608,37 @@ var MultiSelectController = class extends Controller {
|
|
|
405
608
|
#selectionValues = [];
|
|
406
609
|
/** Whether initial normalization finished, so a fields callback cannot mirror stale state. */
|
|
407
610
|
#connected = false;
|
|
611
|
+
/** Owns `hidden` on the regions declared for the empty-result state. */
|
|
612
|
+
#emptyRegion = new StateRegions({ whenTrue: () => this.emptyTargets });
|
|
408
613
|
/** Collapses one batch of target callbacks into a single final-DOM reconciliation. */
|
|
409
614
|
#reconcile = new MicrotaskCoalescer(() => this.#reconcileOptions());
|
|
410
|
-
/** Absorbs the browser's redundant final input after compositionend. */
|
|
411
|
-
#ignorePostCompositionInput = false;
|
|
412
615
|
/** Owns IME lifecycle state; confirmed text emits one filter result. */
|
|
413
616
|
#composition = new CompositionTracker({
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
onEnd: () =>
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
617
|
+
// Apply the confirmed query even in browsers that omit a final input event;
|
|
618
|
+
// where one follows, the tracker folds it so consumers receive one filter
|
|
619
|
+
// event for the confirmed text, not two.
|
|
620
|
+
onEnd: () => this.filter()
|
|
621
|
+
});
|
|
622
|
+
/** Builds one chip from the authored template and owns its diagnostic. */
|
|
623
|
+
#rows = new TemplateRow({
|
|
624
|
+
identifier: this.identifier,
|
|
625
|
+
root: "tag",
|
|
626
|
+
required: ["label"],
|
|
627
|
+
button: "remove",
|
|
628
|
+
outcome: "changed no selection",
|
|
629
|
+
noun: "chip template"
|
|
424
630
|
});
|
|
425
|
-
/** Whether this connection already reported its unusable chip template. */
|
|
426
|
-
#warnedTemplate = false;
|
|
427
631
|
/** Shared delegated interaction for the replaceable row of removable chips. */
|
|
428
632
|
#chipRow = new ChipRow({
|
|
429
633
|
directionElement: this.element,
|
|
430
634
|
getItems: () => this.tagTargets,
|
|
431
|
-
getButton: (tag) => tag.
|
|
635
|
+
getButton: (tag) => matchingPart(tag, `button${this.#rows.selector("remove")}`),
|
|
432
636
|
onRemove: (index) => this.#removeTagAt(index),
|
|
433
637
|
focusAfterEnd: () => this.#focusInput()
|
|
434
638
|
});
|
|
435
639
|
/** Starts closed, syncs chips for any pre-selected options, and listens out. */
|
|
436
640
|
connect() {
|
|
437
|
-
this.#
|
|
641
|
+
this.#rows.connect();
|
|
438
642
|
this.#normalizeSelection([]);
|
|
439
643
|
this.close();
|
|
440
644
|
if (this.hasTagsTarget) {
|
|
@@ -454,14 +658,14 @@ var MultiSelectController = class extends Controller {
|
|
|
454
658
|
*/
|
|
455
659
|
#rebuildTags() {
|
|
456
660
|
if (!this.hasTagsTarget || !this.hasTagTemplateTarget) return;
|
|
457
|
-
const
|
|
661
|
+
const chips = [];
|
|
458
662
|
for (const option of this.#selectedOptions) {
|
|
459
|
-
const
|
|
460
|
-
if (!
|
|
461
|
-
|
|
663
|
+
const chip = this.#buildTag(option);
|
|
664
|
+
if (!chip) return;
|
|
665
|
+
chips.push(chip);
|
|
462
666
|
}
|
|
463
667
|
for (const tag of this.tagTargets) tag.remove();
|
|
464
|
-
this.tagsTarget.append(...
|
|
668
|
+
this.tagsTarget.append(...chips);
|
|
465
669
|
this.#chipRow.ensureTabStop();
|
|
466
670
|
}
|
|
467
671
|
/** Tears down document and chip listeners on disconnect (Turbo included). */
|
|
@@ -469,7 +673,6 @@ var MultiSelectController = class extends Controller {
|
|
|
469
673
|
this.#connected = false;
|
|
470
674
|
this.#reconcile.cancel();
|
|
471
675
|
this.#composition.disconnect();
|
|
472
|
-
this.#ignorePostCompositionInput = false;
|
|
473
676
|
this.#chipRow.disconnect();
|
|
474
677
|
document.removeEventListener("click", this.#onOutsideClick, true);
|
|
475
678
|
this.#releaseTabindex();
|
|
@@ -483,6 +686,10 @@ var MultiSelectController = class extends Controller {
|
|
|
483
686
|
option.removeAttribute("data-active");
|
|
484
687
|
this.#scheduleOptionReconcile();
|
|
485
688
|
}
|
|
689
|
+
/** Settles a region inserted after connect on the side of the current state. */
|
|
690
|
+
emptyTargetConnected() {
|
|
691
|
+
if (this.#connected) this.#reflectEmpty();
|
|
692
|
+
}
|
|
486
693
|
/** Rebinds delegated chip interaction when Turbo replaces the tags container. */
|
|
487
694
|
tagsTargetConnected(tags) {
|
|
488
695
|
this.#chipRow.connect(tags);
|
|
@@ -524,6 +731,8 @@ var MultiSelectController = class extends Controller {
|
|
|
524
731
|
* for an unrelated option would drop focus from a chip's remove button to
|
|
525
732
|
* `<body>`, losing the keyboard user's place for something that did not concern
|
|
526
733
|
* them.
|
|
734
|
+
*
|
|
735
|
+
* @stimeoRenderRoot
|
|
527
736
|
*/
|
|
528
737
|
#reconcileOptions() {
|
|
529
738
|
const visible = this.#visibleOptions;
|
|
@@ -535,7 +744,7 @@ var MultiSelectController = class extends Controller {
|
|
|
535
744
|
const previousLabels = new Map(
|
|
536
745
|
this.tagTargets.map((tag) => [
|
|
537
746
|
tag.dataset.value ?? "",
|
|
538
|
-
(tag.
|
|
747
|
+
readLabel(matchingPart(tag, this.#rows.selector("label")))
|
|
539
748
|
])
|
|
540
749
|
);
|
|
541
750
|
const priority = [
|
|
@@ -613,15 +822,11 @@ var MultiSelectController = class extends Controller {
|
|
|
613
822
|
/** Removes composition listeners when the active input is replaced or removed. */
|
|
614
823
|
inputTargetDisconnected(input) {
|
|
615
824
|
this.#composition.unobserve(input);
|
|
616
|
-
this.#ignorePostCompositionInput = false;
|
|
617
825
|
}
|
|
618
826
|
/** Filters confirmed input text, opens, and re-seeds the active option. */
|
|
619
827
|
filter(event) {
|
|
620
828
|
if (!this.hasInputTarget) return;
|
|
621
|
-
if (event && this.#
|
|
622
|
-
this.#ignorePostCompositionInput = false;
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
829
|
+
if (event && this.#composition.consumesConfirmedInput(event)) return;
|
|
625
830
|
if (this.#composition.isComposing(event)) return;
|
|
626
831
|
const query = this.inputTarget.value.trim().toLowerCase();
|
|
627
832
|
for (const option of this.optionTargets) {
|
|
@@ -639,7 +844,8 @@ var MultiSelectController = class extends Controller {
|
|
|
639
844
|
*
|
|
640
845
|
* Needs the input, which owns `aria-expanded` and `aria-activedescendant`: a
|
|
641
846
|
* list shown without one is a popup no assistive technology is told about. So
|
|
642
|
-
* opening is skipped entirely, where {@link close
|
|
847
|
+
* opening is skipped entirely, where {@link MultiSelectController.close | close}
|
|
848
|
+
* still closes.
|
|
643
849
|
*/
|
|
644
850
|
open() {
|
|
645
851
|
if (!this.hasListTarget || !this.hasInputTarget) {
|
|
@@ -811,7 +1017,7 @@ var MultiSelectController = class extends Controller {
|
|
|
811
1017
|
option.setAttribute("aria-selected", "true");
|
|
812
1018
|
}
|
|
813
1019
|
this.#refreshRoving();
|
|
814
|
-
this.#syncFields();
|
|
1020
|
+
this.#syncFields(true);
|
|
815
1021
|
const values = this.#values;
|
|
816
1022
|
this.#selectionValues = values;
|
|
817
1023
|
this.#announceTransition(
|
|
@@ -836,79 +1042,47 @@ var MultiSelectController = class extends Controller {
|
|
|
836
1042
|
for (const tag of this.tagTargets) {
|
|
837
1043
|
const option = options.get(tag.dataset.value ?? "");
|
|
838
1044
|
const text = this.#optionLabel(option);
|
|
839
|
-
const label = tag.
|
|
840
|
-
const button = tag.
|
|
841
|
-
'button[data-stimeo--multi-select-target~="remove"]'
|
|
842
|
-
);
|
|
1045
|
+
const label = matchingPart(tag, this.#rows.selector("label"));
|
|
1046
|
+
const button = matchingPart(tag, `button${this.#rows.selector("remove")}`);
|
|
843
1047
|
const name = this.#removeName(text, this.#optionValue(option));
|
|
844
1048
|
if (!label || !button || !name) continue;
|
|
845
|
-
if (label
|
|
1049
|
+
if (readLabel(label) !== text) writeLabel(label, text);
|
|
846
1050
|
if (button.getAttribute("aria-label") !== name) button.setAttribute("aria-label", name);
|
|
847
1051
|
}
|
|
848
1052
|
}
|
|
849
1053
|
/** Builds one chip from the template for `option`. */
|
|
850
1054
|
#appendTag(option) {
|
|
851
1055
|
if (!this.hasTagsTarget) {
|
|
852
|
-
this.#
|
|
1056
|
+
this.#rows.report('a "tags" target to append the chip to');
|
|
853
1057
|
return false;
|
|
854
1058
|
}
|
|
855
|
-
const
|
|
856
|
-
if (!
|
|
857
|
-
this.tagsTarget.appendChild(
|
|
1059
|
+
const chip = this.#buildTag(option);
|
|
1060
|
+
if (!chip) return false;
|
|
1061
|
+
this.tagsTarget.appendChild(chip);
|
|
858
1062
|
return true;
|
|
859
1063
|
}
|
|
860
1064
|
/**
|
|
861
1065
|
* Builds one fully named chip without mutating the live tag row, or `null`
|
|
862
|
-
* when the authored template cannot produce one
|
|
863
|
-
*
|
|
864
|
-
*
|
|
1066
|
+
* when the authored template cannot produce one — in which case the selection
|
|
1067
|
+
* stays untouched and the row names the missing part once per connection.
|
|
1068
|
+
* Both callers establish the template first: a field authored without
|
|
1069
|
+
* `tagTemplate` renders no chips at all — a supported configuration — and
|
|
1070
|
+
* never reaches here.
|
|
865
1071
|
*/
|
|
866
1072
|
#buildTag(option) {
|
|
867
|
-
const fragment = this.tagTemplateTarget.content.cloneNode(true);
|
|
868
|
-
const tag = fragment.querySelector('[data-stimeo--multi-select-target~="tag"]');
|
|
869
|
-
const label = fragment.querySelector(
|
|
870
|
-
'[data-stimeo--multi-select-target~="label"]'
|
|
871
|
-
);
|
|
872
|
-
const button = fragment.querySelector(
|
|
873
|
-
'button[data-stimeo--multi-select-target~="remove"]'
|
|
874
|
-
);
|
|
875
1073
|
const text = this.#optionLabel(option);
|
|
876
1074
|
const value = this.#optionValue(option);
|
|
877
|
-
const
|
|
878
|
-
if (!
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
}
|
|
884
|
-
tag.dataset.value = value;
|
|
885
|
-
label.textContent = text;
|
|
886
|
-
button.setAttribute("aria-label", fillTemplate(removeName, { label: text, value }));
|
|
887
|
-
button.tabIndex = -1;
|
|
888
|
-
return fragment;
|
|
889
|
-
}
|
|
890
|
-
/**
|
|
891
|
-
* Reports an unusable chip template to the author, once per connection.
|
|
892
|
-
*
|
|
893
|
-
* The selection itself stays untouched — no `aria-selected`, chip, hidden
|
|
894
|
-
* field, announcement, or event moves. Without this line the only symptom is
|
|
895
|
-
* a listbox whose options refuse to select, and the two causes the Inspector
|
|
896
|
-
* cannot see statically (a name that renders empty from a missing
|
|
897
|
-
* translation, a server-rendered template) would have no diagnostic anywhere.
|
|
898
|
-
*/
|
|
899
|
-
#warnTemplate(missing) {
|
|
900
|
-
if (!this.#warnedTemplate) {
|
|
901
|
-
this.#warnedTemplate = true;
|
|
902
|
-
console.warn(
|
|
903
|
-
`Stimeo UI: "${this.identifier}" changed no selection because its chip template lacks ${missing}.`
|
|
904
|
-
);
|
|
905
|
-
}
|
|
906
|
-
return null;
|
|
1075
|
+
const row = this.#rows.instantiate(this.tagTemplateTarget, { label: text, value });
|
|
1076
|
+
if (!row) return null;
|
|
1077
|
+
row.root.dataset.value = value;
|
|
1078
|
+
writeLabel(row.slots.label, text);
|
|
1079
|
+
row.button.tabIndex = -1;
|
|
1080
|
+
return row.root;
|
|
907
1081
|
}
|
|
908
1082
|
/** Expands the current template's localized remove-button name. */
|
|
909
1083
|
#removeName(label, value) {
|
|
910
1084
|
if (!this.hasTagTemplateTarget) return null;
|
|
911
|
-
const template = this.tagTemplateTarget.content.querySelector(
|
|
1085
|
+
const template = this.tagTemplateTarget.content.querySelector(`button${this.#rows.selector("remove")}`)?.getAttribute("aria-label")?.trim();
|
|
912
1086
|
return template ? fillTemplate(template, { label, value }) : null;
|
|
913
1087
|
}
|
|
914
1088
|
/** Removes the chip mirroring `option`, if present. */
|
|
@@ -926,7 +1100,7 @@ var MultiSelectController = class extends Controller {
|
|
|
926
1100
|
if (option) option.setAttribute("aria-selected", "false");
|
|
927
1101
|
tag.remove();
|
|
928
1102
|
this.#refreshRoving();
|
|
929
|
-
this.#syncFields();
|
|
1103
|
+
this.#syncFields(true);
|
|
930
1104
|
const values = this.#values;
|
|
931
1105
|
this.#selectionValues = values;
|
|
932
1106
|
this.#announceTransition(
|
|
@@ -947,9 +1121,9 @@ var MultiSelectController = class extends Controller {
|
|
|
947
1121
|
* `aria-activedescendant` (the attribute is removed, not emptied, when null).
|
|
948
1122
|
*
|
|
949
1123
|
* The state half runs even with no input, so a `close()` that cannot touch ARIA
|
|
950
|
-
* still clears it: {@link open} seeds an active
|
|
951
|
-
* so a stale one makes the next open skip the
|
|
952
|
-
* gets no `aria-activedescendant` at all.
|
|
1124
|
+
* still clears it: {@link MultiSelectController.open | open} seeds an active
|
|
1125
|
+
* option only when there is none, so a stale one makes the next open skip the
|
|
1126
|
+
* seeding and a replacement input gets no `aria-activedescendant` at all.
|
|
953
1127
|
*/
|
|
954
1128
|
#setActive(option) {
|
|
955
1129
|
const activeId = option ? ensureId(option, "stimeo-ms-opt") : null;
|
|
@@ -977,12 +1151,14 @@ var MultiSelectController = class extends Controller {
|
|
|
977
1151
|
this.#setActive(active);
|
|
978
1152
|
}
|
|
979
1153
|
}
|
|
980
|
-
/**
|
|
1154
|
+
/**
|
|
1155
|
+
* Reflects whether the open list currently has no visible option targets, on
|
|
1156
|
+
* the root's state attribute and on the regions declared for that state.
|
|
1157
|
+
*/
|
|
981
1158
|
#reflectEmpty() {
|
|
982
|
-
this.
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
);
|
|
1159
|
+
const empty = !this.#isClosed && this.#visibleOptions.length === 0;
|
|
1160
|
+
this.element.toggleAttribute(`data-${this.identifier}-empty`, empty);
|
|
1161
|
+
this.#emptyRegion.reflect(this.element, empty);
|
|
986
1162
|
}
|
|
987
1163
|
/**
|
|
988
1164
|
* Mirrors the selected values into named hidden inputs under the `fields`
|
|
@@ -991,24 +1167,22 @@ var MultiSelectController = class extends Controller {
|
|
|
991
1167
|
* `form` value is set, each input gets a matching `form` attribute so the picker
|
|
992
1168
|
* can submit with a `<form>` it lives outside of.
|
|
993
1169
|
*/
|
|
994
|
-
#syncFields() {
|
|
1170
|
+
#syncFields(notify = false) {
|
|
995
1171
|
if (!this.hasFieldsTarget) return;
|
|
996
|
-
this.
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
input.name = this.nameValue;
|
|
1001
|
-
input.value = value;
|
|
1002
|
-
if (this.hasFormValue && this.formValue !== "") input.setAttribute("form", this.formValue);
|
|
1003
|
-
return input;
|
|
1004
|
-
})
|
|
1005
|
-
);
|
|
1172
|
+
const options = { name: this.nameValue, form: this.formValue };
|
|
1173
|
+
if (writeFields(this.fieldsTarget, this.#values, options) && notify) {
|
|
1174
|
+
commitField(this.fieldsTarget);
|
|
1175
|
+
}
|
|
1006
1176
|
}
|
|
1007
1177
|
/** Keeps exactly one chip remove button tabbable after the set changes. */
|
|
1008
1178
|
#refreshRoving() {
|
|
1009
1179
|
this.#chipRow.ensureTabStop();
|
|
1010
1180
|
}
|
|
1011
|
-
/**
|
|
1181
|
+
/**
|
|
1182
|
+
* Sends one localized selection transition through the page's shared announcer.
|
|
1183
|
+
*
|
|
1184
|
+
* @stimeoRuntimeOnly The texts word the one announcement of this change.
|
|
1185
|
+
*/
|
|
1012
1186
|
#announceTransition(selected, label, value, count) {
|
|
1013
1187
|
const template = selected ? this.announceTextValue : this.announceRemovedTextValue;
|
|
1014
1188
|
announce(fillTemplate(template, { label, value, count }));
|