@vuetify/v0 0.0.18 → 0.0.20

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,54 +78,263 @@ 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
+ */
129
338
  /* @__NO_SIDE_EFFECTS__ */
130
339
  function mergeDeep(target, ...sources) {
131
340
  if (sources.length === 0) return target;
@@ -141,6 +350,21 @@ function mergeDeep(target, ...sources) {
141
350
  }
142
351
  return /* @__PURE__ */ mergeDeep(target, ...sources);
143
352
  }
353
+ /**
354
+ * Generates a random 7-character alphanumeric ID
355
+ *
356
+ * @returns A random string of 7 characters (a-z, 0-9)
357
+ *
358
+ * @remarks
359
+ * Uses `Math.random()` converted to base-36. Not cryptographically secure.
360
+ * Suitable for unique keys in UI components, not for security purposes.
361
+ *
362
+ * @example
363
+ * ```ts
364
+ * genId() // 'k7x9m2p'
365
+ * genId() // 'a3b8c1d'
366
+ * ```
367
+ */
144
368
  /* @__NO_SIDE_EFFECTS__ */
145
369
  function genId() {
146
370
  return Math.random().toString(36).slice(2, 9);
@@ -252,22 +476,6 @@ var Atom_default = _sfc_main$27;
252
476
  //#endregion
253
477
  //#region src/composables/createContext/index.ts
254
478
  /**
255
- * @module createContext
256
- *
257
- * @see https://0.vuetifyjs.com/composables/foundation/create-context
258
- *
259
- * @remarks
260
- * Factory for creating type-safe Vue dependency injection contexts.
261
- *
262
- * Provides a wrapper around Vue's provide/inject that throws errors when context is not found,
263
- * eliminating silent failures and improving developer experience. Supports both app-level and
264
- * component-level provision.
265
- *
266
- * Supports two modes:
267
- * - **Static key**: `createContext('my-key')` - key is fixed at creation time
268
- * - **Dynamic key**: `createContext()` or `createContext({ suffix: 'item' })` - key provided at runtime
269
- */
270
- /**
271
479
  * Injects a context provided by an ancestor component.
272
480
  *
273
481
  * @param key The key of the context to inject.
@@ -513,7 +721,7 @@ const SUPPORTS_MATCH_MEDIA = IN_BROWSER && "matchMedia" in window && typeof wind
513
721
  const SUPPORTS_OBSERVER = IN_BROWSER && "ResizeObserver" in window;
514
722
  const SUPPORTS_INTERSECTION_OBSERVER = IN_BROWSER && "IntersectionObserver" in window;
515
723
  const SUPPORTS_MUTATION_OBSERVER = IN_BROWSER && "MutationObserver" in window;
516
- const version = "0.0.18";
724
+ const version = "0.0.20";
517
725
  const __LOGGER_ENABLED__ = false;
518
726
 
519
727
  //#endregion
@@ -815,21 +1023,6 @@ function useLogger(namespace = "v0:logger") {
815
1023
  //#endregion
816
1024
  //#region src/composables/useRegistry/index.ts
817
1025
  /**
818
- * @module useRegistry
819
- *
820
- * @remarks
821
- * A foundational composable for managing collections of items (tickets) with:
822
- * - Unique ID-based access
823
- * - Index-based ordering
824
- * - Value-based reverse lookup
825
- * - Automatic reindexing
826
- * - Optional event emission
827
- * - Performance-optimized caching
828
- *
829
- * The registry serves as the base for many other composables in the system,
830
- * including useSelection, useForm, useTimeline, and more.
831
- */
832
- /**
833
1026
  * Creates a new registry instance.
834
1027
  *
835
1028
  * @param options The options for the registry instance.
@@ -873,17 +1066,21 @@ function useRegistry(options) {
873
1066
  }
874
1067
  function on(event, cb) {
875
1068
  if (!events) {
876
- logger.warn(`Attempted to register event listener for "${event}" but events are disabled.`);
1069
+ logger.warn(`Events are disabled. Initialize with \`useRegistry({ events: true })\` to enable.`);
877
1070
  return;
878
1071
  }
879
1072
  if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
880
1073
  listeners.get(event).add(cb);
881
1074
  }
882
1075
  function off(event, cb) {
1076
+ if (!events) {
1077
+ logger.warn(`Events are disabled. Initialize with \`useRegistry({ events: true })\` to enable.`);
1078
+ return;
1079
+ }
883
1080
  listeners.get(event)?.delete(cb);
884
1081
  }
885
1082
  function dispose() {
886
- if (listeners.size > 0) listeners.clear();
1083
+ listeners.clear();
887
1084
  clear();
888
1085
  }
889
1086
  function get(id) {
@@ -972,9 +1169,9 @@ function useRegistry(options) {
972
1169
  return entries$1;
973
1170
  }
974
1171
  function clear() {
975
- if (collection.size > 0) collection.clear();
976
- if (catalog.size > 0) catalog.clear();
977
- if (directory.size > 0) directory.clear();
1172
+ collection.clear();
1173
+ catalog.clear();
1174
+ directory.clear();
978
1175
  invalidate();
979
1176
  indexDependentCount = 0;
980
1177
  needsReindex = false;
@@ -983,7 +1180,7 @@ function useRegistry(options) {
983
1180
  }
984
1181
  function invalidate() {
985
1182
  if (batching) return;
986
- if (cache.size > 0) cache.clear();
1183
+ cache.clear();
987
1184
  }
988
1185
  function queueEmit(event, data) {
989
1186
  if (batching) pendingEmits.push({
@@ -998,7 +1195,7 @@ function useRegistry(options) {
998
1195
  pendingEmits = [];
999
1196
  try {
1000
1197
  const result = fn();
1001
- if (cache.size > 0) cache.clear();
1198
+ cache.clear();
1002
1199
  for (const { event, data } of pendingEmits) emit(event, data);
1003
1200
  return result;
1004
1201
  } finally {
@@ -1009,8 +1206,8 @@ function useRegistry(options) {
1009
1206
  function reindex() {
1010
1207
  const startIndex = minDirtyIndex === Infinity ? 0 : minDirtyIndex;
1011
1208
  if (startIndex === 0) {
1012
- if (catalog.size > 0) catalog.clear();
1013
- if (directory.size > 0) directory.clear();
1209
+ catalog.clear();
1210
+ directory.clear();
1014
1211
  }
1015
1212
  invalidate();
1016
1213
  let index = 0;
@@ -1037,7 +1234,7 @@ function useRegistry(options) {
1037
1234
  const size = collection.size;
1038
1235
  const id = registration.id ?? /* @__PURE__ */ genId();
1039
1236
  if (has(id)) {
1040
- logger.warn(`Ticket with id "${id}" already exists in the registry. Skipping registration.`);
1237
+ logger.warn(`Ticket "${id}" already exists. Use \`upsert()\` to update or check \`has()\` before registering.`);
1041
1238
  return get(id);
1042
1239
  }
1043
1240
  const valueIsUndefined = /* @__PURE__ */ isUndefined(registration.value);
@@ -1087,12 +1284,16 @@ function useRegistry(options) {
1087
1284
  }
1088
1285
  if (removed.length === 0) return;
1089
1286
  invalidate();
1090
- if (events) for (const ticket of removed) emit("unregister:ticket", ticket);
1287
+ for (const ticket of removed) queueEmit("unregister:ticket", ticket);
1091
1288
  needsReindex = true;
1092
1289
  }
