stimeo-ui 0.6.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 +62 -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/auto_submit_controller.d.ts +2 -0
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +47 -5
- 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/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 +3 -0
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +3 -0
- 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/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 +2 -0
- 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 +2 -0
- 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/idle_controller.d.ts +2 -0
- 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/meter_controller.d.ts +2 -0
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +2 -0
- 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 +2 -0
- package/dist/controllers/number_input_controller.js +47 -5
- 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 +2 -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 +2 -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 +2 -0
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.d.ts +30 -16
- package/dist/controllers/persist_controller.js +432 -122
- 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.js +77 -4
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +2 -0
- 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 +3 -0
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +2 -0
- package/dist/controllers/rating_controller.js +9 -5
- 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.map +1 -1
- package/dist/controllers/roving_controller.d.ts +2 -0
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.d.ts +23 -15
- package/dist/controllers/scroll_area_controller.js +462 -162
- 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.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 +2 -0
- 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 +2 -0
- 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 +2 -0
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/transition_controller.d.ts +2 -0
- 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 +2 -1
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.js +2387 -861
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +107 -3
- package/dist/inspector/cli.js +192 -14
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +254 -46
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +7 -7
- package/dist/inspector/manifest.json +434 -44
- package/dist/positioning/index.d.ts +2 -0
- package/dist/positioning/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -21,17 +21,21 @@ var AttributeLease = class {
|
|
|
21
21
|
written: value
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
else element.setAttribute(this.#attribute, value);
|
|
24
|
+
this.#reflect(element, value);
|
|
26
25
|
}
|
|
27
26
|
/** Returns one lease without overwriting a value subsequently authored by a consumer. */
|
|
28
27
|
return(element) {
|
|
29
28
|
const record = this.#records.get(element);
|
|
30
29
|
if (!record) return;
|
|
31
30
|
this.#records.delete(element);
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
|
|
31
|
+
const stillOwned = element.getAttribute(this.#attribute) === record.written;
|
|
32
|
+
if (stillOwned) this.#reflect(element, record.original);
|
|
33
|
+
}
|
|
34
|
+
/** Reflects only a real value transition, avoiding self-triggered mutation work. */
|
|
35
|
+
#reflect(element, value) {
|
|
36
|
+
if (element.getAttribute(this.#attribute) === value) return;
|
|
37
|
+
if (value === null) element.removeAttribute(this.#attribute);
|
|
38
|
+
else element.setAttribute(this.#attribute, value);
|
|
35
39
|
}
|
|
36
40
|
/** Returns every outstanding lease using the same ownership check as {@link return}. */
|
|
37
41
|
returnAll() {
|
|
@@ -39,6 +43,113 @@ var AttributeLease = class {
|
|
|
39
43
|
}
|
|
40
44
|
};
|
|
41
45
|
|
|
46
|
+
// src/utils/before_cache_reset.ts
|
|
47
|
+
var BeforeCacheReset = class _BeforeCacheReset {
|
|
48
|
+
/** Every subscribed instance, iterated by the one shared document listener. */
|
|
49
|
+
static #subscribers = /* @__PURE__ */ new Set();
|
|
50
|
+
/** The shared listener; installed while at least one instance is subscribed. */
|
|
51
|
+
static #onBeforeCache = () => {
|
|
52
|
+
for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
|
|
53
|
+
};
|
|
54
|
+
#rewind;
|
|
55
|
+
/** @param rewind - the pass that returns this controller's state to its initial form. */
|
|
56
|
+
constructor(rewind) {
|
|
57
|
+
this.#rewind = rewind;
|
|
58
|
+
}
|
|
59
|
+
/** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
|
|
60
|
+
activate() {
|
|
61
|
+
const first = _BeforeCacheReset.#subscribers.size === 0;
|
|
62
|
+
_BeforeCacheReset.#subscribers.add(this);
|
|
63
|
+
if (first) {
|
|
64
|
+
document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
|
|
68
|
+
deactivate() {
|
|
69
|
+
_BeforeCacheReset.#subscribers.delete(this);
|
|
70
|
+
if (_BeforeCacheReset.#subscribers.size > 0) return;
|
|
71
|
+
document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// src/utils/focus_candidate.ts
|
|
76
|
+
function inheritsFieldsetDisabled(control) {
|
|
77
|
+
let fieldset = control.closest("fieldset[disabled]");
|
|
78
|
+
while (fieldset) {
|
|
79
|
+
const legend = Array.from(fieldset.children).find((child) => child.tagName === "LEGEND");
|
|
80
|
+
if (!legend?.contains(control)) return true;
|
|
81
|
+
fieldset = fieldset.parentElement?.closest("fieldset[disabled]") ?? null;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
function canTakeFocus(element) {
|
|
86
|
+
if (element.closest("[hidden], [inert]")) return false;
|
|
87
|
+
if (element instanceof HTMLInputElement && element.type === "hidden") return false;
|
|
88
|
+
if (!("disabled" in element)) return true;
|
|
89
|
+
if (element.disabled) return false;
|
|
90
|
+
return !inheritsFieldsetDisabled(element);
|
|
91
|
+
}
|
|
92
|
+
var TAB_STOP_CANDIDATE_SELECTOR = [
|
|
93
|
+
"a[href]",
|
|
94
|
+
"area[href]",
|
|
95
|
+
"button",
|
|
96
|
+
"input",
|
|
97
|
+
"select",
|
|
98
|
+
"textarea",
|
|
99
|
+
"summary",
|
|
100
|
+
"iframe",
|
|
101
|
+
"audio[controls]",
|
|
102
|
+
"video[controls]",
|
|
103
|
+
"[tabindex]",
|
|
104
|
+
"[contenteditable]"
|
|
105
|
+
].join(",");
|
|
106
|
+
function isRenderedForFocus(element) {
|
|
107
|
+
const check = element.checkVisibility;
|
|
108
|
+
return typeof check === "function" ? check.call(element, { visibilityProperty: true }) : true;
|
|
109
|
+
}
|
|
110
|
+
function authoredTabindex(element) {
|
|
111
|
+
const value = element.getAttribute("tabindex");
|
|
112
|
+
if (value === null || !/^[+-]?\d+$/.test(value.trim())) return null;
|
|
113
|
+
return Number(value);
|
|
114
|
+
}
|
|
115
|
+
function hasNativeTabStop(element) {
|
|
116
|
+
if (element instanceof HTMLAnchorElement || element instanceof HTMLAreaElement) {
|
|
117
|
+
return element.hasAttribute("href");
|
|
118
|
+
}
|
|
119
|
+
if (element instanceof HTMLButtonElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
if (element instanceof HTMLInputElement) return element.type !== "hidden";
|
|
123
|
+
if (element instanceof HTMLIFrameElement) return true;
|
|
124
|
+
if (element.tagName === "AUDIO" || element.tagName === "VIDEO") {
|
|
125
|
+
return element.hasAttribute("controls");
|
|
126
|
+
}
|
|
127
|
+
if (element instanceof HTMLElement && element.tagName === "SUMMARY") {
|
|
128
|
+
const details = element.parentElement;
|
|
129
|
+
return details instanceof HTMLDetailsElement && Array.from(details.children).find((child) => child.tagName === "SUMMARY") === element;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
function hasEditableTabStop(element) {
|
|
134
|
+
const value = element.getAttribute("contenteditable")?.toLowerCase();
|
|
135
|
+
return value === "" || value === "true" || value === "plaintext-only";
|
|
136
|
+
}
|
|
137
|
+
function isTabStop(element) {
|
|
138
|
+
if (!canTakeFocus(element) || !isRenderedForFocus(element)) return false;
|
|
139
|
+
const tabindex = authoredTabindex(element);
|
|
140
|
+
if (tabindex !== null) return tabindex >= 0;
|
|
141
|
+
return hasNativeTabStop(element) || hasEditableTabStop(element);
|
|
142
|
+
}
|
|
143
|
+
function firstTabStop(root) {
|
|
144
|
+
for (const candidate of root.querySelectorAll(TAB_STOP_CANDIDATE_SELECTOR)) {
|
|
145
|
+
if (isTabStop(candidate)) return candidate;
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
function hasTabStop(root) {
|
|
150
|
+
return firstTabStop(root) !== null;
|
|
151
|
+
}
|
|
152
|
+
|
|
42
153
|
// src/utils/layout_observer.ts
|
|
43
154
|
var LayoutObserver = class {
|
|
44
155
|
#callback;
|
|
@@ -142,32 +253,55 @@ var MicrotaskCoalescer = class {
|
|
|
142
253
|
}
|
|
143
254
|
};
|
|
144
255
|
|
|
145
|
-
// src/utils/
|
|
146
|
-
var
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
};
|
|
153
|
-
#rewind;
|
|
154
|
-
/** @param rewind - the pass that returns this controller's state to its initial form. */
|
|
155
|
-
constructor(rewind) {
|
|
156
|
-
this.#rewind = rewind;
|
|
256
|
+
// src/utils/style_property_lease.ts
|
|
257
|
+
var StylePropertyLease = class {
|
|
258
|
+
#property;
|
|
259
|
+
#records = /* @__PURE__ */ new Map();
|
|
260
|
+
/** @param property - The CSS property whose temporary values this lease owns. */
|
|
261
|
+
constructor(property) {
|
|
262
|
+
this.#property = property;
|
|
157
263
|
}
|
|
158
|
-
/**
|
|
159
|
-
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
264
|
+
/** Writes or removes the leased declaration while preserving its authored value. */
|
|
265
|
+
write(element, value, priority = "") {
|
|
266
|
+
const existing = this.#records.get(element);
|
|
267
|
+
if (existing) {
|
|
268
|
+
existing.writtenValue = value;
|
|
269
|
+
existing.writtenPriority = value === null ? "" : priority;
|
|
270
|
+
} else {
|
|
271
|
+
this.#records.set(element, {
|
|
272
|
+
originalValue: element.style.getPropertyValue(this.#property),
|
|
273
|
+
originalPriority: element.style.getPropertyPriority(this.#property),
|
|
274
|
+
writtenValue: value,
|
|
275
|
+
writtenPriority: value === null ? "" : priority
|
|
276
|
+
});
|
|
164
277
|
}
|
|
278
|
+
this.#reflect(element, value, priority);
|
|
165
279
|
}
|
|
166
|
-
/**
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (
|
|
170
|
-
|
|
280
|
+
/** Returns one lease without overwriting a later consumer declaration. */
|
|
281
|
+
return(element) {
|
|
282
|
+
const record = this.#records.get(element);
|
|
283
|
+
if (!record) return;
|
|
284
|
+
this.#records.delete(element);
|
|
285
|
+
const style = element.style;
|
|
286
|
+
const stillOwned = style.getPropertyValue(this.#property) === (record.writtenValue ?? "") && style.getPropertyPriority(this.#property) === record.writtenPriority;
|
|
287
|
+
if (stillOwned) {
|
|
288
|
+
this.#reflect(element, record.originalValue, record.originalPriority);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/** Returns every outstanding declaration lease. */
|
|
292
|
+
returnAll() {
|
|
293
|
+
for (const element of Array.from(this.#records.keys())) this.return(element);
|
|
294
|
+
}
|
|
295
|
+
/** Reflects only a real declaration transition. */
|
|
296
|
+
#reflect(element, value, priority) {
|
|
297
|
+
const style = element.style;
|
|
298
|
+
const nextValue = value ?? "";
|
|
299
|
+
const nextPriority = value === null ? "" : priority;
|
|
300
|
+
if (style.getPropertyValue(this.#property) === nextValue && style.getPropertyPriority(this.#property) === nextPriority) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (value === null) style.removeProperty(this.#property);
|
|
304
|
+
else style.setProperty(this.#property, value, priority);
|
|
171
305
|
}
|
|
172
306
|
};
|
|
173
307
|
|
|
@@ -203,15 +337,6 @@ var TabindexLoan = class {
|
|
|
203
337
|
};
|
|
204
338
|
|
|
205
339
|
// src/controllers/scroll_area_controller.ts
|
|
206
|
-
var FOCUSABLE_SELECTOR = [
|
|
207
|
-
"a[href]",
|
|
208
|
-
"button:not([disabled])",
|
|
209
|
-
"input:not([disabled])",
|
|
210
|
-
"select:not([disabled])",
|
|
211
|
-
"textarea:not([disabled])",
|
|
212
|
-
"[tabindex]:not([tabindex='-1'])",
|
|
213
|
-
"[contenteditable='true']"
|
|
214
|
-
].join(",");
|
|
215
340
|
var EDGE_EPSILON = 1;
|
|
216
341
|
var ScrollAreaController = class extends Controller {
|
|
217
342
|
static targets = ["viewport"];
|
|
@@ -219,31 +344,60 @@ var ScrollAreaController = class extends Controller {
|
|
|
219
344
|
orientation: { type: String, default: "vertical" }
|
|
220
345
|
};
|
|
221
346
|
static events = ["reach"];
|
|
222
|
-
#layout = new LayoutObserver(() => this.#
|
|
223
|
-
/** Current element receiving scroll, resize, mutation, and keyboard-reach behavior. */
|
|
224
|
-
#viewport = null;
|
|
225
|
-
/** Guards target callbacks before connect and after disconnect. */
|
|
226
|
-
#connected = false;
|
|
227
|
-
/** Collapses target replacement callbacks into one final-DOM rebind. */
|
|
347
|
+
#layout = new LayoutObserver(() => this.#refresh());
|
|
228
348
|
#rebind = new MicrotaskCoalescer(() => this.#syncViewport());
|
|
229
|
-
|
|
230
|
-
#content = null;
|
|
231
|
-
/** Last edge reported via `reach`, so the event fires once per arrival. */
|
|
232
|
-
#lastEdge = null;
|
|
233
|
-
/** Whether this controller added `tabindex`, so teardown only removes its own. */
|
|
349
|
+
#beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
|
|
234
350
|
#tabindex = new TabindexLoan("0");
|
|
235
|
-
/** Temporarily owns a derived `role="region"` without losing an authored replacement. */
|
|
236
351
|
#role = new AttributeLease("role");
|
|
352
|
+
#overflowState = new AttributeLease("data-overflow");
|
|
353
|
+
#scrollState = new AttributeLease("data-scroll");
|
|
354
|
+
#progress = new StylePropertyLease("--stimeo--scroll-progress");
|
|
355
|
+
#viewport = null;
|
|
356
|
+
#content = null;
|
|
357
|
+
#nameSources = null;
|
|
358
|
+
#nameGraph = null;
|
|
359
|
+
#observedNameIds = /* @__PURE__ */ new Set();
|
|
360
|
+
#observedNameSources = [];
|
|
361
|
+
#fonts = null;
|
|
362
|
+
#scrollFrame = null;
|
|
363
|
+
#overflowing = false;
|
|
364
|
+
#lastEdge = null;
|
|
365
|
+
#ownedHostMutations = /* @__PURE__ */ new Map();
|
|
237
366
|
#onScroll = () => {
|
|
238
|
-
this.#
|
|
367
|
+
if (this.#scrollFrame !== null) return;
|
|
368
|
+
this.#scrollFrame = requestAnimationFrame(() => {
|
|
369
|
+
this.#scrollFrame = null;
|
|
370
|
+
const viewport = this.#viewport;
|
|
371
|
+
if (viewport) this.#syncPosition(viewport, this.#overflowing);
|
|
372
|
+
});
|
|
373
|
+
};
|
|
374
|
+
#onLoad = () => {
|
|
375
|
+
this.#refresh();
|
|
376
|
+
};
|
|
377
|
+
#onFontsSettled = () => {
|
|
378
|
+
this.#refresh();
|
|
239
379
|
};
|
|
240
380
|
connect() {
|
|
241
|
-
this.#
|
|
381
|
+
this.#beforeCache.activate();
|
|
242
382
|
this.#rebind.activate();
|
|
243
383
|
this.#layout.observeViewport();
|
|
384
|
+
this.#bindFonts();
|
|
244
385
|
this.#syncViewport();
|
|
245
386
|
}
|
|
246
|
-
|
|
387
|
+
disconnect() {
|
|
388
|
+
this.#beforeCache.deactivate();
|
|
389
|
+
this.#rebind.cancel();
|
|
390
|
+
this.#cancelScrollFrame();
|
|
391
|
+
if (this.#viewport) this.#unbindViewport(this.#viewport);
|
|
392
|
+
this.#layout.disconnect();
|
|
393
|
+
this.#unbindFonts();
|
|
394
|
+
this.#clearHostState();
|
|
395
|
+
}
|
|
396
|
+
/** Re-measures when a retained viewport's orientation Value is morphed. */
|
|
397
|
+
orientationValueChanged() {
|
|
398
|
+
this.#refresh();
|
|
399
|
+
}
|
|
400
|
+
/** Schedules a complete resource rebind for a runtime viewport target. */
|
|
247
401
|
viewportTargetConnected() {
|
|
248
402
|
this.#rebind.schedule();
|
|
249
403
|
}
|
|
@@ -251,149 +405,295 @@ var ScrollAreaController = class extends Controller {
|
|
|
251
405
|
viewportTargetDisconnected() {
|
|
252
406
|
this.#rebind.schedule();
|
|
253
407
|
}
|
|
254
|
-
/** Rebinds every viewport-owned resource against the final target in
|
|
408
|
+
/** Rebinds every viewport-owned resource against the final target in the mutation batch. */
|
|
255
409
|
#syncViewport() {
|
|
256
|
-
if (!this.#connected) return;
|
|
257
410
|
const next = this.hasViewportTarget ? this.viewportTarget : null;
|
|
258
411
|
if (next === this.#viewport) {
|
|
259
|
-
this.#
|
|
412
|
+
if (next) this.#refresh();
|
|
413
|
+
else this.#clearHostState();
|
|
260
414
|
return;
|
|
261
415
|
}
|
|
262
416
|
if (this.#viewport) this.#unbindViewport(this.#viewport);
|
|
263
417
|
this.#viewport = next;
|
|
264
|
-
if (!next)
|
|
418
|
+
if (!next) {
|
|
419
|
+
this.#clearHostState();
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
265
422
|
next.addEventListener("scroll", this.#onScroll, { passive: true });
|
|
423
|
+
next.addEventListener("load", this.#onLoad, true);
|
|
266
424
|
this.#layout.observe(next);
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
425
|
+
this.#bindContentObserver(next);
|
|
426
|
+
this.#refresh();
|
|
427
|
+
}
|
|
428
|
+
/** Observes structural and attribute changes that can alter overflow or Tab stops. */
|
|
429
|
+
#bindContentObserver(viewport) {
|
|
430
|
+
this.#content = new MutationObserver((records) => {
|
|
431
|
+
if (this.#viewport !== viewport || !this.#hasRelevantContentMutation(records)) return;
|
|
432
|
+
this.#refresh();
|
|
433
|
+
});
|
|
434
|
+
this.#content.observe(viewport, {
|
|
435
|
+
subtree: true,
|
|
436
|
+
childList: true,
|
|
437
|
+
attributes: true,
|
|
438
|
+
characterData: true
|
|
439
|
+
});
|
|
279
440
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
441
|
+
/** Ignores only state-hook mutations whose exact value this controller just wrote. */
|
|
442
|
+
#hasRelevantContentMutation(records) {
|
|
443
|
+
let relevant = false;
|
|
444
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
445
|
+
for (const record of records) {
|
|
446
|
+
const attribute = record.attributeName;
|
|
447
|
+
if (record.type === "attributes" && record.target === this.element && attribute !== null && this.#ownedHostMutations.has(attribute) && this.element.getAttribute(attribute) === this.#ownedHostMutations.get(attribute)) {
|
|
448
|
+
consumed.add(attribute);
|
|
449
|
+
} else {
|
|
450
|
+
relevant = true;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
for (const attribute of consumed) this.#ownedHostMutations.delete(attribute);
|
|
454
|
+
return relevant;
|
|
286
455
|
}
|
|
287
|
-
/** Releases every resource and
|
|
456
|
+
/** Releases every resource and borrowed attribute owned by one former viewport. */
|
|
288
457
|
#unbindViewport(viewport) {
|
|
458
|
+
this.#cancelScrollFrame();
|
|
289
459
|
viewport.removeEventListener("scroll", this.#onScroll);
|
|
460
|
+
viewport.removeEventListener("load", this.#onLoad, true);
|
|
290
461
|
this.#layout.unobserve(viewport);
|
|
291
462
|
this.#content?.disconnect();
|
|
292
463
|
this.#content = null;
|
|
293
|
-
this.#
|
|
294
|
-
|
|
464
|
+
this.#disconnectNameSources();
|
|
465
|
+
this.#clearViewportAttributes(viewport);
|
|
466
|
+
this.#ownedHostMutations.clear();
|
|
467
|
+
this.#viewport = null;
|
|
468
|
+
this.#overflowing = false;
|
|
295
469
|
this.#lastEdge = null;
|
|
296
470
|
}
|
|
297
|
-
/**
|
|
298
|
-
#
|
|
299
|
-
const
|
|
300
|
-
if (!
|
|
301
|
-
|
|
302
|
-
this.#
|
|
303
|
-
|
|
304
|
-
this
|
|
305
|
-
this
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
this.#lastEdge = null;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* Measures overflow and reflects the `data-overflow` hook.
|
|
316
|
-
*
|
|
317
|
-
* The write is skipped when the value is unchanged. An identical `setAttribute` still
|
|
318
|
-
* queues a MutationRecord, and markup that puts the viewport target on the controller
|
|
319
|
-
* element itself would then have the content observer trigger its own next callback.
|
|
320
|
-
*/
|
|
321
|
-
#syncOverflow(vp) {
|
|
322
|
-
const overflowing = this.#measureOverflow(vp);
|
|
323
|
-
const next = overflowing ? "true" : "false";
|
|
324
|
-
if (this.element.getAttribute("data-overflow") !== next) {
|
|
325
|
-
this.element.setAttribute("data-overflow", next);
|
|
326
|
-
}
|
|
471
|
+
/** Runs the full structural and positional measurement pass. */
|
|
472
|
+
#refresh() {
|
|
473
|
+
const viewport = this.#viewport;
|
|
474
|
+
if (!viewport) return;
|
|
475
|
+
this.#cancelScrollFrame();
|
|
476
|
+
this.#syncNameSources(viewport);
|
|
477
|
+
this.#overflowing = this.#syncOverflow(viewport);
|
|
478
|
+
this.#syncKeyboardReach(viewport, this.#overflowing);
|
|
479
|
+
this.#syncPosition(viewport, this.#overflowing);
|
|
480
|
+
}
|
|
481
|
+
/** Measures overflow and reflects the state hook without identical DOM writes. */
|
|
482
|
+
#syncOverflow(viewport) {
|
|
483
|
+
const overflowing = this.#measureOverflow(viewport);
|
|
484
|
+
this.#writeHostAttribute(this.#overflowState, "data-overflow", overflowing ? "true" : "false");
|
|
327
485
|
return overflowing;
|
|
328
486
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
const
|
|
332
|
-
|
|
487
|
+
/** Measures the primary-axis position and dispatches a newly established edge. */
|
|
488
|
+
#syncPosition(viewport, overflowing) {
|
|
489
|
+
const { position, progress } = this.#measurePosition(viewport);
|
|
490
|
+
this.#writeHostAttribute(this.#scrollState, "data-scroll", position);
|
|
491
|
+
this.#writeHostProgress(String(progress));
|
|
492
|
+
const edge = overflowing && (position === "start" || position === "end") ? position : null;
|
|
493
|
+
if (edge === this.#lastEdge) return;
|
|
494
|
+
this.#lastEdge = edge;
|
|
495
|
+
if (edge) this.dispatch("reach", { detail: { edge } });
|
|
496
|
+
}
|
|
497
|
+
/** Writes one leased host attribute and records self-generated observer input. */
|
|
498
|
+
#writeHostAttribute(lease, attribute, value) {
|
|
499
|
+
const before = this.element.getAttribute(attribute);
|
|
500
|
+
lease.write(this.element, value);
|
|
501
|
+
this.#recordOwnedHostMutation(attribute, before);
|
|
502
|
+
}
|
|
503
|
+
/** Writes the leased progress property and records its serialized style mutation. */
|
|
504
|
+
#writeHostProgress(value) {
|
|
505
|
+
const before = this.element.getAttribute("style");
|
|
506
|
+
this.#progress.write(this.element, value);
|
|
507
|
+
this.#recordOwnedHostMutation("style", before);
|
|
508
|
+
}
|
|
509
|
+
/** Records an exact host mutation only when the host is also the observed viewport. */
|
|
510
|
+
#recordOwnedHostMutation(attribute, before) {
|
|
511
|
+
if (this.#viewport !== this.element) return;
|
|
512
|
+
const after = this.element.getAttribute(attribute);
|
|
513
|
+
if (after !== before) this.#ownedHostMutations.set(attribute, after);
|
|
514
|
+
}
|
|
515
|
+
/** Returns host hooks to authored values and clears edge bookkeeping. */
|
|
516
|
+
#clearHostState() {
|
|
517
|
+
this.#overflowState.returnAll();
|
|
518
|
+
this.#scrollState.returnAll();
|
|
519
|
+
this.#progress.returnAll();
|
|
520
|
+
this.#ownedHostMutations.clear();
|
|
521
|
+
this.#overflowing = false;
|
|
522
|
+
this.#lastEdge = null;
|
|
523
|
+
}
|
|
524
|
+
/** Whether the configured axis currently has a scroll range. */
|
|
525
|
+
#measureOverflow(viewport) {
|
|
526
|
+
const orientation = this.orientationValue;
|
|
527
|
+
const vertical = orientation !== "horizontal" && viewport.scrollHeight > viewport.clientHeight + EDGE_EPSILON;
|
|
528
|
+
const horizontal = orientation !== "vertical" && viewport.scrollWidth > viewport.clientWidth + EDGE_EPSILON;
|
|
333
529
|
return vertical || horizontal;
|
|
334
530
|
}
|
|
335
|
-
/**
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
531
|
+
/** Reports the primary-axis position bucket and normalized 0–1 progress. */
|
|
532
|
+
#measurePosition(viewport) {
|
|
533
|
+
const horizontalPrimary = this.orientationValue === "horizontal" || this.orientationValue === "both" && viewport.scrollHeight <= viewport.clientHeight + EDGE_EPSILON;
|
|
534
|
+
const { position: scrollPosition, max: maxScroll } = logicalScrollMetrics(
|
|
535
|
+
viewport,
|
|
536
|
+
horizontalPrimary
|
|
537
|
+
);
|
|
342
538
|
if (maxScroll <= EDGE_EPSILON) return { position: "start", progress: 0 };
|
|
343
|
-
const progress = Math.min(1, Math.max(0,
|
|
344
|
-
if (
|
|
345
|
-
if (
|
|
539
|
+
const progress = Math.min(1, Math.max(0, scrollPosition / maxScroll));
|
|
540
|
+
if (scrollPosition <= EDGE_EPSILON) return { position: "start", progress };
|
|
541
|
+
if (scrollPosition >= maxScroll - EDGE_EPSILON) return { position: "end", progress };
|
|
346
542
|
return { position: "middle", progress };
|
|
347
543
|
}
|
|
348
|
-
/**
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
if (!
|
|
358
|
-
this.#role.write(vp, "region");
|
|
359
|
-
}
|
|
544
|
+
/** Borrows keyboard reach only when no usable descendant already owns a Tab stop. */
|
|
545
|
+
#syncKeyboardReach(viewport, overflowing) {
|
|
546
|
+
const wantsTabindex = overflowing && !hasTabStop(viewport);
|
|
547
|
+
if (!wantsTabindex) {
|
|
548
|
+
this.#clearViewportAttributes(viewport);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
this.#tabindex.lend(viewport);
|
|
552
|
+
if (this.#hasAccessibleName(viewport)) {
|
|
553
|
+
if (!viewport.hasAttribute("role")) this.#role.write(viewport, "region");
|
|
360
554
|
} else {
|
|
361
|
-
this.#
|
|
555
|
+
this.#role.return(viewport);
|
|
362
556
|
}
|
|
363
557
|
}
|
|
364
|
-
/**
|
|
365
|
-
#
|
|
558
|
+
/** Returns only the viewport attributes borrowed by this controller. */
|
|
559
|
+
#clearViewportAttributes(viewport) {
|
|
366
560
|
this.#tabindex.returnAll();
|
|
367
|
-
this.#role.return(
|
|
561
|
+
this.#role.return(viewport);
|
|
562
|
+
}
|
|
563
|
+
/** Whether the viewport already has a non-empty accessible name. */
|
|
564
|
+
#hasAccessibleName(viewport) {
|
|
565
|
+
const sources = this.#resolveNameSources(viewport);
|
|
566
|
+
if (sources.length > 0) {
|
|
567
|
+
return sources.some((source) => {
|
|
568
|
+
const label = source.getAttribute("aria-label")?.trim();
|
|
569
|
+
return Boolean(label || source.textContent?.trim());
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
return Boolean(viewport.getAttribute("aria-label")?.trim());
|
|
573
|
+
}
|
|
574
|
+
/** Resolves unique, live `aria-labelledby` references in authored order. */
|
|
575
|
+
#resolveNameSources(viewport) {
|
|
576
|
+
const ids = this.#nameReferenceIds(viewport);
|
|
577
|
+
const sources = [];
|
|
578
|
+
const seen = /* @__PURE__ */ new Set();
|
|
579
|
+
for (const id of ids) {
|
|
580
|
+
const source = viewport.ownerDocument.getElementById(id);
|
|
581
|
+
if (source && !seen.has(source)) {
|
|
582
|
+
seen.add(source);
|
|
583
|
+
sources.push(source);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return sources;
|
|
368
587
|
}
|
|
369
|
-
/**
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
* stop — leaving it unreachable by keyboard exactly while it has nothing else to
|
|
375
|
-
* offer. Only rendered candidates count.
|
|
376
|
-
*/
|
|
377
|
-
#hasFocusableContent(vp) {
|
|
378
|
-
return Array.from(vp.querySelectorAll(FOCUSABLE_SELECTOR)).some(
|
|
379
|
-
(el) => this.#isRendered(el)
|
|
588
|
+
/** Observes external name sources so a retained region never becomes unnamed. */
|
|
589
|
+
#syncNameSources(viewport) {
|
|
590
|
+
const ids = this.#nameReferenceIds(viewport);
|
|
591
|
+
const sources = this.#resolveNameSources(viewport).filter(
|
|
592
|
+
(source) => !viewport.contains(source)
|
|
380
593
|
);
|
|
594
|
+
if (ids.length === this.#observedNameIds.size && ids.every((id) => this.#observedNameIds.has(id)) && sources.length === this.#observedNameSources.length && sources.every((source, index) => source === this.#observedNameSources[index])) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
this.#disconnectNameSources();
|
|
598
|
+
this.#observedNameIds = new Set(ids);
|
|
599
|
+
this.#observedNameSources = sources;
|
|
600
|
+
if (ids.length > 0) {
|
|
601
|
+
this.#nameGraph = new MutationObserver((records) => {
|
|
602
|
+
if (this.#viewport === viewport && this.#hasRelevantNameGraphMutation(records)) {
|
|
603
|
+
this.#refresh();
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
const documentElement = viewport.ownerDocument.documentElement;
|
|
607
|
+
if (documentElement) {
|
|
608
|
+
this.#nameGraph.observe(documentElement, {
|
|
609
|
+
subtree: true,
|
|
610
|
+
childList: true,
|
|
611
|
+
attributes: true,
|
|
612
|
+
attributeFilter: ["id"],
|
|
613
|
+
attributeOldValue: true
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (sources.length === 0) return;
|
|
618
|
+
this.#nameSources = new MutationObserver(() => {
|
|
619
|
+
this.#refresh();
|
|
620
|
+
});
|
|
621
|
+
for (const source of sources) {
|
|
622
|
+
this.#nameSources.observe(source, {
|
|
623
|
+
subtree: true,
|
|
624
|
+
childList: true,
|
|
625
|
+
characterData: true,
|
|
626
|
+
attributes: true
|
|
627
|
+
});
|
|
628
|
+
}
|
|
381
629
|
}
|
|
382
|
-
/**
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
630
|
+
/** Returns normalized unique label-reference ids in authored order. */
|
|
631
|
+
#nameReferenceIds(viewport) {
|
|
632
|
+
const tokens = viewport.getAttribute("aria-labelledby")?.trim().split(/\s+/).filter(Boolean);
|
|
633
|
+
return Array.from(new Set(tokens));
|
|
634
|
+
}
|
|
635
|
+
/** Whether document-graph changes can resolve or invalidate a referenced id. */
|
|
636
|
+
#hasRelevantNameGraphMutation(records) {
|
|
637
|
+
for (const record of records) {
|
|
638
|
+
if (record.type === "attributes") {
|
|
639
|
+
const current = record.target.id;
|
|
640
|
+
if (this.#observedNameIds.has(current) || this.#observedNameIds.has(record.oldValue ?? "")) {
|
|
641
|
+
return true;
|
|
642
|
+
}
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
for (const node of [...record.addedNodes, ...record.removedNodes]) {
|
|
646
|
+
if (this.#nodeContainsObservedNameId(node)) return true;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
/** Whether a changed subtree contains one of the current label-reference ids. */
|
|
652
|
+
#nodeContainsObservedNameId(node) {
|
|
653
|
+
if (!(node instanceof Element)) return false;
|
|
654
|
+
if (this.#observedNameIds.has(node.id)) return true;
|
|
655
|
+
return Array.from(node.querySelectorAll("[id]")).some(
|
|
656
|
+
(element) => this.#observedNameIds.has(element.id)
|
|
657
|
+
);
|
|
394
658
|
}
|
|
395
|
-
|
|
396
|
-
|
|
659
|
+
/** Stops observing accessible-name sources. */
|
|
660
|
+
#disconnectNameSources() {
|
|
661
|
+
this.#nameSources?.disconnect();
|
|
662
|
+
this.#nameSources = null;
|
|
663
|
+
this.#nameGraph?.disconnect();
|
|
664
|
+
this.#nameGraph = null;
|
|
665
|
+
this.#observedNameIds.clear();
|
|
666
|
+
this.#observedNameSources = [];
|
|
667
|
+
}
|
|
668
|
+
/** Subscribes to font completion when the current engine exposes `document.fonts`. */
|
|
669
|
+
#bindFonts() {
|
|
670
|
+
const fonts = this.element.ownerDocument.fonts;
|
|
671
|
+
if (!fonts || this.#fonts === fonts) return;
|
|
672
|
+
this.#unbindFonts();
|
|
673
|
+
this.#fonts = fonts;
|
|
674
|
+
fonts.addEventListener("loadingdone", this.#onFontsSettled);
|
|
675
|
+
fonts.addEventListener("loadingerror", this.#onFontsSettled);
|
|
676
|
+
}
|
|
677
|
+
/** Removes the font completion subscriptions. */
|
|
678
|
+
#unbindFonts() {
|
|
679
|
+
this.#fonts?.removeEventListener("loadingdone", this.#onFontsSettled);
|
|
680
|
+
this.#fonts?.removeEventListener("loadingerror", this.#onFontsSettled);
|
|
681
|
+
this.#fonts = null;
|
|
682
|
+
}
|
|
683
|
+
/** Cancels a pending scroll frame. */
|
|
684
|
+
#cancelScrollFrame() {
|
|
685
|
+
if (this.#scrollFrame === null) return;
|
|
686
|
+
cancelAnimationFrame(this.#scrollFrame);
|
|
687
|
+
this.#scrollFrame = null;
|
|
688
|
+
}
|
|
689
|
+
/** Suspends live resources and returns all derived state before snapshotting. */
|
|
690
|
+
#rewindForCache() {
|
|
691
|
+
this.#rebind.cancel();
|
|
692
|
+
this.#cancelScrollFrame();
|
|
693
|
+
if (this.#viewport) this.#unbindViewport(this.#viewport);
|
|
694
|
+
this.#layout.disconnect();
|
|
695
|
+
this.#unbindFonts();
|
|
696
|
+
this.#clearHostState();
|
|
397
697
|
}
|
|
398
698
|
};
|
|
399
699
|
|