@vizejs/ui 0.343.0 → 0.347.7

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.
Files changed (44) hide show
  1. package/dist/{button-D7sM9Xmj.d.mts → button-BMIqJ_M5.d.mts} +1 -1
  2. package/dist/button.d.mts +1 -1
  3. package/dist/button.mjs +1 -1
  4. package/dist/{checkbox-DkwZFC80.d.mts → checkbox-sbhaWekB.d.mts} +1 -1
  5. package/dist/checkbox.d.mts +1 -1
  6. package/dist/collection-CoOROaho.mjs +453 -0
  7. package/dist/collection-aHZ6Pf0e.d.mts +185 -0
  8. package/dist/collection.d.mts +2 -0
  9. package/dist/collection.mjs +2 -0
  10. package/dist/controllable-state.d.mts +1 -1
  11. package/dist/id-BSgJwErt.d.mts +127 -0
  12. package/dist/id-CPBdeoL3.mjs +137 -0
  13. package/dist/id.d.mts +2 -0
  14. package/dist/id.mjs +2 -0
  15. package/dist/index.d.mts +12 -6
  16. package/dist/index.mjs +9 -4
  17. package/dist/interaction-modality-CO-eQzNy.mjs +304 -0
  18. package/dist/interaction-modality-tee79ZBZ.d.mts +89 -0
  19. package/dist/interaction-modality.d.mts +2 -0
  20. package/dist/interaction-modality.mjs +2 -0
  21. package/dist/long-press-CRjjjJK2.d.mts +115 -0
  22. package/dist/long-press.d.mts +2 -0
  23. package/dist/long-press.mjs +246 -0
  24. package/dist/media-pdf.d.mts +1 -1
  25. package/dist/media-pdf.mjs +1 -1
  26. package/dist/media.d.mts +1 -1
  27. package/dist/media.mjs +1 -1
  28. package/dist/press-Bp57IOe2.mjs +615 -0
  29. package/dist/press-types-B8Ssxqg4.d.mts +112 -0
  30. package/dist/press.d.mts +14 -0
  31. package/dist/press.mjs +2 -0
  32. package/dist/primitive.d.mts +1 -1
  33. package/dist/primitive.mjs +1 -1
  34. package/dist/visually-hidden.d.mts +1 -1
  35. package/dist/visually-hidden.mjs +1 -1
  36. package/package.json +34 -7
  37. /package/dist/{button-8BOlFJNu.mjs → button-CaLxP2l2.mjs} +0 -0
  38. /package/dist/{controllable-state-DXsYJ3yl.d.mts → controllable-state-B9v6FClp.d.mts} +0 -0
  39. /package/dist/{pdf-source-COxN3A1l.d.mts → pdf-source-BbZKF_dS.d.mts} +0 -0
  40. /package/dist/{pdf-source-C52YE8tp.mjs → pdf-source-C9zNQd9l.mjs} +0 -0
  41. /package/dist/{primitive-DJB7pOf3.mjs → primitive-BE28SwD7.mjs} +0 -0
  42. /package/dist/{primitive-BtvwikH1.d.mts → primitive-Bog8qvd8.d.mts} +0 -0
  43. /package/dist/{visually-hidden-QENRapzW.mjs → visually-hidden-CTSV3ctZ.mjs} +0 -0
  44. /package/dist/{visually-hidden-BegtnMng.d.mts → visually-hidden-CekDwEwR.d.mts} +0 -0
@@ -1,4 +1,4 @@
1
- import { n as PrimitiveElement, t as PrimitiveAs } from "./primitive-BtvwikH1.mjs";
1
+ import { n as PrimitiveElement, t as PrimitiveAs } from "./primitive-Bog8qvd8.mjs";
2
2
  import * as _$vue from "vue";
3
3
 
4
4
  //#region src/button-keyboard.d.ts
package/dist/button.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { i as getButtonKeyboardAction, n as ButtonKeyboardAction, r as ButtonKeyboardPhase, t as _default } from "./button-D7sM9Xmj.mjs";
1
+ import { i as getButtonKeyboardAction, n as ButtonKeyboardAction, r as ButtonKeyboardPhase, t as _default } from "./button-BMIqJ_M5.mjs";
2
2
  export { _default as Button, ButtonKeyboardAction, ButtonKeyboardPhase, getButtonKeyboardAction };
package/dist/button.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { n as getButtonKeyboardAction, t as ActionButton_default } from "./button-8BOlFJNu.mjs";
1
+ import { n as getButtonKeyboardAction, t as ActionButton_default } from "./button-CaLxP2l2.mjs";
2
2
  export { ActionButton_default as Button, getButtonKeyboardAction };
@@ -1,4 +1,4 @@
1
- import { r as StateUpdate } from "./controllable-state-DXsYJ3yl.mjs";
1
+ import { r as StateUpdate } from "./controllable-state-B9v6FClp.mjs";
2
2
  import * as _$vue from "vue";
3
3
 
4
4
  //#region src/checkbox-state.d.ts