1093
1290
  function seek(direction = "first", from, predicate) {
1094
1291
  if (collection.size === 0) return void 0;
1095
1292
  if (needsReindex) reindex();
1293
+ if (!predicate && /* @__PURE__ */ isUndefined(from)) {
1294
+ const tickets$1 = values();
1295
+ return direction === "first" ? tickets$1[0] : tickets$1.at(-1);
1296
+ }
1096
1297
  const tickets = values();
1097
1298
  const index = /* @__PURE__ */ isUndefined(from) ? void 0 : Math.max(0, Math.min(from, tickets.length - 1));
1098
1299
  if (direction === "last") {
@@ -1179,21 +1380,6 @@ function createRegistryContext(_options = {}) {
1179
1380
  //#endregion
1180
1381
  //#region src/composables/useSelection/index.ts
1181
1382
  /**
1182
- * @module useSelection
1183
- *
1184
- * @remarks
1185
- * Base composable for managing selected items in a collection with Set-based tracking.
1186
- *
1187
- * Key features:
1188
- * - Set-based selectedIds for O(1) selection checks
1189
- * - Mandatory selection mode (prevents deselecting last item)
1190
- * - Auto-enrollment option (selects non-disabled items on register)
1191
- * - Disabled item filtering
1192
- * - Computed selectedItems and selectedValues Sets
1193
- *
1194
- * Extends useRegistry and serves as the base for useSingle, useGroup, useStep, and useFeatures.
1195
- */
1196
- /**
1197
1383
  * Creates a new selection instance for managing multiple selected items.
1198
1384
  *
1199
1385
  * Extends `useRegistry` with selection tracking via a reactive `Set` of selected IDs.
@@ -1569,20 +1755,6 @@ function toArray(value) {
1569
1755
  //#endregion
1570
1756
  //#region src/composables/useProxyModel/index.ts
1571
1757
  /**
1572
- * @module useProxyModel
1573
- *
1574
- * @remarks
1575
- * Proxy composable for bidirectional sync between selection registry and v-model.
1576
- *
1577
- * Key features:
1578
- * - Bidirectional synchronization
1579
- * - Array and single-value modes
1580
- * - Automatic cleanup on scope disposal
1581
- * - Perfect for form controls with selection backing
1582
- *
1583
- * Bridges the gap between selection composables and Vue's v-model.
1584
- */
1585
- /**
1586
1758
  * Syncs a ref with a selection registry bidirectionally.
1587
1759
  *
1588
1760
  * @param registry The selection registry to bind to.
@@ -1655,7 +1827,8 @@ function useProxyModel(registry, model, options) {
1655
1827
  flush: "sync",
1656
1828
  deep: multiple
1657
1829
  });
1658
- function onRegister(ticket) {
1830
+ function onRegister(data) {
1831
+ const ticket = data;
1659
1832
  if (!pending.has(ticket.value) || ticket.disabled) return;
1660
1833
  registryWatch.pause();
1661
1834
  modelWatch.pause();
@@ -1917,21 +2090,6 @@ const ExpansionPanel = {
1917
2090
  //#endregion
1918
2091
  //#region src/composables/useProxyRegistry/index.ts
1919
2092
  /**
1920
- * @module useProxyRegistry
1921
- *
1922
- * @remarks
1923
- * Proxy composable for reactive registry keys, values, entries, and size.
1924
- *
1925
- * Key features:
1926
- * - Reactive proxy for registry data
1927
- * - Deep or shallow reactivity options
1928
- * - Event-based updates
1929
- * - Automatic cleanup on scope disposal
1930
- * - Transforms Map-based registry into reactive refs
1931
- *
1932
- * Perfect for exposing registry data as reactive computed properties.
1933
- */
1934
- /**
1935
2093
  * Creates a proxy registry that provides reactive objects for registry data.
1936
2094
  *
1937
2095
  * @param registry The registry instance to proxy.
@@ -1981,26 +2139,6 @@ function useProxyRegistry(registry, options) {
1981
2139
  //#endregion
1982
2140
  //#region src/composables/useGroup/index.ts
1983
2141
  /**
1984
- * @module useGroup
1985
- *
1986
- * @remarks
1987
- * Multi-selection composable that extends useSelection with batch operations and tri-state support.
1988
- *
1989
- * Key features:
1990
- * - Batch operations (select/unselect/toggle accept ID | ID[])
1991
- * - Tri-state support via mixed/indeterminate state (mix/unmix)
1992
- * - selectedIndexes computed Set for position-based tracking
1993
- * - Perfect for checkbox trees, multi-select dropdowns, filter panels
1994
- *
1995
- * Tri-state behavior:
1996
- * - Items can be selected, mixed (indeterminate), or unselected
1997
- * - select() clears mixed state, mix() clears selected state (mutually exclusive)
1998
- * - toggle() on a mixed item selects it (resolves positively)
1999
- *
2000
- * Inheritance chain: useRegistry → useSelection → useGroup
2001
- * Extended by: useFeatures
2002
- */
2003
- /**
2004
2142
  * Creates a new group instance with batch selection and tri-state support.
2005
2143
  *
2006
2144
  * Extends `createSelection` to support selecting, unselecting, and toggling multiple items
@@ -2385,1345 +2523,1239 @@ const Group = {
2385
2523
  };
2386
2524
 
2387
2525
  //#endregion
2388
- //#region src/composables/useHydration/index.ts
2526
+ //#region src/composables/useLocale/adapters/v0.ts
2389
2527
  /**
2390
- * @module useHydration
2391
- *
2392
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2393
- *
2394
- * @remarks
2395
- * SSR hydration state management composable.
2396
- *
2397
- * 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
2528
+ * Vuetify0.x locale adapter implementation
2403
2529
  *
2404
- * Essential for composables that need to behave differently during SSR vs client-side.
2530
+ * This adapter provides translation and number formatting
2531
+ * capabilities using the Intl API and supports both
2532
+ * numbered ({0}, {1}) and named ({name}) variables in translation strings.
2405
2533
  */
2534
+ var Vuetify0LocaleAdapter = class {
2535
+ t(message, ...params) {
2536
+ let resolvedMessage = message;
2537
+ if (params.length > 0 && /* @__PURE__ */ isObject(params[0])) {
2538
+ const variables = params[0];
2539
+ resolvedMessage = resolvedMessage.replace(/{([a-zA-Z][a-zA-Z0-9_]*)}/g, (match, name) => {
2540
+ return /* @__PURE__ */ isUndefined(variables[name]) ? match : String(variables[name]);
2541
+ });
2542
+ params = params.slice(1);
2543
+ }
2544
+ resolvedMessage = resolvedMessage.replace(/\{(\d+)\}/g, (match, index) => {
2545
+ const idx = Number.parseInt(index, 10);
2546
+ if (!/* @__PURE__ */ isUndefined(params[idx])) return String(params[idx]);
2547
+ return match;
2548
+ });
2549
+ return resolvedMessage;
2550
+ }
2551
+ n(value, locale, ...params) {
2552
+ if (!IN_BROWSER || !locale) return value.toString();
2553
+ const options = params[0];
2554
+ return new Intl.NumberFormat(String(locale), options).format(value);
2555
+ }
2556
+ };
2557
+
2558
+ //#endregion
2559
+ //#region src/composables/useSingle/index.ts
2406
2560
  /**
2407
- * Creates a new hydration instance.
2561
+ * Creates a new single selection instance that enforces only one selected item at a time.
2408
2562
  *
2409
- * @returns A new hydration instance.
2563
+ * Extends `createSelection` by automatically clearing previous selections when a new item is selected.
2564
+ * Adds computed singular properties: `selectedId`, `selectedItem`, `selectedIndex`, `selectedValue`.
2410
2565
  *
2411
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2566
+ * @param options The options for the single selection instance.
2567
+ * @template Z The type of the single selection ticket.
2568
+ * @template E The type of the single selection context.
2569
+ * @returns A new single selection instance with single-selection enforcement.
2412
2570
  *
2413
- * @example
2414
- * ```ts
2415
- * import { createHydration } from '@vuetify/v0'
2571
+ * @remarks
2572
+ * **Key Differences from `createSelection`:**
2573
+ * - Automatically clears `selectedIds` before selecting a new item (enforces single selection)
2574
+ * - Provides singular computed properties instead of plural sets
2575
+ * - Perfect for tabs, radio buttons, theme selectors, and other single-choice UI components
2416
2576
  *
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.
2577
+ * **Computed Properties:**
2578
+ * - `selectedId`: The ID of the selected item (undefined if none selected)
2579
+ * - `selectedItem`: The selected ticket object (undefined if none selected)
2580
+ * - `selectedIndex`: The index of the selected item (-1 if none selected)
2581
+ * - `selectedValue`: The value of the selected item (undefined if none selected)
2441
2582
  *
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.
2583
+ * **Inheritance Chain:**
2584
+ * `useRegistry` `createSelection` `createSingle` `createStep`
2445
2585
  *
2446
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2586
+ * @see https://0.vuetifyjs.com/composables/selection/use-single
2447
2587
  *
2448
2588
  * @example
2449
2589
  * ```ts
2450
- * import { createHydrationContext } from '@vuetify/v0'
2590
+ * import { createSingle } from '@vuetify/v0'
2451
2591
  *
2452
- * export const [useHydrationContext, provideHydrationContext, context] = createHydrationContext({
2453
- * namespace: 'app:hydration',
2454
- * })
2592
+ * const tabs = createSingle({ mandatory: true })
2593
+ *
2594
+ * tabs.onboard([
2595
+ * { id: 'home', value: 'Home' },
2596
+ * { id: 'about', value: 'About' },
2597
+ * { id: 'contact', value: 'Contact' },
2598
+ * ])
2599
+ *
2600
+ * tabs.first() // Select first tab
2601
+ *
2602
+ * console.log(tabs.selectedId.value) // 'home'
2603
+ * console.log(tabs.selectedIndex.value) // 0
2604
+ *
2605
+ * tabs.select('about') // Switch to about tab
2606
+ * console.log(tabs.selectedId.value) // 'about'
2607
+ * console.log(tabs.selectedIds.size) // 1 (always enforces single selection)
2455
2608
  * ```
2456
2609
  */
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);
2610
+ function createSingle(_options = {}) {
2611
+ const { mandatory = false, multiple = false, ...options } = _options;
2612
+ const registry = createSelection({
2613
+ ...options,
2614
+ mandatory,
2615
+ multiple
2616
+ });
2617
+ const selectedId = computed(() => registry.selectedIds.values().next().value);
2618
+ const selectedItem = computed(() => registry.selectedItems.value.values().next().value);
2619
+ const selectedIndex = computed(() => selectedItem.value?.index ?? -1);
2620
+ const selectedValue = computed(() => selectedItem.value?.value);
2621
+ function unselect(id) {
2622
+ if (mandatory && registry.selectedIds.size === 1) return;
2623
+ registry.selectedIds.delete(id);
2463
2624
  }
2464
- return createTrinity(useHydrationContext, provideHydrationContext, context);
2625
+ function toggle(id) {
2626
+ if (registry.selectedIds.has(id)) unselect(id);
2627
+ else registry.select(id);
2628
+ }
2629
+ return {
2630
+ ...registry,
2631
+ selectedId,
2632
+ selectedItem,
2633
+ selectedIndex,
2634
+ selectedValue,
2635
+ unselect,
2636
+ toggle,
2637
+ get size() {
2638
+ return registry.size;
2639
+ }
2640
+ };
2465
2641
  }
2466
2642
  /**
2467
- * Creates a new hydration plugin.
2643
+ * Creates a new single selection context.
2468
2644
  *
2469
- * @param options The options for the hydration plugin.
2470
- * @template E The type of the hydration context.
2471
- * @returns A new hydration plugin.
2645
+ * @param options The options for the single selection context.
2646
+ * @template Z The type of the single selection ticket.
2647
+ * @template E The type of the single selection context.
2648
+ * @returns A new single selection context.
2472
2649
  *
2473
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2650
+ * @see https://0.vuetifyjs.com/composables/selection/use-single
2474
2651
  *
2475
2652
  * @example
2476
2653
  * ```ts
2477
- * import { createApp } from 'vue'
2478
- * import { createHydrationPlugin } from '@vuetify/v0'
2479
- * import App from './App.vue'
2654
+ * import { createSingleContext } from '@vuetify/v0'
2480
2655
  *
2481
- * const app = createApp(App)
2656
+ * // With default namespace 'v0:single'
2657
+ * export const [useSingle, provideSingle, context] = createSingleContext()
2482
2658
  *
2483
- * app.use(createHydrationPlugin())
2659
+ * // In a parent component:
2660
+ * provideSingle()
2484
2661
  *
2485
- * app.mount('#app')
2662
+ * // In a child component:
2663
+ * const single = useSingle()
2664
+ * single.select('tab-1')
2486
2665
  * ```
2487
2666
  */
2488
- function createHydrationPlugin(_options = {}) {
2489
- const { namespace = "v0:hydration", ...options } = _options;
2490
- const [, provideHydrationContext, context] = createHydrationContext({
2491
- ...options,
2492
- namespace
2493
- });
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
- } });
2504
- }
2505
- });
2667
+ function createSingleContext(_options = {}) {
2668
+ const { namespace = "v0:single", ...options } = _options;
2669
+ const [useSingleContext, _provideSingleContext] = createContext(namespace);
2670
+ const context = createSingle(options);
2671
+ function provideSingleContext(_context = context, app) {
2672
+ return _provideSingleContext(_context, app);
2673
+ }
2674
+ return createTrinity(useSingleContext, provideSingleContext, context);
2506
2675
  }
2507
2676
  /**
2508
- * Returns the current hydration instance.
2677
+ * Returns the current single selection instance.
2509
2678
  *
2510
- * @param namespace The namespace for the hydration context. Defaults to `v0:hydration`.
2511
- * @returns The current hydration instance.
2679
+ * @param namespace The namespace for the single selection context. Defaults to `'v0:single'`.
2680
+ * @returns The current single selection instance.
2512
2681
  *
2513
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2682
+ * @see https://0.vuetifyjs.com/composables/selection/use-single
2514
2683
  *
2515
2684
  * @example
2516
2685
  * ```vue
2517
2686
  * <script setup lang="ts">
2518
- * import { useHydration } from '@vuetify/v0'
2687
+ * import { useSingle } from '@vuetify/v0'
2519
2688
  *
2520
- * const hydration = useHydration()
2689
+ * const tabs = useSingle()
2521
2690
  * <\/script>
2522
2691
  *
2523
2692
  * <template>
2524
2693
  * <div>
2525
- * <p>Is hydrated: {{ hydration.isHydrated.value }}</p>
2694
+ * <p>Selected: {{ tabs.selectedId }}</p>
2526
2695
  * </div>
2527
2696
  * </template>
2528
2697
  * ```
2529
2698
  */
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
- }
2699
+ function useSingle(namespace = "v0:single") {
2700
+ return useContext(namespace);
2538
2701
  }
2539
2702
 
2540
2703
  //#endregion
