@vuetify/v0 0.0.18 → 0.0.21

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.
@@ -1,4 +1,4 @@
1
- import { computed, createBlock, createCommentVNode, createPropsRestProxy, createTextVNode, defineComponent, effectScope, getCurrentInstance, guardReactiveProps, inject, isRef, mergeModels, mergeProps, normalizeProps, onBeforeUnmount, onMounted, onScopeDispose, onUnmounted, openBlock, provide, reactive, readonly, ref, renderSlot, resolveDynamicComponent, shallowReactive, shallowReadonly, shallowRef, toDisplayString, toRef, toValue, unref, useAttrs, useId, useModel, useTemplateRef, vShow, watch, withCtx, withDirectives } from "vue";
1
+ import { computed, createBlock, createCommentVNode, createPropsRestProxy, createTextVNode, defineComponent, effectScope, getCurrentInstance, guardReactiveProps, inject, isRef, mergeModels, mergeProps, normalizeProps, onBeforeUnmount, onMounted, onScopeDispose, onUnmounted, openBlock, provide, reactive, readonly, ref, renderSlot, resolveDynamicComponent, shallowReactive, shallowReadonly, shallowRef, toDisplayString, toRef, toValue, unref, useAttrs, useId, useModel, useTemplateRef, vShow, watch, watchEffect, withCtx, withDirectives } from "vue";
2
2
 
3
3
  //#region src/constants/htmlElements.ts