@@ -1,2 +1,2 @@
1
- import { n as CheckboxState, r as getCheckboxState, t as _default } from "./checkbox-DkwZFC80.mjs";
1
+ import { n as CheckboxState, r as getCheckboxState, t as _default } from "./checkbox-sbhaWekB.mjs";
2
2
  export { _default as Checkbox, CheckboxState, getCheckboxState };
@@ -0,0 +1,453 @@
1
+ import { computed, getCurrentScope, onScopeDispose, shallowRef, toValue, watch } from "vue";
2
+ //#region src/collection-keys.ts
3
+ /** Reject keys that cannot serve as stable, serializable item identities. */
4
+ function validateCollectionKey(key) {
5
+ if (typeof key === "string") {
6
+ if (key.length === 0 || containsAsciiControl(key)) throw new Error("VIZE_UI_COLLECTION_KEY_VALUE: a string key must be non-empty and contain no ASCII controls");
7
+ return;
8
+ }
9
+ if (!Number.isSafeInteger(key) || Object.is(key, -0)) throw new Error("VIZE_UI_COLLECTION_KEY_VALUE: a numeric key must be a safe integer other than negative zero");
10
+ }
11
+ /** Whether a resolved item list still contains a key. */
12
+ function hasCollectionKey(items, key) {
13
+ return items.some((item) => item.key === key);
14
+ }
15
+ /**
16
+ * Pick the logical focus successor for a key that left the navigable set.
17
+ *
18
+ * The next navigable item wins, then the previous one, so removing or
19
+ * disabling the active item never orphans logical focus.
20
+ */
21
+ function recoverCollectionKey(removedKey, previousItems, nextItems) {
22
+ if (nextItems.length === 0) return null;
23
+ const nextKeys = new Set(nextItems.map((item) => item.key));
24
+ const previousIndex = previousItems.findIndex((item) => item.key === removedKey);
25
+ if (previousIndex < 0) return nextItems[0]?.key ?? null;
26
+ for (let index = previousIndex + 1; index < previousItems.length; index++) {
27
+ const key = previousItems[index]?.key;
28
+ if (key !== void 0 && nextKeys.has(key)) return key;
29
+ }
30
+ for (let index = previousIndex - 1; index >= 0; index--) {
31
+ const key = previousItems[index]?.key;
32
+ if (key !== void 0 && nextKeys.has(key)) return key;
33
+ }
34
+ return nextItems[0]?.key ?? null;
35
+ }
36
+ function containsAsciiControl(value) {
37
+ for (let index = 0; index < value.length; index++) {
38
+ const code = value.charCodeAt(index);
39
+ if (code <= 31 || code === 127) return true;
40
+ }
41
+ return false;
42
+ }
43
+ //#endregion
44
+ //#region src/collection-observer.ts
45
+ const orderAndTextMutations = {
46
+ attributeFilter: [
47
+ "alt",
48
+ "aria-hidden",
49
+ "aria-label",
50
+ "aria-labelledby",
51
+ "hidden",
52
+ "inert",
53
+ "title",
54
+ "type",
55
+ "value"
56
+ ],
57
+ attributes: true,
58
+ characterData: true,
59
+ childList: true,
60
+ subtree: true
61
+ };
62
+ const mountMutations = {
63
+ childList: true,
64
+ subtree: true
65
+ };
66
+ /**
67
+ * Observe DOM ordering and accessible text mutations for registered items.
68
+ *
69
+ * One observer is created per owning document. Connected items use their
70
+ * closest common ancestor and disconnected items are observed directly, so
71
+ * attribute and text noise from the rest of the document is never observed.
72
+ * Disconnected items additionally use a structure-only document observer that
73
+ * reports only once one of them mounts, because their insertion point is
74
+ * unknowable beforehand.
75
+ */
76
+ function observeCollectionMutations(items, onMutation) {
77
+ const elementsByDocument = /* @__PURE__ */ new Map();
78
+ for (const { element } of items) {
79
+ if (element === null) continue;
80
+ const elements = elementsByDocument.get(element.ownerDocument) ?? [];
81
+ elements.push(element);
82
+ elementsByDocument.set(element.ownerDocument, elements);
83
+ }
84
+ const observers = [];
85
+ for (const [ownerDocument, elements] of elementsByDocument) {
86
+ const MutationObserverConstructor = ownerDocument.defaultView?.MutationObserver;
87
+ if (MutationObserverConstructor === void 0) continue;
88
+ const observer = new MutationObserverConstructor(onMutation);
89
+ const connectedElements = elements.filter((element) => element.isConnected);
90
+ const disconnectedElements = elements.filter((element) => !element.isConnected);
91
+ const commonAncestor = connectedElements.length === elements.length ? findCollectionObservationRoot(connectedElements) : null;
92
+ const connectedRoot = commonAncestor === null ? findCollectionObservationRoot(connectedElements) : commonAncestor;
93
+ const targets = commonAncestor === null ? new Set([...connectedRoot === null ? connectedElements : [connectedRoot], ...disconnectedElements]) : new Set([commonAncestor]);
94
+ for (const target of targets) observer.observe(target, orderAndTextMutations);
95
+ observers.push(observer);
96
+ if (disconnectedElements.length === 0) continue;
97
+ const mountObserver = new MutationObserverConstructor(() => {
98
+ if (disconnectedElements.some((element) => element.isConnected)) onMutation();
99
+ });
100
+ mountObserver.observe(ownerDocument.documentElement, mountMutations);
101
+ observers.push(mountObserver);
102
+ }
103
+ return observers;
104
+ }
105
+ function findCollectionObservationRoot(elements) {
106
+ const firstElement = elements[0];
107
+ if (firstElement === void 0) return null;
108
+ let candidate = firstElement.parentNode ?? firstElement;
109
+ while (candidate !== null) {
110
+ if (elements.every((element) => candidate === element || candidate?.contains(element) === true)) return candidate;
111
+ candidate = candidate.parentNode;
112
+ }
113
+ return null;
114
+ }
115
+ //#endregion
116
+ //#region src/collection-text.ts
117
+ const ignoredTextTags = new Set([
118
+ "NOSCRIPT",
119
+ "SCRIPT",
120
+ "STYLE",
121
+ "TEMPLATE"
122
+ ]);
123
+ /**
124
+ * Normalize authored or extracted typeahead text without applying a locale.
125
+ *
126
+ * Unicode is normalized to NFC and every whitespace run becomes one ASCII
127
+ * space. Locale-sensitive equality remains the responsibility of a collator.
128
+ */
129
+ function normalizeCollectionTextValue(value) {
130
+ return value.normalize("NFC").trim().replace(/\s+/gu, " ");
131
+ }
132
+ /**
133
+ * Extract a practical accessible text value from an item element.
134
+ *
135
+ * `aria-labelledby` and `aria-label` take precedence, hidden descendants are
136
+ * excluded unless explicitly referenced, and image/input fallbacks are
137
+ * included. Complex widgets should still provide `textValue` explicitly when
138
+ * their typeahead label differs from their accessible name.
139
+ */
140
+ function extractCollectionTextValue(element) {
141
+ return normalizeCollectionTextValue(readElementText(element, /* @__PURE__ */ new Set(), false));
142
+ }
143
+ /** Whether a candidate text starts with a query under locale-aware comparison. */
144
+ function collectionTextStartsWith(candidate, query, collator) {
145
+ let codeUnitLength = 0;
146
+ for (const character of candidate) {
147
+ codeUnitLength += character.length;
148
+ if (collator.compare(candidate.slice(0, codeUnitLength), query) === 0) return true;
149
+ }
150
+ return false;
151
+ }
152
+ function readElementText(element, visited, referenced) {
153
+ if (visited.has(element)) return "";
154
+ visited.add(element);
155
+ if (!referenced && isCollectionTextHidden(element)) return "";
156
+ const labelledBy = element.getAttribute("aria-labelledby");
157
+ if (labelledBy !== null) {
158
+ const root = element.getRootNode();
159
+ const referenceRoot = hasGetElementById(root) ? root : element.ownerDocument;
160
+ const labelledText = labelledBy.split(/\s+/u).filter((id) => id.length > 0).map((id) => referenceRoot.getElementById(id)).filter((label) => label !== null).map((label) => readElementText(label, visited, true)).join(" ");
161
+ if (normalizeCollectionTextValue(labelledText).length > 0) return labelledText;
162
+ }
163
+ const ariaLabel = element.getAttribute("aria-label");
164
+ if (ariaLabel !== null && normalizeCollectionTextValue(ariaLabel).length > 0) return ariaLabel;
165
+ const tagName = element.tagName.toUpperCase();
166
+ if (tagName === "IMG" || tagName === "AREA") {
167
+ const alternative = element.getAttribute("alt");
168
+ if (alternative !== null) return alternative;
169
+ }
170
+ if (tagName === "INPUT") {
171
+ const type = element.getAttribute("type")?.toLowerCase();
172
+ if (type === "button" || type === "reset" || type === "submit") {
173
+ const value = element.value;
174
+ if (value.length > 0) return value;
175
+ }
176
+ }
177
+ if (!ignoredTextTags.has(tagName)) {
178
+ const fragments = [];
179
+ for (const child of element.childNodes) if (child.nodeType === 3) fragments.push(child.nodeValue ?? "");
180
+ else if (child.nodeType === 1) fragments.push(readElementText(child, visited, referenced));
181
+ const content = fragments.join("");
182
+ if (normalizeCollectionTextValue(content).length > 0) return content;
183
+ }
184
+ return element.getAttribute("title") ?? "";
185
+ }
186
+ function hasGetElementById(node) {
187
+ return "getElementById" in node && typeof node.getElementById === "function";
188
+ }
189
+ function isCollectionTextHidden(element) {
190
+ return element.hasAttribute("hidden") || element.hasAttribute("inert") || element.getAttribute("aria-hidden")?.trim().toLowerCase() === "true";
191
+ }
192
+ //#endregion
193
+ //#region src/collection-order.ts
194
+ const documentPositionDisconnected = 1;
195
+ const documentPositionPreceding = 2;
196
+ const documentPositionFollowing = 4;
197
+ /** Resolve every reactive source of one record into an immutable snapshot. */
198
+ function resolveCollectionRecord(record) {
199
+ const { input } = record;
200
+ const element = input.element === void 0 ? null : toValue(input.element) ?? null;
201
+ const explicitText = input.textValue === void 0 ? void 0 : toValue(input.textValue);
202
+ const order = input.order === void 0 ? void 0 : toValue(input.order);
203
+ if (order !== void 0 && !Number.isSafeInteger(order)) throw new Error(`VIZE_UI_COLLECTION_ORDER_VALUE: ${String(input.key)} requires a safe integer order`);
204
+ return {
205
+ sequence: record.sequence,
206
+ item: Object.freeze({
207
+ key: input.key,
208
+ value: input.value,
209
+ element,
210
+ textValue: explicitText === null || explicitText === void 0 ? element === null ? "" : extractCollectionTextValue(element) : normalizeCollectionTextValue(explicitText),
211
+ disabled: input.disabled === void 0 ? false : Boolean(toValue(input.disabled)),
212
+ order
213
+ })
214
+ };
215
+ }
216
+ /**
217
+ * Sort resolved records in place using explicit order, DOM order, or
218
+ * registration order, rejecting ambiguous mixes of explicit orders.
219
+ */
220
+ function sortCollectionRecords(records) {
221
+ const explicitOrderCount = records.filter(({ item }) => item.order !== void 0).length;
222
+ if (explicitOrderCount > 0 && explicitOrderCount !== records.length) throw new Error("VIZE_UI_COLLECTION_ORDER_PARTIAL: every item must provide order when explicit ordering is used");
223
+ if (explicitOrderCount === records.length && records.length > 0) {
224
+ const seenOrders = /* @__PURE__ */ new Set();
225
+ for (const { item } of records) {
226
+ const order = item.order;
227
+ if (order === void 0) continue;
228
+ if (seenOrders.has(order)) throw new Error(`VIZE_UI_COLLECTION_ORDER_DUPLICATE: order ${String(order)} is registered more than once`);
229
+ seenOrders.add(order);
230
+ }
231
+ records.sort((left, right) => (left.item.order ?? 0) - (right.item.order ?? 0));
232
+ return;
233
+ }
234
+ const ownerDocument = records[0]?.item.element?.ownerDocument;
235
+ if (!(ownerDocument !== void 0 && records.every(({ item }) => item.element !== null && item.element.isConnected && item.element.ownerDocument === ownerDocument))) {
236
+ records.sort((left, right) => left.sequence - right.sequence);
237
+ return;
238
+ }
239
+ records.sort((left, right) => {
240
+ const leftElement = left.item.element;
241
+ const rightElement = right.item.element;
242
+ if (leftElement !== null && rightElement !== null && leftElement !== rightElement) {
243
+ const position = leftElement.compareDocumentPosition(rightElement);
244
+ if ((position & documentPositionDisconnected) === 0) {
245
+ if ((position & documentPositionFollowing) !== 0) return -1;
246
+ if ((position & documentPositionPreceding) !== 0) return 1;
247
+ }
248
+ }
249
+ return left.sequence - right.sequence;
250
+ });
251
+ }
252
+ //#endregion
253
+ //#region src/collection-registry.ts
254
+ /**
255
+ * Create an SSR-safe, mutation-aware registry for one compound collection.
256
+ *
257
+ * The registry never moves DOM focus itself. Consumers bind `activeKey` to
258
+ * roving tabindex or `aria-activedescendant`; synchronous recovery guarantees
259
+ * that removing or disabling the active item selects the next navigable item,
260
+ * then the previous one, before the mutation can leave focus state orphaned.
261
+ */
262
+ function createCollectionRegistry(options = {}) {
263
+ const disabledBehavior = options.disabledBehavior ?? "skip";
264
+ const collator = options.collator ?? new Intl.Collator(void 0, {
265
+ sensitivity: "base",
266
+ usage: "search"
267
+ });
268
+ const records = /* @__PURE__ */ new Map();
269
+ const revision = shallowRef(0);
270
+ const activeKeyState = shallowRef(null);
271
+ const activeKey = computed(() => activeKeyState.value);
272
+ let sequence = 0;
273
+ let disposed = false;
274
+ let mutationObservers = [];
275
+ let observedElements;
276
+ let observedConnected = [];
277
+ const items = computed(() => {
278
+ revision.value;
279
+ const resolved = [...records.values()].map(resolveCollectionRecord);
280
+ sortCollectionRecords(resolved);
281
+ return Object.freeze(resolved.map(({ item }) => item));
282
+ });
283
+ const navigableItems = computed(() => {
284
+ const resolved = items.value.filter((item) => disabledBehavior === "focusable" || !item.disabled);
285
+ return Object.freeze(resolved);
286
+ });
287
+ const updateActiveKey = (key, reason) => {
288
+ const previousKey = activeKeyState.value;
289
+ if (previousKey === key) return false;
290
+ activeKeyState.value = key;
291
+ options.onActiveKeyChange?.(Object.freeze({
292
+ key,
293
+ previousKey,
294
+ reason
295
+ }));
296
+ return true;
297
+ };
298
+ const stopRecovery = watch(navigableItems, (nextItems, previousItems) => {
299
+ const activeKey = activeKeyState.value;
300
+ if (activeKey === null || hasCollectionKey(nextItems, activeKey)) return;
301
+ const reason = hasCollectionKey(items.value, activeKey) ? "item-disabled" : "item-removed";
302
+ updateActiveKey(recoverCollectionKey(activeKey, previousItems, nextItems), reason);
303
+ }, { flush: "sync" });
304
+ const stopObservation = watch(items, (nextItems) => {
305
+ const nextElements = nextItems.map(({ element }) => element);
306
+ const nextConnected = nextElements.map((element) => element?.isConnected === true);
307
+ if (observedElements !== void 0 && observedElements.length === nextElements.length && observedElements.every((element, index) => element === nextElements[index] && observedConnected[index] === nextConnected[index])) return;
308
+ for (const observer of mutationObservers) observer.disconnect();
309
+ mutationObservers = observeCollectionMutations(nextItems, () => {
310
+ if (!disposed) revision.value += 1;
311
+ });
312
+ observedElements = nextElements;
313
+ observedConnected = nextConnected;
314
+ }, {
315
+ flush: "sync",
316
+ immediate: true
317
+ });
318
+ const assertMutable = () => {
319
+ if (disposed) throw new Error("VIZE_UI_COLLECTION_DISPOSED: a disposed registry cannot be mutated");
320
+ };
321
+ const refresh = () => {
322
+ assertMutable();
323
+ revision.value += 1;
324
+ };
325
+ const getItem = (key) => items.value.find((item) => item.key === key);
326
+ const setActiveKey = (key) => {
327
+ assertMutable();
328
+ if (key === null) return updateActiveKey(null, "programmatic");
329
+ const item = getItem(key);
330
+ if (item === void 0) throw new Error(`VIZE_UI_COLLECTION_KEY_MISSING: no item is registered for ${String(key)}`);
331
+ if (disabledBehavior === "skip" && item.disabled) throw new Error(`VIZE_UI_COLLECTION_KEY_DISABLED: ${String(key)} is not navigable`);
332
+ return updateActiveKey(key, "programmatic");
333
+ };
334
+ const getNavigationKey = (direction, navigationOptions = {}) => {
335
+ const candidates = navigableItems.value;
336
+ if (candidates.length === 0) return null;
337
+ if (direction === "first") return candidates[0]?.key ?? null;
338
+ if (direction === "last") return candidates.at(-1)?.key ?? null;
339
+ const fromKey = navigationOptions.fromKey === void 0 ? activeKeyState.value : navigationOptions.fromKey;
340
+ if (fromKey === null) return direction === "next" ? candidates[0]?.key ?? null : candidates.at(-1)?.key ?? null;
341
+ const candidateIndex = candidates.findIndex((item) => item.key === fromKey);
342
+ if (candidateIndex >= 0) {
343
+ const target = candidates[candidateIndex + (direction === "next" ? 1 : -1)];
344
+ if (target !== void 0) return target.key;
345
+ if (navigationOptions.loop === true) return direction === "next" ? candidates[0]?.key ?? null : candidates.at(-1)?.key ?? null;
346
+ return fromKey;
347
+ }
348
+ const allItems = items.value;
349
+ const itemIndex = allItems.findIndex((item) => item.key === fromKey);
350
+ if (itemIndex >= 0) {
351
+ const step = direction === "next" ? 1 : -1;
352
+ for (let index = itemIndex + step; index >= 0 && index < allItems.length; index += step) {
353
+ const candidate = allItems[index];
354
+ if (candidate !== void 0 && hasCollectionKey(candidates, candidate.key)) return candidate.key;
355
+ }
356
+ if (navigationOptions.loop !== true) return null;
357
+ }
358
+ return direction === "next" ? candidates[0]?.key ?? null : candidates.at(-1)?.key ?? null;
359
+ };
360
+ const moveActive = (direction, navigationOptions = {}) => {
361
+ assertMutable();
362
+ const target = getNavigationKey(direction, navigationOptions);
363
+ if (target !== null) updateActiveKey(target, "navigation");
364
+ return target;
365
+ };
366
+ const findByTextValue = (query, searchOptions = {}) => {
367
+ const normalizedQuery = normalizeCollectionTextValue(query);
368
+ if (normalizedQuery.length === 0) return null;
369
+ const candidates = navigableItems.value;
370
+ if (candidates.length === 0) return null;
371
+ const fromKey = searchOptions.fromKey === void 0 ? activeKeyState.value : searchOptions.fromKey;
372
+ const fromIndex = fromKey === null ? -1 : candidates.findIndex((candidate) => candidate.key === fromKey);
373
+ const after = candidates.slice(fromIndex + 1);
374
+ const before = searchOptions.loop === false ? [] : candidates.slice(0, fromIndex + 1);
375
+ const searchCandidates = [...after, ...before];
376
+ const searchCollator = searchOptions.collator ?? collator;
377
+ const match = searchOptions.match ?? "prefix";
378
+ for (const candidate of searchCandidates) {
379
+ if (candidate.textValue.length === 0) continue;
380
+ if (match === "exact" ? searchCollator.compare(candidate.textValue, normalizedQuery) === 0 : collectionTextStartsWith(candidate.textValue, normalizedQuery, searchCollator)) return candidate.key;
381
+ }
382
+ return null;
383
+ };
384
+ const moveActiveByTextValue = (query, searchOptions = {}) => {
385
+ assertMutable();
386
+ const target = findByTextValue(query, searchOptions);
387
+ if (target !== null) updateActiveKey(target, "typeahead");
388
+ return target;
389
+ };
390
+ const register = (input) => {
391
+ assertMutable();
392
+ validateCollectionKey(input.key);
393
+ if (records.has(input.key)) throw new Error(`VIZE_UI_COLLECTION_KEY_DUPLICATE: ${String(input.key)} is already registered`);
394
+ const record = {
395
+ input: Object.freeze({ ...input }),
396
+ sequence: sequence++
397
+ };
398
+ records.set(input.key, record);
399
+ try {
400
+ revision.value += 1;
401
+ } catch (error) {
402
+ records.delete(input.key);
403
+ revision.value += 1;
404
+ throw error;
405
+ }
406
+ const unregister = () => {
407
+ if (records.get(input.key) !== record) return false;
408
+ records.delete(input.key);
409
+ revision.value += 1;
410
+ return true;
411
+ };
412
+ const registration = Object.freeze({
413
+ key: input.key,
414
+ registered: computed(() => {
415
+ revision.value;
416
+ return records.get(input.key) === record;
417
+ }),
418
+ unregister
419
+ });
420
+ if (getCurrentScope() !== void 0) onScopeDispose(unregister);
421
+ return registration;
422
+ };
423
+ const dispose = () => {
424
+ if (disposed) return false;
425
+ disposed = true;
426
+ stopRecovery();
427
+ stopObservation();
428
+ for (const observer of mutationObservers) observer.disconnect();
429
+ mutationObservers = [];
430
+ records.clear();
431
+ revision.value += 1;
432
+ updateActiveKey(null, "registry-disposed");
433
+ return true;
434
+ };
435
+ const registry = Object.freeze({
436
+ items,
437
+ navigableItems,
438
+ activeKey,
439
+ register,
440
+ getItem,
441
+ refresh,
442
+ setActiveKey,
443
+ getNavigationKey,
444
+ moveActive,
445
+ findByTextValue,
446
+ moveActiveByTextValue,
447
+ dispose
448
+ });
449
+ if (getCurrentScope() !== void 0) onScopeDispose(dispose);
450
+ return registry;
451
+ }
452
+ //#endregion
453
+ export { extractCollectionTextValue as n, normalizeCollectionTextValue as r, createCollectionRegistry as t };
@@ -0,0 +1,185 @@
1
+ import { ComputedRef, MaybeRefOrGetter } from "vue";
2
+
3
+ //#region src/collection-types.d.ts
4
+ /** Serializable identity accepted by every collection primitive. */
5
+ type CollectionKey = string | number;
6
+ /** How disabled items participate in focus navigation and typeahead. */
7
+ type CollectionDisabledBehavior = "skip" | "focusable";
8
+ /** Direction understood by {@link CollectionRegistry.getNavigationKey}. */
9
+ type CollectionNavigationDirection = "first" | "last" | "next" | "previous";
10
+ /** Why the registry changed its active key. */
11
+ type CollectionActiveChangeReason = "programmatic" | "navigation" | "typeahead" | "item-removed" | "item-disabled" | "registry-disposed";
12
+ /** Reactive sources registered for one logical collection item. */
13
+ interface CollectionItemInput<Key extends CollectionKey, Value> {
14
+ /** Stable, request-independent identity. Keys must be unique within a registry. */
15
+ readonly key: Key;
16
+ /** Consumer-owned data associated with this item. */
17
+ readonly value: Value;
18
+ /** Rendered element used for live DOM ordering and text extraction. */
19
+ readonly element?: MaybeRefOrGetter<Element | null | undefined>;
20
+ /**
21
+ * Explicit typeahead text. `null` and `undefined` select accessible DOM text
22
+ * extraction; an empty string deliberately opts the item out of typeahead.
23
+ */
24
+ readonly textValue?: MaybeRefOrGetter<string | null | undefined>;
25
+ /** Whether activation is disabled. Navigation follows `disabledBehavior`. */
26
+ readonly disabled?: MaybeRefOrGetter<boolean | undefined>;
27
+ /**
28
+ * Deterministic order for virtualized, portalled, or server-only items.
29
+ *
30
+ * If one item supplies an order, every item must supply a unique safe integer.
31
+ * Otherwise DOM order is used once every item is connected in one document;
32
+ * registration order is the SSR, partial-mount, and disconnected fallback.
33
+ */
34
+ readonly order?: MaybeRefOrGetter<number | undefined>;
35
+ }
36
+ /** Immutable, resolved view of a registered collection item. */
37
+ interface CollectionItem<Key extends CollectionKey, Value> {
38
+ /** Stable item identity. */
39
+ readonly key: Key;
40
+ /** Consumer-owned item data. */
41
+ readonly value: Value;
42
+ /** Current rendered element, or `null` during SSR and before mount. */
43
+ readonly element: Element | null;
44
+ /** Normalized text used by typeahead. */
45
+ readonly textValue: string;
46
+ /** Current disabled state. */
47
+ readonly disabled: boolean;
48
+ /** Explicit virtual order, or `undefined` when DOM order is authoritative. */
49
+ readonly order: number | undefined;
50
+ }
51
+ /** Active-key transition reported to collection consumers. */
52
+ interface CollectionActiveChange<Key extends CollectionKey> {
53
+ /** New active key, or `null` when no navigable item remains. */
54
+ readonly key: Key | null;
55
+ /** Active key before the transition. */
56
+ readonly previousKey: Key | null;
57
+ /** Stable cause suitable for focus and announcement policies. */
58
+ readonly reason: CollectionActiveChangeReason;
59
+ }
60
+ /** Options for {@link createCollectionRegistry}. */
61
+ interface CollectionRegistryOptions<Key extends CollectionKey> {
62
+ /**
63
+ * Whether disabled items are omitted from navigation or remain focusable.
64
+ *
65
+ * Menus commonly use `"focusable"`; listboxes and roving tab stops commonly
66
+ * use `"skip"`.
67
+ *
68
+ * @default "skip"
69
+ */
70
+ readonly disabledBehavior?: CollectionDisabledBehavior;
71
+ /** Locale-aware comparator used by typeahead matching. */
72
+ readonly collator?: Intl.Collator;
73
+ /** Called synchronously after a distinct active-key transition. */
74
+ readonly onActiveKeyChange?: (change: CollectionActiveChange<Key>) => void;
75
+ }
76
+ /** Options for directional collection navigation. */
77
+ interface CollectionNavigationOptions<Key extends CollectionKey> {
78
+ /**
79
+ * Key navigation starts from.
80
+ *
81
+ * @default The registry active key
82
+ */
83
+ readonly fromKey?: Key | null;
84
+ /**
85
+ * Whether navigation wraps at the first and last item.
86
+ *
87
+ * @default false
88
+ */
89
+ readonly loop?: boolean;
90
+ }
91
+ /** Options for locale-aware collection text lookup. */
92
+ interface CollectionTextSearchOptions<Key extends CollectionKey> {
93
+ /**
94
+ * Search begins after this key so repeated input cycles matching items.
95
+ *
96
+ * @default The registry active key
97
+ */
98
+ readonly fromKey?: Key | null;
99
+ /**
100
+ * Match normalized prefixes or the complete normalized text.
101
+ *
102
+ * @default "prefix"
103
+ */
104
+ readonly match?: "prefix" | "exact";
105
+ /**
106
+ * Whether search wraps to the beginning of the collection.
107
+ *
108
+ * @default true
109
+ */
110
+ readonly loop?: boolean;
111
+ /** Per-search locale comparator override. */
112
+ readonly collator?: Intl.Collator;
113
+ }
114
+ /** Idempotent lifecycle handle returned by {@link CollectionRegistry.register}. */
115
+ interface CollectionRegistration<Key extends CollectionKey> {
116
+ /** Registered stable key. */
117
+ readonly key: Key;
118
+ /** Whether this exact registration still owns its key. */
119
+ readonly registered: ComputedRef<boolean>;
120
+ /** Remove this registration and report whether it was present. */
121
+ readonly unregister: () => boolean;
122
+ }
123
+ /** Type-safe registry shared by compound collection components. */
124
+ interface CollectionRegistry<Key extends CollectionKey, Value> {
125
+ /** All items in deterministic virtual, DOM, or registration order. */
126
+ readonly items: ComputedRef<readonly CollectionItem<Key, Value>[]>;
127
+ /** Items eligible for focus navigation under the disabled policy. */
128
+ readonly navigableItems: ComputedRef<readonly CollectionItem<Key, Value>[]>;
129
+ /** Logical focus key used by roving-tabindex and `aria-activedescendant` adapters. */
130
+ readonly activeKey: ComputedRef<Key | null>;
131
+ /** Register one item. Vue scope disposal unregisters it automatically. */
132
+ readonly register: (input: CollectionItemInput<Key, Value>) => CollectionRegistration<Key>;
133
+ /** Resolve the latest immutable snapshot for a key. */
134
+ readonly getItem: (key: Key) => CollectionItem<Key, Value> | undefined;
135
+ /**
136
+ * Re-resolve reactive sources, DOM order, and extracted text immediately.
137
+ *
138
+ * A scoped MutationObserver calls this automatically for connected items;
139
+ * the explicit hook supports custom renderers and observer-free environments.
140
+ */
141
+ readonly refresh: () => void;
142
+ /** Set a registered, navigable active key, or clear it with `null`. */
143
+ readonly setActiveKey: (key: Key | null) => boolean;
144
+ /** Resolve a directional target without changing active state. */
145
+ readonly getNavigationKey: (direction: CollectionNavigationDirection, options?: CollectionNavigationOptions<Key>) => Key | null;
146
+ /** Move active state directionally and return the resulting target. */
147
+ readonly moveActive: (direction: CollectionNavigationDirection, options?: CollectionNavigationOptions<Key>) => Key | null;
148
+ /** Find the next locale-aware text match without changing active state. */
149
+ readonly findByTextValue: (query: string, options?: CollectionTextSearchOptions<Key>) => Key | null;
150
+ /** Move active state to the next locale-aware text match. */
151
+ readonly moveActiveByTextValue: (query: string, options?: CollectionTextSearchOptions<Key>) => Key | null;
152
+ /** Stop observation, clear items, and reject future mutations. */
153
+ readonly dispose: () => boolean;
154
+ }
155
+ //#endregion
156
+ //#region src/collection-registry.d.ts
157
+ /**
158
+ * Create an SSR-safe, mutation-aware registry for one compound collection.
159
+ *
160
+ * The registry never moves DOM focus itself. Consumers bind `activeKey` to
161
+ * roving tabindex or `aria-activedescendant`; synchronous recovery guarantees
162
+ * that removing or disabling the active item selects the next navigable item,
163
+ * then the previous one, before the mutation can leave focus state orphaned.
164
+ */
165
+ declare function createCollectionRegistry<Key extends CollectionKey, Value>(options?: CollectionRegistryOptions<Key>): CollectionRegistry<Key, Value>;
166
+ //#endregion
167
+ //#region src/collection-text.d.ts
168
+ /**
169
+ * Normalize authored or extracted typeahead text without applying a locale.
170
+ *
171
+ * Unicode is normalized to NFC and every whitespace run becomes one ASCII
172
+ * space. Locale-sensitive equality remains the responsibility of a collator.
173
+ */
174
+ declare function normalizeCollectionTextValue(value: string): string;
175
+ /**
176
+ * Extract a practical accessible text value from an item element.
177
+ *
178
+ * `aria-labelledby` and `aria-label` take precedence, hidden descendants are
179
+ * excluded unless explicitly referenced, and image/input fallbacks are
180
+ * included. Complex widgets should still provide `textValue` explicitly when
181
+ * their typeahead label differs from their accessible name.
182
+ */
183
+ declare function extractCollectionTextValue(element: Element): string;
184
+ //#endregion
185
+ export { CollectionActiveChangeReason as a, CollectionItemInput as c, CollectionNavigationOptions as d, CollectionRegistration as f, CollectionTextSearchOptions as h, CollectionActiveChange as i, CollectionKey as l, CollectionRegistryOptions as m, normalizeCollectionTextValue as n, CollectionDisabledBehavior as o, CollectionRegistry as p, createCollectionRegistry as r, CollectionItem as s, extractCollectionTextValue as t, CollectionNavigationDirection as u };
@@ -0,0 +1,2 @@
1
+ import { a as CollectionActiveChangeReason, c as CollectionItemInput, d as CollectionNavigationOptions, f as CollectionRegistration, h as CollectionTextSearchOptions, i as CollectionActiveChange, l as CollectionKey, m as CollectionRegistryOptions, n as normalizeCollectionTextValue, o as CollectionDisabledBehavior, p as CollectionRegistry, r as createCollectionRegistry, s as CollectionItem, t as extractCollectionTextValue, u as CollectionNavigationDirection } from "./collection-aHZ6Pf0e.mjs";
2
+ export { CollectionActiveChange, CollectionActiveChangeReason, CollectionDisabledBehavior, CollectionItem, CollectionItemInput, CollectionKey, CollectionNavigationDirection, CollectionNavigationOptions, CollectionRegistration, CollectionRegistry, CollectionRegistryOptions, CollectionTextSearchOptions, createCollectionRegistry, extractCollectionTextValue, normalizeCollectionTextValue };
@@ -0,0 +1,2 @@
1
+ import { n as extractCollectionTextValue, r as normalizeCollectionTextValue, t as createCollectionRegistry } from "./collection-CoOROaho.mjs";
2
+ export { createCollectionRegistry, extractCollectionTextValue, normalizeCollectionTextValue };