2541
- //#region src/composables/useResizeObserver/index.ts
2542
- /**
2543
- * @module useResizeObserver
2544
- *
2545
- * @remarks
2546
- * ResizeObserver composable with lifecycle management.
2547
- *
2548
- * 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)
2555
- *
2556
- * Perfect for responsive components and size-based rendering.
2557
- */
2704
+ //#region src/composables/useTokens/index.ts
2558
2705
  /**
2559
- * A composable that uses the Resize Observer API to detect when an element's
2560
- * size changes.
2706
+ * Creates a new token instance.
2561
2707
  *
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.
2708
+ * @param tokens The tokens to use.
2709
+ * @param options The options for the token instance.
2710
+ * @template Z The type of the token ticket.
2711
+ * @template E The type of the token context.
2712
+ * @returns A new token instance.
2566
2713
  *
2567
- * @see https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
2568
- * @see https://0.vuetifyjs.com/composables/system/use-resize-observer
2714
+ * @see https://www.designtokens.org/tr/drafts/format/
2715
+ * @see https://0.vuetifyjs.com/composables/registration/use-tokens
2569
2716
  *
2570
2717
  * @example
2571
2718
  * ```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)
2719
+ * import { useTokens } from '@vuetify/v0'
2578
2720
  *
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
- * }
2721
+ * const tokens = useTokens({
2722
+ * colors: {
2723
+ * primary: '#3b82f6',
2724
+ * secondary: '{colors.primary}', // Alias reference
2588
2725
  * },
2589
- * { immediate: true }
2590
- * )
2591
- *
2592
- * // Pause observation
2593
- * pause()
2726
+ * })
2594
2727
  *
2595
- * // Resume observation
2596
- * resume()
2728
+ * console.log(tokens.resolve('{colors.primary}')) // '#3b82f6'
2729
+ * console.log(tokens.resolve('{colors.secondary}')) // '#3b82f6'
2597
2730
  * ```
2598
2731
  */
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();
2732
+ function createTokens(tokens = {}, options = {}) {
2733
+ const logger = useLogger();
2734
+ const registry = useRegistry(options);
2735
+ const cache = /* @__PURE__ */ new Map();
2736
+ registry.onboard(flatten(tokens, options.prefix, !!options.flat));
2737
+ function isAlias(token) {
2738
+ return /* @__PURE__ */ isString(token) && token.length > 2 && token[0] === "{" && token.at(-1) === "}";
2648
2739
  }
2649
- function stop() {
2650
- cleanup();
2740
+ function isTokenAlias(value) {
2741
+ return /* @__PURE__ */ isObject(value) && "$value" in value;
2651
2742
  }
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.
2743
+ function resolve(token, visited = /* @__PURE__ */ new Set()) {
2744
+ const cacheKey = /* @__PURE__ */ isString(token) ? token : JSON.stringify(token);
2745
+ const cached = cache.get(cacheKey);
2746
+ if (!/* @__PURE__ */ isUndefined(cached)) return cached;
2747
+ const reference = isTokenAlias(token) ? token.$value : token;
2748
+ const isAliasReference = /* @__PURE__ */ isString(reference) && isAlias(reference);
2749
+ const clean = isAliasReference ? reference.slice(1, -1) : String(reference);
2750
+ if (visited.has(clean)) {
2751
+ logger.warn(`Circular alias detected for "${clean}"`);
2752
+ cache.set(cacheKey, void 0);
2753
+ return;
2754
+ }
2755
+ visited.add(clean);
2756
+ let found = registry.get(clean);
2757
+ let segments = [];
2758
+ if (!found && clean.includes(".")) {
2759
+ const parts = clean.split(".");
2760
+ for (let i = parts.length - 1; i > 0; i--) {
2761
+ const prefix = parts.slice(0, i).join(".");
2762
+ const suffix = parts.slice(i);
2763
+ const candidate = registry.get(prefix);
2764
+ if (!/* @__PURE__ */ isUndefined(candidate?.value)) {
2765
+ found = candidate;
2766
+ segments = suffix;
2767
+ break;
2768
+ }
2769
+ }
2770
+ }
2771
+ if (/* @__PURE__ */ isUndefined(found?.value)) {
2772
+ if (isAliasReference) logger.warn(`Alias not found for "${String(reference)}"`);
2773
+ cache.set(cacheKey, void 0);
2774
+ return;
2775
+ }
2776
+ let result;
2777
+ let current = found.value;
2778
+ if (segments.length > 0) {
2779
+ if (isTokenAlias(current)) current = current.$value;
2780
+ for (const segment of segments) {
2781
+ if (!/* @__PURE__ */ isObject(current) || !(segment in current)) {
2782
+ current = void 0;
2783
+ break;
2784
+ }
2785
+ current = current[segment];
2786
+ if (isTokenAlias(current)) current = current.$value;
2787
+ }
2788
+ if (/* @__PURE__ */ isUndefined(current)) {
2789
+ logger.warn(`Path not found inside "${clean}": ${segments.join(".")}`);
2790
+ cache.set(cacheKey, void 0);
2791
+ return;
2792
+ }
2793
+ result = current;
2794
+ } else if (isTokenAlias(current)) {
2795
+ const inner = current.$value;
2796
+ if (/* @__PURE__ */ isString(inner) && isAlias(inner)) return resolve(inner, visited);
2797
+ result = inner;
2798
+ } else if (/* @__PURE__ */ isString(current) && isAlias(current)) return resolve(current, visited);
2799
+ else result = current;
2800
+ cache.set(cacheKey, result);
2801
+ return result;
2802
+ }
2803
+ return {
2804
+ ...registry,
2805
+ resolve,
2806
+ isAlias,
2807
+ get size() {
2808
+ return registry.size;
2809
+ }
2810
+ };
2811
+ }
2812
+ /**
2813
+ * Creates a new token context.
2667
2814
  *
2668
- * @see https://0.vuetifyjs.com/composables/system/use-resize-observer#use-element-size
2815
+ * @param namespace The namespace for the token context.
2816
+ * @param tokens The tokens to use.
2817
+ * @template Z The type of the token ticket.
2818
+ * @template E The type of the token context.
2819
+ * @returns A new token context.
2820
+ *
2821
+ * @see https://0.vuetifyjs.com/composables/registration/use-tokens
2669
2822
  *
2670
2823
  * @example
2671
2824
  * ```ts
2672
- * import { ref, watchEffect } from 'vue'
2673
- * import { useElementSize } from '@vuetify/v0'
2674
- *
2675
- * const box = ref<HTMLElement>()
2676
- * const { width, height } = useElementSize(box)
2825
+ * import { createTokensContext } from '@vuetify/v0'
2677
2826
  *
2678
- * // Width and height are reactive refs
2679
- * watchEffect(() => {
2680
- * console.log('Box size:', width.value, 'x', height.value)
2827
+ * export const [useTokens, provideTokens, context] = createTokensContext({
2828
+ * namespace: 'v0:tokens',
2829
+ * tokens: {
2830
+ * colors: {
2831
+ * primary: '#3b82f6',
2832
+ * secondary: '{colors.primary}', // Alias reference
2833
+ * },
2834
+ * },
2681
2835
  * })
2682
2836
  * ```
2683
2837
  */
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;
2692
- }
2693
- }, { immediate: true });
2694
- function pause() {
2695
- width.value = 0;
2696
- height.value = 0;
2697
- _pause();
2838
+ function createTokensContext(_options) {
2839
+ const { namespace = "v0:tokens", tokens = {}, ...options } = _options;
2840
+ const [useTokensContext, _provideTokensContext] = createContext(namespace);
2841
+ const context = createTokens(tokens, options);
2842
+ function provideTokensContext(_context = context, app) {
2843
+ return _provideTokensContext(_context, app);
2698
2844
  }
2699
- return {
2700
- width,
2701
- height,
2702
- isActive,
2703
- isPaused,
2704
- pause,
2705
- resume,
2706
- stop
2707
- };
2845
+ return createTrinity(useTokensContext, provideTokensContext, context);
2708
2846
  }
2709
-
2710
- //#endregion
2711
- //#region src/composables/useOverflow/index.ts
2712
2847
  /**
2713
- * @module useOverflow
2714
- *
2715
- * @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.
2848
+ * Returns the current tokens instance.
2718
2849
  *
2719
- * 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.
2725
- *
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.
2728
- */
2729
- /**
2730
- * Creates a new overflow context for computing how many items fit in a container.
2850
+ * @param namespace The namespace for the tokens context. Defaults to `'v0:tokens'`.
2851
+ * @returns The current tokens instance.
2731
2852
  *
2732
- * @param options Configuration options
2733
- * @returns Overflow context with container ref, capacity, and measurement functions
2853
+ * @see https://0.vuetifyjs.com/composables/registration/use-tokens
2734
2854
  *
2735
- * @example Variable-width mode (Breadcrumbs)
2855
+ * @example
2736
2856
  * ```vue
2737
- * <script lang="ts" setup>
2738
- * import { useTemplateRef } from 'vue'
2739
- * import { createOverflow } from '@vuetify/v0'
2857
+ * <script setup lang="ts">
2858
+ * import { useTokens } from '@vuetify/v0'
2740
2859
  *
2741
- * const containerRef = useTemplateRef('container')
2742
- * const overflow = createOverflow({
2743
- * container: containerRef,
2744
- * gap: 8,
2745
- * reserved: 40,
2746
- * })
2860
+ * const tokens = useTokens()
2747
2861
  * <\/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
- * ```
2762
- *
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
2862
  * ```
2771
2863
  */
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);
2790
- }
2791
- function reset() {
2792
- widths.value = /* @__PURE__ */ new Map();
2793
- }
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++;
2801
- }
2802
- return sum;
2803
- });
2804
- 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
2837
- };
2864
+ function useTokens(namespace = "v0:tokens") {
2865
+ return useContext(namespace);
2838
2866
  }
2839
2867
  /**
2840
- * Creates an overflow context with dependency injection support.
2841
- *
2842
- * @param options Configuration options including namespace
2843
- * @returns Trinity tuple: [useContext, provideContext, defaultContext]
2844
- *
2845
- * @example
2846
- * ```ts
2847
- * // Create injectable context
2848
- * const [useOverflow, provideOverflow, overflow] = createOverflowContext({
2849
- * namespace: 'my-overflow',
2850
- * gap: 8,
2851
- * reserved: 160,
2852
- * })
2868
+ * Flattens a nested collection of tokens into a flat array of tokens.
2869
+ * Each token is represented by an object containing its ID & value.
2870
+ * @param tokens The collection of tokens to flatten.
2871
+ * @param prefix An optional prefix to prepend to each token ID.
2872
+ * @returns An array of flattened tokens, each with an ID and value.
2873
+ */
2874
+ function flatten(tokens, prefix = "", flat = false) {
2875
+ const flattened = [];
2876
+ const stack = [{
2877
+ tokens,
2878
+ prefix,
2879
+ flat
2880
+ }];
2881
+ while (stack.length > 0) {
2882
+ const { tokens: currentTokens, prefix: currentPrefix, flat: flat$1 } = stack.pop();
2883
+ const meta = {};
2884
+ for (const k in currentTokens) if (k.startsWith("$")) meta[k] = currentTokens[k];
2885
+ if (Object.keys(meta).length > 0 && currentPrefix) flattened.push({
2886
+ id: currentPrefix,
2887
+ value: meta
2888
+ });
2889
+ for (const key in currentTokens) {
2890
+ if (key.startsWith("$")) continue;
2891
+ const value = currentTokens[key];
2892
+ const id = currentPrefix ? `${currentPrefix}.${key}` : key;
2893
+ if (!/* @__PURE__ */ isObject(value)) {
2894
+ flattened.push({
2895
+ id,
2896
+ value
2897
+ });
2898
+ continue;
2899
+ }
2900
+ if ("$value" in value) {
2901
+ flattened.push({
2902
+ id,
2903
+ value
2904
+ });
2905
+ const inner = value.$value;
2906
+ if (/* @__PURE__ */ isObject(inner) && !flat$1) for (const innerKey in inner) {
2907
+ if (innerKey.startsWith("$")) continue;
2908
+ const child = inner[innerKey];
2909
+ const childId = `${id}.${innerKey}`;
2910
+ if (!/* @__PURE__ */ isObject(child)) flattened.push({
2911
+ id: childId,
2912
+ value: child
2913
+ });
2914
+ else if ("$value" in child) flattened.push({
2915
+ id: childId,
2916
+ value: child
2917
+ });
2918
+ else stack.push({
2919
+ tokens: child,
2920
+ prefix: childId,
2921
+ flat: flat$1
2922
+ });
2923
+ }
2924
+ continue;
2925
+ }
2926
+ if (flat$1) {
2927
+ flattened.push({
2928
+ id,
2929
+ value
2930
+ });
2931
+ continue;
2932
+ }
2933
+ stack.push({
2934
+ tokens: value,
2935
+ prefix: id,
2936
+ flat: flat$1
2937
+ });
2938
+ }
2939
+ }
2940
+ return flattened;
2941
+ }
2942
+
2943
+ //#endregion
2944
+ //#region src/composables/useLocale/index.ts
2945
+ /**
2946
+ * Creates a new locale instance.
2853
2947
  *
2854
- * // In parent component
2855
- * provideOverflow()
2948
+ * @param options The options for the locale instance.
2949
+ * @template Z The type of the locale ticket.
2950
+ * @template E The type of the locale context.
2951
+ * @returns A new locale instance.
2856
2952
  *
2857
- * // In child component
2858
- * const overflow = useOverflow()
2859
- * ```
2953
+ * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2860
2954
  */
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);
2955
+ function createLocale(_options = {}) {
2956
+ const { adapter = new Vuetify0LocaleAdapter(), messages = {}, ...options } = _options;
2957
+ const tokens = createTokens(messages);
2958
+ const registry = createSingle(options);
2959
+ for (const id in messages) {
2960
+ registry.register({ id });
2961
+ if (id === options.default && !registry.selectedId.value) registry.select(id);
2867
2962
  }
2868
- return createTrinity(useOverflowContext, provideOverflowContext, context);
2963
+ function t(key, params, fallback) {
2964
+ const locale = registry.selectedId.value;
2965
+ const args = toArray(params);
2966
+ if (!locale) return adapter.t(fallback ?? key, ...args);
2967
+ const path = `${locale}.${key}`;
2968
+ const message = tokens.get(path)?.value;
2969
+ const template = /* @__PURE__ */ isString(message) ? resolve(locale, message) : fallback ?? key;
2970
+ return adapter.t(template, ...args);
2971
+ }
2972
+ function n(value, ...params) {
2973
+ return adapter.n(value, registry.selectedId.value, ...params);
2974
+ }
2975
+ function resolve(locale, str) {
2976
+ return str.replace(/{([a-zA-Z0-9.-_]+)}/g, (match, key) => {
2977
+ const [prefix, ...rest] = key.split(".");
2978
+ const target = registry.has(prefix) ? prefix : locale;
2979
+ const path = `${target}.${registry.has(prefix) ? rest.join(".") : key}`;
2980
+ const resolved = tokens.get(path)?.value;
2981
+ if (/* @__PURE__ */ isString(resolved)) return resolve(target, resolved);
2982
+ return match;
2983
+ });
2984
+ }
2985
+ return {
2986
+ ...registry,
2987
+ t,
2988
+ n,
2989
+ get size() {
2990
+ return registry.size;
2991
+ }
2992
+ };
2993
+ }
2994
+ function createLocaleFallback() {
2995
+ return {
2996
+ size: 0,
2997
+ t: (key, _params, fallback) => fallback ?? key,
2998
+ n: String
2999
+ };
2869
3000
  }
2870
3001
  /**
2871
- * Returns the current overflow context from dependency injection.
3002
+ * Creates a new locale context.
2872
3003
  *
2873
- * @param namespace The namespace for the overflow context. Defaults to `v0:overflow`.
2874
- * @returns The current overflow context.
3004
+ * @param options The options for the locale context.
3005
+ * @template Z The type of the locale ticket.
3006
+ * @template E The type of the locale context.
3007
+ * @returns A new locale context.
3008
+ *
3009
+ * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2875
3010
  *
2876
3011
  * @example
2877
- * ```vue
2878
- * <script lang="ts" setup>
2879
- * import { useOverflow } from '@vuetify/v0'
3012
+ * ```ts
3013
+ * import { createLocaleContext } from '@vuetify/v0'
2880
3014
  *
2881
- * // Inject overflow context provided by parent
2882
- * const overflow = useOverflow()
2883
- * <\/script>
3015
+ * export const [useAppLocale, provideAppLocale, appLocale] = createLocaleContext({
3016
+ * namespace: 'app:locale',
3017
+ * messages: {
3018
+ * en: { hello: 'Hello' },
3019
+ * es: { hello: 'Hola' },
3020
+ * },
3021
+ * })
2884
3022
  *
2885
- * <template>
2886
- * <div>
2887
- * <p>Capacity: {{ overflow.capacity.value }}</p>
2888
- * </div>
2889
- * </template>
3023
+ * // In a parent component:
3024
+ * provideAppLocale()
3025
+ *
3026
+ * // In a child component:
3027
+ * const locale = useAppLocale()
3028
+ * locale.select('es')
2890
3029
  * ```
2891
3030
  */