4
4
  const selfClosingTags = [
@@ -78,69 +78,299 @@ function isSelfClosingTag(tag) {
78
78
 
79
79
  //#endregion
80
80
  //#region src/utilities/helpers.ts
81
+ /**
82
+ * Checks if a value is a function
83
+ *
84
+ * @param item The value to check
85
+ * @returns True if the value is a function
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * isFunction(() => {}) // true
90
+ * isFunction('string') // false
91
+ * ```
92
+ */
81
93
  /* @__NO_SIDE_EFFECTS__ */
82
94
  function isFunction(item) {
83
95
  return typeof item === "function";
84
96
  }
97
+ /**
98
+ * Checks if a value is a string
99
+ *
100
+ * @param item The value to check
101
+ * @returns True if the value is a string
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * isString('hello') // true
106
+ * isString(123) // false
107
+ * ```
108
+ */
85
109
  /* @__NO_SIDE_EFFECTS__ */
86
110
  function isString(item) {
87
111
  return typeof item === "string";
88
112
  }
113
+ /**
114
+ * Checks if a value is a number
115
+ *
116
+ * @param item The value to check
117
+ * @returns True if the value is a number (including NaN)
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * isNumber(123) // true
122
+ * isNumber(NaN) // true
123
+ * isNumber('123') // false
124
+ * ```
125
+ *
126
+ * @see {@link isNaN} to check for NaN specifically
127
+ */
89
128
  /* @__NO_SIDE_EFFECTS__ */
90
129
  function isNumber(item) {
91
130
  return typeof item === "number";
92
131
  }
132
+ /**
133
+ * Checks if a value is a boolean
134
+ *
135
+ * @param item The value to check
136
+ * @returns True if the value is a boolean
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * isBoolean(true) // true
141
+ * isBoolean(false) // true
142
+ * isBoolean(0) // false
143
+ * ```
144
+ */
93
145
  /* @__NO_SIDE_EFFECTS__ */
94
146
  function isBoolean(item) {
95
147
  return typeof item === "boolean";
96
148
  }
149
+ /**
150
+ * Checks if a value is a plain object (excludes null and arrays)
151
+ *
152
+ * @param item The value to check
153
+ * @returns True if the value is a plain object
154
+ *
155
+ * @remarks
156
+ * Returns false for null and arrays, even though `typeof null === 'object'`
157
+ * and `typeof [] === 'object'` in JavaScript.
158
+ *
159
+ * @example
160
+ * ```ts
161
+ * isObject({}) // true
162
+ * isObject({ a: 1 }) // true
163
+ * isObject(null) // false
164
+ * isObject([]) // false
165
+ * ```
166
+ *
167
+ * @see {@link isArray} to check for arrays
168
+ * @see {@link isNull} to check for null
169
+ */
97
170
  /* @__NO_SIDE_EFFECTS__ */
98
171
  function isObject(item) {
99
172
  return typeof item === "object" && item !== null && !Array.isArray(item);
100
173
  }
174
+ /**
175
+ * Checks if a value is an array
176
+ *
177
+ * @param item The value to check
178
+ * @returns True if the value is an array
179
+ *
180
+ * @example
181
+ * ```ts
182
+ * isArray([]) // true
183
+ * isArray([1, 2, 3]) // true
184
+ * isArray('string') // false
185
+ * ```
186
+ */
101
187
  /* @__NO_SIDE_EFFECTS__ */
102
188
  function isArray(item) {
103
189
  return Array.isArray(item);
104
190
  }
191
+ /**
192
+ * Checks if a value is null
193
+ *
194
+ * @param item The value to check
195
+ * @returns True if the value is null
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * isNull(null) // true
200
+ * isNull(undefined) // false
201
+ * ```
202
+ *
203
+ * @see {@link isUndefined} to check for undefined
204
+ * @see {@link isNullOrUndefined} to check for either
205
+ */
105
206
  /* @__NO_SIDE_EFFECTS__ */
106
207
  function isNull(item) {
107
208
  return item === null;
108
209
  }
210
+ /**
211
+ * Checks if a value is null or undefined
212
+ *
213
+ * @param item The value to check
214
+ * @returns True if the value is null or undefined
215
+ *
216
+ * @remarks
217
+ * Uses loose equality (`== null`) which matches both null and undefined.
218
+ *
219
+ * @example
220
+ * ```ts
221
+ * isNullOrUndefined(null) // true
222
+ * isNullOrUndefined(undefined) // true
223
+ * isNullOrUndefined(0) // false
224
+ * isNullOrUndefined('') // false
225
+ * ```
226
+ *
227
+ * @see {@link isNull} to check for null only
228
+ * @see {@link isUndefined} to check for undefined only
229
+ */
109
230
  /* @__NO_SIDE_EFFECTS__ */
110
231
  function isNullOrUndefined(item) {
111
232
  return item == null;
112
233
  }
234
+ /**
235
+ * Checks if a value is undefined
236
+ *
237
+ * @param item The value to check
238
+ * @returns True if the value is undefined
239
+ *
240
+ * @example
241
+ * ```ts
242
+ * isUndefined(undefined) // true
243
+ * isUndefined(null) // false
244
+ * ```
245
+ *
246
+ * @see {@link isNull} to check for null
247
+ * @see {@link isNullOrUndefined} to check for either
248
+ */
113
249
  /* @__NO_SIDE_EFFECTS__ */
114
250
  function isUndefined(item) {
115
251
  return item === void 0;
116
252
  }
253
+ /**
254
+ * Checks if a value is a primitive (string, number, or boolean)
255
+ *
256
+ * @param item The value to check
257
+ * @returns True if the value is a string, number, or boolean
258
+ *
259
+ * @example
260
+ * ```ts
261
+ * isPrimitive('hello') // true
262
+ * isPrimitive(123) // true
263
+ * isPrimitive(true) // true
264
+ * isPrimitive({}) // false
265
+ * isPrimitive(null) // false
266
+ * ```
267
+ */
117
268
  /* @__NO_SIDE_EFFECTS__ */
118
269
  function isPrimitive(item) {
119
270
  return typeof item === "string" || typeof item === "number" || typeof item === "boolean";
120
271
  }
272
+ /**
273
+ * Checks if a value is a symbol
274
+ *
275
+ * @param item The value to check
276
+ * @returns True if the value is a symbol
277
+ *
278
+ * @example
279
+ * ```ts
280
+ * isSymbol(Symbol('test')) // true
281
+ * isSymbol('symbol') // false
282
+ * ```
283
+ */
121
284
  /* @__NO_SIDE_EFFECTS__ */
122
285
  function isSymbol(item) {
123
286
  return typeof item === "symbol";
124
287
  }
288
+ /**
289
+ * Checks if a value is NaN (Not a Number)
290
+ *
291
+ * @param item The value to check
292
+ * @returns True if the value is NaN
293
+ *
294
+ * @remarks
295
+ * Uses `Number.isNaN()` which only returns true for the actual NaN value,
296
+ * unlike the global `isNaN()` which coerces the argument to a number first.
297
+ *
298
+ * @example
299
+ * ```ts
300
+ * isNaN(NaN) // true
301
+ * isNaN(123) // false
302
+ * isNaN('hello') // false (unlike global isNaN)
303
+ * isNaN(undefined) // false (unlike global isNaN)
304
+ * ```
305
+ *
306
+ * @see {@link isNumber} to check if a value is a number type
307
+ */
125
308
  /* @__NO_SIDE_EFFECTS__ */
126
309
  function isNaN(item) {
127
310
  return /* @__PURE__ */ isNumber(item) && Number.isNaN(item);
128
311
  }
312
+ /**
313
+ * Deeply merges source objects into a target object
314
+ *
315
+ * @param target The target object to merge into (will be mutated)
316
+ * @param sources One or more source objects to merge from
317
+ * @returns The mutated target object
318
+ *
319
+ * @remarks
320
+ * - Mutates the target object in place
321
+ * - Nested objects are recursively merged
322
+ * - Arrays are replaced, not merged
323
+ * - Primitives from sources overwrite target values
324
+ *
325
+ * @example
326
+ * ```ts
327
+ * const target = { a: 1, b: { c: 2 } }
328
+ * mergeDeep(target, { b: { d: 3 } })
329
+ * // target is now { a: 1, b: { c: 2, d: 3 } }
330
+ *
331
+ * // Multiple sources
332
+ * mergeDeep({}, { a: 1 }, { b: 2 }) // { a: 1, b: 2 }
333
+ *
334
+ * // Arrays are replaced
335
+ * mergeDeep({ arr: [1, 2] }, { arr: [3] }) // { arr: [3] }
336
+ * ```
337
+ */
338
+ const UNSAFE_KEYS = new Set([
339
+ "__proto__",
340
+ "constructor",
341
+ "prototype"
342
+ ]);
129
343
  /* @__NO_SIDE_EFFECTS__ */
130
344
  function mergeDeep(target, ...sources) {
131
345
  if (sources.length === 0) return target;
132
346
  const source = sources.shift();
133
- if (/* @__PURE__ */ isObject(target) && /* @__PURE__ */ isObject(source)) {
134
- for (const key in source) if (Object.prototype.hasOwnProperty.call(source, key)) {
135
- const sourceValue = source[key];
136
- const targetValue = target[key];
137
- if (/* @__PURE__ */ isObject(sourceValue)) {
138
- if (!/* @__PURE__ */ isObject(targetValue)) Object.assign(target, { [key]: {} });
139
- } else Object.assign(target, { [key]: sourceValue });
140
- }
347
+ if (/* @__PURE__ */ isObject(target) && /* @__PURE__ */ isObject(source)) for (const key in source) {
348
+ if (UNSAFE_KEYS.has(key)) continue;
349
+ if (!Object.prototype.hasOwnProperty.call(source, key)) continue;
350
+ const sourceValue = source[key];
351
+ const targetValue = target[key];
352
+ if (/* @__PURE__ */ isObject(sourceValue)) {
353
+ if (!/* @__PURE__ */ isObject(targetValue)) Object.assign(target, { [key]: {} });
354
+ target[key];
355
+ } else Object.assign(target, { [key]: sourceValue });
141
356
  }
142
357
  return /* @__PURE__ */ mergeDeep(target, ...sources);
143
358
  }
359
+ /**
360
+ * Generates a random 7-character alphanumeric ID
361
+ *
362
+ * @returns A random string of 7 characters (a-z, 0-9)
363
+ *
364
+ * @remarks
365
+ * Uses `Math.random()` converted to base-36. Not cryptographically secure.
366
+ * Suitable for unique keys in UI components, not for security purposes.
367
+ *
368
+ * @example
369
+ * ```ts
370
+ * genId() // 'k7x9m2p'
371
+ * genId() // 'a3b8c1d'
372
+ * ```
373
+ */
144
374
  /* @__NO_SIDE_EFFECTS__ */
145
375
  function genId() {
146
376
  return Math.random().toString(36).slice(2, 9);
@@ -216,6 +446,27 @@ function debounce(fn, delay) {
216
446
 
217
447
  //#endregion
218
448
  //#region src/components/Atom/Atom.vue
449
+ /**
450
+ * @module Atom
451
+ *
452
+ * @remarks
453
+ * Foundation component providing polymorphic rendering with three modes:
454
+ * 1. **Element Mode** (default): Renders as any HTML element via the `as` prop
455
+ * 2. **Renderless Mode**: Renders slot content directly without wrapper
456
+ * 3. **Null Mode**: Equivalent to renderless when `as={null}`
457
+ *
458
+ * Key features:
459
+ * - Polymorphic element rendering (div, button, span, etc.)
460
+ * - Self-closing tag detection (img, input, br, hr, etc.)
461
+ * - Automatic attribute forwarding to rendered element
462
+ * - Generic slot props typing for type-safe attribute passing
463
+ * - Template ref exposure for DOM element access
464
+ * - Renderless mode for maximum flexibility
465
+ *
466
+ * The Atom component is the lowest-level primitive in the component system,
467
+ * serving as the foundation for all other components that need polymorphic
468
+ * rendering capabilities.
469
+ */
219
470
  const _sfc_main$27 = /* @__PURE__ */ defineComponent({
220
471
  name: "Atom",
221
472
  __name: "Atom",
@@ -394,6 +645,18 @@ function createTrinity(useContext$1, provideContext$1, context) {
394
645
  ];
395
646
  }
396
647
 
648
+ //#endregion
649
+ //#region src/constants/globals.ts
650
+ const IN_BROWSER = typeof window !== "undefined";
651
+ const SUPPORTS_TOUCH = IN_BROWSER && ("ontouchstart" in window || window.navigator.maxTouchPoints > 0);
652
+ const SUPPORTS_MATCH_MEDIA = IN_BROWSER && "matchMedia" in window && typeof window.matchMedia === "function";
653
+ const SUPPORTS_OBSERVER = IN_BROWSER && "ResizeObserver" in window;
654
+ const SUPPORTS_INTERSECTION_OBSERVER = IN_BROWSER && "IntersectionObserver" in window;
655
+ const SUPPORTS_MUTATION_OBSERVER = IN_BROWSER && "MutationObserver" in window;
656
+ const version = "0.0.21";
657
+ /* v8 ignore next -- build-time constant, __DEV__ short-circuits in tests */
658
+ const __LOGGER_ENABLED__ = false;
659
+
397
660
  //#endregion
398
661
  //#region src/composables/createPlugin/index.ts
399
662
  /**
@@ -505,17 +768,6 @@ var PinoLoggerAdapter = class {
505
768
  }
506
769
  };
507
770
 
508
- //#endregion
509
- //#region src/constants/globals.ts
510
- const IN_BROWSER = typeof window !== "undefined";
511
- const SUPPORTS_TOUCH = IN_BROWSER && ("ontouchstart" in window || window.navigator.maxTouchPoints > 0);
512
- const SUPPORTS_MATCH_MEDIA = IN_BROWSER && "matchMedia" in window && typeof window.matchMedia === "function";
513
- const SUPPORTS_OBSERVER = IN_BROWSER && "ResizeObserver" in window;
514
- const SUPPORTS_INTERSECTION_OBSERVER = IN_BROWSER && "IntersectionObserver" in window;
515
- const SUPPORTS_MUTATION_OBSERVER = IN_BROWSER && "MutationObserver" in window;
516
- const version = "0.0.18";
517
- const __LOGGER_ENABLED__ = false;
518
-
519
771
  //#endregion
520
772
  //#region src/composables/useLogger/adapters/v0.ts
521
773
  /**
@@ -561,10 +813,12 @@ var Vuetify0LoggerAdapter = class {
561
813
  }
562
814
  timestamp() {
563
815
  if (!IN_BROWSER) return (/* @__PURE__ */ new Date()).toISOString();
816
+ /* v8 ignore next -- defensive fallback, toTimeString always returns valid format */
564
817
  return (/* @__PURE__ */ new Date()).toTimeString().split(" ")[0] ?? "";
565
818
  }
566
819
  style(level) {
567
820
  if (!this.colors || !IN_BROWSER) return "";
821
+ /* v8 ignore next -- LogLevel union is exhaustive */
568
822
  return {
569
823
  trace: "color: #64748b",
570
824
  debug: "color: #3b82f6",
@@ -817,6 +1071,8 @@ function useLogger(namespace = "v0:logger") {
817
1071
  /**
818
1072
  * @module useRegistry
819
1073
  *
1074
+ * @see https://0.vuetifyjs.com/composables/registration/use-registry
1075
+ *
820
1076
  * @remarks
821
1077
  * A foundational composable for managing collections of items (tickets) with:
822
1078
  * - Unique ID-based access
@@ -873,17 +1129,21 @@ function useRegistry(options) {
873
1129
  }
874
1130
  function on(event, cb) {
875
1131
  if (!events) {
876
- logger.warn(`Attempted to register event listener for "${event}" but events are disabled.`);
1132
+ logger.warn(`Events are disabled. Initialize with \`useRegistry({ events: true })\` to enable.`);
877
1133
  return;
878
1134
  }
879
1135
  if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
880
1136
  listeners.get(event).add(cb);
881
1137
  }
882
1138
  function off(event, cb) {
1139
+ if (!events) {
1140
+ logger.warn(`Events are disabled. Initialize with \`useRegistry({ events: true })\` to enable.`);
1141
+ return;
1142
+ }
883
1143
  listeners.get(event)?.delete(cb);
884
1144
  }
885
1145
  function dispose() {
886
- if (listeners.size > 0) listeners.clear();
1146
+ listeners.clear();
887
1147
  clear();
888
1148
  }
889
1149
  function get(id) {
@@ -972,9 +1232,9 @@ function useRegistry(options) {
972
1232
  return entries$1;
973
1233
  }
974
1234
  function clear() {
975
- if (collection.size > 0) collection.clear();
976
- if (catalog.size > 0) catalog.clear();
977
- if (directory.size > 0) directory.clear();
1235
+ collection.clear();
1236
+ catalog.clear();
1237
+ directory.clear();
978
1238
  invalidate();
979
1239
  indexDependentCount = 0;
980
1240
  needsReindex = false;
@@ -983,7 +1243,7 @@ function useRegistry(options) {
983
1243
  }
984
1244
  function invalidate() {
985
1245
  if (batching) return;
986
- if (cache.size > 0) cache.clear();
1246
+ cache.clear();
987
1247
  }
988
1248
  function queueEmit(event, data) {
989
1249
  if (batching) pendingEmits.push({
@@ -998,7 +1258,7 @@ function useRegistry(options) {
998
1258
  pendingEmits = [];
999
1259
  try {
1000
1260
  const result = fn();
1001
- if (cache.size > 0) cache.clear();
1261
+ cache.clear();
1002
1262
  for (const { event, data } of pendingEmits) emit(event, data);
1003
1263
  return result;
1004
1264
  } finally {
@@ -1009,8 +1269,8 @@ function useRegistry(options) {
1009
1269
  function reindex() {
1010
1270
  const startIndex = minDirtyIndex === Infinity ? 0 : minDirtyIndex;
1011
1271
  if (startIndex === 0) {
1012
- if (catalog.size > 0) catalog.clear();
1013
- if (directory.size > 0) directory.clear();
1272
+ catalog.clear();
1273
+ directory.clear();
1014
1274
  }
1015
1275
  invalidate();
1016
1276
  let index = 0;
@@ -1037,7 +1297,7 @@ function useRegistry(options) {
1037
1297
  const size = collection.size;
1038
1298
  const id = registration.id ?? /* @__PURE__ */ genId();
1039
1299
  if (has(id)) {
1040
- logger.warn(`Ticket with id "${id}" already exists in the registry. Skipping registration.`);
1300
+ logger.warn(`Ticket "${id}" already exists. Use \`upsert()\` to update or check \`has()\` before registering.`);
1041
1301
  return get(id);
1042
1302
  }
1043
1303
  const valueIsUndefined = /* @__PURE__ */ isUndefined(registration.value);
@@ -1087,14 +1347,18 @@ function useRegistry(options) {
1087
1347
  }
1088
1348
  if (removed.length === 0) return;
1089
1349
  invalidate();
1090
- if (events) for (const ticket of removed) emit("unregister:ticket", ticket);
1350
+ for (const ticket of removed) queueEmit("unregister:ticket", ticket);
1091
1351
  needsReindex = true;
1092
1352
  }
1093
1353
  function seek(direction = "first", from, predicate) {
1094
1354
  if (collection.size === 0) return void 0;
1095
1355
  if (needsReindex) reindex();
1356
+ if (!predicate && /* @__PURE__ */ isUndefined(from)) {
1357
+ const tickets$1 = values();
1358
+ return direction === "first" ? tickets$1[0] : tickets$1.at(-1);
1359
+ }
1096
1360
  const tickets = values();
1097
- const index = /* @__PURE__ */ isUndefined(from) ? void 0 : Math.max(0, Math.min(from, tickets.length - 1));
1361
+ const index = /* @__PURE__ */ isUndefined(from) ? void 0 : /* @__PURE__ */ clamp(from, 0, tickets.length - 1);
1098
1362
  if (direction === "last") {
1099
1363
  const start = /* @__PURE__ */ isUndefined(index) ? tickets.length - 1 : index;
1100
1364
  for (let i = start; i >= 0; i--) {
@@ -1563,7 +1827,7 @@ const Avatar = {
1563
1827
  * ```
1564
1828
  */
1565
1829
  function toArray(value) {
1566
- return /* @__PURE__ */ isNullOrUndefined(value) ? [] : Array.isArray(value) ? value : [value];
1830
+ return /* @__PURE__ */ isNullOrUndefined(value) ? [] : /* @__PURE__ */ isArray(value) ? value : [value];
1567
1831
  }
1568
1832
 
1569
1833
  //#endregion
@@ -1655,7 +1919,8 @@ function useProxyModel(registry, model, options) {
1655
1919
  flush: "sync",
1656
1920
  deep: multiple
1657
1921
  });
1658
- function onRegister(ticket) {
1922
+ function onRegister(data) {
1923
+ const ticket = data;
1659
1924
  if (!pending.has(ticket.value) || ticket.disabled) return;
1660
1925
  registryWatch.pause();
1661
1926
  modelWatch.pause();
@@ -2385,1345 +2650,1350 @@ const Group = {
2385
2650
  };
2386
2651
 
2387
2652
  //#endregion
2388
- //#region src/composables/useHydration/index.ts
2653
+ //#region src/composables/useSingle/index.ts
2389
2654
  /**
2390
- * @module useHydration
2391
- *
2392
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2655
+ * @module useSingle
2393
2656
  *
2394
2657
  * @remarks
2395
- * SSR hydration state management composable.
2658
+ * Single-selection composable that extends useSelection to enforce only one selected item.
2396
2659
  *
2397
2660
  * Key features:
2398
- * - Hydration state detection (browser vs SSR)
2399
- * - Root component detection
2400
- * - Readonly hydration state refs
2401
- * - Plugin installation support
2402
- * - Perfect for hydration-safe rendering
2661
+ * - Auto-clears previous selection when selecting new item
2662
+ * - Singular computed properties (selectedId, selectedItem, selectedIndex, selectedValue)
2663
+ * - Perfect for tabs, radio buttons, theme selectors
2403
2664
  *
2404
- * Essential for composables that need to behave differently during SSR vs client-side.
2665
+ * Inheritance chain: useRegistry useSelection useSingle
2405
2666
  */
2406
2667
  /**
2407
- * Creates a new hydration instance.
2668
+ * Creates a new single selection instance that enforces only one selected item at a time.
2408
2669
  *
2409
- * @returns A new hydration instance.
2670
+ * Extends `createSelection` by automatically clearing previous selections when a new item is selected.
2671
+ * Adds computed singular properties: `selectedId`, `selectedItem`, `selectedIndex`, `selectedValue`.
2410
2672
  *
2411
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2673
+ * @param options The options for the single selection instance.
2674
+ * @template Z The type of the single selection ticket.
2675
+ * @template E The type of the single selection context.
2676
+ * @returns A new single selection instance with single-selection enforcement.
2412
2677
  *
2413
- * @example
2414
- * ```ts
2415
- * import { createHydration } from '@vuetify/v0'
2678
+ * @remarks
2679
+ * **Key Differences from `createSelection`:**
2680
+ * - Automatically clears `selectedIds` before selecting a new item (enforces single selection)
2681
+ * - Provides singular computed properties instead of plural sets
2682
+ * - Perfect for tabs, radio buttons, theme selectors, and other single-choice UI components
2416
2683
  *
2417
- * const hydration = createHydration()
2418
- * console.log(hydration.isHydrated.value) // false
2419
- * hydration.hydrate()
2420
- * console.log(hydration.isHydrated.value) // true
2421
- * ```
2422
- */
2423
- function createHydration() {
2424
- const isHydrated = shallowRef(false);
2425
- function hydrate() {
2426
- isHydrated.value = true;
2427
- }
2428
- return {
2429
- isHydrated: shallowReadonly(isHydrated),
2430
- hydrate
2431
- };
2432
- }
2433
- function createFallbackHydration() {
2434
- return {
2435
- isHydrated: shallowReadonly(shallowRef(true)),
2436
- hydrate: () => {}
2437
- };
2438
- }
2439
- /**
2440
- * Creates a new hydration context trinity.
2684
+ * **Computed Properties:**
2685
+ * - `selectedId`: The ID of the selected item (undefined if none selected)
2686
+ * - `selectedItem`: The selected ticket object (undefined if none selected)
2687
+ * - `selectedIndex`: The index of the selected item (-1 if none selected)
2688
+ * - `selectedValue`: The value of the selected item (undefined if none selected)
2441
2689
  *
2442
- * @param options Options for creating the hydration context.
2443
- * @template E The type of the hydration context.
2444
- * @returns A new hydration context trinity.
2690
+ * **Inheritance Chain:**
2691
+ * `useRegistry` `createSelection` `createSingle` `createStep`
2445
2692
  *
2446
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2693
+ * @see https://0.vuetifyjs.com/composables/selection/use-single
2447
2694
  *
2448
2695
  * @example
2449
2696
  * ```ts
2450
- * import { createHydrationContext } from '@vuetify/v0'
2697
+ * import { createSingle } from '@vuetify/v0'
2451
2698
  *
2452
- * export const [useHydrationContext, provideHydrationContext, context] = createHydrationContext({
2453
- * namespace: 'app:hydration',
2454
- * })
2455
- * ```
2456
- */
2457
- function createHydrationContext(_options = {}) {
2458
- const { namespace = "v0:hydration" } = _options;
2459
- const [useHydrationContext, _provideHydrationContext] = createContext(namespace);
2460
- const context = createHydration();
2461
- function provideHydrationContext(_context = context, app) {
2462
- return _provideHydrationContext(_context, app);
2463
- }
2464
- return createTrinity(useHydrationContext, provideHydrationContext, context);
2465
- }
2466
- /**
2467
- * Creates a new hydration plugin.
2699
+ * const tabs = createSingle({ mandatory: true })
2468
2700
  *
2469
- * @param options The options for the hydration plugin.
2470
- * @template E The type of the hydration context.
2471
- * @returns A new hydration plugin.
2701
+ * tabs.onboard([
2702
+ * { id: 'home', value: 'Home' },
2703
+ * { id: 'about', value: 'About' },
2704
+ * { id: 'contact', value: 'Contact' },
2705
+ * ])
2472
2706
  *
2473
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2707
+ * tabs.first() // Select first tab
2474
2708
  *
2475
- * @example
2476
- * ```ts
2477
- * import { createApp } from 'vue'
2478
- * import { createHydrationPlugin } from '@vuetify/v0'
2479
- * import App from './App.vue'
2480
- *
2481
- * const app = createApp(App)
2482
- *
2483
- * app.use(createHydrationPlugin())
2709
+ * console.log(tabs.selectedId.value) // 'home'
2710
+ * console.log(tabs.selectedIndex.value) // 0
2484
2711
  *
2485
- * app.mount('#app')
2712
+ * tabs.select('about') // Switch to about tab
2713
+ * console.log(tabs.selectedId.value) // 'about'
2714
+ * console.log(tabs.selectedIds.size) // 1 (always enforces single selection)
2486
2715
  * ```
2487
2716
  */
2488
- function createHydrationPlugin(_options = {}) {
2489
- const { namespace = "v0:hydration", ...options } = _options;
2490
- const [, provideHydrationContext, context] = createHydrationContext({
2717
+ function createSingle(_options = {}) {
2718
+ const { mandatory = false, multiple = false, ...options } = _options;
2719
+ const registry = createSelection({
2491
2720
  ...options,
2492
- namespace
2721
+ mandatory,
2722
+ multiple
2493
2723
  });
2494
- return createPlugin({
2495
- namespace,
2496
- provide: (app) => {
2497
- provideHydrationContext(context, app);
2498
- },
2499
- setup: (app) => {
2500
- app.mixin({ mounted() {
2501
- if (this.$parent !== null) return;
2502
- context.hydrate();
2503
- } });
2724
+ const selectedId = computed(() => registry.selectedIds.values().next().value);
2725
+ const selectedItem = computed(() => registry.selectedItems.value.values().next().value);
2726
+ const selectedIndex = computed(() => selectedItem.value?.index ?? -1);
2727
+ const selectedValue = computed(() => selectedItem.value?.value);
2728
+ function unselect(id) {
2729
+ if (mandatory && registry.selectedIds.size === 1) return;
2730
+ registry.selectedIds.delete(id);
2731
+ }
2732
+ function toggle(id) {
2733
+ if (registry.selectedIds.has(id)) unselect(id);
2734
+ else registry.select(id);
2735
+ }
2736
+ return {
2737
+ ...registry,
2738
+ selectedId,
2739
+ selectedItem,
2740
+ selectedIndex,
2741
+ selectedValue,
2742
+ unselect,
2743
+ toggle,
2744
+ get size() {
2745
+ return registry.size;
2504
2746
  }
2505
- });
2747
+ };
2506
2748
  }
2507
2749
  /**
2508
- * Returns the current hydration instance.
2750
+ * Creates a new single selection context.
2509
2751
  *
2510
- * @param namespace The namespace for the hydration context. Defaults to `v0:hydration`.
2511
- * @returns The current hydration instance.
2752
+ * @param options The options for the single selection context.
2753
+ * @template Z The type of the single selection ticket.
2754
+ * @template E The type of the single selection context.
2755
+ * @returns A new single selection context.
2512
2756
  *
2513
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2757
+ * @see https://0.vuetifyjs.com/composables/selection/use-single
2758
+ *
2759
+ * @example
2760
+ * ```ts
2761
+ * import { createSingleContext } from '@vuetify/v0'
2762
+ *
2763
+ * // With default namespace 'v0:single'
2764
+ * export const [useSingle, provideSingle, context] = createSingleContext()
2765
+ *
2766
+ * // In a parent component:
2767
+ * provideSingle()
2768
+ *
2769
+ * // In a child component:
2770
+ * const single = useSingle()
2771
+ * single.select('tab-1')
2772
+ * ```
2773
+ */
2774
+ function createSingleContext(_options = {}) {
2775
+ const { namespace = "v0:single", ...options } = _options;
2776
+ const [useSingleContext, _provideSingleContext] = createContext(namespace);
2777
+ const context = createSingle(options);
2778
+ function provideSingleContext(_context = context, app) {
2779
+ return _provideSingleContext(_context, app);
2780
+ }
2781
+ return createTrinity(useSingleContext, provideSingleContext, context);
2782
+ }
2783
+ /**
2784
+ * Returns the current single selection instance.
2785
+ *
2786
+ * @param namespace The namespace for the single selection context. Defaults to `'v0:single'`.
2787
+ * @returns The current single selection instance.
2788
+ *
2789
+ * @see https://0.vuetifyjs.com/composables/selection/use-single
2514
2790
  *
2515
2791
  * @example
2516
2792
  * ```vue
2517
2793
  * <script setup lang="ts">
2518
- * import { useHydration } from '@vuetify/v0'
2794
+ * import { useSingle } from '@vuetify/v0'
2519
2795
  *
2520
- * const hydration = useHydration()
2796
+ * const tabs = useSingle()
2521
2797
  * <\/script>
2522
2798
  *
2523
2799
  * <template>
2524
2800
  * <div>
2525
- * <p>Is hydrated: {{ hydration.isHydrated.value }}</p>
2801
+ * <p>Selected: {{ tabs.selectedId }}</p>
2526
2802
  * </div>
2527
2803
  * </template>
2528
2804
  * ```
2529
2805
  */
2530
- function useHydration(namespace = "v0:hydration") {
2531
- const fallback = createFallbackHydration();
2532
- if (!getCurrentInstance()) return fallback;
2533
- try {
2534
- return useContext(namespace, fallback);
2535
- } catch {
2536
- return fallback;
2537
- }
2806
+ function useSingle(namespace = "v0:single") {
2807
+ return useContext(namespace);
2538
2808
  }
2539
2809
 
2540
2810
  //#endregion
2541
- //#region src/composables/useResizeObserver/index.ts
2811
+ //#region src/composables/useTokens/index.ts
2542
2812
  /**
2543
- * @module useResizeObserver
2813
+ * @module useTokens
2814
+ *
2815
+ * @see https://0.vuetifyjs.com/composables/registration/use-tokens
2544
2816
  *
2545
2817
  * @remarks
2546
- * ResizeObserver composable with lifecycle management.
2818
+ * Design token registry with alias resolution and W3C Design Tokens format support.
2547
2819
  *
2548
2820
  * Key features:
2549
- * - ResizeObserver API wrapper
2550
- * - Pause/resume/stop functionality
2551
- * - Automatic cleanup on unmount
2552
- * - SSR-safe (checks SUPPORTS_OBSERVER)
2553
- * - Hydration-aware
2554
- * - Box model options (content-box/border-box)
2821
+ * - Alias resolution with circular reference detection
2822
+ * - Nested token flattening with dot notation
2823
+ * - W3C Design Tokens format ($value, $type, $description, $extensions)
2824
+ * - Path-based resolution (e.g., {colors}.blue.500)
2825
+ * - Resolution caching for performance (~28,590 ops/sec)
2555
2826
  *
2556
- * Perfect for responsive components and size-based rendering.
2827
+ * Used by useTheme, useLocale, and useFeatures for token-based configuration.
2557
2828
  */
2558
2829
  /**
2559
- * A composable that uses the Resize Observer API to detect when an element's
2560
- * size changes.
2830
+ * Creates a new token instance.
2561
2831
  *
2562
- * @param target The element to observe.
2563
- * @param callback The callback to execute when the element's size changes.
2564
- * @param options The options for the Resize Observer.
2565
- * @returns An object with methods to control the observer.
2832
+ * @param tokens The tokens to use.
2833
+ * @param options The options for the token instance.
2834
+ * @template Z The type of the token ticket.
2835
+ * @template E The type of the token context.
2836
+ * @returns A new token instance.
2566
2837
  *
2567
- * @see https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
2568
- * @see https://0.vuetifyjs.com/composables/system/use-resize-observer
2838
+ * @see https://www.designtokens.org/tr/drafts/format/
2839
+ * @see https://0.vuetifyjs.com/composables/registration/use-tokens
2569
2840
  *
2570
2841
  * @example
2571
2842
  * ```ts
2572
- * import { ref } from 'vue'
2573
- * import { useResizeObserver } from '@vuetify/v0'
2574
- *
2575
- * const el = ref<HTMLElement>()
2576
- * const width = ref(0)
2577
- * const height = ref(0)
2843
+ * import { useTokens } from '@vuetify/v0'
2578
2844
  *
2579
- * const { pause, resume, isPaused } = useResizeObserver(
2580
- * el,
2581
- * (entries) => {
2582
- * const entry = entries[0]
2583
- * if (entry) {
2584
- * width.value = entry.contentRect.width
2585
- * height.value = entry.contentRect.height
2586
- * console.log('Size changed:', width.value, 'x', height.value)
2587
- * }
2845
+ * const tokens = useTokens({
2846
+ * colors: {
2847
+ * primary: '#3b82f6',
2848
+ * secondary: '{colors.primary}', // Alias reference
2588
2849
  * },
2589
- * { immediate: true }
2590
- * )
2591
- *
2592
- * // Pause observation
2593
- * pause()
2850
+ * })
2594
2851
  *
2595
- * // Resume observation
2596
- * resume()
2852
+ * console.log(tokens.resolve('{colors.primary}')) // '#3b82f6'
2853
+ * console.log(tokens.resolve('{colors.secondary}')) // '#3b82f6'
2597
2854
  * ```
2598
2855
  */
2599
- function useResizeObserver(target, callback, options = {}) {
2600
- const { isHydrated } = useHydration();
2601
- const observer = shallowRef();
2602
- const isPaused = shallowRef(false);
2603
- const isActive = toRef(() => !!observer.value);
2604
- function setup() {
2605
- if (!isHydrated.value || !SUPPORTS_OBSERVER || !target.value || isPaused.value) return;
2606
- observer.value = new ResizeObserver((entries) => {
2607
- callback(entries.map((entry) => ({
2608
- contentRect: {
2609
- width: entry.contentRect.width,
2610
- height: entry.contentRect.height,
2611
- top: entry.contentRect.top,
2612
- left: entry.contentRect.left
2613
- },
2614
- target: entry.target
2615
- })));
2616
- });
2617
- observer.value.observe(target.value, { box: options.box || "content-box" });
2618
- if (options.immediate) {
2619
- const rect = target.value.getBoundingClientRect();
2620
- callback([{
2621
- contentRect: {
2622
- width: rect.width,
2623
- height: rect.height,
2624
- top: rect.top,
2625
- left: rect.left
2626
- },
2627
- target: target.value
2628
- }]);
2629
- }
2630
- }
2631
- watch([isHydrated, target], () => {
2632
- cleanup();
2633
- setup();
2634
- }, { immediate: true });
2635
- function cleanup() {
2636
- if (observer.value) {
2637
- observer.value.disconnect();
2638
- observer.value = void 0;
2639
- }
2640
- }
2641
- function pause() {
2642
- isPaused.value = true;
2643
- observer.value?.disconnect();
2644
- }
2645
- function resume() {
2646
- isPaused.value = false;
2647
- setup();
2856
+ function createTokens(tokens = {}, options = {}) {
2857
+ const logger = useLogger();
2858
+ const registry = useRegistry(options);
2859
+ const cache = /* @__PURE__ */ new Map();
2860
+ registry.onboard(flatten(tokens, options.prefix, !!options.flat));
2861
+ function isAlias(token) {
2862
+ return /* @__PURE__ */ isString(token) && token.length > 2 && token[0] === "{" && token.at(-1) === "}";
2648
2863
  }
2649
- function stop() {
2650
- cleanup();
2864
+ function isTokenAlias(value) {
2865
+ return /* @__PURE__ */ isObject(value) && "$value" in value;
2651
2866
  }
2652
- onScopeDispose(stop, true);
2653
- return {
2654
- isActive: shallowReadonly(isActive),
2655
- isPaused: shallowReadonly(isPaused),
2656
- pause,
2657
- resume,
2658
- stop
2659
- };
2660
- }
2661
- /**
2662
- * A convenience composable that uses the Resize Observer API to track an
2663
- * element's size.
2664
- *
2665
- * @param target The element to observe.
2666
- * @returns An object with the element's width and height.
2667
- *
2668
- * @see https://0.vuetifyjs.com/composables/system/use-resize-observer#use-element-size
2669
- *
2670
- * @example
2671
- * ```ts
2672
- * import { ref, watchEffect } from 'vue'
2673
- * import { useElementSize } from '@vuetify/v0'
2867
+ function resolve(token, visited = /* @__PURE__ */ new Set()) {
2868
+ const cacheKey = /* @__PURE__ */ isString(token) ? token : JSON.stringify(token);
2869
+ const cached = cache.get(cacheKey);
2870
+ if (!/* @__PURE__ */ isUndefined(cached)) return cached;
2871
+ const reference = isTokenAlias(token) ? token.$value : token;
2872
+ const isAliasReference = /* @__PURE__ */ isString(reference) && isAlias(reference);
2873
+ const clean = isAliasReference ? reference.slice(1, -1) : String(reference);
2874
+ if (visited.has(clean)) {
2875
+ logger.warn(`Circular alias detected for "${clean}"`);
2876
+ cache.set(cacheKey, void 0);
2877
+ return;
2878
+ }
2879
+ visited.add(clean);
2880
+ let found = registry.get(clean);
2881
+ let segments = [];
2882
+ if (!found && clean.includes(".")) {
2883
+ const parts = clean.split(".");
2884
+ for (let i = parts.length - 1; i > 0; i--) {
2885
+ const prefix = parts.slice(0, i).join(".");
2886
+ const suffix = parts.slice(i);
2887
+ const candidate = registry.get(prefix);
2888
+ if (!/* @__PURE__ */ isUndefined(candidate?.value)) {
2889
+ found = candidate;
2890
+ segments = suffix;
2891
+ break;
2892
+ }
2893
+ }
2894
+ }
2895
+ if (/* @__PURE__ */ isUndefined(found?.value)) {
2896
+ if (isAliasReference) logger.warn(`Alias not found for "${String(reference)}"`);
2897
+ cache.set(cacheKey, void 0);
2898
+ return;
2899
+ }
2900
+ let result;
2901
+ let current = found.value;
2902
+ if (segments.length > 0) {
2903
+ if (isTokenAlias(current)) current = current.$value;
2904
+ for (const segment of segments) {
2905
+ if (!/* @__PURE__ */ isObject(current) || !(segment in current)) {
2906
+ current = void 0;
2907
+ break;
2908
+ }
2909
+ current = current[segment];
2910
+ if (isTokenAlias(current)) current = current.$value;
2911
+ }
2912
+ if (/* @__PURE__ */ isUndefined(current)) {
2913
+ logger.warn(`Path not found inside "${clean}": ${segments.join(".")}`);
2914
+ cache.set(cacheKey, void 0);
2915
+ return;
2916
+ }
2917
+ result = current;
2918
+ } else if (isTokenAlias(current)) {
2919
+ const inner = current.$value;
2920
+ if (/* @__PURE__ */ isString(inner) && isAlias(inner)) return resolve(inner, visited);
2921
+ result = inner;
2922
+ } else if (/* @__PURE__ */ isString(current) && isAlias(current)) return resolve(current, visited);
2923
+ else result = current;
2924
+ cache.set(cacheKey, result);
2925
+ return result;
2926
+ }
2927
+ return {
2928
+ ...registry,
2929
+ resolve,
2930
+ isAlias,
2931
+ get size() {
2932
+ return registry.size;
2933
+ }
2934
+ };
2935
+ }
2936
+ /**
2937
+ * Creates a new token context.
2674
2938
  *
2675
- * const box = ref<HTMLElement>()
2676
- * const { width, height } = useElementSize(box)
2939
+ * @param namespace The namespace for the token context.
2940
+ * @param tokens The tokens to use.
2941
+ * @template Z The type of the token ticket.
2942
+ * @template E The type of the token context.
2943
+ * @returns A new token context.
2677
2944
  *
2678
- * // Width and height are reactive refs
2679
- * watchEffect(() => {
2680
- * console.log('Box size:', width.value, 'x', height.value)
2945
+ * @see https://0.vuetifyjs.com/composables/registration/use-tokens
2946
+ *
2947
+ * @example
2948
+ * ```ts
2949
+ * import { createTokensContext } from '@vuetify/v0'
2950
+ *
2951
+ * export const [useTokens, provideTokens, context] = createTokensContext({
2952
+ * namespace: 'v0:tokens',
2953
+ * tokens: {
2954
+ * colors: {
2955
+ * primary: '#3b82f6',
2956
+ * secondary: '{colors.primary}', // Alias reference
2957
+ * },
2958
+ * },
2681
2959
  * })
2682
2960
  * ```
2683
2961
  */
2684
- function useElementSize(target) {
2685
- const width = shallowRef(0);
2686
- const height = shallowRef(0);
2687
- const { pause: _pause, resume, stop, isActive, isPaused } = useResizeObserver(target, (entries) => {
2688
- const entry = entries[0];
2689
- if (entry) {
2690
- width.value = entry.contentRect.width;
2691
- height.value = entry.contentRect.height;
2962
+ function createTokensContext(_options) {
2963
+ const { namespace = "v0:tokens", tokens = {}, ...options } = _options;
2964
+ const [useTokensContext, _provideTokensContext] = createContext(namespace);
2965
+ const context = createTokens(tokens, options);
2966
+ function provideTokensContext(_context = context, app) {
2967
+ return _provideTokensContext(_context, app);
2968
+ }
2969
+ return createTrinity(useTokensContext, provideTokensContext, context);
2970
+ }
2971
+ /**
2972
+ * Returns the current tokens instance.
2973
+ *
2974
+ * @param namespace The namespace for the tokens context. Defaults to `'v0:tokens'`.
2975
+ * @returns The current tokens instance.
2976
+ *
2977
+ * @see https://0.vuetifyjs.com/composables/registration/use-tokens
2978
+ *
2979
+ * @example
2980
+ * ```vue
2981
+ * <script setup lang="ts">
2982
+ * import { useTokens } from '@vuetify/v0'
2983
+ *
2984
+ * const tokens = useTokens()
2985
+ * <\/script>
2986
+ * ```
2987
+ */
2988
+ function useTokens(namespace = "v0:tokens") {
2989
+ return useContext(namespace);
2990
+ }
2991
+ /**
2992
+ * Flattens a nested collection of tokens into a flat array of tokens.
2993
+ * Each token is represented by an object containing its ID & value.
2994
+ * @param tokens The collection of tokens to flatten.
2995
+ * @param prefix An optional prefix to prepend to each token ID.
2996
+ * @returns An array of flattened tokens, each with an ID and value.
2997
+ */
2998
+ function flatten(tokens, prefix = "", flat = false) {
2999
+ const flattened = [];
3000
+ const stack = [{
3001
+ tokens,
3002
+ prefix,
3003
+ flat
3004
+ }];
3005
+ while (stack.length > 0) {
3006
+ const { tokens: currentTokens, prefix: currentPrefix, flat: flat$1 } = stack.pop();
3007
+ const meta = {};
3008
+ for (const k in currentTokens) if (k.startsWith("$")) meta[k] = currentTokens[k];
3009
+ if (Object.keys(meta).length > 0 && currentPrefix) flattened.push({
3010
+ id: currentPrefix,
3011
+ value: meta
3012
+ });
3013
+ for (const key in currentTokens) {
3014
+ if (key.startsWith("$")) continue;
3015
+ const value = currentTokens[key];
3016
+ const id = currentPrefix ? `${currentPrefix}.${key}` : key;
3017
+ if (!/* @__PURE__ */ isObject(value)) {
3018
+ flattened.push({
3019
+ id,
3020
+ value
3021
+ });
3022
+ continue;
3023
+ }
3024
+ if ("$value" in value) {
3025
+ flattened.push({
3026
+ id,
3027
+ value
3028
+ });
3029
+ const inner = value.$value;
3030
+ if (/* @__PURE__ */ isObject(inner) && !flat$1) for (const innerKey in inner) {
3031
+ if (innerKey.startsWith("$")) continue;
3032
+ const child = inner[innerKey];
3033
+ const childId = `${id}.${innerKey}`;
3034
+ if (!/* @__PURE__ */ isObject(child)) flattened.push({
3035
+ id: childId,
3036
+ value: child
3037
+ });
3038
+ else if ("$value" in child) flattened.push({
3039
+ id: childId,
3040
+ value: child
3041
+ });
3042
+ else stack.push({
3043
+ tokens: child,
3044
+ prefix: childId,
3045
+ flat: flat$1
3046
+ });
3047
+ }
3048
+ continue;
3049
+ }
3050
+ if (flat$1) {
3051
+ flattened.push({
3052
+ id,
3053
+ value
3054
+ });
3055
+ continue;
3056
+ }
3057
+ stack.push({
3058
+ tokens: value,
3059
+ prefix: id,
3060
+ flat: flat$1
3061
+ });
2692
3062
  }
2693
- }, { immediate: true });
2694
- function pause() {
2695
- width.value = 0;
2696
- height.value = 0;
2697
- _pause();
2698
3063
  }
2699
- return {
2700
- width,
2701
- height,
2702
- isActive,
2703
- isPaused,
2704
- pause,
2705
- resume,
2706
- stop
2707
- };
3064
+ return flattened;
2708
3065
  }
2709
3066
 
2710
3067
  //#endregion
2711
- //#region src/composables/useOverflow/index.ts
3068
+ //#region src/composables/useLocale/adapters/v0.ts
2712
3069
  /**
2713
- * @module useOverflow
3070
+ * Vuetify0.x locale adapter implementation
3071
+ *
3072
+ * This adapter provides translation and number formatting
3073
+ * capabilities using the Intl API and supports both
3074
+ * numbered ({0}, {1}) and named ({name}) variables in translation strings.
3075
+ */
3076
+ var Vuetify0LocaleAdapter = class {
3077
+ t(message, ...params) {
3078
+ let resolvedMessage = message;
3079
+ if (params.length > 0 && /* @__PURE__ */ isObject(params[0])) {
3080
+ const variables = params[0];
3081
+ resolvedMessage = resolvedMessage.replace(/{([a-zA-Z][a-zA-Z0-9_]*)}/g, (match, name) => {
3082
+ return /* @__PURE__ */ isUndefined(variables[name]) ? match : String(variables[name]);
3083
+ });
3084
+ params = params.slice(1);
3085
+ }
3086
+ resolvedMessage = resolvedMessage.replace(/\{(\d+)\}/g, (match, index) => {
3087
+ const idx = Number.parseInt(index, 10);
3088
+ if (!/* @__PURE__ */ isUndefined(params[idx])) return String(params[idx]);
3089
+ return match;
3090
+ });
3091
+ return resolvedMessage;
3092
+ }
3093
+ n(value, locale, ...params) {
3094
+ if (!IN_BROWSER || !locale) return value.toString();
3095
+ const options = params[0];
3096
+ return new Intl.NumberFormat(String(locale), options).format(value);
3097
+ }
3098
+ };
3099
+
3100
+ //#endregion
3101
+ //#region src/composables/useLocale/index.ts
3102
+ /**
3103
+ * @module useLocale
2714
3104
  *
2715
3105
  * @remarks
2716
- * Composable for computing how many items fit in a container based on available width.
2717
- * Enables responsive truncation logic for Pagination, Breadcrumbs, and similar components.
3106
+ * Internationalization (i18n) composable with adapter pattern for message translation.
2718
3107
  *
2719
3108
  * Key features:
2720
- * - Container width tracking via ResizeObserver
2721
- * - Two modes: variable-width (per-item) or uniform-width (sample-based)
2722
- * - Computes capacity (how many items fit)
2723
- * - SSR-safe with Infinity fallback
2724
- * - Supports reserved space for nav buttons, ellipsis, etc.
3109
+ * - Locale selection with createSingle
3110
+ * - Token-based message storage with useTokens
3111
+ * - Numbered and named placeholder support ({0}, {name})
3112
+ * - Number formatting with Intl.NumberFormat
3113
+ * - Adapter pattern for integration with i18n providers
2725
3114
  *
2726
- * Use variable mode (default) for items with different widths like Breadcrumbs.
2727
- * Use uniform mode (itemWidth option) for same-width items like Pagination buttons.
3115
+ * Integrates with createSingle for locale selection and useTokens for message resolution.
2728
3116
  */
2729
3117
  /**
2730
- * Creates a new overflow context for computing how many items fit in a container.
2731
- *
2732
- * @param options Configuration options
2733
- * @returns Overflow context with container ref, capacity, and measurement functions
2734
- *
2735
- * @example Variable-width mode (Breadcrumbs)
2736
- * ```vue
2737
- * <script lang="ts" setup>
2738
- * import { useTemplateRef } from 'vue'
2739
- * import { createOverflow } from '@vuetify/v0'
2740
- *
2741
- * const containerRef = useTemplateRef('container')
2742
- * const overflow = createOverflow({
2743
- * container: containerRef,
2744
- * gap: 8,
2745
- * reserved: 40,
2746
- * })
2747
- * <\/script>
2748
- *
2749
- * <template>
2750
- * <div ref="container">
2751
- * <span
2752
- * v-for="(item, i) in items.slice(0, overflow.capacity.value)"
2753
- * :key="i"
2754
- * :ref="el => overflow.measure(i, el)"
2755
- * >
2756
- * {{ item }}
2757
- * </span>
2758
- * <span v-if="overflow.isOverflowing.value">...</span>
2759
- * </div>
2760
- * </template>
2761
- * ```
3118
+ * Creates a new locale instance.
2762
3119
  *
2763
- * @example Uniform-width mode (Pagination)
2764
- * ```ts
2765
- * const overflow = createOverflow({
2766
- * container: () => atom.value?.element,
2767
- * itemWidth: buttonWidth,
2768
- * reserved: () => buttonWidth.value * 4,
2769
- * })
2770
- * ```
3120
+ * @param options The options for the locale instance.
3121
+ * @template Z The type of the locale ticket.
3122
+ * @template E The type of the locale context.
3123
+ * @returns A new locale instance.
3124
+ *
3125
+ * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2771
3126
  */
2772
- function createOverflow(options = {}) {
2773
- const { container: _container, gap = 0, reserved = 0, itemWidth, reverse } = options;
2774
- const container = /* @__PURE__ */ isUndefined(_container) ? shallowRef() : toRef(_container);
2775
- const widths = shallowRef(/* @__PURE__ */ new Map());
2776
- const { width } = useElementSize(container);
2777
- function measure(index, el) {
2778
- if (!el) {
2779
- if (widths.value.has(index)) {
2780
- const next = new Map(widths.value);
2781
- next.delete(index);
2782
- widths.value = next;
2783
- }
2784
- return;
2785
- }
2786
- const style = getComputedStyle(el);
2787
- const marginX = Number.parseFloat(style.marginLeft) + Number.parseFloat(style.marginRight);
2788
- const w = el.offsetWidth + marginX;
2789
- if (widths.value.get(index) !== w) widths.value = new Map(widths.value).set(index, w);
3127
+ function createLocale(_options = {}) {
3128
+ const { adapter = new Vuetify0LocaleAdapter(), messages = {}, ...options } = _options;
3129
+ const tokens = createTokens(messages);
3130
+ const registry = createSingle(options);
3131
+ for (const id in messages) {
3132
+ registry.register({ id });
3133
+ if (id === options.default && !registry.selectedId.value) registry.select(id);
2790
3134
  }
2791
- function reset() {
2792
- widths.value = /* @__PURE__ */ new Map();
3135
+ function t(key, params, fallback) {
3136
+ const locale = registry.selectedId.value;
3137
+ const args = toArray(params);
3138
+ if (!locale) return adapter.t(fallback ?? key, ...args);
3139
+ const path = `${locale}.${key}`;
3140
+ const message = tokens.get(path)?.value;
3141
+ const template = /* @__PURE__ */ isString(message) ? resolve(locale, message) : fallback ?? key;
3142
+ return adapter.t(template, ...args);
2793
3143
  }
2794
- const total = computed(() => {
2795
- const g = toValue(gap);
2796
- let sum = 0;
2797
- let count = 0;
2798
- for (const w of widths.value.values()) {
2799
- sum += w + (count > 0 ? g : 0);
2800
- count++;
3144
+ function n(value, ...params) {
3145
+ return adapter.n(value, registry.selectedId.value, ...params);
3146
+ }
3147
+ function resolve(locale, str) {
3148
+ return str.replace(/{([a-zA-Z0-9.-_]+)}/g, (match, key) => {
3149
+ const [prefix, ...rest] = key.split(".");
3150
+ const target = registry.has(prefix) ? prefix : locale;
3151
+ const path = `${target}.${registry.has(prefix) ? rest.join(".") : key}`;
3152
+ const resolved = tokens.get(path)?.value;
3153
+ if (/* @__PURE__ */ isString(resolved)) return resolve(target, resolved);
3154
+ return match;
3155
+ });
3156
+ }
3157
+ return {
3158
+ ...registry,
3159
+ t,
3160
+ n,
3161
+ get size() {
3162
+ return registry.size;
2801
3163
  }
2802
- return sum;
2803
- });
3164
+ };
3165
+ }
3166
+ function createLocaleFallback() {
2804
3167
  return {
2805
- container,
2806
- width,
2807
- capacity: computed(() => {
2808
- const available = width.value - toValue(reserved);
2809
- if (width.value === 0) return Infinity;
2810
- if (available <= 0) return 0;
2811
- const g = toValue(gap);
2812
- const uniformWidth = toValue(itemWidth);
2813
- if (uniformWidth && uniformWidth > 0) {
2814
- const first = uniformWidth;
2815
- const subsequent = uniformWidth + g;
2816
- if (available < first) return 0;
2817
- return Math.max(1, Math.floor((available - first) / subsequent) + 1);
2818
- }
2819
- const entries = [...widths.value.entries()].toSorted((a, b) => a[0] - b[0]);
2820
- if (toValue(reverse)) entries.reverse();
2821
- let sum = 0;
2822
- let count = 0;
2823
- for (const [, w] of entries) {
2824
- const next = sum + w + (count > 0 ? g : 0);
2825
- if (next > available) break;
2826
- sum = next;
2827
- count++;
2828
- }
2829
- return count;
2830
- }),
2831
- total,
2832
- isOverflowing: toRef(() => {
2833
- return total.value > width.value - toValue(reserved);
2834
- }),
2835
- measure,
2836
- reset
3168
+ size: 0,
3169
+ t: (key, _params, fallback) => fallback ?? key,
3170
+ n: String
2837
3171
  };
2838
3172
  }
2839
3173
  /**
2840
- * Creates an overflow context with dependency injection support.
3174
+ * Creates a new locale context.
2841
3175
  *
2842
- * @param options Configuration options including namespace
2843
- * @returns Trinity tuple: [useContext, provideContext, defaultContext]
3176
+ * @param options The options for the locale context.
3177
+ * @template Z The type of the locale ticket.
3178
+ * @template E The type of the locale context.
3179
+ * @returns A new locale context.
3180
+ *
3181
+ * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2844
3182
  *
2845
3183
  * @example
2846
3184
  * ```ts
2847
- * // Create injectable context
2848
- * const [useOverflow, provideOverflow, overflow] = createOverflowContext({
2849
- * namespace: 'my-overflow',
2850
- * gap: 8,
2851
- * reserved: 160,
3185
+ * import { createLocaleContext } from '@vuetify/v0'
3186
+ *
3187
+ * export const [useAppLocale, provideAppLocale, appLocale] = createLocaleContext({
3188
+ * namespace: 'app:locale',
3189
+ * messages: {
3190
+ * en: { hello: 'Hello' },
3191
+ * es: { hello: 'Hola' },
3192
+ * },
2852
3193
  * })
2853
3194
  *
2854
- * // In parent component
2855
- * provideOverflow()
3195
+ * // In a parent component:
3196
+ * provideAppLocale()
2856
3197
  *
2857
- * // In child component
2858
- * const overflow = useOverflow()
3198
+ * // In a child component:
3199
+ * const locale = useAppLocale()
3200
+ * locale.select('es')
2859
3201
  * ```
2860
3202
  */
2861
- function createOverflowContext(_options = {}) {
2862
- const { namespace = "v0:overflow", ...options } = _options;
2863
- const [useOverflowContext, _provideOverflowContext] = createContext(namespace);
2864
- const context = createOverflow(options);
2865
- function provideOverflowContext(_context = context, app) {
2866
- return _provideOverflowContext(_context, app);
3203
+ function createLocaleContext(_options = {}) {
3204
+ const { namespace = "v0:locale", ...options } = _options;
3205
+ const [useLocaleContext, _provideLocaleContext] = createContext(namespace);
3206
+ const context = createLocale(options);
3207
+ function provideLocaleContext(_context = context, app) {
3208
+ return _provideLocaleContext(_context, app);
2867
3209
  }
2868
- return createTrinity(useOverflowContext, provideOverflowContext, context);
3210
+ return createTrinity(useLocaleContext, provideLocaleContext, context);
2869
3211
  }
2870
3212
  /**
2871
- * Returns the current overflow context from dependency injection.
3213
+ * Creates a new locale plugin.
2872
3214
  *
2873
- * @param namespace The namespace for the overflow context. Defaults to `v0:overflow`.
2874
- * @returns The current overflow context.
3215
+ * @param options The options for the locale plugin.
3216
+ * @template Z The type of the locale ticket.
3217
+ * @template E The type of the locale context.
3218
+ * @template R The type of the token ticket.
3219
+ * @template O The type of the token context.
3220
+ * @returns A new locale plugin.
2875
3221
  *
2876
- * @example
2877
- * ```vue
2878
- * <script lang="ts" setup>
2879
- * import { useOverflow } from '@vuetify/v0'
3222
+ * @see https://0.vuetifyjs.com/composables/plugins/use-locale
3223
+ */
3224
+ function createLocalePlugin(_options = {}) {
3225
+ const { namespace = "v0:locale", adapter = new Vuetify0LocaleAdapter(), messages = {}, ...options } = _options;
3226
+ const [, provideLocaleContext, context] = createLocaleContext({
3227
+ ...options,
3228
+ namespace,
3229
+ adapter,
3230
+ messages
3231
+ });
3232
+ return createPlugin({
3233
+ namespace,
3234
+ provide: (app) => {
3235
+ provideLocaleContext(context, app);
3236
+ }
3237
+ });
3238
+ }
3239
+ /**
3240
+ * Returns the current locale instance.
2880
3241
  *
2881
- * // Inject overflow context provided by parent
2882
- * const overflow = useOverflow()
2883
- * <\/script>
3242
+ * @returns The current locale instance.
2884
3243
  *
2885
- * <template>
2886
- * <div>
2887
- * <p>Capacity: {{ overflow.capacity.value }}</p>
2888
- * </div>
2889
- * </template>
3244
+ * @see https://0.vuetifyjs.com/composables/plugins/use-locale
3245
+ */
3246
+ function useLocale(namespace = "v0:locale") {
3247
+ const fallback = createLocaleFallback();
3248
+ if (!getCurrentInstance()) return fallback;
3249
+ try {
3250
+ return useContext(namespace, fallback);
3251
+ } catch {
3252
+ return fallback;
3253
+ }
3254
+ }
3255
+
3256
+ //#endregion
3257
+ //#region src/composables/useHydration/index.ts
3258
+ /**
3259
+ * @module useHydration
3260
+ *
3261
+ * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
3262
+ *
3263
+ * @remarks
3264
+ * SSR hydration state management composable.
3265
+ *
3266
+ * Key features:
3267
+ * - Hydration state detection (browser vs SSR)
3268
+ * - Root component detection
3269
+ * - Readonly hydration state refs
3270
+ * - Plugin installation support
3271
+ * - Perfect for hydration-safe rendering
3272
+ *
3273
+ * Essential for composables that need to behave differently during SSR vs client-side.
3274
+ */
3275
+ /**
3276
+ * Creates a new hydration instance.
3277
+ *
3278
+ * @returns A new hydration instance.
3279
+ *
3280
+ * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
3281
+ *
3282
+ * @example
3283
+ * ```ts
3284
+ * import { createHydration } from '@vuetify/v0'
3285
+ *
3286
+ * const hydration = createHydration()
3287
+ * console.log(hydration.isHydrated.value) // false
3288
+ * hydration.hydrate()
3289
+ * console.log(hydration.isHydrated.value) // true
2890
3290
  * ```
2891
3291
  */
2892
- function useOverflow(namespace = "v0:overflow") {
2893
- return useContext(namespace);
3292
+ function createHydration() {
3293
+ const isHydrated = shallowRef(false);
3294
+ function hydrate() {
3295
+ isHydrated.value = true;
3296
+ }
3297
+ return {
3298
+ isHydrated: shallowReadonly(isHydrated),
3299
+ hydrate
3300
+ };
3301
+ }
3302
+ function createFallbackHydration() {
3303
+ return {
3304
+ isHydrated: shallowReadonly(shallowRef(true)),
3305
+ hydrate: () => {}
3306
+ };
2894
3307
  }
2895
-
2896
- //#endregion
2897
- //#region src/composables/usePagination/index.ts
2898
3308
  /**
2899
- * @module usePagination
2900
- *
2901
- * @remarks
2902
- * Lightweight pagination composable for navigating through pages.
2903
- *
2904
- * Key features:
2905
- * - No registry overhead - just a bounded integer
2906
- * - Direct ref support for v-model compatibility
2907
- * - Navigation methods: next, prev, first, last
2908
- * - Computed visible items with ellipsis
2909
- * - Trinity pattern for dependency injection
3309
+ * Creates a new hydration context trinity.
2910
3310
  *
2911
- * Unlike registry-based composables, pagination tracks a single number
2912
- * within a range, making it efficient for large page counts.
2913
- */
2914
- /**
2915
- * Creates a pagination instance.
3311
+ * @param options Options for creating the hydration context.
3312
+ * @template E The type of the hydration context.
3313
+ * @returns A new hydration context trinity.
2916
3314
  *
2917
- * @param options The options for the pagination instance.
2918
- * @returns A pagination context with navigation methods.
3315
+ * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2919
3316
  *
2920
3317
  * @example
2921
3318
  * ```ts
2922
- * import { createPagination } from '@vuetify/v0'
2923
- *
2924
- * // Basic usage
2925
- * const pagination = createPagination({ size: 100 })
2926
- * pagination.next()
2927
- * pagination.items.value // [{ type: 'page', value: 1 }, { type: 'page', value: 2 }, ...]
3319
+ * import { createHydrationContext } from '@vuetify/v0'
2928
3320
  *
2929
- * // With v-model (pass a ref)
2930
- * const page = ref(1)
2931
- * const pagination = createPagination({ page, size: 100 })
2932
- * // Mutating pagination.page or the passed ref syncs both
3321
+ * export const [useHydrationContext, provideHydrationContext, context] = createHydrationContext({
3322
+ * namespace: 'app:hydration',
3323
+ * })
2933
3324
  * ```
2934
3325
  */
2935
- function createPagination(_options = {}) {
2936
- const { page: _page = 1, itemsPerPage: _itemsPerPage = 10, size: _size = 0, visible: _visible = 7, ellipsis = "..." } = _options;
2937
- const page = isRef(_page) ? _page : shallowRef(_page);
2938
- const pages = computed(() => {
2939
- const size = toValue(_size);
2940
- const perPage = toValue(_itemsPerPage);
2941
- if (size <= 0 || /* @__PURE__ */ isNaN(size)) return 0;
2942
- return Math.ceil(size / perPage);
2943
- });
2944
- function first() {
2945
- page.value = 1;
2946
- }
2947
- function last() {
2948
- page.value = Math.max(1, pages.value);
2949
- }
2950
- function next() {
2951
- if (page.value < pages.value) page.value++;
2952
- }
2953
- function prev() {
2954
- if (page.value > 1) page.value--;
2955
- }
2956
- function select(value) {
2957
- if (value < 1) page.value = 1;
2958
- else if (value > pages.value) page.value = Math.max(1, pages.value);
2959
- else page.value = value;
2960
- }
2961
- const isFirst = computed(() => page.value <= 1);
2962
- const isLast = computed(() => page.value >= pages.value);
2963
- const pageStart = computed(() => (page.value - 1) * toValue(_itemsPerPage));
2964
- const pageStop = computed(() => Math.min(pageStart.value + toValue(_itemsPerPage), toValue(_size)));
2965
- function toPage(value) {
2966
- return {
2967
- type: "page",
2968
- value
2969
- };
2970
- }
2971
- function toEllipsis() {
2972
- return ellipsis === false ? false : {
2973
- type: "ellipsis",
2974
- value: ellipsis
2975
- };
2976
- }
2977
- function filter(array) {
2978
- return array.filter((item) => item !== false);
3326
+ function createHydrationContext(_options = {}) {
3327
+ const { namespace = "v0:hydration" } = _options;
3328
+ const [useHydrationContext, _provideHydrationContext] = createContext(namespace);
3329
+ const context = createHydration();
3330
+ function provideHydrationContext(_context = context, app) {
3331
+ return _provideHydrationContext(_context, app);
2979
3332
  }
2980
- return {
2981
- page,
2982
- ellipsis,
2983
- items: computed(() => {
2984
- const pageCount = pages.value;
2985
- const visible = toValue(_visible);
2986
- const current = page.value;
2987
- if (pageCount <= 0 || /* @__PURE__ */ isNaN(pageCount) || pageCount > Number.MAX_SAFE_INTEGER) return [];
2988
- if (visible <= 0) return [];
2989
- if (visible <= 2) return [toPage(current)];
2990
- if (pageCount <= visible) return (/* @__PURE__ */ range(pageCount, 1)).map(toPage);
2991
- if (visible === 3) {
2992
- const mid = current <= 1 ? 2 : current >= pageCount ? pageCount - 1 : current;
2993
- return [
2994
- toPage(1),
2995
- toPage(mid),
2996
- toPage(pageCount)
2997
- ];
2998
- }
2999
- const boundary = visible - 2;
3000
- const middle = visible - 4;
3001
- if (middle <= 0) {
3002
- if (current <= boundary) return filter([
3003
- ...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
3004
- toEllipsis(),
3005
- toPage(pageCount)
3006
- ]);
3007
- if (current > pageCount - boundary) return filter([
3008
- toPage(1),
3009
- toEllipsis(),
3010
- ...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
3011
- ]);
3012
- return current <= Math.ceil(pageCount / 2) ? filter([
3013
- toPage(1),
3014
- toPage(current),
3015
- toEllipsis(),
3016
- toPage(pageCount)
3017
- ]) : filter([
3018
- toPage(1),
3019
- toEllipsis(),
3020
- toPage(current),
3021
- toPage(pageCount)
3022
- ]);
3023
- }
3024
- const leftThreshold = boundary - 1;
3025
- const rightThreshold = pageCount - boundary + 2;
3026
- if (current <= leftThreshold) return filter([
3027
- ...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
3028
- toEllipsis(),
3029
- toPage(pageCount)
3030
- ]);
3031
- else if (current >= rightThreshold) return filter([
3032
- toPage(1),
3033
- toEllipsis(),
3034
- ...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
3035
- ]);
3036
- else {
3037
- const start = current - Math.floor(middle / 2);
3038
- return filter([
3039
- toPage(1),
3040
- toEllipsis(),
3041
- ...(/* @__PURE__ */ range(middle, start)).map(toPage),
3042
- toEllipsis(),
3043
- toPage(pageCount)
3044
- ]);
3045
- }
3046
- }),
3047
- pageStart,
3048
- pageStop,
3049
- isFirst,
3050
- isLast,
3051
- first,
3052
- last,
3053
- next,
3054
- prev,
3055
- select,
3056
- get itemsPerPage() {
3057
- return toValue(_itemsPerPage);
3058
- },
3059
- get size() {
3060
- return toValue(_size);
3061
- },
3062
- get pages() {
3063
- return pages.value;
3064
- }
3065
- };
3333
+ return createTrinity(useHydrationContext, provideHydrationContext, context);
3066
3334
  }
3067
3335
  /**
3068
- * Creates a pagination context for dependency injection.
3336
+ * Creates a new hydration plugin.
3069
3337
  *
3070
- * @param options The options including namespace.
3071
- * @returns A trinity: [usePagination, providePagination, defaultContext]
3338
+ * @param options The options for the hydration plugin.
3339
+ * @template E The type of the hydration context.
3340
+ * @returns A new hydration plugin.
3341
+ *
3342
+ * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
3072
3343
  *
3073
3344
  * @example
3074
3345
  * ```ts
3075
- * // With default namespace 'v0:pagination'
3076
- * const [usePagination, providePaginationContext] = createPaginationContext({ size: 50 })
3346
+ * import { createApp } from 'vue'
3347
+ * import { createHydrationPlugin } from '@vuetify/v0'
3348
+ * import App from './App.vue'
3077
3349
  *
3078
- * // Or with custom namespace
3079
- * const [usePagination, providePaginationContext] = createPaginationContext({
3080
- * namespace: 'my-pagination',
3081
- * size: 50,
3082
- * })
3350
+ * const app = createApp(App)
3083
3351
  *
3084
- * // Parent component
3085
- * providePaginationContext()
3352
+ * app.use(createHydrationPlugin())
3086
3353
  *
3087
- * // Child component
3088
- * const pagination = usePagination()
3089
- * pagination.next()
3354
+ * app.mount('#app')
3090
3355
  * ```
3091
3356
  */
3092
- function createPaginationContext(_options = {}) {
3093
- const { namespace = "v0:pagination", ...options } = _options;
3094
- const [usePaginationContext, _providePaginationContext] = createContext(namespace);
3095
- const context = createPagination(options);
3096
- function providePaginationContext(_context = context, app) {
3097
- return _providePaginationContext(_context, app);
3098
- }
3099
- return createTrinity(usePaginationContext, providePaginationContext, context);
3357
+ function createHydrationPlugin(_options = {}) {
3358
+ const { namespace = "v0:hydration", ...options } = _options;
3359
+ const [, provideHydrationContext, context] = createHydrationContext({
3360
+ ...options,
3361
+ namespace
3362
+ });
3363
+ return createPlugin({
3364
+ namespace,
3365
+ provide: (app) => {
3366
+ provideHydrationContext(context, app);
3367
+ },
3368
+ setup: (app) => {
3369
+ app.mixin({ mounted() {
3370
+ if (!/* @__PURE__ */ isNull(this.$parent)) return;
3371
+ context.hydrate();
3372
+ } });
3373
+ }
3374
+ });
3100
3375
  }
3101
3376
  /**
3102
- * Returns the current pagination instance from context.
3377
+ * Returns the current hydration instance.
3103
3378
  *
3104
- * @param namespace The namespace. @default 'v0:pagination'
3105
- * @returns The pagination context.
3379
+ * @param namespace The namespace for the hydration context. Defaults to `v0:hydration`.
3380
+ * @returns The current hydration instance.
3381
+ *
3382
+ * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
3106
3383
  *
3107
3384
  * @example
3108
3385
  * ```vue
3109
- * <script setup>
3110
- * import { usePagination } from '@vuetify/v0'
3386
+ * <script setup lang="ts">
3387
+ * import { useHydration } from '@vuetify/v0'
3111
3388
  *
3112
- * const pagination = usePagination()
3389
+ * const hydration = useHydration()
3113
3390
  * <\/script>
3114
3391
  *
3115
3392
  * <template>
3116
- * <button @click="pagination.prev()" :disabled="pagination.isFirst.value">Prev</button>
3117
- * <button @click="pagination.next()" :disabled="pagination.isLast.value">Next</button>
3393
+ * <div>
3394
+ * <p>Is hydrated: {{ hydration.isHydrated.value }}</p>
3395
+ * </div>
3118
3396
  * </template>
3119
3397
  * ```
3120
3398
  */
3121
- function usePagination(namespace = "v0:pagination") {
3122
- return useContext(namespace);
3399
+ function useHydration(namespace = "v0:hydration") {
3400
+ const fallback = createFallbackHydration();
3401
+ if (!getCurrentInstance()) return fallback;
3402
+ try {
3403
+ return useContext(namespace, fallback);
3404
+ } catch {
3405
+ return fallback;
3406
+ }
3123
3407
  }
3124
3408
 
3125
3409
  //#endregion
3126
- //#region src/composables/useSingle/index.ts
3410
+ //#region src/composables/useResizeObserver/index.ts
3127
3411
  /**
3128
- * @module useSingle
3412
+ * @module useResizeObserver
3129
3413
  *
3130
3414
  * @remarks
3131
- * Single-selection composable that extends useSelection to enforce only one selected item.
3415
+ * ResizeObserver composable with lifecycle management.
3132
3416
  *
3133
3417
  * Key features:
3134
- * - Auto-clears previous selection when selecting new item
3135
- * - Singular computed properties (selectedId, selectedItem, selectedIndex, selectedValue)
3136
- * - Perfect for tabs, radio buttons, theme selectors
3418
+ * - ResizeObserver API wrapper
3419
+ * - Pause/resume/stop functionality
3420
+ * - Automatic cleanup on unmount
3421
+ * - SSR-safe (checks SUPPORTS_OBSERVER)
3422
+ * - Hydration-aware
3423
+ * - Box model options (content-box/border-box)
3137
3424
  *
3138
- * Inheritance chain: useRegistry useSelection useSingle
3425
+ * Perfect for responsive components and size-based rendering.
3139
3426
  */
3140
3427
  /**
3141
- * Creates a new single selection instance that enforces only one selected item at a time.
3142
- *
3143
- * Extends `createSelection` by automatically clearing previous selections when a new item is selected.
3144
- * Adds computed singular properties: `selectedId`, `selectedItem`, `selectedIndex`, `selectedValue`.
3145
- *
3146
- * @param options The options for the single selection instance.
3147
- * @template Z The type of the single selection ticket.
3148
- * @template E The type of the single selection context.
3149
- * @returns A new single selection instance with single-selection enforcement.
3150
- *
3151
- * @remarks
3152
- * **Key Differences from `createSelection`:**
3153
- * - Automatically clears `selectedIds` before selecting a new item (enforces single selection)
3154
- * - Provides singular computed properties instead of plural sets
3155
- * - Perfect for tabs, radio buttons, theme selectors, and other single-choice UI components
3156
- *
3157
- * **Computed Properties:**
3158
- * - `selectedId`: The ID of the selected item (undefined if none selected)
3159
- * - `selectedItem`: The selected ticket object (undefined if none selected)
3160
- * - `selectedIndex`: The index of the selected item (-1 if none selected)
3161
- * - `selectedValue`: The value of the selected item (undefined if none selected)
3428
+ * A composable that uses the Resize Observer API to detect when an element's
3429
+ * size changes.
3162
3430
  *
3163
- * **Inheritance Chain:**
3164
- * `useRegistry` `createSelection` `createSingle` `createStep`
3431
+ * @param target The element to observe.
3432
+ * @param callback The callback to execute when the element's size changes.
3433
+ * @param options The options for the Resize Observer.
3434
+ * @returns An object with methods to control the observer.
3165
3435
  *
3166
- * @see https://0.vuetifyjs.com/composables/selection/use-single
3436
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
3437
+ * @see https://0.vuetifyjs.com/composables/system/use-resize-observer
3167
3438
  *
3168
3439
  * @example
3169
3440
  * ```ts
3170
- * import { createSingle } from '@vuetify/v0'
3171
- *
3172
- * const tabs = createSingle({ mandatory: true })
3441
+ * import { ref } from 'vue'
3442
+ * import { useResizeObserver } from '@vuetify/v0'
3173
3443
  *
3174
- * tabs.onboard([
3175
- * { id: 'home', value: 'Home' },
3176
- * { id: 'about', value: 'About' },
3177
- * { id: 'contact', value: 'Contact' },
3178
- * ])
3444
+ * const el = ref<HTMLElement>()
3445
+ * const width = ref(0)
3446
+ * const height = ref(0)
3179
3447
  *
3180
- * tabs.first() // Select first tab
3448
+ * const { pause, resume, isPaused } = useResizeObserver(
3449
+ * el,
3450
+ * (entries) => {
3451
+ * const entry = entries[0]
3452
+ * if (entry) {
3453
+ * width.value = entry.contentRect.width
3454
+ * height.value = entry.contentRect.height
3455
+ * console.log('Size changed:', width.value, 'x', height.value)
3456
+ * }
3457
+ * },
3458
+ * { immediate: true }
3459
+ * )
3181
3460
  *
3182
- * console.log(tabs.selectedId.value) // 'home'
3183
- * console.log(tabs.selectedIndex.value) // 0
3461
+ * // Pause observation
3462
+ * pause()
3184
3463
  *
3185
- * tabs.select('about') // Switch to about tab
3186
- * console.log(tabs.selectedId.value) // 'about'
3187
- * console.log(tabs.selectedIds.size) // 1 (always enforces single selection)
3464
+ * // Resume observation
3465
+ * resume()
3188
3466
  * ```
3189
3467
  */
3190
- function createSingle(_options = {}) {
3191
- const { mandatory = false, multiple = false, ...options } = _options;
3192
- const registry = createSelection({
3193
- ...options,
3194
- mandatory,
3195
- multiple
3468
+ function useResizeObserver(target, callback, options = {}) {
3469
+ const { isHydrated } = useHydration();
3470
+ const observer = shallowRef();
3471
+ const isPaused = shallowRef(false);
3472
+ const isActive = toRef(() => !!observer.value);
3473
+ function setup() {
3474
+ if (/* @__PURE__ */ isNull(observer.value)) return;
3475
+ if (!isHydrated.value || !SUPPORTS_OBSERVER || !target.value || isPaused.value) return;
3476
+ observer.value = new ResizeObserver((entries) => {
3477
+ callback(entries.map((entry) => ({
3478
+ contentRect: {
3479
+ width: entry.contentRect.width,
3480
+ height: entry.contentRect.height,
3481
+ top: entry.contentRect.top,
3482
+ left: entry.contentRect.left
3483
+ },
3484
+ target: entry.target
3485
+ })));
3486
+ if (options.once) stop();
3487
+ });
3488
+ observer.value.observe(target.value, { box: options.box || "content-box" });
3489
+ if (options.immediate) {
3490
+ const rect = target.value.getBoundingClientRect();
3491
+ callback([{
3492
+ contentRect: {
3493
+ width: rect.width,
3494
+ height: rect.height,
3495
+ top: rect.top,
3496
+ left: rect.left
3497
+ },
3498
+ target: target.value
3499
+ }]);
3500
+ }
3501
+ }
3502
+ watchEffect(() => {
3503
+ const hydrated = isHydrated.value;
3504
+ const el = target.value;
3505
+ cleanup();
3506
+ if (hydrated && el) setup();
3196
3507
  });
3197
- const selectedId = computed(() => registry.selectedIds.values().next().value);
3198
- const selectedItem = computed(() => registry.selectedItems.value.values().next().value);
3199
- const selectedIndex = computed(() => selectedItem.value?.index ?? -1);
3200
- const selectedValue = computed(() => selectedItem.value?.value);
3201
- function unselect(id) {
3202
- if (mandatory && registry.selectedIds.size === 1) return;
3203
- registry.selectedIds.delete(id);
3508
+ function cleanup() {
3509
+ if (observer.value) {
3510
+ observer.value.disconnect();
3511
+ observer.value = void 0;
3512
+ }
3204
3513
  }
3205
- function toggle(id) {
3206
- if (registry.selectedIds.has(id)) unselect(id);
3207
- else registry.select(id);
3514
+ function pause() {
3515
+ isPaused.value = true;
3516
+ observer.value?.disconnect();
3517
+ }
3518
+ function resume() {
3519
+ isPaused.value = false;
3520
+ setup();
3521
+ }
3522
+ function stop() {
3523
+ cleanup();
3524
+ observer.value = null;
3208
3525
  }
3526
+ onScopeDispose(stop, true);
3209
3527
  return {
3210
- ...registry,
3211
- selectedId,
3212
- selectedItem,
3213
- selectedIndex,
3214
- selectedValue,
3215
- unselect,
3216
- toggle,
3217
- get size() {
3218
- return registry.size;
3219
- }
3528
+ isActive: shallowReadonly(isActive),
3529
+ isPaused: shallowReadonly(isPaused),
3530
+ pause,
3531
+ resume,
3532
+ stop
3220
3533
  };
3221
3534
  }
3222
3535
  /**
3223
- * Creates a new single selection context.
3536
+ * A convenience composable that uses the Resize Observer API to track an
3537
+ * element's size.
3224
3538
  *
3225
- * @param options The options for the single selection context.
3226
- * @template Z The type of the single selection ticket.
3227
- * @template E The type of the single selection context.
3228
- * @returns A new single selection context.
3539
+ * @param target The element to observe.
3540
+ * @returns An object with the element's width and height.
3229
3541
  *
3230
- * @see https://0.vuetifyjs.com/composables/selection/use-single
3542
+ * @see https://0.vuetifyjs.com/composables/system/use-resize-observer#use-element-size
3231
3543
  *
3232
3544
  * @example
3233
3545
  * ```ts
3234
- * import { createSingleContext } from '@vuetify/v0'
3235
- *
3236
- * // With default namespace 'v0:single'
3237
- * export const [useSingle, provideSingle, context] = createSingleContext()
3546
+ * import { ref, watchEffect } from 'vue'
3547
+ * import { useElementSize } from '@vuetify/v0'
3238
3548
  *
3239
- * // In a parent component:
3240
- * provideSingle()
3549
+ * const box = ref<HTMLElement>()
3550
+ * const { width, height } = useElementSize(box)
3241
3551
  *
3242
- * // In a child component:
3243
- * const single = useSingle()
3244
- * single.select('tab-1')
3552
+ * // Width and height are reactive refs
3553
+ * watchEffect(() => {
3554
+ * console.log('Box size:', width.value, 'x', height.value)
3555
+ * })
3245
3556
  * ```
3246
3557
  */
3247
- function createSingleContext(_options = {}) {
3248
- const { namespace = "v0:single", ...options } = _options;
3249
- const [useSingleContext, _provideSingleContext] = createContext(namespace);
3250
- const context = createSingle(options);
3251
- function provideSingleContext(_context = context, app) {
3252
- return _provideSingleContext(_context, app);
3558
+ function useElementSize(target) {
3559
+ const width = shallowRef(0);
3560
+ const height = shallowRef(0);
3561
+ const { pause: _pause, resume, stop, isActive, isPaused } = useResizeObserver(target, (entries) => {
3562
+ const entry = entries[0];
3563
+ if (entry) {
3564
+ width.value = entry.contentRect.width;
3565
+ height.value = entry.contentRect.height;
3566
+ }
3567
+ }, { immediate: true });
3568
+ function pause() {
3569
+ width.value = 0;
3570
+ height.value = 0;
3571
+ _pause();
3253
3572
  }
3254
- return createTrinity(useSingleContext, provideSingleContext, context);
3255
- }
3256
- /**
3257
- * Returns the current single selection instance.
3258
- *
3259
- * @param namespace The namespace for the single selection context. Defaults to `'v0:single'`.
3260
- * @returns The current single selection instance.
3261
- *
3262
- * @see https://0.vuetifyjs.com/composables/selection/use-single
3263
- *
3264
- * @example
3265
- * ```vue
3266
- * <script setup lang="ts">
3267
- * import { useSingle } from '@vuetify/v0'
3268
- *
3269
- * const tabs = useSingle()
3270
- * <\/script>
3271
- *
3272
- * <template>
3273
- * <div>
3274
- * <p>Selected: {{ tabs.selectedId }}</p>
3275
- * </div>
3276
- * </template>
3277
- * ```
3278
- */
3279
- function useSingle(namespace = "v0:single") {
3280
- return useContext(namespace);
3573
+ return {
3574
+ width,
3575
+ height,
3576
+ isActive,
3577
+ isPaused,
3578
+ pause,
3579
+ resume,
3580
+ stop
3581
+ };
3281
3582
  }
3282
3583
 
3283
3584
  //#endregion
3284
- //#region src/composables/useTokens/index.ts
3285
- /**
3286
- * @module useTokens
3287
- *
3288
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
3585
+ //#region src/composables/useOverflow/index.ts
3586
+ /**
3587
+ * @module useOverflow
3289
3588
  *
3290
3589
  * @remarks
3291
- * Design token registry with alias resolution and W3C Design Tokens format support.
3590
+ * Composable for computing how many items fit in a container based on available width.
3591
+ * Enables responsive truncation logic for Pagination, Breadcrumbs, and similar components.
3292
3592
  *
3293
3593
  * Key features:
3294
- * - Alias resolution with circular reference detection
3295
- * - Nested token flattening with dot notation
3296
- * - W3C Design Tokens format ($value, $type, $description, $extensions)
3297
- * - Path-based resolution (e.g., {colors}.blue.500)
3298
- * - Resolution caching for performance (~28,590 ops/sec)
3594
+ * - Container width tracking via ResizeObserver
3595
+ * - Two modes: variable-width (per-item) or uniform-width (sample-based)
3596
+ * - Computes capacity (how many items fit)
3597
+ * - SSR-safe with Infinity fallback
3598
+ * - Supports reserved space for nav buttons, ellipsis, etc.
3299
3599
  *
3300
- * Used by useTheme, useLocale, and useFeatures for token-based configuration.
3600
+ * Use variable mode (default) for items with different widths like Breadcrumbs.
3601
+ * Use uniform mode (itemWidth option) for same-width items like Pagination buttons.
3301
3602
  */
3302
3603
  /**
3303
- * Creates a new token instance.
3604
+ * Creates a new overflow context for computing how many items fit in a container.
3304
3605
  *
3305
- * @param tokens The tokens to use.
3306
- * @param options The options for the token instance.
3307
- * @template Z The type of the token ticket.
3308
- * @template E The type of the token context.
3309
- * @returns A new token instance.
3606
+ * @param options Configuration options
3607
+ * @returns Overflow context with container ref, capacity, and measurement functions
3310
3608
  *
3311
- * @see https://www.designtokens.org/tr/drafts/format/
3312
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
3609
+ * @example Variable-width mode (Breadcrumbs)
3610
+ * ```vue
3611
+ * <script lang="ts" setup>
3612
+ * import { useTemplateRef } from 'vue'
3613
+ * import { createOverflow } from '@vuetify/v0'
3313
3614
  *
3314
- * @example
3315
- * ```ts
3316
- * import { useTokens } from '@vuetify/v0'
3615
+ * const containerRef = useTemplateRef('container')
3616
+ * const overflow = createOverflow({
3617
+ * container: containerRef,
3618
+ * gap: 8,
3619
+ * reserved: 40,
3620
+ * })
3621
+ * <\/script>
3317
3622
  *
3318
- * const tokens = useTokens({
3319
- * colors: {
3320
- * primary: '#3b82f6',
3321
- * secondary: '{colors.primary}', // Alias reference
3322
- * },
3323
- * })
3623
+ * <template>
3624
+ * <div ref="container">
3625
+ * <span
3626
+ * v-for="(item, i) in items.slice(0, overflow.capacity.value)"
3627
+ * :key="i"
3628
+ * :ref="el => overflow.measure(i, el)"
3629
+ * >
3630
+ * {{ item }}
3631
+ * </span>
3632
+ * <span v-if="overflow.isOverflowing.value">...</span>
3633
+ * </div>
3634
+ * </template>
3635
+ * ```
3324
3636
  *
3325
- * console.log(tokens.resolve('{colors.primary}')) // '#3b82f6'
3326
- * console.log(tokens.resolve('{colors.secondary}')) // '#3b82f6'
3637
+ * @example Uniform-width mode (Pagination)
3638
+ * ```ts
3639
+ * const overflow = createOverflow({
3640
+ * container: () => atom.value?.element,
3641
+ * itemWidth: buttonWidth,
3642
+ * reserved: () => buttonWidth.value * 4,
3643
+ * })
3327
3644
  * ```
3328
3645
  */
3329
- function createTokens(tokens = {}, options = {}) {
3330
- const logger = useLogger();
3331
- const registry = useRegistry(options);
3332
- const cache = /* @__PURE__ */ new Map();
3333
- registry.onboard(flatten(tokens, options.prefix, !!options.flat));
3334
- function isAlias(token) {
3335
- return /* @__PURE__ */ isString(token) && token.length > 2 && token[0] === "{" && token.at(-1) === "}";
3336
- }
3337
- function isTokenAlias(value) {
3338
- return /* @__PURE__ */ isObject(value) && "$value" in value;
3339
- }
3340
- function resolve(token, visited = /* @__PURE__ */ new Set()) {
3341
- const cacheKey = /* @__PURE__ */ isString(token) ? token : JSON.stringify(token);
3342
- const cached = cache.get(cacheKey);
3343
- if (!/* @__PURE__ */ isUndefined(cached)) return cached;
3344
- const reference = isTokenAlias(token) ? token.$value : token;
3345
- const isAliasReference = /* @__PURE__ */ isString(reference) && isAlias(reference);
3346
- const clean = isAliasReference ? reference.slice(1, -1) : String(reference);
3347
- if (visited.has(clean)) {
3348
- logger.warn(`Circular alias detected for "${clean}"`);
3349
- cache.set(cacheKey, void 0);
3350
- return;
3351
- }
3352
- visited.add(clean);
3353
- let found = registry.get(clean);
3354
- let segments = [];
3355
- if (!found && clean.includes(".")) {
3356
- const parts = clean.split(".");
3357
- for (let i = parts.length - 1; i > 0; i--) {
3358
- const prefix = parts.slice(0, i).join(".");
3359
- const suffix = parts.slice(i);
3360
- const candidate = registry.get(prefix);
3361
- if (!/* @__PURE__ */ isUndefined(candidate?.value)) {
3362
- found = candidate;
3363
- segments = suffix;
3364
- break;
3365
- }
3646
+ function createOverflow(options = {}) {
3647
+ const { container: _container, gap = 0, reserved = 0, itemWidth, reverse } = options;
3648
+ const container = /* @__PURE__ */ isUndefined(_container) ? shallowRef() : toRef(_container);
3649
+ const widths = shallowRef(/* @__PURE__ */ new Map());
3650
+ const { width } = useElementSize(container);
3651
+ function measure(index, el) {
3652
+ if (!el) {
3653
+ if (widths.value.has(index)) {
3654
+ const next = new Map(widths.value);
3655
+ next.delete(index);
3656
+ widths.value = next;
3366
3657
  }
3367
- }
3368
- if (/* @__PURE__ */ isUndefined(found?.value)) {
3369
- if (isAliasReference) logger.warn(`Alias not found for "${String(reference)}"`);
3370
- cache.set(cacheKey, void 0);
3371
3658
  return;
3372
3659
  }
3373
- let result;
3374
- let current = found.value;
3375
- if (segments.length > 0) {
3376
- if (isTokenAlias(current)) current = current.$value;
3377
- for (const segment of segments) {
3378
- if (!/* @__PURE__ */ isObject(current) || !(segment in current)) {
3379
- current = void 0;
3380
- break;
3381
- }
3382
- current = current[segment];
3383
- if (isTokenAlias(current)) current = current.$value;
3384
- }
3385
- if (/* @__PURE__ */ isUndefined(current)) {
3386
- logger.warn(`Path not found inside "${clean}": ${segments.join(".")}`);
3387
- cache.set(cacheKey, void 0);
3388
- return;
3389
- }
3390
- result = current;
3391
- } else if (isTokenAlias(current)) {
3392
- const inner = current.$value;
3393
- if (/* @__PURE__ */ isString(inner) && isAlias(inner)) return resolve(inner, visited);
3394
- result = inner;
3395
- } else if (/* @__PURE__ */ isString(current) && isAlias(current)) return resolve(current, visited);
3396
- else result = current;
3397
- cache.set(cacheKey, result);
3398
- return result;
3660
+ const style = getComputedStyle(el);
3661
+ const marginX = Number.parseFloat(style.marginLeft) + Number.parseFloat(style.marginRight);
3662
+ const w = el.offsetWidth + marginX;
3663
+ if (widths.value.get(index) !== w) widths.value = new Map(widths.value).set(index, w);
3399
3664
  }
3400
- return {
3401
- ...registry,
3402
- resolve,
3403
- isAlias,
3404
- get size() {
3405
- return registry.size;
3665
+ function reset() {
3666
+ widths.value = /* @__PURE__ */ new Map();
3667
+ }
3668
+ const total = computed(() => {
3669
+ const g = toValue(gap);
3670
+ let sum = 0;
3671
+ let count = 0;
3672
+ for (const w of widths.value.values()) {
3673
+ sum += w + (count > 0 ? g : 0);
3674
+ count++;
3406
3675
  }
3676
+ return sum;
3677
+ });
3678
+ return {
3679
+ container,
3680
+ width,
3681
+ capacity: computed(() => {
3682
+ const available = width.value - toValue(reserved);
3683
+ if (width.value === 0) return Infinity;
3684
+ if (available <= 0) return 0;
3685
+ const g = toValue(gap);
3686
+ const uniformWidth = toValue(itemWidth);
3687
+ if (uniformWidth && uniformWidth > 0) {
3688
+ const first = uniformWidth;
3689
+ const subsequent = uniformWidth + g;
3690
+ if (available < first) return 0;
3691
+ return Math.max(1, Math.floor((available - first) / subsequent) + 1);
3692
+ }
3693
+ const entries = [...widths.value.entries()].toSorted((a, b) => a[0] - b[0]);
3694
+ if (toValue(reverse)) entries.reverse();
3695
+ let sum = 0;
3696
+ let count = 0;
3697
+ for (const [, w] of entries) {
3698
+ const next = sum + w + (count > 0 ? g : 0);
3699
+ if (next > available) break;
3700
+ sum = next;
3701
+ count++;
3702
+ }
3703
+ return count;
3704
+ }),
3705
+ total,
3706
+ isOverflowing: toRef(() => {
3707
+ return total.value > width.value - toValue(reserved);
3708
+ }),
3709
+ measure,
3710
+ reset
3407
3711
  };
3408
3712
  }
3409
3713
  /**
3410
- * Creates a new token context.
3411
- *
3412
- * @param namespace The namespace for the token context.
3413
- * @param tokens The tokens to use.
3414
- * @template Z The type of the token ticket.
3415
- * @template E The type of the token context.
3416
- * @returns A new token context.
3714
+ * Creates an overflow context with dependency injection support.
3417
3715
  *
3418
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
3716
+ * @param options Configuration options including namespace
3717
+ * @returns Trinity tuple: [useContext, provideContext, defaultContext]
3419
3718
  *
3420
3719
  * @example
3421
3720
  * ```ts
3422
- * import { createTokensContext } from '@vuetify/v0'
3423
- *
3424
- * export const [useTokens, provideTokens, context] = createTokensContext({
3425
- * namespace: 'v0:tokens',
3426
- * tokens: {
3427
- * colors: {
3428
- * primary: '#3b82f6',
3429
- * secondary: '{colors.primary}', // Alias reference
3430
- * },
3431
- * },
3721
+ * // Create injectable context
3722
+ * const [useOverflow, provideOverflow, overflow] = createOverflowContext({
3723
+ * namespace: 'my-overflow',
3724
+ * gap: 8,
3725
+ * reserved: 160,
3432
3726
  * })
3727
+ *
3728
+ * // In parent component
3729
+ * provideOverflow()
3730
+ *
3731
+ * // In child component
3732
+ * const overflow = useOverflow()
3433
3733
  * ```
3434
3734
  */
3435
- function createTokensContext(_options) {
3436
- const { namespace = "v0:tokens", tokens = {}, ...options } = _options;
3437
- const [useTokensContext, _provideTokensContext] = createContext(namespace);
3438
- const context = createTokens(tokens, options);
3439
- function provideTokensContext(_context = context, app) {
3440
- return _provideTokensContext(_context, app);
3735
+ function createOverflowContext(_options = {}) {
3736
+ const { namespace = "v0:overflow", ...options } = _options;
3737
+ const [useOverflowContext, _provideOverflowContext] = createContext(namespace);
3738
+ const context = createOverflow(options);
3739
+ function provideOverflowContext(_context = context, app) {
3740
+ return _provideOverflowContext(_context, app);
3441
3741
  }
3442
- return createTrinity(useTokensContext, provideTokensContext, context);
3443
- }
3444
- /**
3445
- * Returns the current tokens instance.
3446
- *
3447
- * @param namespace The namespace for the tokens context. Defaults to `'v0:tokens'`.
3448
- * @returns The current tokens instance.
3742
+ return createTrinity(useOverflowContext, provideOverflowContext, context);
3743
+ }
3744
+ /**
3745
+ * Returns the current overflow context from dependency injection.
3449
3746
  *
3450
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
3747
+ * @param namespace The namespace for the overflow context. Defaults to `v0:overflow`.
3748
+ * @returns The current overflow context.
3451
3749
  *
3452
3750
  * @example
3453
3751
  * ```vue
3454
- * <script setup lang="ts">
3455
- * import { useTokens } from '@vuetify/v0'
3752
+ * <script lang="ts" setup>
3753
+ * import { useOverflow } from '@vuetify/v0'
3456
3754
  *
3457
- * const tokens = useTokens()
3755
+ * // Inject overflow context provided by parent
3756
+ * const overflow = useOverflow()
3458
3757
  * <\/script>
3758
+ *
3759
+ * <template>
3760
+ * <div>
3761
+ * <p>Capacity: {{ overflow.capacity.value }}</p>
3762
+ * </div>
3763
+ * </template>
3459
3764
  * ```
3460
3765
  */
3461
- function useTokens(namespace = "v0:tokens") {
3766
+ function useOverflow(namespace = "v0:overflow") {
3462
3767
  return useContext(namespace);
3463
3768
  }
3464
- /**
3465
- * Flattens a nested collection of tokens into a flat array of tokens.
3466
- * Each token is represented by an object containing its ID & value.
3467
- * @param tokens The collection of tokens to flatten.
3468
- * @param prefix An optional prefix to prepend to each token ID.
3469
- * @returns An array of flattened tokens, each with an ID and value.
3470
- */
3471
- function flatten(tokens, prefix = "", flat = false) {
3472
- const flattened = [];
3473
- const stack = [{
3474
- tokens,
3475
- prefix,
3476
- flat
3477
- }];
3478
- while (stack.length > 0) {
3479
- const { tokens: currentTokens, prefix: currentPrefix, flat: flat$1 } = stack.pop();
3480
- const meta = {};
3481
- for (const k in currentTokens) if (k.startsWith("$")) meta[k] = currentTokens[k];
3482
- if (Object.keys(meta).length > 0 && currentPrefix) flattened.push({
3483
- id: currentPrefix,
3484
- value: meta
3485
- });
3486
- for (const key in currentTokens) {
3487
- if (key.startsWith("$")) continue;
3488
- const value = currentTokens[key];
3489
- const id = currentPrefix ? `${currentPrefix}.${key}` : key;
3490
- if (!/* @__PURE__ */ isObject(value)) {
3491
- flattened.push({
3492
- id,
3493
- value
3494
- });
3495
- continue;
3496
- }
3497
- if ("$value" in value) {
3498
- flattened.push({
3499
- id,
3500
- value
3501
- });
3502
- const inner = value.$value;
3503
- if (/* @__PURE__ */ isObject(inner) && !flat$1) for (const innerKey in inner) {
3504
- if (innerKey.startsWith("$")) continue;
3505
- const child = inner[innerKey];
3506
- const childId = `${id}.${innerKey}`;
3507
- if (!/* @__PURE__ */ isObject(child)) flattened.push({
3508
- id: childId,
3509
- value: child
3510
- });
3511
- else if ("$value" in child) flattened.push({
3512
- id: childId,
3513
- value: child
3514
- });
3515
- else stack.push({
3516
- tokens: child,
3517
- prefix: childId,
3518
- flat: flat$1
3519
- });
3520
- }
3521
- continue;
3522
- }
3523
- if (flat$1) {
3524
- flattened.push({
3525
- id,
3526
- value
3527
- });
3528
- continue;
3529
- }
3530
- stack.push({
3531
- tokens: value,
3532
- prefix: id,
3533
- flat: flat$1
3534
- });
3535
- }
3536
- }
3537
- return flattened;
3538
- }
3539
-
3540
- //#endregion
3541
- //#region src/composables/useLocale/adapters/v0.ts
3542
- /**
3543
- * Vuetify0.x locale adapter implementation
3544
- *
3545
- * This adapter provides translation and number formatting
3546
- * capabilities using the Intl API and supports both
3547
- * numbered ({0}, {1}) and named ({name}) variables in translation strings.
3548
- */
3549
- var Vuetify0LocaleAdapter = class {
3550
- t(message, ...params) {
3551
- let resolvedMessage = message;
3552
- if (params.length > 0 && /* @__PURE__ */ isObject(params[0])) {
3553
- const variables = params[0];
3554
- resolvedMessage = resolvedMessage.replace(/{([a-zA-Z][a-zA-Z0-9_]*)}/g, (match, name) => {
3555
- return /* @__PURE__ */ isUndefined(variables[name]) ? match : String(variables[name]);
3556
- });
3557
- params = params.slice(1);
3558
- }
3559
- resolvedMessage = resolvedMessage.replace(/\{(\d+)\}/g, (match, index) => {
3560
- const idx = Number.parseInt(index, 10);
3561
- if (!/* @__PURE__ */ isUndefined(params[idx])) return String(params[idx]);
3562
- return match;
3563
- });
3564
- return resolvedMessage;
3565
- }
3566
- n(value, locale, ...params) {
3567
- if (!IN_BROWSER || !locale) return value.toString();
3568
- const options = params[0];
3569
- return new Intl.NumberFormat(String(locale), options).format(value);
3570
- }
3571
- };
3572
3769
 
3573
3770
  //#endregion
3574
- //#region src/composables/useLocale/index.ts
3771
+ //#region src/composables/usePagination/index.ts
3575
3772
  /**
3576
- * @module useLocale
3773
+ * @module usePagination
3577
3774
  *
3578
3775
  * @remarks
3579
- * Internationalization (i18n) composable with adapter pattern for message translation.
3776
+ * Lightweight pagination composable for navigating through pages.
3580
3777
  *
3581
3778
  * Key features:
3582
- * - Locale selection with createSingle
3583
- * - Token-based message storage with useTokens
3584
- * - Numbered and named placeholder support ({0}, {name})
3585
- * - Number formatting with Intl.NumberFormat
3586
- * - Adapter pattern for integration with i18n providers
3779
+ * - No registry overhead - just a bounded integer
3780
+ * - Direct ref support for v-model compatibility
3781
+ * - Navigation methods: next, prev, first, last
3782
+ * - Computed visible items with ellipsis
3783
+ * - Trinity pattern for dependency injection
3587
3784
  *
3588
- * Integrates with createSingle for locale selection and useTokens for message resolution.
3785
+ * Unlike registry-based composables, pagination tracks a single number
3786
+ * within a range, making it efficient for large page counts.
3589
3787
  */
3590
3788
  /**
3591
- * Creates a new locale instance.
3789
+ * Creates a pagination instance.
3592
3790
  *
3593
- * @param options The options for the locale instance.
3594
- * @template Z The type of the locale ticket.
3595
- * @template E The type of the locale context.
3596
- * @returns A new locale instance.
3791
+ * @param options The options for the pagination instance.
3792
+ * @returns A pagination context with navigation methods.
3597
3793
  *
3598
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
3794
+ * @example
3795
+ * ```ts
3796
+ * import { createPagination } from '@vuetify/v0'
3797
+ *
3798
+ * // Basic usage
3799
+ * const pagination = createPagination({ size: 100 })
3800
+ * pagination.next()
3801
+ * pagination.items.value // [{ type: 'page', value: 1 }, { type: 'page', value: 2 }, ...]
3802
+ *
3803
+ * // With v-model (pass a ref)
3804
+ * const page = ref(1)
3805
+ * const pagination = createPagination({ page, size: 100 })
3806
+ * // Mutating pagination.page or the passed ref syncs both
3807
+ * ```
3599
3808
  */
3600
- function createLocale(_options = {}) {
3601
- const { adapter = new Vuetify0LocaleAdapter(), messages = {}, ...options } = _options;
3602
- const tokens = createTokens(messages);
3603
- const registry = createSingle(options);
3604
- for (const id in messages) {
3605
- registry.register({ id });
3606
- if (id === options.default && !registry.selectedId.value) registry.select(id);
3809
+ function createPagination(_options = {}) {
3810
+ const { page: _page = 1, itemsPerPage: _itemsPerPage = 10, size: _size = 0, visible: _visible = 7, ellipsis = "..." } = _options;
3811
+ const page = isRef(_page) ? _page : shallowRef(_page);
3812
+ const pages = computed(() => {
3813
+ const size = toValue(_size);
3814
+ const perPage = toValue(_itemsPerPage);
3815
+ if (size <= 0 || /* @__PURE__ */ isNaN(size)) return 0;
3816
+ return Math.ceil(size / perPage);
3817
+ });
3818
+ function first() {
3819
+ page.value = 1;
3607
3820
  }
3608
- function t(key, params, fallback) {
3609
- const locale = registry.selectedId.value;
3610
- const args = toArray(params);
3611
- if (!locale) return adapter.t(fallback ?? key, ...args);
3612
- const path = `${locale}.${key}`;
3613
- const message = tokens.get(path)?.value;
3614
- const template = /* @__PURE__ */ isString(message) ? resolve(locale, message) : fallback ?? key;
3615
- return adapter.t(template, ...args);
3821
+ function last() {
3822
+ page.value = Math.max(1, pages.value);
3616
3823
  }
3617
- function n(value, ...params) {
3618
- return adapter.n(value, registry.selectedId.value, ...params);
3824
+ function next() {
3825
+ if (page.value < pages.value) page.value++;
3619
3826
  }
3620
- function resolve(locale, str) {
3621
- return str.replace(/{([a-zA-Z0-9.-_]+)}/g, (match, key) => {
3622
- const [prefix, ...rest] = key.split(".");
3623
- const target = registry.has(prefix) ? prefix : locale;
3624
- const path = `${target}.${registry.has(prefix) ? rest.join(".") : key}`;
3625
- const resolved = tokens.get(path)?.value;
3626
- if (/* @__PURE__ */ isString(resolved)) return resolve(target, resolved);
3627
- return match;
3628
- });
3827
+ function prev() {
3828
+ if (page.value > 1) page.value--;
3829
+ }
3830
+ function select(value) {
3831
+ if (value < 1) page.value = 1;
3832
+ else if (value > pages.value) page.value = Math.max(1, pages.value);
3833
+ else page.value = value;
3834
+ }
3835
+ const isFirst = computed(() => page.value <= 1);
3836
+ const isLast = computed(() => page.value >= pages.value);
3837
+ const pageStart = computed(() => (page.value - 1) * toValue(_itemsPerPage));
3838
+ const pageStop = computed(() => Math.min(pageStart.value + toValue(_itemsPerPage), toValue(_size)));
3839
+ function toPage(value) {
3840
+ return {
3841
+ type: "page",
3842
+ value
3843
+ };
3844
+ }
3845
+ function toEllipsis() {
3846
+ return ellipsis === false ? false : {
3847
+ type: "ellipsis",
3848
+ value: ellipsis
3849
+ };
3850
+ }
3851
+ function filter(array) {
3852
+ return array.filter((item) => item !== false);
3629
3853
  }
3630
3854
  return {
3631
- ...registry,
3632
- t,
3633
- n,
3855
+ page,
3856
+ ellipsis,
3857
+ items: computed(() => {
3858
+ const pageCount = pages.value;
3859
+ const visible = toValue(_visible);
3860
+ const current = page.value;
3861
+ if (pageCount <= 0 || /* @__PURE__ */ isNaN(pageCount) || pageCount > Number.MAX_SAFE_INTEGER) return [];
3862
+ if (visible <= 0) return [];
3863
+ if (visible <= 2) return [toPage(current)];
3864
+ if (pageCount <= visible) return (/* @__PURE__ */ range(pageCount, 1)).map(toPage);
3865
+ if (visible === 3) {
3866
+ const mid = current <= 1 ? 2 : current >= pageCount ? pageCount - 1 : current;
3867
+ return [
3868
+ toPage(1),
3869
+ toPage(mid),
3870
+ toPage(pageCount)
3871
+ ];
3872
+ }
3873
+ const boundary = visible - 2;
3874
+ const middle = visible - 4;
3875
+ if (middle <= 0) {
3876
+ if (current <= boundary) return filter([
3877
+ ...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
3878
+ toEllipsis(),
3879
+ toPage(pageCount)
3880
+ ]);
3881
+ if (current > pageCount - boundary) return filter([
3882
+ toPage(1),
3883
+ toEllipsis(),
3884
+ ...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
3885
+ ]);
3886
+ return current <= Math.ceil(pageCount / 2) ? filter([
3887
+ toPage(1),
3888
+ toPage(current),
3889
+ toEllipsis(),
3890
+ toPage(pageCount)
3891
+ ]) : filter([
3892
+ toPage(1),
3893
+ toEllipsis(),
3894
+ toPage(current),
3895
+ toPage(pageCount)
3896
+ ]);
3897
+ }
3898
+ const leftThreshold = boundary - 1;
3899
+ const rightThreshold = pageCount - boundary + 2;
3900
+ if (current <= leftThreshold) return filter([
3901
+ ...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
3902
+ toEllipsis(),
3903
+ toPage(pageCount)
3904
+ ]);
3905
+ else if (current >= rightThreshold) return filter([
3906
+ toPage(1),
3907
+ toEllipsis(),
3908
+ ...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
3909
+ ]);
3910
+ else {
3911
+ const start = current - Math.floor(middle / 2);
3912
+ return filter([
3913
+ toPage(1),
3914
+ toEllipsis(),
3915
+ ...(/* @__PURE__ */ range(middle, start)).map(toPage),
3916
+ toEllipsis(),
3917
+ toPage(pageCount)
3918
+ ]);
3919
+ }
3920
+ }),
3921
+ pageStart,
3922
+ pageStop,
3923
+ isFirst,
3924
+ isLast,
3925
+ first,
3926
+ last,
3927
+ next,
3928
+ prev,
3929
+ select,
3930
+ get itemsPerPage() {
3931
+ return toValue(_itemsPerPage);
3932
+ },
3634
3933
  get size() {
3635
- return registry.size;
3934
+ return toValue(_size);
3935
+ },
3936
+ get pages() {
3937
+ return pages.value;
3636
3938
  }
3637
3939
  };
3638
3940
  }
3639
- function createLocaleFallback() {
3640
- return {
3641
- size: 0,
3642
- t: (key, _params, fallback) => fallback ?? key,
3643
- n: String
3644
- };
3645
- }
3646
3941
  /**
3647
- * Creates a new locale context.
3648
- *
3649
- * @param options The options for the locale context.
3650
- * @template Z The type of the locale ticket.
3651
- * @template E The type of the locale context.
3652
- * @returns A new locale context.
3942
+ * Creates a pagination context for dependency injection.
3653
3943
  *
3654
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
3944
+ * @param options The options including namespace.
3945
+ * @returns A trinity: [usePagination, providePagination, defaultContext]
3655
3946
  *
3656
3947
  * @example
3657
3948
  * ```ts
3658
- * import { createLocaleContext } from '@vuetify/v0'
3949
+ * // With default namespace 'v0:pagination'
3950
+ * const [usePagination, providePaginationContext] = createPaginationContext({ size: 50 })
3659
3951
  *
3660
- * export const [useAppLocale, provideAppLocale, appLocale] = createLocaleContext({
3661
- * namespace: 'app:locale',
3662
- * messages: {
3663
- * en: { hello: 'Hello' },
3664
- * es: { hello: 'Hola' },
3665
- * },
3952
+ * // Or with custom namespace
3953
+ * const [usePagination, providePaginationContext] = createPaginationContext({
3954
+ * namespace: 'my-pagination',
3955
+ * size: 50,
3666
3956
  * })
3667
3957
  *
3668
- * // In a parent component:
3669
- * provideAppLocale()
3958
+ * // Parent component
3959
+ * providePaginationContext()
3670
3960
  *
3671
- * // In a child component:
3672
- * const locale = useAppLocale()
3673
- * locale.select('es')
3961
+ * // Child component
3962
+ * const pagination = usePagination()
3963
+ * pagination.next()
3674
3964
  * ```
3675
3965
  */
3676
- function createLocaleContext(_options = {}) {
3677
- const { namespace = "v0:locale", ...options } = _options;
3678
- const [useLocaleContext, _provideLocaleContext] = createContext(namespace);
3679
- const context = createLocale(options);
3680
- function provideLocaleContext(_context = context, app) {
3681
- return _provideLocaleContext(_context, app);
3966
+ function createPaginationContext(_options = {}) {
3967
+ const { namespace = "v0:pagination", ...options } = _options;
3968
+ const [usePaginationContext, _providePaginationContext] = createContext(namespace);
3969
+ const context = createPagination(options);
3970
+ function providePaginationContext(_context = context, app) {
3971
+ return _providePaginationContext(_context, app);
3682
3972
  }
3683
- return createTrinity(useLocaleContext, provideLocaleContext, context);
3973
+ return createTrinity(usePaginationContext, providePaginationContext, context);
3684
3974
  }
3685
3975
  /**
3686
- * Creates a new locale plugin.
3976
+ * Returns the current pagination instance from context.
3687
3977
  *
3688
- * @param options The options for the locale plugin.
3689
- * @template Z The type of the locale ticket.
3690
- * @template E The type of the locale context.
3691
- * @template R The type of the token ticket.
3692
- * @template O The type of the token context.
3693
- * @returns A new locale plugin.
3978
+ * @param namespace The namespace. @default 'v0:pagination'
3979
+ * @returns The pagination context.
3694
3980
  *
3695
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
3696
- */
3697
- function createLocalePlugin(_options = {}) {
3698
- const { namespace = "v0:locale", adapter = new Vuetify0LocaleAdapter(), messages = {}, ...options } = _options;
3699
- const [, provideLocaleContext, context] = createLocaleContext({
3700
- ...options,
3701
- namespace,
3702
- adapter,
3703
- messages
3704
- });
3705
- return createPlugin({
3706
- namespace,
3707
- provide: (app) => {
3708
- provideLocaleContext(context, app);
3709
- }
3710
- });
3711
- }
3712
- /**
3713
- * Returns the current locale instance.
3981
+ * @example
3982
+ * ```vue
3983
+ * <script setup lang="ts">
3984
+ * import { usePagination } from '@vuetify/v0'
3714
3985
  *
3715
- * @returns The current locale instance.
3986
+ * const pagination = usePagination()
3987
+ * <\/script>
3716
3988
  *
3717
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
3989
+ * <template>
3990
+ * <button @click="pagination.prev()" :disabled="pagination.isFirst.value">Prev</button>
3991
+ * <button @click="pagination.next()" :disabled="pagination.isLast.value">Next</button>
3992
+ * </template>
3993
+ * ```
3718
3994
  */
3719
- function useLocale(namespace = "v0:locale") {
3720
- const fallback = createLocaleFallback();
3721
- if (!getCurrentInstance()) return fallback;
3722
- try {
3723
- return useContext(namespace, fallback);
3724
- } catch {
3725
- return fallback;
3726
- }
3995
+ function usePagination(namespace = "v0:pagination") {
3996
+ return useContext(namespace);
3727
3997
  }
3728
3998
 
3729
3999
  //#endregion
@@ -5134,7 +5404,7 @@ function useEventListener(target, event, listener, options) {
5134
5404
  * @see https://0.vuetifyjs.com/composables/system/use-event-listener
5135
5405
  */
5136
5406
  function useWindowEventListener(event, listener, options) {
5137
- return useEventListener(window, event, listener, options);
5407
+ return IN_BROWSER ? useEventListener(window, event, listener, options) : () => {};
5138
5408
  }
5139
5409
  /**
5140
5410
  * Attaches an event listener to the document.
@@ -5148,7 +5418,7 @@ function useWindowEventListener(event, listener, options) {
5148
5418
  * @see https://0.vuetifyjs.com/composables/system/use-event-listener
5149
5419
  */
5150
5420
  function useDocumentEventListener(event, listener, options) {
5151
- return useEventListener(document, event, listener, options);
5421
+ return IN_BROWSER ? useEventListener(document, event, listener, options) : () => {};
5152
5422
  }
5153
5423
 
5154
5424
  //#endregion
@@ -5371,7 +5641,7 @@ function createBreakpointsPlugin(_options = {}) {
5371
5641
  },
5372
5642
  setup: (app) => {
5373
5643
  app.mixin({ mounted() {
5374
- if (this.$parent !== null) return;
5644
+ if (!/* @__PURE__ */ isNull(this.$parent)) return;
5375
5645
  const hydration = useHydration();
5376
5646
  function listener() {
5377
5647
  context.update();
@@ -5416,6 +5686,236 @@ function useBreakpoints(namespace = "v0:breakpoints") {
5416
5686
  return useContext(namespace);
5417
5687
  }
5418
5688
 
5689
+ //#endregion
5690
+ //#region src/composables/useClickOutside/index.ts
5691
+ /**
5692
+ * @module useClickOutside
5693
+ *
5694
+ * @remarks
5695
+ * Detects clicks outside of specified element(s) with automatic cleanup.
5696
+ *
5697
+ * Key features:
5698
+ * - Two-phase detection (pointerdown → pointerup) prevents drag-out false positives
5699
+ * - Touch scroll threshold ignores swipes/scrolls on mobile
5700
+ * - Capture phase listeners work with stopPropagation
5701
+ * - Pause/resume/stop functionality
5702
+ * - Optional iframe focus detection
5703
+ * - SSR-safe (no-op when not in browser)
5704
+ *
5705
+ * Common use cases: closing popovers, dropdowns, modals, and menus.
5706
+ *
5707
+ * Accessibility: This composable handles pointer interactions only. For accessible
5708
+ * components (dialogs, popovers, menus), pair with `useKeydown` for Escape key
5709
+ * dismissal per WCAG/APG requirements.
5710
+ */
5711
+ /**
5712
+ * Detects clicks outside of the specified element(s).
5713
+ *
5714
+ * Uses two-phase detection (pointerdown → pointerup) to prevent false positives
5715
+ * when users drag from inside to outside an element.
5716
+ *
5717
+ * @param target Element ref(s) to detect clicks outside of. Accepts a single ref/getter or array of refs/getters.
5718
+ * @param handler Callback invoked when a click outside is detected.
5719
+ * @param options Configuration options.
5720
+ * @returns An object with methods to control the listener.
5721
+ *
5722
+ * @see https://0.vuetifyjs.com/composables/system/use-click-outside
5723
+ *
5724
+ * @example Native element ref
5725
+ * ```ts
5726
+ * const menuRef = useTemplateRef<HTMLElement>('menu')
5727
+ *
5728
+ * useClickOutside(menuRef, () => { isOpen.value = false })
5729
+ * ```
5730
+ *
5731
+ * @example Component ref (e.g., Atom)
5732
+ * ```ts
5733
+ * const atomRef = useTemplateRef<AtomExpose>('atom')
5734
+ *
5735
+ * // Pass the exposed element TemplateRef via getter
5736
+ * useClickOutside(
5737
+ * () => atomRef.value?.element,
5738
+ * () => { isOpen.value = false }
5739
+ * )
5740
+ * ```
5741
+ *
5742
+ * @example Multiple targets
5743
+ * ```ts
5744
+ * const popoverRef = useTemplateRef<AtomExpose>('popover')
5745
+ * const anchorRef = useTemplateRef<HTMLElement>('anchor')
5746
+ *
5747
+ * useClickOutside(
5748
+ * [() => popoverRef.value?.element, anchorRef],
5749
+ * () => { isOpen.value = false }
5750
+ * )
5751
+ * ```
5752
+ *
5753
+ * @example Ignoring elements (CSS selectors or refs)
5754
+ * ```ts
5755
+ * useClickOutside(
5756
+ * () => navRef.value?.element,
5757
+ * () => { isOpen.value = false },
5758
+ * { ignore: ['[data-app-bar]'] }
5759
+ * )
5760
+ * ```
5761
+ */
5762
+ function useClickOutside(target, handler, options = {}) {
5763
+ const { capture = true, touchScrollThreshold = 30, detectIframe = false, ignore = [] } = options;
5764
+ const isPaused = shallowRef(false);
5765
+ const isActive = toRef(() => !isPaused.value);
5766
+ let initialTarget = null;
5767
+ let startPosition = {
5768
+ x: 0,
5769
+ y: 0
5770
+ };
5771
+ let cleanupPointerDown;
5772
+ let cleanupPointerUp;
5773
+ let cleanupBlur;
5774
+ /**
5775
+ * Resolve target(s) to an array of HTMLElements.
5776
+ */
5777
+ function getTargets() {
5778
+ return toArray(target).map((source) => toValue(source)).filter((el) => !/* @__PURE__ */ isNullOrUndefined(el));
5779
+ }
5780
+ /**
5781
+ * Resolve ignore targets to a tuple of [selectors, elements].
5782
+ * Called once per event to avoid repeated toValue() calls in hot path.
5783
+ */
5784
+ function resolveIgnoreTargets() {
5785
+ const ignoreTargets = toValue(ignore);
5786
+ if (ignoreTargets.length === 0) return [[], []];
5787
+ const selectors = [];
5788
+ const elements = [];
5789
+ for (const ignoreTarget of ignoreTargets) if (/* @__PURE__ */ isString(ignoreTarget)) selectors.push(ignoreTarget);
5790
+ else {
5791
+ const ignoreEl = toValue(ignoreTarget);
5792
+ if (ignoreEl) elements.push(ignoreEl);
5793
+ }
5794
+ return [selectors, elements];
5795
+ }
5796
+ /**
5797
+ * Check if an element matches resolved ignore targets.
5798
+ */
5799
+ function isIgnored(el, selectors, elements) {
5800
+ if (!el) return false;
5801
+ for (const selector of selectors) try {
5802
+ if (el.matches(selector) || !/* @__PURE__ */ isNull(el.closest(selector))) return true;
5803
+ } catch {}
5804
+ for (const ignoreEl of elements) if (ignoreEl === el || ignoreEl.contains(el)) return true;
5805
+ return false;
5806
+ }
5807
+ /**
5808
+ * Check if any element in the event path should be ignored.
5809
+ */
5810
+ function shouldIgnore(path) {
5811
+ const [selectors, elements] = resolveIgnoreTargets();
5812
+ if (selectors.length === 0 && elements.length === 0) return false;
5813
+ return path.some((node) => node instanceof Element && isIgnored(node, selectors, elements));
5814
+ }
5815
+ /**
5816
+ * Check if the event target is outside all target elements.
5817
+ */
5818
+ function isOutside(eventTarget) {
5819
+ if (!eventTarget) return false;
5820
+ if (!(eventTarget instanceof Node)) return false;
5821
+ const targets = getTargets();
5822
+ if (targets.length === 0) return false;
5823
+ return targets.every((el) => {
5824
+ return el !== eventTarget && !el.contains(eventTarget);
5825
+ });
5826
+ }
5827
+ /**
5828
+ * Validate that the target is still in the DOM.
5829
+ */
5830
+ function isValidTarget(eventTarget) {
5831
+ if (!(eventTarget instanceof Element)) return false;
5832
+ if (!eventTarget.isConnected) return false;
5833
+ return true;
5834
+ }
5835
+ /**
5836
+ * Handle pointerdown - store initial target and position.
5837
+ */
5838
+ function onPointerDown(event) {
5839
+ if (isPaused.value) return;
5840
+ if (event.defaultPrevented) return;
5841
+ initialTarget = event.composedPath()[0] ?? event.target;
5842
+ startPosition = {
5843
+ x: event.clientX,
5844
+ y: event.clientY
5845
+ };
5846
+ }
5847
+ /**
5848
+ * Handle pointerup - check if it's an outside click.
5849
+ */
5850
+ function onPointerUp(event) {
5851
+ if (isPaused.value) return;
5852
+ if (event.defaultPrevented) return;
5853
+ if (!initialTarget) return;
5854
+ const pointerdownTarget = initialTarget;
5855
+ initialTarget = null;
5856
+ if (!isValidTarget(pointerdownTarget)) return;
5857
+ const path = event.composedPath();
5858
+ const pointerupTarget = path[0] ?? event.target;
5859
+ if (event.pointerType === "touch") {
5860
+ const dx = Math.abs(event.clientX - startPosition.x);
5861
+ const dy = Math.abs(event.clientY - startPosition.y);
5862
+ if (dx >= touchScrollThreshold || dy >= touchScrollThreshold) return;
5863
+ }
5864
+ if (isOutside(pointerdownTarget) && isOutside(pointerupTarget) && !shouldIgnore(path)) handler(event);
5865
+ }
5866
+ /**
5867
+ * Handle window blur - detect focus moving to iframe.
5868
+ */
5869
+ function onBlur(event) {
5870
+ if (isPaused.value) return;
5871
+ if (event.defaultPrevented) return;
5872
+ if (document.activeElement instanceof HTMLIFrameElement) {
5873
+ const iframeIsOutside = getTargets().every((el) => !el.contains(document.activeElement));
5874
+ const [selectors, elements] = resolveIgnoreTargets();
5875
+ if (iframeIsOutside && !isIgnored(document.activeElement, selectors, elements)) handler(event);
5876
+ }
5877
+ }
5878
+ function setup() {
5879
+ cleanupPointerDown = useDocumentEventListener("pointerdown", onPointerDown, capture);
5880
+ cleanupPointerUp = useDocumentEventListener("pointerup", onPointerUp, capture);
5881
+ if (!detectIframe) return;
5882
+ cleanupBlur = useWindowEventListener("blur", onBlur, capture);
5883
+ }
5884
+ function cleanup() {
5885
+ cleanupPointerDown?.();
5886
+ cleanupPointerUp?.();
5887
+ cleanupBlur?.();
5888
+ cleanupPointerDown = void 0;
5889
+ cleanupPointerUp = void 0;
5890
+ cleanupBlur = void 0;
5891
+ }
5892
+ function pause() {
5893
+ if (isPaused.value) return;
5894
+ isPaused.value = true;
5895
+ initialTarget = null;
5896
+ cleanup();
5897
+ }
5898
+ function resume() {
5899
+ if (!isPaused.value) return;
5900
+ isPaused.value = false;
5901
+ setup();
5902
+ }
5903
+ function stop() {
5904
+ isPaused.value = true;
5905
+ initialTarget = null;
5906
+ cleanup();
5907
+ }
5908
+ setup();
5909
+ onScopeDispose(stop, true);
5910
+ return {
5911
+ isActive: shallowReadonly(isActive),
5912
+ isPaused: shallowReadonly(isPaused),
5913
+ pause,
5914
+ resume,
5915
+ stop
5916
+ };
5917
+ }
5918
+
5419
5919
  //#endregion
5420
5920
  //#region src/composables/useFeatures/index.ts
5421
5921
  /**
@@ -5613,7 +6113,7 @@ function useFeatures(namespace = "v0:features") {
5613
6113
  * Filters arrays based on query strings with configurable matching strategies.
5614
6114
  */
5615
6115
  function defaultFilter(query, item, keys, mode = "some") {
5616
- const queries = Array.isArray(query) ? query.map((q) => String(q).toLowerCase()) : [String(query).toLowerCase()];
6116
+ const queries = toArray(query).map((q) => String(q).toLowerCase());
5617
6117
  function match(value, q) {
5618
6118
  return String(value).toLowerCase().includes(q);
5619
6119
  }
@@ -5656,7 +6156,7 @@ function createFilter(options = {}) {
5656
6156
  return { items: computed(() => {
5657
6157
  const q = toValue(queryRef);
5658
6158
  query.value = q;
5659
- const queries = (Array.isArray(q) ? q : [q]).filter((q$1) => String(q$1).trim());
6159
+ const queries = toArray(q).filter((q$1) => String(q$1).trim());
5660
6160
  if (queries.length === 0) return itemsRef.value;
5661
6161
  const queryParam = queries.length === 1 ? queries[0] : queries;
5662
6162
  return itemsRef.value.filter((item) => filterFunction(queryParam, item));
@@ -5819,7 +6319,7 @@ function createForm(options) {
5819
6319
  return parse(validateOn).includes(event);
5820
6320
  }
5821
6321
  const isValidating = computed(() => {
5822
- for (const ticket of registry.collection.values()) if (ticket.isValidating.value) return true;
6322
+ for (const ticket of registry.values()) if (ticket.isValidating.value) return true;
5823
6323
  return false;
5824
6324
  });
5825
6325
  const isValid = computed(() => {
@@ -5827,7 +6327,7 @@ function createForm(options) {
5827
6327
  for (const ticket of registry.values()) {
5828
6328
  hasFields = true;
5829
6329
  if (ticket.isValid.value === false) return false;
5830
- if (ticket.isValid.value === null) return null;
6330
+ if (/* @__PURE__ */ isNull(ticket.isValid.value)) return null;
5831
6331
  }
5832
6332
  return hasFields ? true : null;
5833
6333
  });
@@ -5843,7 +6343,7 @@ function createForm(options) {
5843
6343
  return validating.map((id$1) => registry.get(id$1)).filter(Boolean).every((ticket) => ticket.isValid.value === true);
5844
6344
  }
5845
6345
  function register(registration) {
5846
- const model = shallowRef(registration.value == null ? "" : toValue(registration.value));
6346
+ const model = shallowRef(/* @__PURE__ */ isNullOrUndefined(registration.value) ? "" : toValue(registration.value));
5847
6347
  const rules = registration.rules || [];
5848
6348
  const errors = shallowRef([]);
5849
6349
  const isValidating$1 = shallowRef(false);
@@ -6042,12 +6542,14 @@ function useForm(namespace = "v0:form") {
6042
6542
  */
6043
6543
  function useIntersectionObserver(target, callback, options = {}) {
6044
6544
  const { isHydrated } = useHydration();
6545
+ const targetRef = isRef(target) ? target : shallowRef(target);
6045
6546
  const observer = shallowRef();
6046
6547
  const isPaused = shallowRef(false);
6047
6548
  const isIntersecting = shallowRef(false);
6048
6549
  const isActive = toRef(() => !!observer.value);
6049
6550
  function setup() {
6050
- if (!isHydrated.value || !SUPPORTS_INTERSECTION_OBSERVER || !target.value || isPaused.value) return;
6551
+ if (/* @__PURE__ */ isNull(observer.value)) return;
6552
+ if (!isHydrated.value || !SUPPORTS_INTERSECTION_OBSERVER || !targetRef.value || isPaused.value) return;
6051
6553
  observer.value = new IntersectionObserver((entries) => {
6052
6554
  const transformedEntries = entries.map((entry) => ({
6053
6555
  boundingClientRect: entry.boundingClientRect,
@@ -6061,26 +6563,29 @@ function useIntersectionObserver(target, callback, options = {}) {
6061
6563
  const latestEntry = transformedEntries.at(-1);
6062
6564
  if (latestEntry) isIntersecting.value = latestEntry.isIntersecting;
6063
6565
  callback(transformedEntries);
6566
+ if (options.once && latestEntry?.isIntersecting) stop();
6064
6567
  }, {
6065
6568
  root: options.root || null,
6066
6569
  rootMargin: options.rootMargin || "0px",
6067
6570
  threshold: options.threshold || 0
6068
6571
  });
6069
- observer.value.observe(target.value);
6572
+ observer.value.observe(targetRef.value);
6070
6573
  if (options.immediate) callback([{
6071
- boundingClientRect: target.value.getBoundingClientRect(),
6574
+ boundingClientRect: targetRef.value.getBoundingClientRect(),
6072
6575
  intersectionRatio: 0,
6073
6576
  intersectionRect: new DOMRect(0, 0, 0, 0),
6074
6577
  isIntersecting: false,
6075
6578
  rootBounds: null,
6076
- target: target.value,
6579
+ target: targetRef.value,
6077
6580
  time: performance.now()
6078
6581
  }]);
6079
6582
  }
6080
- watch([isHydrated, target], () => {
6583
+ watchEffect(() => {
6584
+ const hydrated = isHydrated.value;
6585
+ const target$1 = targetRef.value;
6081
6586
  cleanup();
6082
- setup();
6083
- }, { immediate: true });
6587
+ if (hydrated && target$1) setup();
6588
+ });
6084
6589
  function cleanup() {
6085
6590
  if (observer.value) {
6086
6591
  observer.value.disconnect();
@@ -6098,6 +6603,7 @@ function useIntersectionObserver(target, callback, options = {}) {
6098
6603
  }
6099
6604
  function stop() {
6100
6605
  cleanup();
6606
+ observer.value = null;
6101
6607
  }
6102
6608
  onScopeDispose(stop, true);
6103
6609
  return {
@@ -6194,6 +6700,10 @@ function useElementIntersection(target, options = {}) {
6194
6700
  * ```ts
6195
6701
  * import { useKeydown } from '@vuetify/v0'
6196
6702
  *
6703
+ * // Single handler
6704
+ * useKeydown({ key: 'Escape', handler: () => console.log('Escape pressed') })
6705
+ *
6706
+ * // Multiple handlers
6197
6707
  * const { isActive, start, stop } = useKeydown([
6198
6708
  * { key: 'Enter', handler: () => console.log('Enter pressed') },
6199
6709
  * { key: 'Escape', handler: () => console.log('Escape pressed'), preventDefault: true },
@@ -6209,8 +6719,7 @@ function useKeydown(handlers) {
6209
6719
  let cleanup = null;
6210
6720
  const isActive = toRef(() => !!cleanup);
6211
6721
  function onKeydown(event) {
6212
- const handlerList = toValue(handlers);
6213
- const handler = (Array.isArray(handlerList) ? handlerList : [handlerList]).find((h) => h.key === event.key);
6722
+ const handler = toArray(toValue(handlers)).find((h) => h.key === event.key);
6214
6723
  if (handler) {
6215
6724
  if (handler.preventDefault) event.preventDefault();
6216
6725
  if (handler.stopPropagation) event.stopPropagation();
@@ -6300,7 +6809,7 @@ function useMutationObserver(target, callback, options = {}) {
6300
6809
  const { isHydrated } = useHydration();
6301
6810
  const observer = shallowRef();
6302
6811
  const isPaused = shallowRef(false);
6303
- const isActive = computed(() => !!observer.value);
6812
+ const isActive = toRef(() => !!observer.value);
6304
6813
  const observerOptions = {
6305
6814
  childList: options.childList ?? true,
6306
6815
  attributes: options.attributes ?? false,
@@ -6311,6 +6820,7 @@ function useMutationObserver(target, callback, options = {}) {
6311
6820
  attributeFilter: options.attributeFilter
6312
6821
  };
6313
6822
  function setup() {
6823
+ if (/* @__PURE__ */ isNull(observer.value)) return;
6314
6824
  if (!isHydrated.value || !SUPPORTS_MUTATION_OBSERVER || !target.value || isPaused.value) return;
6315
6825
  observer.value = new MutationObserver((mutations) => {
6316
6826
  callback(mutations.map((mutation) => ({
@@ -6324,6 +6834,7 @@ function useMutationObserver(target, callback, options = {}) {
6324
6834
  attributeNamespace: mutation.attributeNamespace,
6325
6835
  oldValue: mutation.oldValue
6326
6836
  })));
6837
+ if (options.once) stop();
6327
6838
  });
6328
6839
  observer.value.observe(target.value, observerOptions);
6329
6840
  if (options.immediate) {
@@ -6344,12 +6855,15 @@ function useMutationObserver(target, callback, options = {}) {
6344
6855
  attributeNamespace: null,
6345
6856
  oldValue: null
6346
6857
  }]);
6858
+ if (options.once) stop();
6347
6859
  }
6348
6860
  }
6349
- watch([isHydrated, target], () => {
6861
+ watchEffect(() => {
6862
+ const hydrated = isHydrated.value;
6863
+ const el = target.value;
6350
6864
  cleanup();
6351
- setup();
6352
- }, { immediate: true });
6865
+ if (hydrated && el) setup();
6866
+ });
6353
6867
  function cleanup() {
6354
6868
  if (observer.value) {
6355
6869
  observer.value.disconnect();
@@ -6366,6 +6880,7 @@ function useMutationObserver(target, callback, options = {}) {
6366
6880
  }
6367
6881
  function stop() {
6368
6882
  cleanup();
6883
+ observer.value = null;
6369
6884
  }
6370
6885
  onScopeDispose(stop, true);
6371
6886
  return {
@@ -7014,10 +7529,12 @@ var Vuetify0ThemeAdapter = class extends ThemeAdapter {
7014
7529
  }
7015
7530
  upsert(styles) {
7016
7531
  if (!IN_BROWSER) return;
7017
- let styleEl = document.querySelector(`#${this.stylesheetId}`);
7532
+ const selector = this.stylesheetId.startsWith("#") ? this.stylesheetId : `#${this.stylesheetId}`;
7533
+ const id = this.stylesheetId.startsWith("#") ? this.stylesheetId.slice(1) : this.stylesheetId;
7534
+ let styleEl = document.querySelector(selector);
7018
7535
  if (!styleEl) {
7019
7536
  styleEl = document.createElement("style");
7020
- styleEl.id = this.stylesheetId.startsWith("#") ? this.stylesheetId.slice(1) : this.stylesheetId;
7537
+ styleEl.id = id;
7021
7538
  if (this.cspNonce) styleEl.setAttribute("nonce", this.cspNonce);
7022
7539
  document.head.append(styleEl);
7023
7540
  }
@@ -7772,7 +8289,7 @@ function useVirtual(items, _options = {}) {
7772
8289
  cancelAnimationFrame(raf);
7773
8290
  cancelAnimationFrame(rebuildRaf);
7774
8291
  cancelAnimationFrame(edgeRaf);
7775
- });
8292
+ }, true);
7776
8293
  return {
7777
8294
  element,
7778
8295
  items: computedItems,
@@ -7788,4 +8305,4 @@ function useVirtual(items, _options = {}) {
7788
8305
  }
7789
8306
 
7790
8307
  //#endregion
7791
- export { Atom_default as Atom, Avatar, AvatarFallback_default as AvatarFallback, AvatarImage_default as AvatarImage, AvatarRoot_default as AvatarRoot, COMMON_ELEMENTS, ConsolaLoggerAdapter, ExpansionPanel, ExpansionPanelActivator_default as ExpansionPanelActivator, ExpansionPanelContent_default as ExpansionPanelContent, ExpansionPanelHeader_default as ExpansionPanelHeader, ExpansionPanelItem_default as ExpansionPanelItem, ExpansionPanelRoot_default as ExpansionPanelRoot, Group, GroupItem_default as GroupItem, GroupRoot_default as GroupRoot, IN_BROWSER, MemoryAdapter, Pagination, PaginationEllipsis_default as PaginationEllipsis, PaginationFirst_default as PaginationFirst, PaginationItem_default as PaginationItem, PaginationLast_default as PaginationLast, PaginationNext_default as PaginationNext, PaginationPrev_default as PaginationPrev, PaginationRoot_default as PaginationRoot, PaginationStatus_default as PaginationStatus, PermissionAdapter, PinoLoggerAdapter, Popover, PopoverAnchor_default as PopoverAnchor, PopoverContent_default as PopoverContent, PopoverRoot_default as PopoverRoot, SELF_CLOSING_TAGS, SUPPORTS_INTERSECTION_OBSERVER, SUPPORTS_MATCH_MEDIA, SUPPORTS_MUTATION_OBSERVER, SUPPORTS_OBSERVER, SUPPORTS_TOUCH, Selection, SelectionItem_default as SelectionItem, SelectionRoot_default as SelectionRoot, Single, SingleItem_default as SingleItem, SingleRoot_default as SingleRoot, Step, StepItem_default as StepItem, StepRoot_default as StepRoot, Vuetify0LocaleAdapter, Vuetify0LoggerAdapter, Vuetify0ThemeAdapter, __LOGGER_ENABLED__, clamp, createBreakpoints, createBreakpointsContext, createBreakpointsPlugin, createContext, createFallbackHydration, createFeatures, createFeaturesContext, createFeaturesPlugin, createFilter, createFilterContext, createForm, createFormContext, createGroup, createGroupContext, createHydration, createHydrationContext, createHydrationPlugin, createLocale, createLocaleContext, createLocaleFallback, createLocalePlugin, createLogger, createLoggerContext, createLoggerPlugin, createOverflow, createOverflowContext, createPagination, createPaginationContext, createPermissions, createPermissionsContext, createPermissionsPlugin, createPlugin, createQueue, createQueueContext, createRegistryContext, createSelection, createSelectionContext, createSingle, createSingleContext, createStep, createStepContext, createStorage, createStorageContext, createStoragePlugin, createTheme, createThemeContext, createThemePlugin, createTimeline, createTimelineContext, createTokens, createTokensContext, createTrinity, debounce, genId, isArray, isBoolean, isFunction, isNaN, isNull, isNullOrUndefined, isNumber, isObject, isPrimitive, isSelfClosingTag, isString, isSymbol, isUndefined, mergeDeep, provideAvatarContext, provideContext, provideExpansionPanelItem, provideExpansionPanelSelection, provideGroupRoot, providePaginationControls, providePaginationItems, providePaginationRoot, providePopoverContext, provideSelectionRoot, provideSingleRoot, provideStepRoot, range, toArray, toReactive, useAvatarRoot, useBreakpoints, useContext, useDocumentEventListener, useElementIntersection, useElementSize, useEventListener, useExpansionPanelItem, useExpansionPanelRoot, useFeatures, useFilter, useFilterContext, useForm, useGroup, useGroupRoot, useHydration, useIntersectionObserver, useKeydown, useLocale, useLogger, useMutationObserver, useOverflow, usePagination, usePaginationControls, usePaginationItems, usePaginationRoot, usePermissions, usePopoverContext, useProxyModel, useProxyRegistry, useQueue, useRegistry, useResizeObserver, useSelection, useSelectionRoot, useSingle, useSingleRoot, useStep, useStepRoot, useStorage, useTheme, useTimeline, useToggleScope, useTokens, useVirtual, useWindowEventListener, version };
8308
+ export { Atom_default as Atom, Avatar, AvatarFallback_default as AvatarFallback, AvatarImage_default as AvatarImage, AvatarRoot_default as AvatarRoot, COMMON_ELEMENTS, ConsolaLoggerAdapter, ExpansionPanel, ExpansionPanelActivator_default as ExpansionPanelActivator, ExpansionPanelContent_default as ExpansionPanelContent, ExpansionPanelHeader_default as ExpansionPanelHeader, ExpansionPanelItem_default as ExpansionPanelItem, ExpansionPanelRoot_default as ExpansionPanelRoot, Group, GroupItem_default as GroupItem, GroupRoot_default as GroupRoot, IN_BROWSER, MemoryAdapter, Pagination, PaginationEllipsis_default as PaginationEllipsis, PaginationFirst_default as PaginationFirst, PaginationItem_default as PaginationItem, PaginationLast_default as PaginationLast, PaginationNext_default as PaginationNext, PaginationPrev_default as PaginationPrev, PaginationRoot_default as PaginationRoot, PaginationStatus_default as PaginationStatus, PermissionAdapter, PinoLoggerAdapter, Popover, PopoverAnchor_default as PopoverAnchor, PopoverContent_default as PopoverContent, PopoverRoot_default as PopoverRoot, SELF_CLOSING_TAGS, SUPPORTS_INTERSECTION_OBSERVER, SUPPORTS_MATCH_MEDIA, SUPPORTS_MUTATION_OBSERVER, SUPPORTS_OBSERVER, SUPPORTS_TOUCH, Selection, SelectionItem_default as SelectionItem, SelectionRoot_default as SelectionRoot, Single, SingleItem_default as SingleItem, SingleRoot_default as SingleRoot, Step, StepItem_default as StepItem, StepRoot_default as StepRoot, Vuetify0LocaleAdapter, Vuetify0LoggerAdapter, Vuetify0ThemeAdapter, __LOGGER_ENABLED__, clamp, createBreakpoints, createBreakpointsContext, createBreakpointsPlugin, createContext, createFallbackHydration, createFeatures, createFeaturesContext, createFeaturesPlugin, createFilter, createFilterContext, createForm, createFormContext, createGroup, createGroupContext, createHydration, createHydrationContext, createHydrationPlugin, createLocale, createLocaleContext, createLocaleFallback, createLocalePlugin, createLogger, createLoggerContext, createLoggerPlugin, createOverflow, createOverflowContext, createPagination, createPaginationContext, createPermissions, createPermissionsContext, createPermissionsPlugin, createPlugin, createQueue, createQueueContext, createRegistryContext, createSelection, createSelectionContext, createSingle, createSingleContext, createStep, createStepContext, createStorage, createStorageContext, createStoragePlugin, createTheme, createThemeContext, createThemePlugin, createTimeline, createTimelineContext, createTokens, createTokensContext, createTrinity, debounce, genId, isArray, isBoolean, isFunction, isNaN, isNull, isNullOrUndefined, isNumber, isObject, isPrimitive, isSelfClosingTag, isString, isSymbol, isUndefined, mergeDeep, provideAvatarContext, provideContext, provideExpansionPanelItem, provideExpansionPanelSelection, provideGroupRoot, providePaginationControls, providePaginationItems, providePaginationRoot, providePopoverContext, provideSelectionRoot, provideSingleRoot, provideStepRoot, range, toArray, toReactive, useAvatarRoot, useBreakpoints, useClickOutside, useContext, useDocumentEventListener, useElementIntersection, useElementSize, useEventListener, useExpansionPanelItem, useExpansionPanelRoot, useFeatures, useFilter, useFilterContext, useForm, useGroup, useGroupRoot, useHydration, useIntersectionObserver, useKeydown, useLocale, useLogger, useMutationObserver, useOverflow, usePagination, usePaginationControls, usePaginationItems, usePaginationRoot, usePermissions, usePopoverContext, useProxyModel, useProxyRegistry, useQueue, useRegistry, useResizeObserver, useSelection, useSelectionRoot, useSingle, useSingleRoot, useStep, useStepRoot, useStorage, useTheme, useTimeline, useToggleScope, useTokens, useVirtual, useWindowEventListener, version };