stimeo-ui 0.14.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 +212 -0
- package/README.md +120 -0
- package/dist/cable/index.js +123 -29
- 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 +96 -62
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.js +83 -7
- 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 +38 -11
- 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 +101 -23
- package/dist/controllers/calendar_controller.js +340 -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 +81 -12
- 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 +85 -17
- 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/count_up_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 +77 -16
- package/dist/controllers/currency_input_controller.js +221 -67
- 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 +41 -6
- package/dist/controllers/date_range_picker_controller.js +151 -30
- 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 +46 -13
- 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 +432 -71
- 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 +45 -8
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.js +82 -25
- 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/local_time_controller.js +10 -5
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.d.ts +3 -3
- package/dist/controllers/masonry_controller.js +31 -15
- 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 +42 -5
- 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 +115 -21
- 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 +141 -44
- 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 +100 -7
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/reading_progress_controller.js +65 -19
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.js +10 -5
- package/dist/controllers/relative_time_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 +75 -27
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.js +37 -16
- package/dist/controllers/scroll_restore_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.js +49 -30
- 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 +71 -26
- 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 +60 -26
- 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 +104 -8
- 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 +45 -9
- 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/textarea_autosize_controller.js +29 -3
- package/dist/controllers/textarea_autosize_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 +55 -15
- package/dist/controllers/toast_controller.js +451 -105
- 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 +5002 -1911
- 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 +529 -61
- package/dist/positioning/index.js +2 -0
- package/dist/positioning/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -13,6 +13,16 @@ 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/field_mirror.ts
|
|
17
|
+
function writeField(field, value) {
|
|
18
|
+
if (field.value === value) return false;
|
|
19
|
+
field.value = value;
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
function commitField(target) {
|
|
23
|
+
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
24
|
+
}
|
|
25
|
+
|
|
16
26
|
// src/utils/focus_candidate.ts
|
|
17
27
|
function inheritsFieldsetDisabled(control) {
|
|
18
28
|
let fieldset = control.closest("fieldset[disabled]");
|
|
@@ -48,6 +58,39 @@ function isInteractiveHost(element) {
|
|
|
48
58
|
return false;
|
|
49
59
|
}
|
|
50
60
|
|
|
61
|
+
// src/utils/microtask_coalescer.ts
|
|
62
|
+
var MicrotaskCoalescer = class {
|
|
63
|
+
#run;
|
|
64
|
+
#queued = false;
|
|
65
|
+
#active = false;
|
|
66
|
+
#generation = 0;
|
|
67
|
+
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
68
|
+
constructor(run) {
|
|
69
|
+
this.#run = run;
|
|
70
|
+
}
|
|
71
|
+
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
72
|
+
activate() {
|
|
73
|
+
this.#active = true;
|
|
74
|
+
}
|
|
75
|
+
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
76
|
+
cancel() {
|
|
77
|
+
this.#active = false;
|
|
78
|
+
this.#queued = false;
|
|
79
|
+
this.#generation += 1;
|
|
80
|
+
}
|
|
81
|
+
/** Requests one pass after the batch settles. Idempotent; inert outside the window. */
|
|
82
|
+
schedule() {
|
|
83
|
+
if (!this.#active || this.#queued) return;
|
|
84
|
+
this.#queued = true;
|
|
85
|
+
const generation = this.#generation;
|
|
86
|
+
queueMicrotask(() => {
|
|
87
|
+
if (generation !== this.#generation || !this.#queued || !this.#active) return;
|
|
88
|
+
this.#queued = false;
|
|
89
|
+
this.#run();
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
51
94
|
// src/utils/roving_tabindex.ts
|
|
52
95
|
var RovingTabindex = class {
|
|
53
96
|
/** Returns the current ordered item elements; called on every operation. */
|
|
@@ -195,14 +238,28 @@ function findTypeaheadMatch(items, from, query, label = (item) => typeaheadLabel
|
|
|
195
238
|
}
|
|
196
239
|
|
|
197
240
|
// src/controllers/tree_view_controller.ts
|
|
241
|
+
var OBSERVED_ATTRIBUTES = ["aria-selected", "data-value"];
|
|
242
|
+
function setAttributeIfChanged(element, name, value) {
|
|
243
|
+
if (element.getAttribute(name) !== value) element.setAttribute(name, value);
|
|
244
|
+
}
|
|
198
245
|
var NESTED_INTERACTIVE = INTERACTIVE_HOST_SELECTOR;
|
|
199
246
|
var TreeViewController = class extends Controller {
|
|
200
|
-
static targets = ["item", "group"];
|
|
247
|
+
static targets = ["item", "group", "field"];
|
|
201
248
|
static actions = ["onClick", "onKeydown", "toggle"];
|
|
202
|
-
static events = ["select", "toggle"];
|
|
249
|
+
static events = ["select", "toggle", "reconcile"];
|
|
203
250
|
#roving = new RovingTabindex(() => this.itemTargets);
|
|
204
251
|
#typeahead = new Typeahead();
|
|
205
252
|
#timers = new SafeTimeout();
|
|
253
|
+
/**
|
|
254
|
+
* Collapses the item and field callbacks and observed item writes of one DOM
|
|
255
|
+
* mutation into a single selection pass; refused before `connect()` and after
|
|
256
|
+
* `disconnect()`.
|
|
257
|
+
*/
|
|
258
|
+
#reconcile = new MicrotaskCoalescer(() => this.#reconcileSelection());
|
|
259
|
+
/** Watches the item attributes a page can rewrite in place; set while connected. */
|
|
260
|
+
#observer = null;
|
|
261
|
+
/** The selection last settled: on connect, by the user, or by a reported pass. */
|
|
262
|
+
#settled = { item: null, value: "" };
|
|
206
263
|
#connected = false;
|
|
207
264
|
/**
|
|
208
265
|
* Item targets in DOM order as of the last connect / target change. A removed
|
|
@@ -247,9 +304,10 @@ var TreeViewController = class extends Controller {
|
|
|
247
304
|
});
|
|
248
305
|
};
|
|
249
306
|
/**
|
|
250
|
-
* Reconciles authored expansion state
|
|
251
|
-
* Idempotent, so a Turbo cache
|
|
252
|
-
* the same listener reference is a
|
|
307
|
+
* Reconciles authored expansion state, establishes the single tab stop, and
|
|
308
|
+
* settles the selection without reporting it. Idempotent, so a Turbo cache
|
|
309
|
+
* restore / morph re-runs it safely (re-adding the same listener reference is a
|
|
310
|
+
* no-op per the DOM spec).
|
|
253
311
|
*/
|
|
254
312
|
connect() {
|
|
255
313
|
this.element.addEventListener("focusin", this.#onFocusIn);
|
|
@@ -258,14 +316,28 @@ var TreeViewController = class extends Controller {
|
|
|
258
316
|
this.#reconcileExpansion();
|
|
259
317
|
this.#normalizeSelection();
|
|
260
318
|
this.#normalizeTabStop();
|
|
319
|
+
this.#mirrorField(false);
|
|
261
320
|
this.#order = [...this.itemTargets];
|
|
321
|
+
this.#settled = this.#selection();
|
|
262
322
|
this.#connected = true;
|
|
323
|
+
this.#reconcile.activate();
|
|
324
|
+
this.#observeItems();
|
|
325
|
+
}
|
|
326
|
+
/** Fills a form field inserted or replaced at runtime with the current selection. */
|
|
327
|
+
fieldTargetConnected() {
|
|
328
|
+
this.#reconcile.schedule();
|
|
263
329
|
}
|
|
264
|
-
/**
|
|
330
|
+
/**
|
|
331
|
+
* Detaches the focus trackers and the item observer, drops a queued selection
|
|
332
|
+
* pass, the typeahead buffer and its timer.
|
|
333
|
+
*/
|
|
265
334
|
disconnect() {
|
|
266
335
|
this.element.removeEventListener("focusin", this.#onFocusIn);
|
|
267
336
|
this.element.removeEventListener("focusout", this.#onFocusOut);
|
|
268
337
|
this.#connected = false;
|
|
338
|
+
this.#reconcile.cancel();
|
|
339
|
+
this.#observer?.disconnect();
|
|
340
|
+
this.#observer = null;
|
|
269
341
|
this.#typeahead.reset();
|
|
270
342
|
this.#timers.clearAll();
|
|
271
343
|
this.#order = [];
|
|
@@ -284,14 +356,15 @@ var TreeViewController = class extends Controller {
|
|
|
284
356
|
/**
|
|
285
357
|
* Re-normalizes the tab stop when a `treeitem` is added at runtime, so an
|
|
286
358
|
* appended item carrying `tabindex="0"` cannot turn the tree into two Tab
|
|
287
|
-
* stops
|
|
288
|
-
*
|
|
359
|
+
* stops, and settles the selection once the batch is in. Skipped before
|
|
360
|
+
* `connect()`: Stimulus registers the initial targets first, and `connect()`
|
|
361
|
+
* owns the initial tab-stop policy.
|
|
289
362
|
*/
|
|
290
363
|
itemTargetConnected() {
|
|
291
364
|
if (!this.#connected) return;
|
|
292
|
-
this.#normalizeSelection();
|
|
293
365
|
this.#normalizeTabStop();
|
|
294
366
|
this.#trackOrder();
|
|
367
|
+
this.#reconcile.schedule();
|
|
295
368
|
}
|
|
296
369
|
/**
|
|
297
370
|
* Brings authored `aria-selected` to the shape the APG requires, without
|
|
@@ -299,18 +372,91 @@ var TreeViewController = class extends Controller {
|
|
|
299
372
|
*
|
|
300
373
|
* Every item gets an explicit value — an absent `aria-selected` means "not
|
|
301
374
|
* selectable" in ARIA, so a forgotten attribute hides a selectable row — and a
|
|
302
|
-
* single-select tree keeps at most one `true`, first in DOM order
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
375
|
+
* single-select tree keeps at most one `true`, first in DOM order: an item the
|
|
376
|
+
* page inserts ahead of the selection, already selected, takes it over.
|
|
377
|
+
* `connect()` reconciles authored expansion the same way. The scan is the
|
|
378
|
+
* `item` target set: a `role="treeitem"` without the target is outside the
|
|
379
|
+
* contract and is neither counted nor written.
|
|
306
380
|
*/
|
|
307
381
|
#normalizeSelection() {
|
|
308
382
|
const items = this.itemTargets;
|
|
309
383
|
const selected = items.find((item) => item.getAttribute("aria-selected") === "true");
|
|
310
384
|
for (const item of items) {
|
|
311
|
-
item
|
|
385
|
+
setAttributeIfChanged(item, "aria-selected", item === selected ? "true" : "false");
|
|
312
386
|
}
|
|
313
387
|
}
|
|
388
|
+
/**
|
|
389
|
+
* Mirrors the selected item's `data-value` into the optional form field.
|
|
390
|
+
* Nothing selected, and an item that carries no value, both submit `""`.
|
|
391
|
+
*/
|
|
392
|
+
#mirrorField(notify) {
|
|
393
|
+
if (!this.hasFieldTarget) return;
|
|
394
|
+
const moved = writeField(this.fieldTarget, this.#selection().value);
|
|
395
|
+
if (moved && notify) commitField(this.fieldTarget);
|
|
396
|
+
}
|
|
397
|
+
/** The selected item target, if any, and the value its field submits. */
|
|
398
|
+
#selection() {
|
|
399
|
+
const item = this.itemTargets.find((candidate) => candidate.getAttribute("aria-selected") === "true") ?? null;
|
|
400
|
+
return { item, value: item?.dataset.value ?? "" };
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* The pass the page's changes run: the selection is normalized and mirrored,
|
|
404
|
+
* then reported if it moved. The report comes last, so items a subscriber
|
|
405
|
+
* rewrites are the next pass's to settle.
|
|
406
|
+
*/
|
|
407
|
+
#reconcileSelection() {
|
|
408
|
+
this.#normalizeSelection();
|
|
409
|
+
this.#dropOwnRecords();
|
|
410
|
+
this.#mirrorField(false);
|
|
411
|
+
this.#reportMove();
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Reports the selection as `reconcile` when its item or value differs from the
|
|
415
|
+
* selection last settled. The settled selection is replaced before dispatching,
|
|
416
|
+
* so a move a subscriber makes is measured against what it was told.
|
|
417
|
+
*/
|
|
418
|
+
#reportMove() {
|
|
419
|
+
const selection = this.#selection();
|
|
420
|
+
if (selection.item === this.#settled.item && selection.value === this.#settled.value) return;
|
|
421
|
+
this.#settled = selection;
|
|
422
|
+
this.dispatch("reconcile", { detail: { item: selection.item } });
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Watches the items' `aria-selected` and `data-value`. The tree's own writes to
|
|
426
|
+
* them are dropped from the queue as they happen, so each record the callback
|
|
427
|
+
* receives is the page's, and only a record on one of the tree's own items
|
|
428
|
+
* schedules a pass.
|
|
429
|
+
*/
|
|
430
|
+
#observeItems() {
|
|
431
|
+
const observer = new MutationObserver((records) => {
|
|
432
|
+
if (this.#concernsItems(records)) this.#reconcile.schedule();
|
|
433
|
+
});
|
|
434
|
+
observer.observe(this.element, {
|
|
435
|
+
subtree: true,
|
|
436
|
+
attributes: true,
|
|
437
|
+
attributeFilter: OBSERVED_ATTRIBUTES
|
|
438
|
+
});
|
|
439
|
+
this.#observer = observer;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Whether any record is about one of this tree's own items. A widget nested in a
|
|
443
|
+
* row writes the same attributes on its own elements, and those are no reason to
|
|
444
|
+
* reconcile the tree.
|
|
445
|
+
*/
|
|
446
|
+
#concernsItems(records) {
|
|
447
|
+
const items = new Set(this.itemTargets);
|
|
448
|
+
return records.some((record) => items.has(record.target));
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Drops the records the tree's own writes just queued, so the observer never
|
|
452
|
+
* takes them for the page's. A page write queued just before goes with them and
|
|
453
|
+
* owes nothing: every such write leaves every item's `aria-selected` at the value
|
|
454
|
+
* the selection gives it, and the selection is then read back from the DOM, so it
|
|
455
|
+
* already holds what the page wrote.
|
|
456
|
+
*/
|
|
457
|
+
#dropOwnRecords() {
|
|
458
|
+
this.#observer?.takeRecords();
|
|
459
|
+
}
|
|
314
460
|
/**
|
|
315
461
|
* Restores the roving invariants when a `treeitem` leaves at runtime — a Turbo
|
|
316
462
|
* Stream `remove`, a morph, or plain DOM surgery. The mirror of
|
|
@@ -326,7 +472,10 @@ var TreeViewController = class extends Controller {
|
|
|
326
472
|
* state.
|
|
327
473
|
*
|
|
328
474
|
* Focus is only *restored*, never *stolen*: DOM focus moves solely when it was
|
|
329
|
-
* inside the removed subtree and the document has nowhere left to put it.
|
|
475
|
+
* inside the removed subtree and the document has nowhere left to put it. The
|
|
476
|
+
* roving recovery runs here, while the pre-removal order is still on record;
|
|
477
|
+
* the selection is settled once the batch is in, and a selected item that left
|
|
478
|
+
* is reported as `reconcile` with no item.
|
|
330
479
|
*/
|
|
331
480
|
itemTargetDisconnected(item) {
|
|
332
481
|
if (!this.#connected || !this.element.isConnected) return;
|
|
@@ -336,6 +485,7 @@ var TreeViewController = class extends Controller {
|
|
|
336
485
|
const next = this.#neighborOf(item);
|
|
337
486
|
if (next) this.#roving.setActive(this.itemTargets.indexOf(next), { focus: stranded });
|
|
338
487
|
}
|
|
488
|
+
this.#reconcile.schedule();
|
|
339
489
|
this.#order = this.#order.filter((tracked) => tracked !== item);
|
|
340
490
|
}
|
|
341
491
|
/**
|
|
@@ -565,8 +715,11 @@ var TreeViewController = class extends Controller {
|
|
|
565
715
|
#select(item) {
|
|
566
716
|
if (this.#isDisabled(item)) return;
|
|
567
717
|
for (const candidate of this.itemTargets) {
|
|
568
|
-
candidate
|
|
718
|
+
setAttributeIfChanged(candidate, "aria-selected", candidate === item ? "true" : "false");
|
|
569
719
|
}
|
|
720
|
+
this.#dropOwnRecords();
|
|
721
|
+
this.#settled = this.#selection();
|
|
722
|
+
this.#mirrorField(true);
|
|
570
723
|
this.dispatch("select", { detail: { item } });
|
|
571
724
|
}
|
|
572
725
|
/** Updates expansion, reconciles a collapsed subtree, then synchronously dispatches `toggle`. */
|