2892
- function useOverflow(namespace = "v0:overflow") {
2893
- return useContext(namespace);
3031
+ function createLocaleContext(_options = {}) {
3032
+ const { namespace = "v0:locale", ...options } = _options;
3033
+ const [useLocaleContext, _provideLocaleContext] = createContext(namespace);
3034
+ const context = createLocale(options);
3035
+ function provideLocaleContext(_context = context, app) {
3036
+ return _provideLocaleContext(_context, app);
3037
+ }
3038
+ return createTrinity(useLocaleContext, provideLocaleContext, context);
3039
+ }
3040
+ /**
3041
+ * Creates a new locale plugin.
3042
+ *
3043
+ * @param options The options for the locale plugin.
3044
+ * @template Z The type of the locale ticket.
3045
+ * @template E The type of the locale context.
3046
+ * @template R The type of the token ticket.
3047
+ * @template O The type of the token context.
3048
+ * @returns A new locale plugin.
3049
+ *
3050
+ * @see https://0.vuetifyjs.com/composables/plugins/use-locale
3051
+ */
3052
+ function createLocalePlugin(_options = {}) {
3053
+ const { namespace = "v0:locale", adapter = new Vuetify0LocaleAdapter(), messages = {}, ...options } = _options;
3054
+ const [, provideLocaleContext, context] = createLocaleContext({
3055
+ ...options,
3056
+ namespace,
3057
+ adapter,
3058
+ messages
3059
+ });
3060
+ return createPlugin({
3061
+ namespace,
3062
+ provide: (app) => {
3063
+ provideLocaleContext(context, app);
3064
+ }
3065
+ });
2894
3066
  }
2895
-
2896
- //#endregion
2897
- //#region src/composables/usePagination/index.ts
2898
3067
  /**
2899
- * @module usePagination
3068
+ * Returns the current locale instance.
2900
3069
  *
2901
- * @remarks
2902
- * Lightweight pagination composable for navigating through pages.
3070
+ * @returns The current locale instance.
2903
3071
  *
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
2910
- *
2911
- * Unlike registry-based composables, pagination tracks a single number
2912
- * within a range, making it efficient for large page counts.
3072
+ * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2913
3073
  */
3074
+ function useLocale(namespace = "v0:locale") {
3075
+ const fallback = createLocaleFallback();
3076
+ if (!getCurrentInstance()) return fallback;
3077
+ try {
3078
+ return useContext(namespace, fallback);
3079
+ } catch {
3080
+ return fallback;
3081
+ }
3082
+ }
3083
+
3084
+ //#endregion
3085
+ //#region src/composables/useHydration/index.ts
2914
3086
  /**
2915
- * Creates a pagination instance.
3087
+ * Creates a new hydration instance.
2916
3088
  *
2917
- * @param options The options for the pagination instance.
2918
- * @returns A pagination context with navigation methods.
3089
+ * @returns A new hydration instance.
3090
+ *
3091
+ * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2919
3092
  *
2920
3093
  * @example
2921
3094
  * ```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 }, ...]
3095
+ * import { createHydration } from '@vuetify/v0'
2928
3096
  *
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
2933
- * ```
2934
- */
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);
3097
+ * const hydration = createHydration()
3098
+ * console.log(hydration.isHydrated.value) // false
3099
+ * hydration.hydrate()
3100
+ * console.log(hydration.isHydrated.value) // true
3101
+ * ```
3102
+ */
3103
+ function createHydration() {
3104
+ const isHydrated = shallowRef(false);
3105
+ function hydrate() {
3106
+ isHydrated.value = true;
2979
3107
  }
2980
3108
  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
- }
3109
+ isHydrated: shallowReadonly(isHydrated),
3110
+ hydrate
3111
+ };
3112
+ }
3113
+ function createFallbackHydration() {
3114
+ return {
3115
+ isHydrated: shallowReadonly(shallowRef(true)),
3116
+ hydrate: () => {}
3065
3117
  };
3066
3118
  }
3067
3119
  /**
3068
- * Creates a pagination context for dependency injection.
3120
+ * Creates a new hydration context trinity.
3069
3121
  *
3070
- * @param options The options including namespace.
3071
- * @returns A trinity: [usePagination, providePagination, defaultContext]
3122
+ * @param options Options for creating the hydration context.
3123
+ * @template E The type of the hydration context.
3124
+ * @returns A new hydration context trinity.
3125
+ *
3126
+ * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
3072
3127
  *
3073
3128
  * @example
3074
3129
  * ```ts
3075
- * // With default namespace 'v0:pagination'
3076
- * const [usePagination, providePaginationContext] = createPaginationContext({ size: 50 })
3130
+ * import { createHydrationContext } from '@vuetify/v0'
3077
3131
  *
3078
- * // Or with custom namespace
3079
- * const [usePagination, providePaginationContext] = createPaginationContext({
3080
- * namespace: 'my-pagination',
3081
- * size: 50,
3132
+ * export const [useHydrationContext, provideHydrationContext, context] = createHydrationContext({
3133
+ * namespace: 'app:hydration',
3082
3134
  * })
3135
+ * ```
3136
+ */
3137
+ function createHydrationContext(_options = {}) {
3138
+ const { namespace = "v0:hydration" } = _options;
3139
+ const [useHydrationContext, _provideHydrationContext] = createContext(namespace);
3140
+ const context = createHydration();
3141
+ function provideHydrationContext(_context = context, app) {
3142
+ return _provideHydrationContext(_context, app);
3143
+ }
3144
+ return createTrinity(useHydrationContext, provideHydrationContext, context);
3145
+ }
3146
+ /**
3147
+ * Creates a new hydration plugin.
3083
3148
  *
3084
- * // Parent component
3085
- * providePaginationContext()
3149
+ * @param options The options for the hydration plugin.
3150
+ * @template E The type of the hydration context.
3151
+ * @returns A new hydration plugin.
3086
3152
  *
3087
- * // Child component
3088
- * const pagination = usePagination()
3089
- * pagination.next()
3153
+ * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
3154
+ *
3155
+ * @example
3156
+ * ```ts
3157
+ * import { createApp } from 'vue'
3158
+ * import { createHydrationPlugin } from '@vuetify/v0'
3159
+ * import App from './App.vue'
3160
+ *
3161
+ * const app = createApp(App)
3162
+ *
3163
+ * app.use(createHydrationPlugin())
3164
+ *
3165
+ * app.mount('#app')
3090
3166
  * ```
3091
3167
  */
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);
3168
+ function createHydrationPlugin(_options = {}) {
3169
+ const { namespace = "v0:hydration", ...options } = _options;
3170
+ const [, provideHydrationContext, context] = createHydrationContext({
3171
+ ...options,
3172
+ namespace
3173
+ });
3174
+ return createPlugin({
3175
+ namespace,
3176
+ provide: (app) => {
3177
+ provideHydrationContext(context, app);
3178
+ },
3179
+ setup: (app) => {
3180
+ app.mixin({ mounted() {
3181
+ if (this.$parent !== null) return;
3182
+ context.hydrate();
3183
+ } });
3184
+ }
3185
+ });
3100
3186
  }
3101
3187
  /**
3102
- * Returns the current pagination instance from context.
3188
+ * Returns the current hydration instance.
3103
3189
  *
3104
- * @param namespace The namespace. @default 'v0:pagination'
3105
- * @returns The pagination context.
3190
+ * @param namespace The namespace for the hydration context. Defaults to `v0:hydration`.
3191
+ * @returns The current hydration instance.
3192
+ *
3193
+ * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
3106
3194
  *
3107
3195
  * @example
3108
3196
  * ```vue
3109
- * <script setup>
3110
- * import { usePagination } from '@vuetify/v0'
3197
+ * <script setup lang="ts">
3198
+ * import { useHydration } from '@vuetify/v0'
3111
3199
  *
3112
- * const pagination = usePagination()
3200
+ * const hydration = useHydration()
3113
3201
  * <\/script>
3114
3202
  *
3115
3203
  * <template>
3116
- * <button @click="pagination.prev()" :disabled="pagination.isFirst.value">Prev</button>
3117
- * <button @click="pagination.next()" :disabled="pagination.isLast.value">Next</button>
3204
+ * <div>
3205
+ * <p>Is hydrated: {{ hydration.isHydrated.value }}</p>
3206
+ * </div>
3118
3207
  * </template>
3119
3208
  * ```
3120
3209
  */
3121
- function usePagination(namespace = "v0:pagination") {
3122
- return useContext(namespace);
3210
+ function useHydration(namespace = "v0:hydration") {
3211
+ const fallback = createFallbackHydration();
3212
+ if (!getCurrentInstance()) return fallback;
3213
+ try {
3214
+ return useContext(namespace, fallback);
3215
+ } catch {
3216
+ return fallback;
3217
+ }
3123
3218
  }
3124
3219
 
3125
3220
  //#endregion
3126
- //#region src/composables/useSingle/index.ts
3221
+ //#region src/composables/useResizeObserver/index.ts
3127
3222
  /**
3128
- * @module useSingle
3223
+ * A composable that uses the Resize Observer API to detect when an element's
3224
+ * size changes.
3129
3225
  *
3130
- * @remarks
3131
- * Single-selection composable that extends useSelection to enforce only one selected item.
3132
- *
3133
- * 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
3137
- *
3138
- * Inheritance chain: useRegistry → useSelection → useSingle
3139
- */
3140
- /**
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)
3162
- *
3163
- * **Inheritance Chain:**
3164
- * `useRegistry` → `createSelection` → `createSingle` → `createStep`
3226
+ * @param target The element to observe.
3227
+ * @param callback The callback to execute when the element's size changes.
3228
+ * @param options The options for the Resize Observer.
3229
+ * @returns An object with methods to control the observer.
3165
3230
  *
3166
- * @see https://0.vuetifyjs.com/composables/selection/use-single
3231
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
3232
+ * @see https://0.vuetifyjs.com/composables/system/use-resize-observer
3167
3233
  *
3168
3234
  * @example
3169
3235
  * ```ts
3170
- * import { createSingle } from '@vuetify/v0'
3171
- *
3172
- * const tabs = createSingle({ mandatory: true })
3236
+ * import { ref } from 'vue'
3237
+ * import { useResizeObserver } from '@vuetify/v0'
3173
3238
  *
3174
- * tabs.onboard([
3175
- * { id: 'home', value: 'Home' },
3176
- * { id: 'about', value: 'About' },
3177
- * { id: 'contact', value: 'Contact' },
3178
- * ])
3239
+ * const el = ref<HTMLElement>()
3240
+ * const width = ref(0)
3241
+ * const height = ref(0)
3179
3242
  *
3180
- * tabs.first() // Select first tab
3243
+ * const { pause, resume, isPaused } = useResizeObserver(
3244
+ * el,
3245
+ * (entries) => {
3246
+ * const entry = entries[0]
3247
+ * if (entry) {
3248
+ * width.value = entry.contentRect.width
3249
+ * height.value = entry.contentRect.height
3250
+ * console.log('Size changed:', width.value, 'x', height.value)
3251
+ * }
3252
+ * },
3253
+ * { immediate: true }
3254
+ * )
3181
3255
  *
3182
- * console.log(tabs.selectedId.value) // 'home'
3183
- * console.log(tabs.selectedIndex.value) // 0
3256
+ * // Pause observation
3257
+ * pause()
3184
3258
  *
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)
3259
+ * // Resume observation
3260
+ * resume()
3188
3261
  * ```
3189
3262
  */
3190
- function createSingle(_options = {}) {
3191
- const { mandatory = false, multiple = false, ...options } = _options;
3192
- const registry = createSelection({
3193
- ...options,
3194
- mandatory,
3195
- multiple
3263
+ function useResizeObserver(target, callback, options = {}) {
3264
+ const { isHydrated } = useHydration();
3265
+ const observer = shallowRef();
3266
+ const isPaused = shallowRef(false);
3267
+ const isActive = toRef(() => !!observer.value);
3268
+ function setup() {
3269
+ if (observer.value === null) return;
3270
+ if (!isHydrated.value || !SUPPORTS_OBSERVER || !target.value || isPaused.value) return;
3271
+ observer.value = new ResizeObserver((entries) => {
3272
+ callback(entries.map((entry) => ({
3273
+ contentRect: {
3274
+ width: entry.contentRect.width,
3275
+ height: entry.contentRect.height,
3276
+ top: entry.contentRect.top,
3277
+ left: entry.contentRect.left
3278
+ },
3279
+ target: entry.target
3280
+ })));
3281
+ if (options.once) stop();
3282
+ });
3283
+ observer.value.observe(target.value, { box: options.box || "content-box" });
3284
+ if (options.immediate) {
3285
+ const rect = target.value.getBoundingClientRect();
3286
+ callback([{
3287
+ contentRect: {
3288
+ width: rect.width,
3289
+ height: rect.height,
3290
+ top: rect.top,
3291
+ left: rect.left
3292
+ },
3293
+ target: target.value
3294
+ }]);
3295
+ }
3296
+ }
3297
+ watchEffect(() => {
3298
+ const hydrated = isHydrated.value;
3299
+ const el = target.value;
3300
+ cleanup();
3301
+ if (hydrated && el) setup();
3196
3302
  });
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);
3303
+ function cleanup() {
3304
+ if (observer.value) {
3305
+ observer.value.disconnect();
3306
+ observer.value = void 0;
3307
+ }
3204
3308
  }
3205
- function toggle(id) {
3206
- if (registry.selectedIds.has(id)) unselect(id);
3207
- else registry.select(id);
3309
+ function pause() {
3310
+ isPaused.value = true;
3311
+ observer.value?.disconnect();
3208
3312
  }
3313
+ function resume() {
3314
+ isPaused.value = false;
3315
+ setup();
3316
+ }
3317
+ function stop() {
3318
+ cleanup();
3319
+ observer.value = null;
3320
+ }
3321
+ onScopeDispose(stop, true);
3209
3322
  return {
3210
- ...registry,
3211
- selectedId,
3212
- selectedItem,
3213
- selectedIndex,
3214
- selectedValue,
3215
- unselect,
3216
- toggle,
3217
- get size() {
3218
- return registry.size;
3219
- }
3323
+ isActive: shallowReadonly(isActive),
3324
+ isPaused: shallowReadonly(isPaused),
3325
+ pause,
3326
+ resume,
3327
+ stop
3220
3328
  };
3221
3329
  }
3222
3330
  /**
3223
- * Creates a new single selection context.
3331
+ * A convenience composable that uses the Resize Observer API to track an
3332
+ * element's size.
3224
3333
  *
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.
3334
+ * @param target The element to observe.
3335
+ * @returns An object with the element's width and height.
3229
3336
  *
3230
- * @see https://0.vuetifyjs.com/composables/selection/use-single
3337
+ * @see https://0.vuetifyjs.com/composables/system/use-resize-observer#use-element-size
3231
3338
  *
3232
3339
  * @example
3233
3340
  * ```ts
3234
- * import { createSingleContext } from '@vuetify/v0'
3235
- *
3236
- * // With default namespace 'v0:single'
3237
- * export const [useSingle, provideSingle, context] = createSingleContext()
3341
+ * import { ref, watchEffect } from 'vue'
3342
+ * import { useElementSize } from '@vuetify/v0'
3238
3343
  *
3239
- * // In a parent component:
3240
- * provideSingle()
3344
+ * const box = ref<HTMLElement>()
3345
+ * const { width, height } = useElementSize(box)
3241
3346
  *
3242
- * // In a child component:
3243
- * const single = useSingle()
3244
- * single.select('tab-1')
3347
+ * // Width and height are reactive refs
3348
+ * watchEffect(() => {
3349
+ * console.log('Box size:', width.value, 'x', height.value)
3350
+ * })
3245
3351
  * ```
3246
3352
  */
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);
3353
+ function useElementSize(target) {
3354
+ const width = shallowRef(0);
3355
+ const height = shallowRef(0);
3356
+ const { pause: _pause, resume, stop, isActive, isPaused } = useResizeObserver(target, (entries) => {
3357
+ const entry = entries[0];
3358
+ if (entry) {
3359
+ width.value = entry.contentRect.width;
3360
+ height.value = entry.contentRect.height;
3361
+ }
3362
+ }, { immediate: true });
3363
+ function pause() {
3364
+ width.value = 0;
3365
+ height.value = 0;
3366
+ _pause();
3253
3367
  }
3254
- return createTrinity(useSingleContext, provideSingleContext, context);
3368
+ return {
3369
+ width,
3370
+ height,
3371
+ isActive,
3372
+ isPaused,
3373
+ pause,
3374
+ resume,
3375
+ stop
3376
+ };
3255
3377
  }
3378
+
3379
+ //#endregion
3380
+ //#region src/composables/useOverflow/index.ts
3256
3381
  /**
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.
3382
+ * Creates a new overflow context for computing how many items fit in a container.
3261
3383
  *
3262
- * @see https://0.vuetifyjs.com/composables/selection/use-single
3384
+ * @param options Configuration options
3385
+ * @returns Overflow context with container ref, capacity, and measurement functions
3263
3386
  *
3264
- * @example
3387
+ * @example Variable-width mode (Breadcrumbs)
3265
3388
  * ```vue
3266
- * <script setup lang="ts">
3267
- * import { useSingle } from '@vuetify/v0'
3389
+ * <script lang="ts" setup>
3390
+ * import { useTemplateRef } from 'vue'
3391
+ * import { createOverflow } from '@vuetify/v0'
3268
3392
  *
3269
- * const tabs = useSingle()
3393
+ * const containerRef = useTemplateRef('container')
3394
+ * const overflow = createOverflow({
3395
+ * container: containerRef,
3396
+ * gap: 8,
3397
+ * reserved: 40,
3398
+ * })
3270
3399
  * <\/script>
3271
3400
  *
3272
3401
  * <template>
3273
- * <div>
3274
- * <p>Selected: {{ tabs.selectedId }}</p>
3402
+ * <div ref="container">
3403
+ * <span
3404
+ * v-for="(item, i) in items.slice(0, overflow.capacity.value)"
3405
+ * :key="i"
3406
+ * :ref="el => overflow.measure(i, el)"
3407
+ * >
3408
+ * {{ item }}
3409
+ * </span>
3410
+ * <span v-if="overflow.isOverflowing.value">...</span>
3275
3411
  * </div>
3276
3412
  * </template>
3277
3413
  * ```
3278
- */
3279
- function useSingle(namespace = "v0:single") {
3280
- return useContext(namespace);
3281
- }
3282
-
3283
- //#endregion
3284
- //#region src/composables/useTokens/index.ts
3285
- /**
3286
- * @module useTokens
3287
- *
3288
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
3289
3414
  *
3290
- * @remarks
3291
- * Design token registry with alias resolution and W3C Design Tokens format support.
3292
- *
3293
- * 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)
3299
- *
3300
- * Used by useTheme, useLocale, and useFeatures for token-based configuration.
3301
- */
3302
- /**
3303
- * Creates a new token instance.
3304
- *
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.
3310
- *
3311
- * @see https://www.designtokens.org/tr/drafts/format/
3312
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
3313
- *
3314
- * @example
3415
+ * @example Uniform-width mode (Pagination)
3315
3416
  * ```ts
3316
- * import { useTokens } from '@vuetify/v0'
3317
- *
3318
- * const tokens = useTokens({
3319
- * colors: {
3320
- * primary: '#3b82f6',
3321
- * secondary: '{colors.primary}', // Alias reference
3322
- * },
3417
+ * const overflow = createOverflow({
3418
+ * container: () => atom.value?.element,
3419
+ * itemWidth: buttonWidth,
3420
+ * reserved: () => buttonWidth.value * 4,
3323
3421
  * })
3324
- *
3325
- * console.log(tokens.resolve('{colors.primary}')) // '#3b82f6'
3326
- * console.log(tokens.resolve('{colors.secondary}')) // '#3b82f6'
3327
3422
  * ```
3328
3423
  */
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
- }
3424
+ function createOverflow(options = {}) {
3425
+ const { container: _container, gap = 0, reserved = 0, itemWidth, reverse } = options;
3426
+ const container = /* @__PURE__ */ isUndefined(_container) ? shallowRef() : toRef(_container);
3427
+ const widths = shallowRef(/* @__PURE__ */ new Map());
3428
+ const { width } = useElementSize(container);
3429
+ function measure(index, el) {
3430
+ if (!el) {
3431
+ if (widths.value.has(index)) {
3432
+ const next = new Map(widths.value);
3433
+ next.delete(index);
3434
+ widths.value = next;
3366
3435
  }
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
3436
  return;
3372
3437
  }
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;
3438
+ const style = getComputedStyle(el);
3439
+ const marginX = Number.parseFloat(style.marginLeft) + Number.parseFloat(style.marginRight);
3440
+ const w = el.offsetWidth + marginX;
3441
+ if (widths.value.get(index) !== w) widths.value = new Map(widths.value).set(index, w);
3399
3442
  }
3400
- return {
3401
- ...registry,
3402
- resolve,
3403
- isAlias,
3404
- get size() {
3405
- return registry.size;
3443
+ function reset() {
3444
+ widths.value = /* @__PURE__ */ new Map();
3445
+ }
3446
+ const total = computed(() => {
3447
+ const g = toValue(gap);
3448
+ let sum = 0;
3449
+ let count = 0;
3450
+ for (const w of widths.value.values()) {
3451
+ sum += w + (count > 0 ? g : 0);
3452
+ count++;
3406
3453
  }
3454
+ return sum;
3455
+ });
3456
+ return {
3457
+ container,
3458
+ width,
3459
+ capacity: computed(() => {
3460
+ const available = width.value - toValue(reserved);
3461
+ if (width.value === 0) return Infinity;
3462
+ if (available <= 0) return 0;
3463
+ const g = toValue(gap);
3464
+ const uniformWidth = toValue(itemWidth);
3465
+ if (uniformWidth && uniformWidth > 0) {
3466
+ const first = uniformWidth;
3467
+ const subsequent = uniformWidth + g;
3468
+ if (available < first) return 0;
3469
+ return Math.max(1, Math.floor((available - first) / subsequent) + 1);
3470
+ }
3471
+ const entries = [...widths.value.entries()].toSorted((a, b) => a[0] - b[0]);
3472
+ if (toValue(reverse)) entries.reverse();
3473
+ let sum = 0;
3474
+ let count = 0;
3475
+ for (const [, w] of entries) {
3476
+ const next = sum + w + (count > 0 ? g : 0);
3477
+ if (next > available) break;
3478
+ sum = next;
3479
+ count++;
3480
+ }
3481
+ return count;
3482
+ }),
3483
+ total,
3484
+ isOverflowing: toRef(() => {
3485
+ return total.value > width.value - toValue(reserved);
3486
+ }),
3487
+ measure,
3488
+ reset
3407
3489
  };
3408
3490
  }
3409
3491
  /**
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.
3492
+ * Creates an overflow context with dependency injection support.
3417
3493
  *
3418
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
3494
+ * @param options Configuration options including namespace
3495
+ * @returns Trinity tuple: [useContext, provideContext, defaultContext]
3419
3496
  *
3420
3497
  * @example
3421
3498
  * ```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
- * },
3432
- * })
3433
- * ```
3434
- */
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);
3441
- }
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.
3449
- *
3450
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
3451
- *
3452
- * @example
3453
- * ```vue
3454
- * <script setup lang="ts">
3455
- * import { useTokens } from '@vuetify/v0'
3456
- *
3457
- * const tokens = useTokens()
3458
- * <\/script>
3459
- * ```
3460
- */
3461
- function useTokens(namespace = "v0:tokens") {
3462
- return useContext(namespace);
3463
- }
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);
3499
+ * // Create injectable context
3500
+ * const [useOverflow, provideOverflow, overflow] = createOverflowContext({
3501
+ * namespace: 'my-overflow',
3502
+ * gap: 8,
3503
+ * reserved: 160,
3504
+ * })
3505
+ *
3506
+ * // In parent component
3507
+ * provideOverflow()
3508
+ *
3509
+ * // In child component
3510
+ * const overflow = useOverflow()
3511
+ * ```
3512
+ */
3513
+ function createOverflowContext(_options = {}) {
3514
+ const { namespace = "v0:overflow", ...options } = _options;
3515
+ const [useOverflowContext, _provideOverflowContext] = createContext(namespace);
3516
+ const context = createOverflow(options);
3517
+ function provideOverflowContext(_context = context, app) {
3518
+ return _provideOverflowContext(_context, app);
3570
3519
  }
3571
- };
3572
-
3573
- //#endregion
3574
- //#region src/composables/useLocale/index.ts
3520
+ return createTrinity(useOverflowContext, provideOverflowContext, context);
3521
+ }
3575
3522
  /**
3576
- * @module useLocale
3523
+ * Returns the current overflow context from dependency injection.
3577
3524
  *
3578
- * @remarks
3579
- * Internationalization (i18n) composable with adapter pattern for message translation.
3525
+ * @param namespace The namespace for the overflow context. Defaults to `v0:overflow`.
3526
+ * @returns The current overflow context.
3580
3527
  *
3581
- * 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
3528
+ * @example
3529
+ * ```vue
3530
+ * <script lang="ts" setup>
3531
+ * import { useOverflow } from '@vuetify/v0'
3532
+ *
3533
+ * // Inject overflow context provided by parent
3534
+ * const overflow = useOverflow()
3535
+ * <\/script>
3587
3536
  *
3588
- * Integrates with createSingle for locale selection and useTokens for message resolution.
3537
+ * <template>
3538
+ * <div>
3539
+ * <p>Capacity: {{ overflow.capacity.value }}</p>
3540
+ * </div>
3541
+ * </template>
3542
+ * ```
3589
3543
  */
3544
+ function useOverflow(namespace = "v0:overflow") {
3545
+ return useContext(namespace);
3546
+ }
3547
+
3548
+ //#endregion
3549
+ //#region src/composables/usePagination/index.ts
3590
3550
  /**
3591
- * Creates a new locale instance.
3551
+ * Creates a pagination instance.
3592
3552
  *
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.
3553
+ * @param options The options for the pagination instance.
3554
+ * @returns A pagination context with navigation methods.
3597
3555
  *
3598
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
3556
+ * @example
3557
+ * ```ts
3558
+ * import { createPagination } from '@vuetify/v0'
3559
+ *
3560
+ * // Basic usage
3561
+ * const pagination = createPagination({ size: 100 })
3562
+ * pagination.next()
3563
+ * pagination.items.value // [{ type: 'page', value: 1 }, { type: 'page', value: 2 }, ...]
3564
+ *
3565
+ * // With v-model (pass a ref)
3566
+ * const page = ref(1)
3567
+ * const pagination = createPagination({ page, size: 100 })
3568
+ * // Mutating pagination.page or the passed ref syncs both
3569
+ * ```
3599
3570
  */
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);
3571
+ function createPagination(_options = {}) {
3572
+ const { page: _page = 1, itemsPerPage: _itemsPerPage = 10, size: _size = 0, visible: _visible = 7, ellipsis = "..." } = _options;
3573
+ const page = isRef(_page) ? _page : shallowRef(_page);
3574
+ const pages = computed(() => {
3575
+ const size = toValue(_size);
3576
+ const perPage = toValue(_itemsPerPage);
3577
+ if (size <= 0 || /* @__PURE__ */ isNaN(size)) return 0;
3578
+ return Math.ceil(size / perPage);
3579
+ });
3580
+ function first() {
3581
+ page.value = 1;
3607
3582
  }
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);
3583
+ function last() {
3584
+ page.value = Math.max(1, pages.value);
3616
3585
  }
3617
- function n(value, ...params) {
3618
- return adapter.n(value, registry.selectedId.value, ...params);
3586
+ function next() {
3587
+ if (page.value < pages.value) page.value++;
3619
3588
  }
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
- });
3589
+ function prev() {
3590
+ if (page.value > 1) page.value--;
3591
+ }
3592
+ function select(value) {
3593
+ if (value < 1) page.value = 1;
3594
+ else if (value > pages.value) page.value = Math.max(1, pages.value);
3595
+ else page.value = value;
3596
+ }
3597
+ const isFirst = computed(() => page.value <= 1);
3598
+ const isLast = computed(() => page.value >= pages.value);
3599
+ const pageStart = computed(() => (page.value - 1) * toValue(_itemsPerPage));
3600
+ const pageStop = computed(() => Math.min(pageStart.value + toValue(_itemsPerPage), toValue(_size)));
3601
+ function toPage(value) {
3602
+ return {
3603
+ type: "page",
3604
+ value
3605
+ };
3606
+ }
3607
+ function toEllipsis() {
3608
+ return ellipsis === false ? false : {
3609
+ type: "ellipsis",
3610
+ value: ellipsis
3611
+ };
3612
+ }
3613
+ function filter(array) {
3614
+ return array.filter((item) => item !== false);
3629
3615
  }
3630
3616
  return {
3631
- ...registry,
3632
- t,
3633
- n,
3617
+ page,
3618
+ ellipsis,
3619
+ items: computed(() => {
3620
+ const pageCount = pages.value;
3621
+ const visible = toValue(_visible);
3622
+ const current = page.value;
3623
+ if (pageCount <= 0 || /* @__PURE__ */ isNaN(pageCount) || pageCount > Number.MAX_SAFE_INTEGER) return [];
3624
+ if (visible <= 0) return [];
3625
+ if (visible <= 2) return [toPage(current)];
3626
+ if (pageCount <= visible) return (/* @__PURE__ */ range(pageCount, 1)).map(toPage);
3627
+ if (visible === 3) {
3628
+ const mid = current <= 1 ? 2 : current >= pageCount ? pageCount - 1 : current;
3629
+ return [
3630
+ toPage(1),
3631
+ toPage(mid),
3632
+ toPage(pageCount)
3633
+ ];
3634
+ }
3635
+ const boundary = visible - 2;
3636
+ const middle = visible - 4;
3637
+ if (middle <= 0) {
3638
+ if (current <= boundary) return filter([
3639
+ ...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
3640
+ toEllipsis(),
3641
+ toPage(pageCount)
3642
+ ]);
3643
+ if (current > pageCount - boundary) return filter([
3644
+ toPage(1),
3645
+ toEllipsis(),
3646
+ ...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
3647
+ ]);
3648
+ return current <= Math.ceil(pageCount / 2) ? filter([
3649
+ toPage(1),
3650
+ toPage(current),
3651
+ toEllipsis(),
3652
+ toPage(pageCount)
3653
+ ]) : filter([
3654
+ toPage(1),
3655
+ toEllipsis(),
3656
+ toPage(current),
3657
+ toPage(pageCount)
3658
+ ]);
3659
+ }
3660
+ const leftThreshold = boundary - 1;
3661
+ const rightThreshold = pageCount - boundary + 2;
3662
+ if (current <= leftThreshold) return filter([
3663
+ ...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
3664
+ toEllipsis(),
3665
+ toPage(pageCount)
3666
+ ]);
3667
+ else if (current >= rightThreshold) return filter([
3668
+ toPage(1),
3669
+ toEllipsis(),
3670
+ ...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
3671
+ ]);
3672
+ else {
3673
+ const start = current - Math.floor(middle / 2);
3674
+ return filter([
3675
+ toPage(1),
3676
+ toEllipsis(),
3677
+ ...(/* @__PURE__ */ range(middle, start)).map(toPage),
3678
+ toEllipsis(),
3679
+ toPage(pageCount)
3680
+ ]);
3681
+ }
3682
+ }),
3683
+ pageStart,
3684
+ pageStop,
3685
+ isFirst,
3686
+ isLast,
3687
+ first,
3688
+ last,
3689
+ next,
3690
+ prev,
3691
+ select,
3692
+ get itemsPerPage() {
3693
+ return toValue(_itemsPerPage);
3694
+ },
3634
3695
  get size() {
3635
- return registry.size;
3696
+ return toValue(_size);
3697
+ },
3698
+ get pages() {
3699
+ return pages.value;
3636
3700
  }
3637
3701
  };
3638
3702
  }
3639
- function createLocaleFallback() {
3640
- return {
3641
- size: 0,
3642
- t: (key, _params, fallback) => fallback ?? key,
3643
- n: String
3644
- };
3645
- }
3646
3703
  /**
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.
3704
+ * Creates a pagination context for dependency injection.
3653
3705
  *
3654
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
3706
+ * @param options The options including namespace.
3707
+ * @returns A trinity: [usePagination, providePagination, defaultContext]
3655
3708
  *
3656
3709
  * @example
3657
3710
  * ```ts
3658
- * import { createLocaleContext } from '@vuetify/v0'
3711
+ * // With default namespace 'v0:pagination'
3712
+ * const [usePagination, providePaginationContext] = createPaginationContext({ size: 50 })
3659
3713
  *
3660
- * export const [useAppLocale, provideAppLocale, appLocale] = createLocaleContext({
3661
- * namespace: 'app:locale',
3662
- * messages: {
3663
- * en: { hello: 'Hello' },
3664
- * es: { hello: 'Hola' },
3665
- * },
3714
+ * // Or with custom namespace
3715
+ * const [usePagination, providePaginationContext] = createPaginationContext({
3716
+ * namespace: 'my-pagination',
3717
+ * size: 50,
3666
3718
  * })
3667
3719
  *
3668
- * // In a parent component:
3669
- * provideAppLocale()
3720
+ * // Parent component
3721
+ * providePaginationContext()
3670
3722
  *
3671
- * // In a child component:
3672
- * const locale = useAppLocale()
3673
- * locale.select('es')
3723
+ * // Child component
3724
+ * const pagination = usePagination()
3725
+ * pagination.next()
3674
3726
  * ```
3675
3727
  */
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);
3728
+ function createPaginationContext(_options = {}) {
3729
+ const { namespace = "v0:pagination", ...options } = _options;
3730
+ const [usePaginationContext, _providePaginationContext] = createContext(namespace);
3731
+ const context = createPagination(options);
3732
+ function providePaginationContext(_context = context, app) {
3733
+ return _providePaginationContext(_context, app);
3682
3734
  }
3683
- return createTrinity(useLocaleContext, provideLocaleContext, context);
3735
+ return createTrinity(usePaginationContext, providePaginationContext, context);
3684
3736
  }
3685
3737
  /**
3686
- * Creates a new locale plugin.
3738
+ * Returns the current pagination instance from context.
3687
3739
  *
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.
3740
+ * @param namespace The namespace. @default 'v0:pagination'
3741
+ * @returns The pagination context.
3694
3742
  *
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.
3743
+ * @example
3744
+ * ```vue
3745
+ * <script setup lang="ts">
3746
+ * import { usePagination } from '@vuetify/v0'
3714
3747
  *
3715
- * @returns The current locale instance.
3748
+ * const pagination = usePagination()
3749
+ * <\/script>
3716
3750
  *
3717
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
3751
+ * <template>
3752
+ * <button @click="pagination.prev()" :disabled="pagination.isFirst.value">Prev</button>
3753
+ * <button @click="pagination.next()" :disabled="pagination.isLast.value">Next</button>
3754
+ * </template>
3755
+ * ```
3718
3756
  */
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
- }
3757
+ function usePagination(namespace = "v0:pagination") {
3758
+ return useContext(namespace);
3727
3759
  }
3728
3760
 
3729
3761
  //#endregion
@@ -4621,20 +4653,6 @@ const Single = {
4621
4653
  //#endregion
4622
4654
  //#region src/composables/useStep/index.ts
4623
4655
  /**
4624
- * @module useStep
4625
- *
4626
- * @remarks
4627
- * Navigation composable that extends useSingle with first/last/next/prev/step methods.
4628
- *
4629
- * Key features:
4630
- * - Configurable circular or bounded navigation
4631
- * - Automatic disabled item skipping
4632
- * - Arbitrary step counts (positive/negative)
4633
- * - Perfect for wizards, carousels, pagination, onboarding flows
4634
- *
4635
- * Inheritance chain: useRegistry → useSelection → useSingle → useStep
4636
- */
4637
- /**
4638
4656
  * Creates a new step instance with navigation through items.
4639
4657
  *
4640
4658
  * Extends `createSingle` with `first()`, `last()`, `next()`, `prev()`, and `step(count)` methods
@@ -4930,21 +4948,6 @@ const Step = {
4930
4948
  //#endregion
4931
4949
  //#region src/composables/toReactive/index.ts
4932
4950
  /**
4933
- * @module toReactive
4934
- *
4935
- * @remarks
4936
- * Utility function to convert values and refs into reactive proxies with ref unwrapping.
4937
- *
4938
- * Key features:
4939
- * - Automatic ref unwrapping
4940
- * - Deep reactive proxying
4941
- * - Map and Set support with ref unwrapping
4942
- * - Nested object/array reactivity
4943
- * - Type preservation
4944
- *
4945
- * Perfect for creating reactive versions of plain objects while automatically unwrapping refs.
4946
- */
4947
- /**
4948
4951
  * Converts a `MaybeRef` to a `UnwrapNestedRefs`.
4949
4952
  *
4950
4953
  * @param objectRef The object to convert.
@@ -5065,21 +5068,6 @@ function toReactive(objectRef) {
5065
5068
  //#endregion
5066
5069
  //#region src/composables/useEventListener/index.ts
5067
5070
  /**
5068
- * @module useEventListener
5069
- *
5070
- * @remarks
5071
- * Event listener composable with automatic cleanup on scope disposal.
5072
- *
5073
- * Key features:
5074
- * - Supports Window, Document, and HTMLElement targets
5075
- * - Reactive targets, events, and listeners
5076
- * - Event options support (capture, passive, once)
5077
- * - Automatic removeEventListener on unmount
5078
- * - Multiple overloads for type safety
5079
- *
5080
- * Perfect for safely managing event listeners in Vue components.
5081
- */
5082
- /**
5083
5071
  * Attaches an event listener to a target.
5084
5072
  *
5085
5073
  * @param target The target to attach the event listener to.
@@ -5134,7 +5122,7 @@ function useEventListener(target, event, listener, options) {
5134
5122
  * @see https://0.vuetifyjs.com/composables/system/use-event-listener
5135
5123
  */
5136
5124
  function useWindowEventListener(event, listener, options) {
5137
- return useEventListener(window, event, listener, options);
5125
+ return IN_BROWSER ? useEventListener(window, event, listener, options) : () => {};
5138
5126
  }
5139
5127
  /**
5140
5128
  * Attaches an event listener to the document.
@@ -5148,30 +5136,12 @@ function useWindowEventListener(event, listener, options) {
5148
5136
  * @see https://0.vuetifyjs.com/composables/system/use-event-listener
5149
5137
  */
5150
5138
  function useDocumentEventListener(event, listener, options) {
5151
- return useEventListener(document, event, listener, options);
5139
+ return IN_BROWSER ? useEventListener(document, event, listener, options) : () => {};
5152
5140
  }
5153
5141
 
5154
5142
  //#endregion
5155
5143
  //#region src/composables/useBreakpoints/index.ts
5156
5144
  /**
5157
- * @module useBreakpoints
5158
- *
5159
- * @see https://0.vuetifyjs.com/composables/plugins/use-breakpoints
5160
- *
5161
- * @remarks
5162
- * Responsive breakpoint detection composable with window resize handling.
5163
- *
5164
- * Key features:
5165
- * - Window matchMedia integration
5166
- * - Six built-in breakpoints (xs, sm, md, lg, xl, xxl)
5167
- * - Automatic resize listener with cleanup
5168
- * - SSR-safe (checks IN_BROWSER)
5169
- * - Hydration-aware
5170
- * - Custom breakpoint configuration
5171
- *
5172
- * Perfect for responsive layouts and conditional rendering based on screen size.
5173
- */
5174
- /**
5175
5145
  * Creates default breakpoint configuration.
5176
5146
  *
5177
5147
  * @returns The default breakpoint configuration object.
@@ -5417,25 +5387,217 @@ function useBreakpoints(namespace = "v0:breakpoints") {
5417
5387
  }
5418
5388
 
5419
5389
  //#endregion
5420
- //#region src/composables/useFeatures/index.ts
5390
+ //#region src/composables/useClickOutside/index.ts
5421
5391
  /**
5422
- * @module useFeatures
5392
+ * Detects clicks outside of the specified element(s).
5423
5393
  *
5424
- * @see https://0.vuetifyjs.com/composables/plugins/use-features
5394
+ * Uses two-phase detection (pointerdown → pointerup) to prevent false positives
5395
+ * when users drag from inside to outside an element.
5425
5396
  *
5426
- * @remarks
5427
- * Feature flag system with boolean and token-based features.
5397
+ * @param target Element ref(s) to detect clicks outside of. Accepts a single ref/getter or array of refs/getters.
5398
+ * @param handler Callback invoked when a click outside is detected.
5399
+ * @param options Configuration options.
5400
+ * @returns An object with methods to control the listener.
5428
5401
  *
5429
- * Key features:
5430
- * - Boolean features (true/false activation)
5431
- * - Token features with $variation support
5432
- * - Auto-selection of enabled features
5433
- * - Multi-select support for feature combinations
5434
- * - Perfect for A/B testing, progressive rollout, feature toggles
5435
- *
5436
- * Inheritance chain: useRegistry → createSelection → createGroup → createFeatures
5437
- * Integrates with useTokens for token-based features.
5402
+ * @see https://0.vuetifyjs.com/composables/system/use-click-outside
5403
+ *
5404
+ * @example Native element ref
5405
+ * ```ts
5406
+ * const menuRef = useTemplateRef<HTMLElement>('menu')
5407
+ *
5408
+ * useClickOutside(menuRef, () => { isOpen.value = false })
5409
+ * ```
5410
+ *
5411
+ * @example Component ref (e.g., Atom)
5412
+ * ```ts
5413
+ * const atomRef = useTemplateRef<AtomExpose>('atom')
5414
+ *
5415
+ * // Pass the exposed element TemplateRef via getter
5416
+ * useClickOutside(
5417
+ * () => atomRef.value?.element,
5418
+ * () => { isOpen.value = false }
5419
+ * )
5420
+ * ```
5421
+ *
5422
+ * @example Multiple targets
5423
+ * ```ts
5424
+ * const popoverRef = useTemplateRef<AtomExpose>('popover')
5425
+ * const anchorRef = useTemplateRef<HTMLElement>('anchor')
5426
+ *
5427
+ * useClickOutside(
5428
+ * [() => popoverRef.value?.element, anchorRef],
5429
+ * () => { isOpen.value = false }
5430
+ * )
5431
+ * ```
5432
+ *
5433
+ * @example Ignoring elements (CSS selectors or refs)
5434
+ * ```ts
5435
+ * useClickOutside(
5436
+ * () => navRef.value?.element,
5437
+ * () => { isOpen.value = false },
5438
+ * { ignore: ['[data-app-bar]'] }
5439
+ * )
5440
+ * ```
5438
5441
  */
5442
+ function useClickOutside(target, handler, options = {}) {
5443
+ const { capture = true, touchScrollThreshold = 30, detectIframe = false, ignore = [] } = options;
5444
+ const isPaused = shallowRef(false);
5445
+ const isActive = toRef(() => !isPaused.value);
5446
+ let initialTarget = null;
5447
+ let startPosition = {
5448
+ x: 0,
5449
+ y: 0
5450
+ };
5451
+ let cleanupPointerDown;
5452
+ let cleanupPointerUp;
5453
+ let cleanupBlur;
5454
+ /**
5455
+ * Resolve target(s) to an array of HTMLElements.
5456
+ */
5457
+ function getTargets() {
5458
+ return (Array.isArray(target) ? target : [target]).map((source) => toValue(source)).filter((el) => !/* @__PURE__ */ isNullOrUndefined(el));
5459
+ }
5460
+ /**
5461
+ * Resolve ignore targets to a tuple of [selectors, elements].
5462
+ * Called once per event to avoid repeated toValue() calls in hot path.
5463
+ */
5464
+ function resolveIgnoreTargets() {
5465
+ const ignoreTargets = toValue(ignore);
5466
+ if (ignoreTargets.length === 0) return [[], []];
5467
+ const selectors = [];
5468
+ const elements = [];
5469
+ for (const ignoreTarget of ignoreTargets) if (/* @__PURE__ */ isString(ignoreTarget)) selectors.push(ignoreTarget);
5470
+ else {
5471
+ const ignoreEl = toValue(ignoreTarget);
5472
+ if (ignoreEl) elements.push(ignoreEl);
5473
+ }
5474
+ return [selectors, elements];
5475
+ }
5476
+ /**
5477
+ * Check if an element matches resolved ignore targets.
5478
+ */
5479
+ function isIgnored(el, selectors, elements) {
5480
+ if (!el) return false;
5481
+ for (const selector of selectors) try {
5482
+ if (el.matches(selector) || !/* @__PURE__ */ isNull(el.closest(selector))) return true;
5483
+ } catch {}
5484
+ for (const ignoreEl of elements) if (ignoreEl === el || ignoreEl.contains(el)) return true;
5485
+ return false;
5486
+ }
5487
+ /**
5488
+ * Check if any element in the event path should be ignored.
5489
+ */
5490
+ function shouldIgnore(path) {
5491
+ const [selectors, elements] = resolveIgnoreTargets();
5492
+ if (selectors.length === 0 && elements.length === 0) return false;
5493
+ return path.some((node) => node instanceof Element && isIgnored(node, selectors, elements));
5494
+ }
5495
+ /**
5496
+ * Check if the event target is outside all target elements.
5497
+ */
5498
+ function isOutside(eventTarget) {
5499
+ if (!eventTarget) return false;
5500
+ if (!(eventTarget instanceof Node)) return false;
5501
+ const targets = getTargets();
5502
+ if (targets.length === 0) return false;
5503
+ return targets.every((el) => {
5504
+ return el !== eventTarget && !el.contains(eventTarget);
5505
+ });
5506
+ }
5507
+ /**
5508
+ * Validate that the target is still in the DOM.
5509
+ */
5510
+ function isValidTarget(eventTarget) {
5511
+ if (!(eventTarget instanceof Element)) return false;
5512
+ if (!eventTarget.isConnected) return false;
5513
+ return true;
5514
+ }
5515
+ /**
5516
+ * Handle pointerdown - store initial target and position.
5517
+ */
5518
+ function onPointerDown(event) {
5519
+ if (isPaused.value) return;
5520
+ if (event.defaultPrevented) return;
5521
+ initialTarget = event.composedPath()[0] ?? event.target;
5522
+ startPosition = {
5523
+ x: event.clientX,
5524
+ y: event.clientY
5525
+ };
5526
+ }
5527
+ /**
5528
+ * Handle pointerup - check if it's an outside click.
5529
+ */
5530
+ function onPointerUp(event) {
5531
+ if (isPaused.value) return;
5532
+ if (event.defaultPrevented) return;
5533
+ if (!initialTarget) return;
5534
+ const pointerdownTarget = initialTarget;
5535
+ initialTarget = null;
5536
+ if (!isValidTarget(pointerdownTarget)) return;
5537
+ const path = event.composedPath();
5538
+ const pointerupTarget = path[0] ?? event.target;
5539
+ if (event.pointerType === "touch") {
5540
+ const dx = Math.abs(event.clientX - startPosition.x);
5541
+ const dy = Math.abs(event.clientY - startPosition.y);
5542
+ if (dx >= touchScrollThreshold || dy >= touchScrollThreshold) return;
5543
+ }
5544
+ if (isOutside(pointerdownTarget) && isOutside(pointerupTarget) && !shouldIgnore(path)) handler(event);
5545
+ }
5546
+ /**
5547
+ * Handle window blur - detect focus moving to iframe.
5548
+ */
5549
+ function onBlur(event) {
5550
+ if (isPaused.value) return;
5551
+ if (event.defaultPrevented) return;
5552
+ if (document.activeElement instanceof HTMLIFrameElement) {
5553
+ const iframeIsOutside = getTargets().every((el) => !el.contains(document.activeElement));
5554
+ const [selectors, elements] = resolveIgnoreTargets();
5555
+ if (iframeIsOutside && !isIgnored(document.activeElement, selectors, elements)) handler(event);
5556
+ }
5557
+ }
5558
+ function setup() {
5559
+ cleanupPointerDown = useDocumentEventListener("pointerdown", onPointerDown, capture);
5560
+ cleanupPointerUp = useDocumentEventListener("pointerup", onPointerUp, capture);
5561
+ if (!detectIframe) return;
5562
+ cleanupBlur = useWindowEventListener("blur", onBlur, capture);
5563
+ }
5564
+ function cleanup() {
5565
+ cleanupPointerDown?.();
5566
+ cleanupPointerUp?.();
5567
+ cleanupBlur?.();
5568
+ cleanupPointerDown = void 0;
5569
+ cleanupPointerUp = void 0;
5570
+ cleanupBlur = void 0;
5571
+ }
5572
+ function pause() {
5573
+ if (isPaused.value) return;
5574
+ isPaused.value = true;
5575
+ initialTarget = null;
5576
+ cleanup();
5577
+ }
5578
+ function resume() {
5579
+ if (!isPaused.value) return;
5580
+ isPaused.value = false;
5581
+ setup();
5582
+ }
5583
+ function stop() {
5584
+ isPaused.value = true;
5585
+ initialTarget = null;
5586
+ cleanup();
5587
+ }
5588
+ setup();
5589
+ onScopeDispose(stop, true);
5590
+ return {
5591
+ isActive: shallowReadonly(isActive),
5592
+ isPaused: shallowReadonly(isPaused),
5593
+ pause,
5594
+ resume,
5595
+ stop
5596
+ };
5597
+ }
5598
+
5599
+ //#endregion
5600
+ //#region src/composables/useFeatures/index.ts
5439
5601
  /**
5440
5602
  * Creates a new features instance.
5441
5603
  *
@@ -5596,22 +5758,6 @@ function useFeatures(namespace = "v0:features") {
5596
5758
 
5597
5759
  //#endregion
5598
5760
  //#region src/composables/useFilter/index.ts
5599
- /**
5600
- * @module useFilter
5601
- *
5602
- * @remarks
5603
- * Reactive array filtering composable with multiple filter modes.
5604
- *
5605
- * Key features:
5606
- * - Four filter modes: some, every, union, intersection
5607
- * - Case-insensitive filtering
5608
- * - Custom filter functions
5609
- * - Reactive updates
5610
- * - Context-based DI support
5611
- * - Perfect for search, multi-criteria filtering
5612
- *
5613
- * Filters arrays based on query strings with configurable matching strategies.
5614
- */
5615
5761
  function defaultFilter(query, item, keys, mode = "some") {
5616
5762
  const queries = Array.isArray(query) ? query.map((q) => String(q).toLowerCase()) : [String(query).toLowerCase()];
5617
5763
  function match(value, q) {
@@ -5765,22 +5911,6 @@ function useFilterContext(namespace = "v0:filter") {
5765
5911
  //#endregion
5766
5912
  //#region src/composables/useForm/index.ts
5767
5913
  /**
5768
- * @module useForm
5769
- *
5770
- * @remarks
5771
- * Form validation composable with async rule support and multiple validation modes.
5772
- *
5773
- * Key features:
5774
- * - Sync and async validation rules
5775
- * - Multiple validation modes (submit, change, combined)
5776
- * - Tri-state isValid (null/true/false)
5777
- * - isPristine tracking
5778
- * - Silent validation mode
5779
- * - Form-level validation and reset
5780
- *
5781
- * Each field is registered with validation rules and tracks its own state independently.
5782
- */
5783
- /**
5784
5914
  * Creates a new form instance.
5785
5915
  *
5786
5916
  * @param options The options for the form instance.
@@ -5986,22 +6116,6 @@ function useForm(namespace = "v0:form") {
5986
6116
  //#endregion
5987
6117
  //#region src/composables/useIntersectionObserver/index.ts
5988
6118
  /**
5989
- * @module useIntersectionObserver
5990
- *
5991
- * @remarks
5992
- * IntersectionObserver composable with lifecycle management.
5993
- *
5994
- * Key features:
5995
- * - IntersectionObserver API wrapper
5996
- * - Pause/resume/stop functionality
5997
- * - Automatic cleanup on unmount
5998
- * - SSR-safe (checks SUPPORTS_INTERSECTION_OBSERVER)
5999
- * - Hydration-aware
6000
- * - Immediate callback option
6001
- *
6002
- * Perfect for lazy loading, infinite scroll, and visibility detection.
6003
- */
6004
- /**
6005
6119
  * A composable that uses the Intersection Observer API to detect when an element
6006
6120
  * is visible in the viewport.
6007
6121
  *
@@ -6042,12 +6156,14 @@ function useForm(namespace = "v0:form") {
6042
6156
  */
6043
6157
  function useIntersectionObserver(target, callback, options = {}) {
6044
6158
  const { isHydrated } = useHydration();
6159
+ const targetRef = isRef(target) ? target : shallowRef(target);
6045
6160
  const observer = shallowRef();
6046
6161
  const isPaused = shallowRef(false);
6047
6162
  const isIntersecting = shallowRef(false);
6048
6163
  const isActive = toRef(() => !!observer.value);
6049
6164
  function setup() {
6050
- if (!isHydrated.value || !SUPPORTS_INTERSECTION_OBSERVER || !target.value || isPaused.value) return;
6165
+ if (observer.value === null) return;
6166
+ if (!isHydrated.value || !SUPPORTS_INTERSECTION_OBSERVER || !targetRef.value || isPaused.value) return;
6051
6167
  observer.value = new IntersectionObserver((entries) => {
6052
6168
  const transformedEntries = entries.map((entry) => ({
6053
6169
  boundingClientRect: entry.boundingClientRect,
@@ -6061,26 +6177,29 @@ function useIntersectionObserver(target, callback, options = {}) {
6061
6177
  const latestEntry = transformedEntries.at(-1);
6062
6178
  if (latestEntry) isIntersecting.value = latestEntry.isIntersecting;
6063
6179
  callback(transformedEntries);
6180
+ if (options.once && latestEntry?.isIntersecting) stop();
6064
6181
  }, {
6065
6182
  root: options.root || null,
6066
6183
  rootMargin: options.rootMargin || "0px",
6067
6184
  threshold: options.threshold || 0
6068
6185
  });
6069
- observer.value.observe(target.value);
6186
+ observer.value.observe(targetRef.value);
6070
6187
  if (options.immediate) callback([{
6071
- boundingClientRect: target.value.getBoundingClientRect(),
6188
+ boundingClientRect: targetRef.value.getBoundingClientRect(),
6072
6189
  intersectionRatio: 0,
6073
6190
  intersectionRect: new DOMRect(0, 0, 0, 0),
6074
6191
  isIntersecting: false,
6075
6192
  rootBounds: null,
6076
- target: target.value,
6193
+ target: targetRef.value,
6077
6194
  time: performance.now()
6078
6195
  }]);
6079
6196
  }
6080
- watch([isHydrated, target], () => {
6197
+ watchEffect(() => {
6198
+ const hydrated = isHydrated.value;
6199
+ const target$1 = targetRef.value;
6081
6200
  cleanup();
6082
- setup();
6083
- }, { immediate: true });
6201
+ if (hydrated && target$1) setup();
6202
+ });
6084
6203
  function cleanup() {
6085
6204
  if (observer.value) {
6086
6205
  observer.value.disconnect();
@@ -6098,6 +6217,7 @@ function useIntersectionObserver(target, callback, options = {}) {
6098
6217
  }
6099
6218
  function stop() {
6100
6219
  cleanup();
6220
+ observer.value = null;
6101
6221
  }
6102
6222
  onScopeDispose(stop, true);
6103
6223
  return {
@@ -6169,20 +6289,6 @@ function useElementIntersection(target, options = {}) {
6169
6289
  //#endregion
6170
6290
  //#region src/composables/useKeydown/index.ts
6171
6291
  /**
6172
- * @module useKeydown
6173
- *
6174
- * @remarks
6175
- * Keydown event listener composable with key filtering.
6176
- *
6177
- * Key features:
6178
- * - Key-specific event handling
6179
- * - preventDefault and stopPropagation options
6180
- * - Automatic cleanup on scope disposal
6181
- * - Built on useEventListener for consistent event handling
6182
- *
6183
- * Simplified wrapper around useEventListener for keyboard interactions.
6184
- */
6185
- /**
6186
6292
  * A composable that adds a keydown event listener to the document.
6187
6293
  *
6188
6294
  * @param handlers The key handlers to add.
@@ -6194,6 +6300,10 @@ function useElementIntersection(target, options = {}) {
6194
6300
  * ```ts
6195
6301
  * import { useKeydown } from '@vuetify/v0'
6196
6302
  *
6303
+ * // Single handler
6304
+ * useKeydown({ key: 'Escape', handler: () => console.log('Escape pressed') })
6305
+ *
6306
+ * // Multiple handlers
6197
6307
  * const { isActive, start, stop } = useKeydown([
6198
6308
  * { key: 'Enter', handler: () => console.log('Enter pressed') },
6199
6309
  * { key: 'Escape', handler: () => console.log('Escape pressed'), preventDefault: true },
@@ -6238,22 +6348,6 @@ function useKeydown(handlers) {
6238
6348
  //#endregion
6239
6349
  //#region src/composables/useMutationObserver/index.ts
6240
6350
  /**
6241
- * @module useMutationObserver
6242
- *
6243
- * @remarks
6244
- * MutationObserver composable with lifecycle management.
6245
- *
6246
- * Key features:
6247
- * - MutationObserver API wrapper
6248
- * - Pause/resume/stop functionality
6249
- * - Automatic cleanup on unmount
6250
- * - SSR-safe (checks SUPPORTS_MUTATION_OBSERVER)
6251
- * - Hydration-aware
6252
- * - Configurable observation options (childList, attributes, characterData, etc.)
6253
- *
6254
- * Perfect for detecting DOM changes and responding to mutations.
6255
- */
6256
- /**
6257
6351
  * A composable that uses the Mutation Observer API to detect changes in the DOM.
6258
6352
  *
6259
6353
  * @param target The element to observe.
@@ -6300,7 +6394,7 @@ function useMutationObserver(target, callback, options = {}) {
6300
6394
  const { isHydrated } = useHydration();
6301
6395
  const observer = shallowRef();
6302
6396
  const isPaused = shallowRef(false);
6303
- const isActive = computed(() => !!observer.value);
6397
+ const isActive = toRef(() => !!observer.value);
6304
6398
  const observerOptions = {
6305
6399
  childList: options.childList ?? true,
6306
6400
  attributes: options.attributes ?? false,
@@ -6311,6 +6405,7 @@ function useMutationObserver(target, callback, options = {}) {
6311
6405
  attributeFilter: options.attributeFilter
6312
6406
  };
6313
6407
  function setup() {
6408
+ if (observer.value === null) return;
6314
6409
  if (!isHydrated.value || !SUPPORTS_MUTATION_OBSERVER || !target.value || isPaused.value) return;
6315
6410
  observer.value = new MutationObserver((mutations) => {
6316
6411
  callback(mutations.map((mutation) => ({
@@ -6324,6 +6419,7 @@ function useMutationObserver(target, callback, options = {}) {
6324
6419
  attributeNamespace: mutation.attributeNamespace,
6325
6420
  oldValue: mutation.oldValue
6326
6421
  })));
6422
+ if (options.once) stop();
6327
6423
  });
6328
6424
  observer.value.observe(target.value, observerOptions);
6329
6425
  if (options.immediate) {
@@ -6344,12 +6440,15 @@ function useMutationObserver(target, callback, options = {}) {
6344
6440
  attributeNamespace: null,
6345
6441
  oldValue: null
6346
6442
  }]);
6443
+ if (options.once) stop();
6347
6444
  }
6348
6445
  }
6349
- watch([isHydrated, target], () => {
6446
+ watchEffect(() => {
6447
+ const hydrated = isHydrated.value;
6448
+ const el = target.value;
6350
6449
  cleanup();
6351
- setup();
6352
- }, { immediate: true });
6450
+ if (hydrated && el) setup();
6451
+ });
6353
6452
  function cleanup() {
6354
6453
  if (observer.value) {
6355
6454
  observer.value.disconnect();
@@ -6366,6 +6465,7 @@ function useMutationObserver(target, callback, options = {}) {
6366
6465
  }
6367
6466
  function stop() {
6368
6467
  cleanup();
6468
+ observer.value = null;
6369
6469
  }
6370
6470
  onScopeDispose(stop, true);
6371
6471
  return {
@@ -6398,23 +6498,6 @@ var Vuetify0PermissionAdapter = class extends PermissionAdapter {
6398
6498
  //#endregion
6399
6499
  //#region src/composables/usePermissions/index.ts
6400
6500
  /**
6401
- * @module usePermissions
6402
- *
6403
- * @see https://0.vuetifyjs.com/composables/plugins/use-permissions
6404
- *
6405
- * @remarks
6406
- * Permission management composable with support for RBAC and ABAC patterns.
6407
- *
6408
- * Key features:
6409
- * - Role-Based Access Control (RBAC) support
6410
- * - Attribute-Based Access Control (ABAC) with context
6411
- * - Functional permission conditions
6412
- * - Token-based permission storage
6413
- * - Adapter pattern for custom permission systems
6414
- *
6415
- * Built on useTokens for flexible permission configuration.
6416
- */
6417
- /**
6418
6501
  * Creates a new permissions instance.
6419
6502
  *
6420
6503
  * @param options The options for the permissions instance.
@@ -6561,21 +6644,6 @@ function usePermissions(namespace = "v0:permissions") {
6561
6644
  //#endregion
6562
6645
  //#region src/composables/useQueue/index.ts
6563
6646
  /**
6564
- * @module useQueue
6565
- *
6566
- * @remarks
6567
- * A queue composable for managing time-based collections with:
6568
- * - Automatic timeout-based removal
6569
- * - Pause/resume functionality
6570
- * - FIFO (First In, First Out) ordering
6571
- * - Manual dismissal support
6572
- * - Queue progression management
6573
- *
6574
- * Built on top of useRegistry, the queue automatically manages timeouts for tickets,
6575
- * ensuring only the first ticket in the queue is active at any time. When an ticket
6576
- * expires or is removed, the next ticket in the queue automatically becomes active.
6577
- */
6578
- /**
6579
6647
  * Creates a new queue instance
6580
6648
  *
6581
6649
  * @param options The options for the queue instance
@@ -6777,21 +6845,6 @@ var MemoryAdapter = class {
6777
6845
  //#endregion
6778
6846
  //#region src/composables/useStorage/index.ts
6779
6847
  /**
6780
- * @module useStorage
6781
- *
6782
- * @remarks
6783
- * Reactive storage composable with adapter pattern for localStorage, sessionStorage, or memory.
6784
- *
6785
- * Key features:
6786
- * - Reactive refs that sync with storage
6787
- * - localStorage, sessionStorage, and memory adapters
6788
- * - Custom serialization support
6789
- * - SSR fallback to memory adapter
6790
- * - Automatic cleanup on remove/clear
6791
- *
6792
- * Uses adapter pattern to abstract storage implementation details.
6793
- */
6794
- /**
6795
6848
  * Creates a new storage instance.
6796
6849
  *
6797
6850
  * @param options The options for the storage instance.
@@ -7028,24 +7081,6 @@ var Vuetify0ThemeAdapter = class extends ThemeAdapter {
7028
7081
  //#endregion
7029
7082
  //#region src/composables/useTheme/index.ts
7030
7083
  /**
7031
- * @module useTheme
7032
- *
7033
- * @see https://0.vuetifyjs.com/composables/plugins/use-theme
7034
- *
7035
- * @remarks
7036
- * Theme management composable with token resolution and CSS variable injection.
7037
- *
7038
- * Key features:
7039
- * - Single-selection theme switching (extends createSingle)
7040
- * - Token alias resolution via useTokens
7041
- * - Lazy theme loading (compute colors only when selected)
7042
- * - CSS variable generation via adapter pattern
7043
- * - SSR support with head integration
7044
- * - Theme cycling
7045
- *
7046
- * Integrates with createSingle for selection and useTokens for color resolution.
7047
- */
7048
- /**
7049
7084
  * Creates a new theme instance.
7050
7085
  *
7051
7086
  * @param options The options for the theme instance.
@@ -7266,21 +7301,6 @@ function useTheme(namespace = "v0:theme") {
7266
7301
  //#endregion
7267
7302
  //#region src/composables/useTimeline/index.ts
7268
7303
  /**
7269
- * @module useTimeline
7270
- *
7271
- * @remarks
7272
- * Bounded undo/redo system with overflow management.
7273
- *
7274
- * Key features:
7275
- * - Fixed-size history (default: 10 items)
7276
- * - Undo/redo stack management
7277
- * - Overflow queue (preserves oldest items)
7278
- * - Automatic reindexing after operations
7279
- * - Perfect for command pattern, history tracking
7280
- *
7281
- * Extends useRegistry with temporal navigation capabilities.
7282
- */
7283
- /**
7284
7304
  * Creates a new timeline instance.
7285
7305
  *
7286
7306
  * @param _options The options for the timeline instance.
@@ -7415,24 +7435,6 @@ function useTimeline(namespace = "v0:timeline") {
7415
7435
  //#endregion
7416
7436
  //#region src/composables/useToggleScope/index.ts
7417
7437
  /**
7418
- * @module useToggleScope
7419
- *
7420
- * @remarks
7421
- * Conditionally manages an effect scope based on a reactive boolean condition.
7422
- * When the source becomes true, creates and runs an effect scope. When false, stops the scope.
7423
- * All reactive effects created within the scoped function are automatically cleaned up on deactivation.
7424
- *
7425
- * Key features:
7426
- * - Uses Vue's effectScope for efficient reactive effect lifecycle management
7427
- * - Automatic cleanup when condition becomes false
7428
- * - Supports optional reset callback for scope restart capability
7429
- * - Handles rapid toggling and parent scope disposal safely
7430
- * - SSR-safe (effectScope is part of Vue core)
7431
- *
7432
- * Perfect for conditional side effects, feature flags, and performance optimization
7433
- * by only running reactive effects when needed.
7434
- */
7435
- /**
7436
7438
  * Conditionally manages an effect scope based on a reactive boolean source.
7437
7439
  *
7438
7440
  * @param source A reactive boolean value or getter that controls the scope lifecycle
@@ -7514,24 +7516,6 @@ function useToggleScope(source, fn) {
7514
7516
  //#endregion
7515
7517
  //#region src/composables/useVirtual/index.ts
7516
7518
  /**
7517
- * @module useVirtual
7518
- *
7519
- * @remarks
7520
- * Virtual scrolling composable for efficiently rendering large lists.
7521
- *
7522
- * Key features:
7523
- * - Renders only visible items (viewport + overscan)
7524
- * - Dynamic or fixed item heights
7525
- * - SSR-safe (checks IN_BROWSER)
7526
- * - Bidirectional scrolling (forward/reverse for chat apps)
7527
- * - Scroll anchoring (maintains position across data changes)
7528
- * - Edge detection for infinite scroll
7529
- * - iOS momentum and elastic scrolling
7530
- * - Configurable overscan (extra items rendered for smooth scrolling)
7531
- *
7532
- * Perfect for large data sets, chat apps, and infinite scroll implementations.
7533
- */
7534
- /**
7535
7519
  * Virtual scrolling composable for efficiently rendering large lists
7536
7520
  *
7537
7521
  * @param items Reactive array of items to virtualize
@@ -7772,7 +7756,7 @@ function useVirtual(items, _options = {}) {
7772
7756
  cancelAnimationFrame(raf);
7773
7757
  cancelAnimationFrame(rebuildRaf);
7774
7758
  cancelAnimationFrame(edgeRaf);
7775
- });
7759
+ }, true);
7776
7760
  return {
7777
7761
  element,
7778
7762
  items: computedItems,
@@ -7788,4 +7772,4 @@ function useVirtual(items, _options = {}) {
7788
7772
  }
7789
7773
 
7790
7774
  //#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 };
7775
+ 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 };