@vuetify/v0 0.0.14 → 0.0.16

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.
@@ -0,0 +1,2765 @@
1
+ import { a as isBoolean, c as isNull, d as isObject, g as mergeDeep, h as isUndefined, l as isNullOrUndefined, o as isFunction, p as isString, r as genId, t as clamp, u as isNumber } from "./utilities-DQWf_4V_.mjs";
2
+ import { C as useResizeObserver, J as createTrinity, O as useHydration, P as toArray, Y as createContext, Z as useContext, k as createGroup, p as createSingle, q as createPlugin, u as createTokens, z as useRegistry } from "./useStep-Bqhi_DDs.mjs";
3
+ import { i as SUPPORTS_MUTATION_OBSERVER, n as SUPPORTS_INTERSECTION_OBSERVER, t as IN_BROWSER } from "./globals-Rnihe4SF.mjs";
4
+ import { computed, effectScope, isRef, onScopeDispose, reactive, readonly, ref, shallowReadonly, shallowRef, toRef, toValue, unref, watch } from "vue";
5
+
6
+ //#region src/composables/toReactive/index.ts
7
+ /**
8
+ * @module toReactive
9
+ *
10
+ * @remarks
11
+ * Utility function to convert values and refs into reactive proxies with ref unwrapping.
12
+ *
13
+ * Key features:
14
+ * - Automatic ref unwrapping
15
+ * - Deep reactive proxying
16
+ * - Map and Set support with ref unwrapping
17
+ * - Nested object/array reactivity
18
+ * - Type preservation
19
+ *
20
+ * Perfect for creating reactive versions of plain objects while automatically unwrapping refs.
21
+ */
22
+ /**
23
+ * Converts a `MaybeRef` to a `UnwrapNestedRefs`.
24
+ *
25
+ * @param objectRef The object to convert.
26
+ * @template Z The type of the object.
27
+ * @returns The converted object.
28
+ *
29
+ * @see https://0.vuetifyjs.com/composables/transformers/to-reactive
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * import { ref } from 'vue'
34
+ * import { toReactive } from '@vuetify/v0'
35
+ *
36
+ * const state = ref({ name: 'John', age: 30 })
37
+ * const rstate = toReactive(state)
38
+ *
39
+ * console.log(rstate.name) // John
40
+ * ```
41
+ */
42
+ function toReactive(objectRef) {
43
+ if (!isRef(objectRef)) return reactive(objectRef);
44
+ const target = objectRef.value;
45
+ if (target instanceof Map) {
46
+ const mapProxy = new Proxy(/* @__PURE__ */ new Map(), { get(_, p) {
47
+ const map = objectRef.value;
48
+ if (p === "get") return (key) => unref(map.get(key));
49
+ if (p === "set") return (key, value) => {
50
+ const existingValue = map.get(key);
51
+ if (isRef(existingValue)) existingValue.value = unref(value);
52
+ else map.set(key, value);
53
+ return mapProxy;
54
+ };
55
+ if (p === "has") return (key) => map.has(key);
56
+ if (p === "delete") return (key) => map.delete(key);
57
+ if (p === "clear") return () => map.clear();
58
+ if (p === "size") return map.size;
59
+ if (p === "keys") return () => map.keys();
60
+ if (p === "values") return function* () {
61
+ for (const value of map.values()) yield unref(value);
62
+ };
63
+ if (p === "entries") return function* () {
64
+ for (const [key, value] of map.entries()) yield [key, unref(value)];
65
+ };
66
+ if (p === "forEach") return (callback, thisArg) => {
67
+ for (const [key, value] of map.entries()) callback.call(thisArg, unref(value), key, mapProxy);
68
+ };
69
+ if (p === Symbol.iterator) return function* () {
70
+ for (const [key, value] of map.entries()) yield [key, unref(value)];
71
+ };
72
+ return Reflect.get(map, p);
73
+ } });
74
+ return reactive(mapProxy);
75
+ }
76
+ if (target instanceof Set) {
77
+ const setProxy = new Proxy(/* @__PURE__ */ new Set(), { get(_, p) {
78
+ const set = objectRef.value;
79
+ if (p === "add") return (value) => {
80
+ set.add(value);
81
+ return setProxy;
82
+ };
83
+ if (p === "has") return (value) => set.has(value);
84
+ if (p === "delete") return (value) => set.delete(value);
85
+ if (p === "clear") return () => set.clear();
86
+ if (p === "size") return set.size;
87
+ if (p === "keys" || p === "values") return function* () {
88
+ for (const value of set.values()) yield unref(value);
89
+ };
90
+ if (p === "entries") return function* () {
91
+ for (const value of set.values()) {
92
+ const unreffedValue = unref(value);
93
+ yield [unreffedValue, unreffedValue];
94
+ }
95
+ };
96
+ if (p === "forEach") return (callback, thisArg) => {
97
+ for (const value of set) {
98
+ const unreffedValue = unref(value);
99
+ callback.call(thisArg, unreffedValue, unreffedValue, setProxy);
100
+ }
101
+ };
102
+ if (p === Symbol.iterator) return function* () {
103
+ for (const value of set.values()) yield unref(value);
104
+ };
105
+ return Reflect.get(set, p);
106
+ } });
107
+ return reactive(setProxy);
108
+ }
109
+ return reactive(new Proxy({}, {
110
+ get(_, p, receiver) {
111
+ return unref(Reflect.get(objectRef.value, p, receiver));
112
+ },
113
+ set(_, p, value) {
114
+ const currentTarget = objectRef.value;
115
+ currentTarget[p] = value;
116
+ return true;
117
+ },
118
+ deleteProperty(_, p) {
119
+ return Reflect.deleteProperty(objectRef.value, p);
120
+ },
121
+ has(_, p) {
122
+ return Reflect.has(objectRef.value, p);
123
+ },
124
+ ownKeys() {
125
+ return Object.keys(objectRef.value);
126
+ },
127
+ getOwnPropertyDescriptor(_, p) {
128
+ const desc = Reflect.getOwnPropertyDescriptor(objectRef.value, p);
129
+ if (!desc) return;
130
+ const newDesc = {
131
+ ...desc,
132
+ configurable: true
133
+ };
134
+ if ("value" in newDesc) newDesc.value = unref(newDesc.value);
135
+ return newDesc;
136
+ }
137
+ }));
138
+ }
139
+
140
+ //#endregion
141
+ //#region src/composables/useEventListener/index.ts
142
+ /**
143
+ * @module useEventListener
144
+ *
145
+ * @remarks
146
+ * Event listener composable with automatic cleanup on scope disposal.
147
+ *
148
+ * Key features:
149
+ * - Supports Window, Document, and HTMLElement targets
150
+ * - Reactive targets, events, and listeners
151
+ * - Event options support (capture, passive, once)
152
+ * - Automatic removeEventListener on unmount
153
+ * - Multiple overloads for type safety
154
+ *
155
+ * Perfect for safely managing event listeners in Vue components.
156
+ */
157
+ /**
158
+ * Attaches an event listener to a target.
159
+ *
160
+ * @param target The target to attach the event listener to.
161
+ * @param event The event to listen for.
162
+ * @param listener The event listener.
163
+ * @param options The event listener options.
164
+ * @returns A function to remove the event listener.
165
+ *
166
+ * @see https://0.vuetifyjs.com/composables/system/use-event-listener
167
+ */
168
+ function useEventListener(target, event, listener, options) {
169
+ const cleanups = [];
170
+ function cleanup() {
171
+ for (const fn of cleanups) fn();
172
+ cleanups.length = 0;
173
+ }
174
+ function register(el, event$1, listener$1, options$1) {
175
+ el.addEventListener(event$1, listener$1, options$1);
176
+ return () => el.removeEventListener(event$1, listener$1, options$1);
177
+ }
178
+ const stopWatcher = watch(() => [
179
+ toValue(target),
180
+ toValue(event),
181
+ unref(listener),
182
+ toValue(options)
183
+ ], ([el, events, listeners, opts]) => {
184
+ cleanup();
185
+ if (!el) return;
186
+ const eventList = toArray(events);
187
+ const listenerList = toArray(listeners);
188
+ for (const event$1 of eventList) for (const listenerFn of listenerList) cleanups.push(register(el, event$1, listenerFn, opts));
189
+ }, {
190
+ immediate: true,
191
+ flush: "post"
192
+ });
193
+ function stop() {
194
+ stopWatcher();
195
+ cleanup();
196
+ }
197
+ onScopeDispose(stop, true);
198
+ return stop;
199
+ }
200
+ /**
201
+ * Attaches an event listener to the window.
202
+ *
203
+ * @param event The event to listen for.
204
+ * @param listener The event listener.
205
+ * @param options The event listener options.
206
+ * @template E The event type.
207
+ * @returns A function to remove the event listener.
208
+ *
209
+ * @see https://0.vuetifyjs.com/composables/system/use-event-listener
210
+ */
211
+ function useWindowEventListener(event, listener, options) {
212
+ return useEventListener(window, event, listener, options);
213
+ }
214
+ /**
215
+ * Attaches an event listener to the document.
216
+ *
217
+ * @param event The event to listen for.
218
+ * @param listener The event listener.
219
+ * @param options The event listener options.
220
+ * @template E The event type.
221
+ * @returns A function to remove the event listener.
222
+ *
223
+ * @see https://0.vuetifyjs.com/composables/system/use-event-listener
224
+ */
225
+ function useDocumentEventListener(event, listener, options) {
226
+ return useEventListener(document, event, listener, options);
227
+ }
228
+
229
+ //#endregion
230
+ //#region src/composables/useBreakpoints/index.ts
231
+ /**
232
+ * @module useBreakpoints
233
+ *
234
+ * @see https://0.vuetifyjs.com/composables/plugins/use-breakpoints
235
+ *
236
+ * @remarks
237
+ * Responsive breakpoint detection composable with window resize handling.
238
+ *
239
+ * Key features:
240
+ * - Window matchMedia integration
241
+ * - Six built-in breakpoints (xs, sm, md, lg, xl, xxl)
242
+ * - Automatic resize listener with cleanup
243
+ * - SSR-safe (checks IN_BROWSER)
244
+ * - Hydration-aware
245
+ * - Custom breakpoint configuration
246
+ *
247
+ * Perfect for responsive layouts and conditional rendering based on screen size.
248
+ */
249
+ /**
250
+ * Creates default breakpoint configuration.
251
+ *
252
+ * @returns The default breakpoint configuration object.
253
+ */
254
+ function createDefaultBreakpoints() {
255
+ return {
256
+ mobileBreakpoint: "md",
257
+ breakpoints: {
258
+ xs: 0,
259
+ sm: 600,
260
+ md: 960,
261
+ lg: 1280,
262
+ xl: 1920,
263
+ xxl: 2560
264
+ }
265
+ };
266
+ }
267
+ /**
268
+ * Creates a new breakpoints instance.
269
+ *
270
+ * @param options The options for the breakpoints instance.
271
+ * @template E The type of the breakpoints context.
272
+ * @returns A new breakpoints instance.
273
+ *
274
+ * @see https://0.vuetifyjs.com/composables/plugins/use-breakpoints
275
+ *
276
+ * @example
277
+ * ```ts
278
+ * import { createBreakpoints } from '@vuetify/v0'
279
+ *
280
+ * export const [useBreakpoints, provideBreakpoints] = createBreakpoints({
281
+ * namespace: 'v0:breakpoints',
282
+ * mobileBreakpoint: 'sm',
283
+ * breakpoints: {
284
+ * xs: 0,
285
+ * sm: 680,
286
+ * md: 1024,
287
+ * lg: 1280,
288
+ * xl: 1920,
289
+ * xxl: 2560,
290
+ * },
291
+ * })
292
+ * ```
293
+ */
294
+ function createBreakpoints(_options = {}) {
295
+ const { mobileBreakpoint, breakpoints } = /* @__PURE__ */ mergeDeep(createDefaultBreakpoints(), _options);
296
+ const sorted = Object.entries(breakpoints).toSorted((a, b) => a[1] - b[1]);
297
+ const names = sorted.map(([n]) => n);
298
+ const mb = /* @__PURE__ */ isNumber(mobileBreakpoint) ? mobileBreakpoint : breakpoints[mobileBreakpoint] ?? breakpoints.md;
299
+ const name = shallowRef("xs");
300
+ const width = shallowRef(0);
301
+ const height = shallowRef(0);
302
+ const isMobile = shallowRef(true);
303
+ const xs = shallowRef(true);
304
+ const sm = shallowRef(false);
305
+ const md = shallowRef(false);
306
+ const lg = shallowRef(false);
307
+ const xl = shallowRef(false);
308
+ const xxl = shallowRef(false);
309
+ const smAndUp = shallowRef(false);
310
+ const mdAndUp = shallowRef(false);
311
+ const lgAndUp = shallowRef(false);
312
+ const xlAndUp = shallowRef(false);
313
+ const xxlAndUp = shallowRef(false);
314
+ const smAndDown = shallowRef(true);
315
+ const mdAndDown = shallowRef(true);
316
+ const lgAndDown = shallowRef(true);
317
+ const xlAndDown = shallowRef(true);
318
+ const xxlAndDown = shallowRef(true);
319
+ function update() {
320
+ if (!IN_BROWSER) return;
321
+ width.value = window.innerWidth;
322
+ height.value = window.innerHeight;
323
+ let current = "xs";
324
+ for (let i = sorted.length - 1; i >= 0; i--) if (width.value >= sorted[i][1]) {
325
+ current = sorted[i][0];
326
+ break;
327
+ }
328
+ name.value = current;
329
+ const index = names.indexOf(current);
330
+ isMobile.value = width.value < mb;
331
+ xs.value = index === 0;
332
+ sm.value = index === 1;
333
+ md.value = index === 2;
334
+ lg.value = index === 3;
335
+ xl.value = index === 4;
336
+ xxl.value = index === 5;
337
+ smAndUp.value = index >= 1;
338
+ mdAndUp.value = index >= 2;
339
+ lgAndUp.value = index >= 3;
340
+ xlAndUp.value = index >= 4;
341
+ xxlAndUp.value = index >= 5;
342
+ smAndDown.value = index <= 1;
343
+ mdAndDown.value = index <= 2;
344
+ lgAndDown.value = index <= 3;
345
+ xlAndDown.value = index <= 4;
346
+ xxlAndDown.value = index <= 5;
347
+ }
348
+ return {
349
+ breakpoints,
350
+ name: readonly(name),
351
+ width: readonly(width),
352
+ height: readonly(height),
353
+ isMobile: readonly(isMobile),
354
+ xs: readonly(xs),
355
+ sm: readonly(sm),
356
+ md: readonly(md),
357
+ lg: readonly(lg),
358
+ xl: readonly(xl),
359
+ xxl: readonly(xxl),
360
+ smAndUp: readonly(smAndUp),
361
+ mdAndUp: readonly(mdAndUp),
362
+ lgAndUp: readonly(lgAndUp),
363
+ xlAndUp: readonly(xlAndUp),
364
+ xxlAndUp: readonly(xxlAndUp),
365
+ smAndDown: readonly(smAndDown),
366
+ mdAndDown: readonly(mdAndDown),
367
+ lgAndDown: readonly(lgAndDown),
368
+ xlAndDown: readonly(xlAndDown),
369
+ xxlAndDown: readonly(xxlAndDown),
370
+ update
371
+ };
372
+ }
373
+ /**
374
+ * Creates a new breakpoints context.
375
+ *
376
+ * @param options The options for the breakpoints context.
377
+ * @template E The type of the breakpoints context.
378
+ * @returns A new breakpoints context.
379
+ *
380
+ * @see https://0.vuetifyjs.com/composables/plugins/use-breakpoints
381
+ *
382
+ * @example
383
+ * ```ts
384
+ * import { createBreakpointsContext } from '@vuetify/v0'
385
+ *
386
+ * export const [useBreakpoints, provideBreakpoints, context] = createBreakpointsContext({
387
+ * namespace: 'v0:breakpoints',
388
+ * mobileBreakpoint: 'sm',
389
+ * })
390
+ * ```
391
+ */
392
+ function createBreakpointsContext(_options = {}) {
393
+ const { namespace = "v0:breakpoints",...options } = _options;
394
+ const [useBreakpointsContext, _provideBreakpointsContext] = createContext(namespace);
395
+ const context = createBreakpoints(options);
396
+ function provideBreakpointsContext(_context = context, app) {
397
+ return _provideBreakpointsContext(_context, app);
398
+ }
399
+ return createTrinity(useBreakpointsContext, provideBreakpointsContext, context);
400
+ }
401
+ /**
402
+ * Creates a new breakpoints plugin.
403
+ *
404
+ * @param options The options for the breakpoints plugin.
405
+ * @template E The type of the breakpoints context.
406
+ * @returns A new breakpoints plugin.
407
+ *
408
+ * @see https://0.vuetifyjs.com/composables/plugins/use-breakpoints
409
+ *
410
+ * @example
411
+ * ```ts
412
+ * import { createApp } from 'vue'
413
+ * import { createBreakpointsPlugin } from '@vuetify/v0'
414
+ * import App from './App.vue'
415
+ *
416
+ * const app = createApp(App)
417
+ *
418
+ * app.use(
419
+ * createBreakpointsPlugin({
420
+ * namespace: 'v0:breakpoints',
421
+ * mobileBreakpoint: 'sm',
422
+ * breakpoints: {
423
+ * xs: 0,
424
+ * sm: 680,
425
+ * md: 1024,
426
+ * lg: 1280,
427
+ * xl: 1920,
428
+ * xxl: 2560,
429
+ * },
430
+ * })
431
+ * )
432
+ *
433
+ * app.mount('#app')
434
+ * ```
435
+ */
436
+ function createBreakpointsPlugin(_options = {}) {
437
+ const { namespace = "v0:breakpoints",...options } = _options;
438
+ const [, provideBreakpointsContext, context] = createBreakpointsContext({
439
+ ...options,
440
+ namespace
441
+ });
442
+ return createPlugin({
443
+ namespace,
444
+ provide: (app) => {
445
+ provideBreakpointsContext(context, app);
446
+ },
447
+ setup: (app) => {
448
+ app.mixin({ mounted() {
449
+ if (this.$parent !== null) return;
450
+ const hydration = useHydration();
451
+ function listener() {
452
+ context.update();
453
+ }
454
+ const unwatch = watch(hydration.isHydrated, (hydrated) => {
455
+ if (hydrated) listener();
456
+ }, { immediate: true });
457
+ const cleanup = useWindowEventListener("resize", listener, { passive: true });
458
+ onScopeDispose(() => {
459
+ cleanup();
460
+ unwatch();
461
+ }, true);
462
+ } });
463
+ }
464
+ });
465
+ }
466
+ /**
467
+ * Returns the current breakpoints instance.
468
+ *
469
+ * @param namespace The namespace for the breakpoints context. Defaults to `v0:breakpoints`.
470
+ * @returns The current breakpoints instance.
471
+ *
472
+ * @see https://0.vuetifyjs.com/composables/plugins/use-breakpoints
473
+ *
474
+ * @example
475
+ * ```vue
476
+ * <script setup lang="ts">
477
+ * import { useBreakpoints } from '@vuetify/v0'
478
+ *
479
+ * const { isMobile, mdAndUp } = useBreakpoints()
480
+ * <\/script>
481
+ *
482
+ * <template>
483
+ * <div class="pa-4">
484
+ * <p v-if="isMobile.value">Mobile layout active</p>
485
+ * <p v-else-if="mdAndUp.value">Medium and up layout active</p>
486
+ * </div>
487
+ * </template>
488
+ * ```
489
+ */
490
+ function useBreakpoints(namespace = "v0:breakpoints") {
491
+ return useContext(namespace);
492
+ }
493
+
494
+ //#endregion
495
+ //#region src/composables/useFeatures/index.ts
496
+ /**
497
+ * @module useFeatures
498
+ *
499
+ * @see https://0.vuetifyjs.com/composables/plugins/use-features
500
+ *
501
+ * @remarks
502
+ * Feature flag system with boolean and token-based features.
503
+ *
504
+ * Key features:
505
+ * - Boolean features (true/false activation)
506
+ * - Token features with $variation support
507
+ * - Auto-selection of enabled features
508
+ * - Multi-select support for feature combinations
509
+ * - Perfect for A/B testing, progressive rollout, feature toggles
510
+ *
511
+ * Inheritance chain: useRegistry → createSelection → createGroup → createFeatures
512
+ * Integrates with useTokens for token-based features.
513
+ */
514
+ /**
515
+ * Creates a new features instance.
516
+ *
517
+ * @param options The options for the features instance.
518
+ * @template Z The type of the feature ticket.
519
+ * @template E The type of the feature context.
520
+ * @returns A new features instance.
521
+ *
522
+ * @see https://0.vuetifyjs.com/composables/plugins/use-features
523
+ *
524
+ * @example
525
+ * ```ts
526
+ * import { createFeatures } from '@vuetify/v0'
527
+ *
528
+ * const [useFeatures, provideFeaturesContext, context] = createFeatures({
529
+ * namespace: 'v0:features',
530
+ * features: {
531
+ * 'dark-mode': true,
532
+ * 'theme-color': { $variation: 'blue' },
533
+ * },
534
+ * })
535
+ * ```
536
+ */
537
+ function createFeatures(_options = {}) {
538
+ const { features,...options } = _options;
539
+ const tokens = createTokens(features, { flat: true });
540
+ const registry = createGroup(options);
541
+ for (const [id, { value }] of tokens.entries()) register({
542
+ id,
543
+ value
544
+ });
545
+ function variation(id, fallback = null) {
546
+ const ticket = registry.get(id);
547
+ if (!ticket) return fallback;
548
+ return /* @__PURE__ */ isObject(ticket.value) ? ticket.value.$variation ?? fallback : ticket.value ?? fallback;
549
+ }
550
+ function register(registration = {}) {
551
+ const item = {
552
+ value: false,
553
+ ...registration
554
+ };
555
+ const ticket = registry.register(item);
556
+ if (/* @__PURE__ */ isBoolean(ticket.value) && ticket.value === true || /* @__PURE__ */ isObject(ticket.value) && /* @__PURE__ */ isBoolean(ticket.value.$value) && ticket.value.$value === true) registry.select(ticket.id);
557
+ return ticket;
558
+ }
559
+ return {
560
+ ...registry,
561
+ variation,
562
+ register,
563
+ get size() {
564
+ return registry.size;
565
+ }
566
+ };
567
+ }
568
+ /**
569
+ * Creates a new features context.
570
+ *
571
+ * @param options The options for the features context.
572
+ * @template Z The type of the feature ticket.
573
+ * @template E The type of the feature context.
574
+ * @returns A new features context.
575
+ *
576
+ * @see https://0.vuetifyjs.com/composables/plugins/use-features
577
+ *
578
+ * @example
579
+ * ```ts
580
+ * import { createFeaturesContext } from '@vuetify/v0'
581
+ *
582
+ * export const [useFeatures, provideFeatures, context] = createFeaturesContext({
583
+ * namespace: 'app:features',
584
+ * features: {
585
+ * 'dark-mode': true,
586
+ * 'theme-color': { $variation: 'blue' },
587
+ * },
588
+ * })
589
+ * ```
590
+ */
591
+ function createFeaturesContext(_options = {}) {
592
+ const { namespace = "v0:features",...options } = _options;
593
+ const [useFeaturesContext, _provideFeaturesContext] = createContext(namespace);
594
+ const context = createFeatures(options);
595
+ function provideFeaturesContext(_context = context, app) {
596
+ return _provideFeaturesContext(_context, app);
597
+ }
598
+ return createTrinity(useFeaturesContext, provideFeaturesContext, context);
599
+ }
600
+ /**
601
+ * Creates a new features plugin.
602
+ *
603
+ * @param options The options for the features plugin.
604
+ * @template Z The type of the feature ticket.
605
+ * @template E The type of the feature context.
606
+ * @returns A new features plugin.
607
+ *
608
+ * @see https://0.vuetifyjs.com/composables/plugins/use-features
609
+ *
610
+ * @example
611
+ * ```ts
612
+ * import { createApp } from 'vue'
613
+ * import { createFeaturesPlugin } from '@vuetify/v0'
614
+ * import App from './App.vue'
615
+ *
616
+ * const app = createApp(App)
617
+ *
618
+ * app.use(
619
+ * createFeaturesPlugin({
620
+ * features: {
621
+ * 'dark-mode': true,
622
+ * 'theme-color': { $variation: 'blue' },
623
+ * },
624
+ * })
625
+ * )
626
+ *
627
+ * app.mount('#app')
628
+ * ```
629
+ */
630
+ function createFeaturesPlugin(_options = {}) {
631
+ const { namespace = "v0:features",...options } = _options;
632
+ const [, provideFeaturesContext, context] = createFeaturesContext({
633
+ ...options,
634
+ namespace
635
+ });
636
+ return createPlugin({
637
+ namespace,
638
+ provide: (app) => {
639
+ provideFeaturesContext(context, app);
640
+ }
641
+ });
642
+ }
643
+ /**
644
+ * Returns the current features instance.
645
+ *
646
+ * @param namespace The namespace for the features context. Defaults to `v0:features`.
647
+ * @template Z The type of the feature ticket.
648
+ * @returns The current features instance.
649
+ *
650
+ * @see https://0.vuetifyjs.com/composables/plugins/use-features
651
+ *
652
+ * @example
653
+ * ```vue
654
+ * <script setup lang="ts">
655
+ * import { useFeatures } from '@vuetify/v0'
656
+ *
657
+ * const features = useFeatures()
658
+ * <\/script>
659
+ *
660
+ * <template>
661
+ * <div>
662
+ * <p>Features: {{ features.get('dark-mode') }}</p>
663
+ * <p>Theme Color: {{ features.variation('theme-color') }}</p>
664
+ * </div>
665
+ * </template>
666
+ * ```
667
+ */
668
+ function useFeatures(namespace = "v0:features") {
669
+ return useContext(namespace);
670
+ }
671
+
672
+ //#endregion
673
+ //#region src/composables/useFilter/index.ts
674
+ /**
675
+ * @module useFilter
676
+ *
677
+ * @remarks
678
+ * Reactive array filtering composable with multiple filter modes.
679
+ *
680
+ * Key features:
681
+ * - Four filter modes: some, every, union, intersection
682
+ * - Case-insensitive filtering
683
+ * - Custom filter functions
684
+ * - Reactive updates
685
+ * - Perfect for search, multi-criteria filtering
686
+ *
687
+ * Filters arrays based on query strings with configurable matching strategies.
688
+ */
689
+ function defaultFilter(query, item, keys, mode = "some") {
690
+ const queries = Array.isArray(query) ? query.map((q) => String(q).toLowerCase()) : [String(query).toLowerCase()];
691
+ function match(value, q) {
692
+ return String(value).toLowerCase().includes(q);
693
+ }
694
+ const stringValues = (/* @__PURE__ */ isObject(item) ? keys?.length ? keys.map((k) => item[k]) : Object.values(item) : [item]).map((v) => String(v).toLowerCase());
695
+ if (mode === "some") return stringValues.some((val) => match(val, queries[0]));
696
+ if (mode === "every") return stringValues.every((val) => match(val, queries[0]));
697
+ if (mode === "union") return queries.some((q) => stringValues.some((val) => match(val, q)));
698
+ if (mode === "intersection") return queries.every((q) => stringValues.some((val) => match(val, q)));
699
+ return false;
700
+ }
701
+ /**
702
+ * A reusable function for filtering an array of items.
703
+ *
704
+ * @param query The query to filter by.
705
+ * @param items The items to filter.
706
+ * @param options The filter options.
707
+ * @template Z The type of the items.
708
+ * @returns The filtered items.
709
+ *
710
+ * @see https://0.vuetifyjs.com/composables/utilities/use-filter
711
+ *
712
+ * @example
713
+ * ```ts
714
+ * import { ref } from 'vue'
715
+ * import { useFilter } from '@vuetify/v0'
716
+ *
717
+ * const items = ref([
718
+ * { name: 'John Doe', age: 30 },
719
+ * { name: 'Jane Doe', age: 25 },
720
+ * { name: 'Peter Jones', age: 40 },
721
+ * ])
722
+ *
723
+ * const query = ref('doe')
724
+ * const { items: filtered } = useFilter(query, items, { keys: ['name'] })
725
+ *
726
+ * console.log(filtered.value) // [ { name: 'John Doe', age: 30 }, { name: 'Jane Doe', age: 25 } ]
727
+ * ```
728
+ */
729
+ function useFilter(query, items, options = {}) {
730
+ const { customFilter, keys, mode = "some" } = options;
731
+ const filterFunction = customFilter ?? ((q, i) => defaultFilter(q, i, keys, mode));
732
+ const itemsRef = isRef(items) ? items : toRef(() => items);
733
+ const queryRef = toRef(query);
734
+ return { items: computed(() => {
735
+ const q = toValue(queryRef);
736
+ const queries = (Array.isArray(q) ? q : [q]).filter((q$1) => String(q$1).trim());
737
+ if (queries.length === 0) return itemsRef.value;
738
+ const queryParam = queries.length === 1 ? queries[0] : queries;
739
+ return itemsRef.value.filter((item) => filterFunction(queryParam, item));
740
+ }) };
741
+ }
742
+
743
+ //#endregion
744
+ //#region src/composables/useForm/index.ts
745
+ /**
746
+ * @module useForm
747
+ *
748
+ * @remarks
749
+ * Form validation composable with async rule support and multiple validation modes.
750
+ *
751
+ * Key features:
752
+ * - Sync and async validation rules
753
+ * - Multiple validation modes (submit, change, combined)
754
+ * - Tri-state isValid (null/true/false)
755
+ * - isPristine tracking
756
+ * - Silent validation mode
757
+ * - Form-level validation and reset
758
+ *
759
+ * Each field is registered with validation rules and tracks its own state independently.
760
+ */
761
+ /**
762
+ * Creates a new form instance.
763
+ *
764
+ * @param options The options for the form instance.
765
+ * @template Z The type of the form ticket.
766
+ * @template E The type of the form context.
767
+ * @returns A new form instance.
768
+ *
769
+ * @see https://0.vuetifyjs.com/composables/forms/use-form
770
+ *
771
+ * @example
772
+ * ```ts
773
+ * import { createForm } from '@vuetify/v0'
774
+ *
775
+ * const form = createForm()
776
+ *
777
+ * const username = form.register({
778
+ * id: 'username',
779
+ * value: '',
780
+ * rules: [(v) => v.length > 0 || 'Username is required'],
781
+ * })
782
+ *
783
+ * await form.submit()
784
+ *
785
+ * console.log(username.errors.value) // ['Username is required']
786
+ *
787
+ * form.reset()
788
+ * ```
789
+ */
790
+ function createForm(options) {
791
+ const registry = useRegistry(options);
792
+ const validateOn = options?.validateOn || "submit";
793
+ function parse(value) {
794
+ return value.toLowerCase().split(/\s+/);
795
+ }
796
+ function validatesOn(event) {
797
+ return parse(validateOn).includes(event);
798
+ }
799
+ const isValidating = computed(() => {
800
+ for (const ticket of registry.collection.values()) if (ticket.isValidating.value) return true;
801
+ return false;
802
+ });
803
+ const isValid = computed(() => {
804
+ let hasFields = false;
805
+ for (const ticket of registry.values()) {
806
+ hasFields = true;
807
+ if (ticket.isValid.value === false) return false;
808
+ if (ticket.isValid.value === null) return null;
809
+ }
810
+ return hasFields ? true : null;
811
+ });
812
+ function reset() {
813
+ for (const ticket of registry.values()) ticket.reset();
814
+ }
815
+ async function submit() {
816
+ return validate(registry.keys());
817
+ }
818
+ async function validate(id) {
819
+ const validating = toArray(id);
820
+ if (validatesOn("submit")) return (await Promise.all(validating.map(async (id$1) => await registry.get(id$1)?.validate() ?? true))).every(Boolean);
821
+ return validating.map((id$1) => registry.get(id$1)).filter(Boolean).every((ticket) => ticket.isValid.value === true);
822
+ }
823
+ function register(registration) {
824
+ const model = shallowRef(registration.value == null ? "" : toValue(registration.value));
825
+ const rules = registration.rules || [];
826
+ const errors = shallowRef([]);
827
+ const isValidating$1 = shallowRef(false);
828
+ const initialValue = model.value;
829
+ const triggers = registration.validateOn || validateOn;
830
+ const isPristine = shallowRef(true);
831
+ const isValid$1 = shallowRef(null);
832
+ function _validatesOn(event) {
833
+ return parse(triggers).includes(event);
834
+ }
835
+ function _reset() {
836
+ model.value = initialValue;
837
+ errors.value = [];
838
+ isPristine.value = true;
839
+ isValid$1.value = null;
840
+ }
841
+ async function validate$1(silent = false) {
842
+ if (rules.length === 0) return isValid$1.value = true;
843
+ isValidating$1.value = true;
844
+ try {
845
+ const errorMessages = (await Promise.all(rules.map((rule) => rule(model.value)))).filter((result) => /* @__PURE__ */ isString(result));
846
+ if (!silent) {
847
+ errors.value = errorMessages;
848
+ isValid$1.value = errorMessages.length === 0;
849
+ isPristine.value = toValue(model) === initialValue;
850
+ }
851
+ return errorMessages.length === 0;
852
+ } finally {
853
+ isValidating$1.value = false;
854
+ }
855
+ }
856
+ const item = {
857
+ ...registration,
858
+ rules,
859
+ errors,
860
+ disabled: registration.disabled || false,
861
+ validateOn: triggers,
862
+ isValidating: isValidating$1,
863
+ isPristine,
864
+ isValid: isValid$1,
865
+ reset: _reset,
866
+ validate: validate$1
867
+ };
868
+ const ticket = registry.register(item);
869
+ Object.defineProperty(ticket, "value", {
870
+ get() {
871
+ return model.value;
872
+ },
873
+ set(val) {
874
+ model.value = val;
875
+ isPristine.value = val === initialValue;
876
+ isValid$1.value = null;
877
+ if (_validatesOn("change")) validate$1();
878
+ },
879
+ enumerable: true,
880
+ configurable: true
881
+ });
882
+ return ticket;
883
+ }
884
+ return {
885
+ ...registry,
886
+ register,
887
+ reset,
888
+ submit,
889
+ validateOn,
890
+ isValid,
891
+ isValidating,
892
+ get size() {
893
+ return registry.size;
894
+ }
895
+ };
896
+ }
897
+ /**
898
+ * Creates a new form context.
899
+ *
900
+ * @param options The options for the form context.
901
+ * @template Z The type of the form ticket.
902
+ * @template E The type of the form context.
903
+ * @returns A new form context.
904
+ *
905
+ * @see https://0.vuetifyjs.com/composables/forms/use-form
906
+ *
907
+ * @example
908
+ * ```ts
909
+ * import { createFormContext } from '@vuetify/v0'
910
+ *
911
+ * // With default namespace 'v0:form'
912
+ * export const [useMyForm, provideMyForm, myForm] = createFormContext({ validateOn: 'change' })
913
+ *
914
+ * // Or with custom namespace
915
+ * export const [useMyForm, provideMyForm, myForm] = createFormContext({
916
+ * namespace: 'my-form',
917
+ * validateOn: 'change',
918
+ * })
919
+ *
920
+ * // In a parent component:
921
+ * provideMyForm()
922
+ *
923
+ * // In a child component:
924
+ * const form = useMyForm()
925
+ * form.register({ id: 'field', value: ref(''), rules: [...] })
926
+ * ```
927
+ */
928
+ function createFormContext(_options = {}) {
929
+ const { namespace = "v0:form",...options } = _options;
930
+ const [useFormContext, _provideFormContext] = createContext(namespace);
931
+ const context = createForm(options);
932
+ function provideFormContext(_context = context, app) {
933
+ return _provideFormContext(_context, app);
934
+ }
935
+ return createTrinity(useFormContext, provideFormContext, context);
936
+ }
937
+ /**
938
+ * Returns the current form instance.
939
+ *
940
+ * @param namespace The namespace for the form context. Defaults to `'v0:form'`.
941
+ * @returns The current form instance.
942
+ *
943
+ * @see https://0.vuetifyjs.com/composables/forms/use-form
944
+ *
945
+ * @example
946
+ * ```vue
947
+ * <script setup lang="ts">
948
+ * import { useForm } from '@vuetify/v0'
949
+ *
950
+ * const form = useForm()
951
+ * <\/script>
952
+ *
953
+ * <template>
954
+ * <div>
955
+ * <p>Form is {{ form.isValid.value ? 'valid' : 'invalid' }}</p>
956
+ * </div>
957
+ * </template>
958
+ * ```
959
+ */
960
+ function useForm(namespace = "v0:form") {
961
+ return useContext(namespace);
962
+ }
963
+
964
+ //#endregion
965
+ //#region src/composables/useIntersectionObserver/index.ts
966
+ /**
967
+ * @module useIntersectionObserver
968
+ *
969
+ * @remarks
970
+ * IntersectionObserver composable with lifecycle management.
971
+ *
972
+ * Key features:
973
+ * - IntersectionObserver API wrapper
974
+ * - Pause/resume/stop functionality
975
+ * - Automatic cleanup on unmount
976
+ * - SSR-safe (checks SUPPORTS_INTERSECTION_OBSERVER)
977
+ * - Hydration-aware
978
+ * - Immediate callback option
979
+ *
980
+ * Perfect for lazy loading, infinite scroll, and visibility detection.
981
+ */
982
+ /**
983
+ * A composable that uses the Intersection Observer API to detect when an element
984
+ * is visible in the viewport.
985
+ *
986
+ * @param target The element to observe.
987
+ * @param callback The callback to execute when the element's intersection changes.
988
+ * @param options The options for the Intersection Observer.
989
+ * @returns An object with methods to control the observer.
990
+ *
991
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver
992
+ * @see https://0.vuetifyjs.com/composables/system/use-intersection-observer
993
+ *
994
+ * @example
995
+ * ```ts
996
+ * import { ref } from 'vue'
997
+ * import { useIntersectionObserver } from '@vuetify/v0'
998
+ *
999
+ * const target = ref<HTMLElement>()
1000
+ * const isVisible = ref(false)
1001
+ *
1002
+ * const { isIntersecting, pause, resume } = useIntersectionObserver(
1003
+ * target,
1004
+ * (entries) => {
1005
+ * const entry = entries[0]
1006
+ * if (entry) {
1007
+ * isVisible.value = entry.isIntersecting
1008
+ * console.log('Element is visible:', entry.isIntersecting)
1009
+ * }
1010
+ * },
1011
+ * { threshold: 0.5 }
1012
+ * )
1013
+ *
1014
+ * // Pause observation
1015
+ * pause()
1016
+ *
1017
+ * // Resume observation
1018
+ * resume()
1019
+ * ```
1020
+ */
1021
+ function useIntersectionObserver(target, callback, options = {}) {
1022
+ const { isHydrated } = useHydration();
1023
+ const observer = shallowRef();
1024
+ const isPaused = shallowRef(false);
1025
+ const isIntersecting = shallowRef(false);
1026
+ const isActive = toRef(() => !!observer.value);
1027
+ function setup() {
1028
+ if (!isHydrated.value || !SUPPORTS_INTERSECTION_OBSERVER || !target.value || isPaused.value) return;
1029
+ observer.value = new IntersectionObserver((entries) => {
1030
+ const transformedEntries = entries.map((entry) => ({
1031
+ boundingClientRect: entry.boundingClientRect,
1032
+ intersectionRatio: entry.intersectionRatio,
1033
+ intersectionRect: entry.intersectionRect,
1034
+ isIntersecting: entry.isIntersecting,
1035
+ rootBounds: entry.rootBounds,
1036
+ target: entry.target,
1037
+ time: entry.time
1038
+ }));
1039
+ const latestEntry = transformedEntries.at(-1);
1040
+ if (latestEntry) isIntersecting.value = latestEntry.isIntersecting;
1041
+ callback(transformedEntries);
1042
+ }, {
1043
+ root: options.root || null,
1044
+ rootMargin: options.rootMargin || "0px",
1045
+ threshold: options.threshold || 0
1046
+ });
1047
+ observer.value.observe(target.value);
1048
+ if (options.immediate) callback([{
1049
+ boundingClientRect: target.value.getBoundingClientRect(),
1050
+ intersectionRatio: 0,
1051
+ intersectionRect: new DOMRect(0, 0, 0, 0),
1052
+ isIntersecting: false,
1053
+ rootBounds: null,
1054
+ target: target.value,
1055
+ time: performance.now()
1056
+ }]);
1057
+ }
1058
+ watch([isHydrated, target], () => {
1059
+ cleanup();
1060
+ setup();
1061
+ }, { immediate: true });
1062
+ function cleanup() {
1063
+ if (observer.value) {
1064
+ observer.value.disconnect();
1065
+ observer.value = void 0;
1066
+ }
1067
+ }
1068
+ function pause() {
1069
+ isPaused.value = true;
1070
+ isIntersecting.value = false;
1071
+ observer.value?.disconnect();
1072
+ }
1073
+ function resume() {
1074
+ isPaused.value = false;
1075
+ setup();
1076
+ }
1077
+ function stop() {
1078
+ cleanup();
1079
+ }
1080
+ onScopeDispose(stop, true);
1081
+ return {
1082
+ isActive: shallowReadonly(isActive),
1083
+ isIntersecting: shallowReadonly(isIntersecting),
1084
+ isPaused: shallowReadonly(isPaused),
1085
+ pause,
1086
+ resume,
1087
+ stop
1088
+ };
1089
+ }
1090
+ /**
1091
+ * A convenience composable that uses the Intersection Observer API to detect
1092
+ * when an element is visible in the viewport.
1093
+ *
1094
+ * @param target The element to observe.
1095
+ * @param options The options for the Intersection Observer.
1096
+ * @returns An object with the intersection state.
1097
+ *
1098
+ * @see https://0.vuetifyjs.com/composables/system/use-intersection-observer
1099
+ *
1100
+ * @example
1101
+ * ```ts
1102
+ * import { ref } from 'vue'
1103
+ * import { useElementIntersection } from '@vuetify/v0'
1104
+ *
1105
+ * const myElement = ref<HTMLElement>()
1106
+ * const { isIntersecting, intersectionRatio } = useElementIntersection(myElement, {
1107
+ * threshold: 0.5
1108
+ * })
1109
+ *
1110
+ * // Use in template to conditionally render or animate
1111
+ * watchEffect(() => {
1112
+ * if (isIntersecting.value) {
1113
+ * console.log('Element is visible!', intersectionRatio.value)
1114
+ * }
1115
+ * })
1116
+ * ```
1117
+ */
1118
+ function useElementIntersection(target, options = {}) {
1119
+ const isIntersecting = shallowRef(false);
1120
+ const intersectionRatio = shallowRef(0);
1121
+ const { pause: _pause, resume, stop, isActive, isPaused } = useIntersectionObserver(target, (entries) => {
1122
+ const entry = entries.at(-1);
1123
+ if (entry) {
1124
+ isIntersecting.value = entry.isIntersecting;
1125
+ intersectionRatio.value = entry.intersectionRatio;
1126
+ }
1127
+ }, {
1128
+ immediate: true,
1129
+ ...options
1130
+ });
1131
+ function pause() {
1132
+ isIntersecting.value = false;
1133
+ intersectionRatio.value = 0;
1134
+ _pause();
1135
+ }
1136
+ return {
1137
+ isIntersecting: shallowReadonly(isIntersecting),
1138
+ intersectionRatio: shallowReadonly(intersectionRatio),
1139
+ isActive,
1140
+ isPaused,
1141
+ pause,
1142
+ resume,
1143
+ stop
1144
+ };
1145
+ }
1146
+
1147
+ //#endregion
1148
+ //#region src/composables/useKeydown/index.ts
1149
+ /**
1150
+ * @module useKeydown
1151
+ *
1152
+ * @remarks
1153
+ * Keydown event listener composable with key filtering.
1154
+ *
1155
+ * Key features:
1156
+ * - Key-specific event handling
1157
+ * - preventDefault and stopPropagation options
1158
+ * - Automatic cleanup on scope disposal
1159
+ * - Built on useEventListener for consistent event handling
1160
+ *
1161
+ * Simplified wrapper around useEventListener for keyboard interactions.
1162
+ */
1163
+ /**
1164
+ * A composable that adds a keydown event listener to the document.
1165
+ *
1166
+ * @param handlers The key handlers to add.
1167
+ * @returns An object with methods to start and stop listening.
1168
+ *
1169
+ * @see https://0.vuetifyjs.com/composables/system/use-keydown
1170
+ *
1171
+ * @example
1172
+ * ```ts
1173
+ * import { useKeydown } from '@vuetify/v0'
1174
+ *
1175
+ * const { isActive, start, stop } = useKeydown([
1176
+ * { key: 'Enter', handler: () => console.log('Enter pressed') },
1177
+ * { key: 'Escape', handler: () => console.log('Escape pressed'), preventDefault: true },
1178
+ * ])
1179
+ *
1180
+ * // Listener is automatically active when called in component setup
1181
+ * // Manually control if needed:
1182
+ * stop()
1183
+ * start()
1184
+ * ```
1185
+ */
1186
+ function useKeydown(handlers) {
1187
+ let cleanup = null;
1188
+ const isActive = toRef(() => !!cleanup);
1189
+ function onKeydown(event) {
1190
+ const handlerList = toValue(handlers);
1191
+ const handler = (Array.isArray(handlerList) ? handlerList : [handlerList]).find((h$1) => h$1.key === event.key);
1192
+ if (handler) {
1193
+ if (handler.preventDefault) event.preventDefault();
1194
+ if (handler.stopPropagation) event.stopPropagation();
1195
+ handler.handler(event);
1196
+ }
1197
+ }
1198
+ function start() {
1199
+ if (cleanup) return;
1200
+ cleanup = useDocumentEventListener("keydown", onKeydown);
1201
+ }
1202
+ function stop() {
1203
+ if (!cleanup) return;
1204
+ cleanup();
1205
+ cleanup = null;
1206
+ }
1207
+ cleanup = useDocumentEventListener("keydown", onKeydown);
1208
+ onScopeDispose(stop);
1209
+ return {
1210
+ isActive,
1211
+ start,
1212
+ stop
1213
+ };
1214
+ }
1215
+
1216
+ //#endregion
1217
+ //#region src/composables/useMutationObserver/index.ts
1218
+ /**
1219
+ * @module useMutationObserver
1220
+ *
1221
+ * @remarks
1222
+ * MutationObserver composable with lifecycle management.
1223
+ *
1224
+ * Key features:
1225
+ * - MutationObserver API wrapper
1226
+ * - Pause/resume/stop functionality
1227
+ * - Automatic cleanup on unmount
1228
+ * - SSR-safe (checks SUPPORTS_MUTATION_OBSERVER)
1229
+ * - Hydration-aware
1230
+ * - Configurable observation options (childList, attributes, characterData, etc.)
1231
+ *
1232
+ * Perfect for detecting DOM changes and responding to mutations.
1233
+ */
1234
+ /**
1235
+ * A composable that uses the Mutation Observer API to detect changes in the DOM.
1236
+ *
1237
+ * @param target The element to observe.
1238
+ * @param callback The callback to execute when a mutation is observed.
1239
+ * @param options The options for the Mutation Observer.
1240
+ * @returns An object with methods to control the observer.
1241
+ *
1242
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
1243
+ * @see https://0.vuetifyjs.com/composables/system/use-mutation-observer
1244
+ *
1245
+ * @example
1246
+ * ```ts
1247
+ * import { ref } from 'vue'
1248
+ * import { useMutationObserver } from '@vuetify/v0'
1249
+ *
1250
+ * const container = ref<HTMLElement>()
1251
+ *
1252
+ * const { pause, resume, isPaused } = useMutationObserver(
1253
+ * container,
1254
+ * (mutations) => {
1255
+ * mutations.forEach((mutation) => {
1256
+ * if (mutation.type === 'childList') {
1257
+ * console.log('Children changed:', mutation.addedNodes, mutation.removedNodes)
1258
+ * } else if (mutation.type === 'attributes') {
1259
+ * console.log('Attribute changed:', mutation.attributeName)
1260
+ * }
1261
+ * })
1262
+ * },
1263
+ * {
1264
+ * childList: true,
1265
+ * attributes: true,
1266
+ * subtree: true
1267
+ * }
1268
+ * )
1269
+ *
1270
+ * // Pause observation
1271
+ * pause()
1272
+ *
1273
+ * // Resume observation
1274
+ * resume()
1275
+ * ```
1276
+ */
1277
+ function useMutationObserver(target, callback, options = {}) {
1278
+ const { isHydrated } = useHydration();
1279
+ const observer = shallowRef();
1280
+ const isPaused = shallowRef(false);
1281
+ const isActive = computed(() => !!observer.value);
1282
+ const observerOptions = {
1283
+ childList: options.childList ?? true,
1284
+ attributes: options.attributes ?? false,
1285
+ characterData: options.characterData ?? false,
1286
+ subtree: options.subtree ?? false,
1287
+ attributeOldValue: options.attributeOldValue ?? false,
1288
+ characterDataOldValue: options.characterDataOldValue ?? false,
1289
+ attributeFilter: options.attributeFilter
1290
+ };
1291
+ function setup() {
1292
+ if (!isHydrated.value || !SUPPORTS_MUTATION_OBSERVER || !target.value || isPaused.value) return;
1293
+ observer.value = new MutationObserver((mutations) => {
1294
+ callback(mutations.map((mutation) => ({
1295
+ type: mutation.type,
1296
+ target: mutation.target,
1297
+ addedNodes: mutation.addedNodes,
1298
+ removedNodes: mutation.removedNodes,
1299
+ previousSibling: mutation.previousSibling,
1300
+ nextSibling: mutation.nextSibling,
1301
+ attributeName: mutation.attributeName,
1302
+ attributeNamespace: mutation.attributeNamespace,
1303
+ oldValue: mutation.oldValue
1304
+ })));
1305
+ });
1306
+ observer.value.observe(target.value, observerOptions);
1307
+ if (options.immediate) {
1308
+ const emptyNodeList = {
1309
+ length: 0,
1310
+ item: () => null,
1311
+ forEach: () => {},
1312
+ *[Symbol.iterator]() {}
1313
+ };
1314
+ callback([{
1315
+ type: "childList",
1316
+ target: target.value,
1317
+ addedNodes: emptyNodeList,
1318
+ removedNodes: emptyNodeList,
1319
+ previousSibling: null,
1320
+ nextSibling: null,
1321
+ attributeName: null,
1322
+ attributeNamespace: null,
1323
+ oldValue: null
1324
+ }]);
1325
+ }
1326
+ }
1327
+ watch([isHydrated, target], () => {
1328
+ cleanup();
1329
+ setup();
1330
+ }, { immediate: true });
1331
+ function cleanup() {
1332
+ if (observer.value) {
1333
+ observer.value.disconnect();
1334
+ observer.value = void 0;
1335
+ }
1336
+ }
1337
+ function pause() {
1338
+ isPaused.value = true;
1339
+ observer.value?.disconnect();
1340
+ }
1341
+ function resume() {
1342
+ isPaused.value = false;
1343
+ setup();
1344
+ }
1345
+ function stop() {
1346
+ cleanup();
1347
+ }
1348
+ onScopeDispose(stop, true);
1349
+ return {
1350
+ isActive: shallowReadonly(isActive),
1351
+ isPaused: shallowReadonly(isPaused),
1352
+ pause,
1353
+ resume,
1354
+ stop
1355
+ };
1356
+ }
1357
+
1358
+ //#endregion
1359
+ //#region src/composables/usePermissions/adapters/adapter.ts
1360
+ var PermissionAdapter = class {};
1361
+
1362
+ //#endregion
1363
+ //#region src/composables/usePermissions/adapters/v0.ts
1364
+ var Vuetify0PermissionAdapter = class extends PermissionAdapter {
1365
+ constructor() {
1366
+ super();
1367
+ }
1368
+ can(role, action, subject, context, permissions) {
1369
+ const access = `${role}.${action}.${subject}`;
1370
+ const ticket = permissions.get(access);
1371
+ if (!ticket || !ticket.value) return false;
1372
+ return /* @__PURE__ */ isFunction(ticket.value) ? ticket.value(context) : ticket.value;
1373
+ }
1374
+ };
1375
+
1376
+ //#endregion
1377
+ //#region src/composables/usePermissions/index.ts
1378
+ /**
1379
+ * @module usePermissions
1380
+ *
1381
+ * @see https://0.vuetifyjs.com/composables/plugins/use-permissions
1382
+ *
1383
+ * @remarks
1384
+ * Permission management composable with support for RBAC and ABAC patterns.
1385
+ *
1386
+ * Key features:
1387
+ * - Role-Based Access Control (RBAC) support
1388
+ * - Attribute-Based Access Control (ABAC) with context
1389
+ * - Functional permission conditions
1390
+ * - Token-based permission storage
1391
+ * - Adapter pattern for custom permission systems
1392
+ *
1393
+ * Built on useTokens for flexible permission configuration.
1394
+ */
1395
+ /**
1396
+ * Creates a new permissions instance.
1397
+ *
1398
+ * @param options The options for the permissions instance.
1399
+ * @template Z The type of the permission ticket.
1400
+ * @template E The type of the permission context.
1401
+ * @returns A new permissions instance.
1402
+ *
1403
+ * @see https://0.vuetifyjs.com/composables/plugins/use-permissions
1404
+ *
1405
+ * @example
1406
+ * ```ts
1407
+ * import { createPermissions } from '@vuetify/v0'
1408
+ *
1409
+ * const [usePermissions, providePermissions] = createPermissions({
1410
+ * namespace: 'v0:permissions',
1411
+ * permissions: {
1412
+ * admin: [['read', 'users']],
1413
+ * editor: [['edit', 'posts']],
1414
+ * },
1415
+ * })
1416
+ * ```
1417
+ */
1418
+ function createPermissions(_options = {}) {
1419
+ const { adapter = new Vuetify0PermissionAdapter(), permissions = {},...options } = _options;
1420
+ const record = {};
1421
+ for (const role in permissions) {
1422
+ if (!record[role]) record[role] = {};
1423
+ for (const [actions, subjects, condition = true] of permissions[role]) for (const action of toArray(actions)) for (const subject of toArray(subjects)) {
1424
+ if (!record[role][action]) record[role][action] = {};
1425
+ record[role][action][subject] = condition;
1426
+ }
1427
+ }
1428
+ const tokens = createTokens(record, options);
1429
+ function can(id, action, subject, context = {}) {
1430
+ return adapter.can(id, action, subject, context, tokens);
1431
+ }
1432
+ return {
1433
+ ...tokens,
1434
+ can
1435
+ };
1436
+ }
1437
+ /**
1438
+ * Creates a new permissions context.
1439
+ *
1440
+ * @param options The options for the permissions context.
1441
+ * @template Z The type of the permission ticket.
1442
+ * @template E The type of the permission context.
1443
+ * @returns A new permissions context.
1444
+ *
1445
+ * @see https://0.vuetifyjs.com/composables/plugins/use-permissions
1446
+ *
1447
+ * @example
1448
+ * ```ts
1449
+ * import { createPermissionsContext } from '@vuetify/v0'
1450
+ *
1451
+ * export const [usePermissions, providePermissions, context] = createPermissionsContext({
1452
+ * namespace: 'app:permissions',
1453
+ * permissions: {
1454
+ * admin: [['read', 'users'], ['edit', 'users']],
1455
+ * editor: [['edit', 'posts']],
1456
+ * },
1457
+ * })
1458
+ * ```
1459
+ */
1460
+ function createPermissionsContext(_options = {}) {
1461
+ const { namespace = "v0:permissions",...options } = _options;
1462
+ const [usePermissionsContext, _providePermissionsContext] = createContext(namespace);
1463
+ const context = createPermissions(options);
1464
+ function providePermissionsContext(_context = context, app) {
1465
+ return _providePermissionsContext(_context, app);
1466
+ }
1467
+ return createTrinity(usePermissionsContext, providePermissionsContext, context);
1468
+ }
1469
+ /**
1470
+ * Creates a new permissions plugin.
1471
+ *
1472
+ * @param options The options for the permissions plugin.
1473
+ * @template Z The type of the permission ticket.
1474
+ * @template E The type of the permission context.
1475
+ * @returns A new permissions plugin.
1476
+ *
1477
+ * @see https://0.vuetifyjs.com/composables/plugins/use-permissions
1478
+ *
1479
+ * @example
1480
+ * ```ts
1481
+ * import { createApp } from 'vue'
1482
+ * import { createPermissionsPlugin } from '@vuetify/v0'
1483
+ * import App from './App.vue'
1484
+ *
1485
+ * const app = createApp(App)
1486
+ *
1487
+ * app.use(
1488
+ * createPermissionsPlugin({
1489
+ * permissions: {
1490
+ * admin: [['read', 'users']],
1491
+ * editor: [['edit', 'posts']],
1492
+ * },
1493
+ * })
1494
+ * )
1495
+ *
1496
+ * app.mount('#app')
1497
+ * ```
1498
+ */
1499
+ function createPermissionsPlugin(_options = {}) {
1500
+ const { namespace = "v0:permissions",...options } = _options;
1501
+ const [, providePermissionContext, context] = createPermissionsContext({
1502
+ ...options,
1503
+ namespace
1504
+ });
1505
+ return createPlugin({
1506
+ namespace,
1507
+ provide: (app) => {
1508
+ providePermissionContext(context, app);
1509
+ }
1510
+ });
1511
+ }
1512
+ /**
1513
+ * Returns the current permissions instance.
1514
+ *
1515
+ * @template Z The type of the permission ticket.
1516
+ * @returns The current permissions instance.
1517
+ *
1518
+ * @see https://0.vuetifyjs.com/composables/plugins/use-permissions
1519
+ *
1520
+ * @example
1521
+ * ```vue
1522
+ * <script setup lang="ts">
1523
+ * import { usePermissions } from '@vuetify/v0'
1524
+ *
1525
+ * const { can } = usePermissions()
1526
+ * <\/script>
1527
+ *
1528
+ * <template>
1529
+ * <div>
1530
+ * <p v-if="can('admin', 'read', 'users')">Admin access</p>
1531
+ * </div>
1532
+ * </template>
1533
+ * ```
1534
+ */
1535
+ function usePermissions(namespace = "v0:permissions") {
1536
+ return useContext(namespace);
1537
+ }
1538
+
1539
+ //#endregion
1540
+ //#region src/composables/useQueue/index.ts
1541
+ /**
1542
+ * @module useQueue
1543
+ *
1544
+ * @remarks
1545
+ * A queue composable for managing time-based collections with:
1546
+ * - Automatic timeout-based removal
1547
+ * - Pause/resume functionality
1548
+ * - FIFO (First In, First Out) ordering
1549
+ * - Manual dismissal support
1550
+ * - Queue progression management
1551
+ *
1552
+ * Built on top of useRegistry, the queue automatically manages timeouts for tickets,
1553
+ * ensuring only the first ticket in the queue is active at any time. When an ticket
1554
+ * expires or is removed, the next ticket in the queue automatically becomes active.
1555
+ */
1556
+ /**
1557
+ * Creates a new queue instance
1558
+ *
1559
+ * @param options The options for the queue instance
1560
+ * @template Z The type of queue ticket that extends QueueTicket. Use this to add custom properties to tickets.
1561
+ * @template E The type of queue context that extends QueueContext<Z>. Use this when extending the queue with additional methods.
1562
+ * @returns A new queue instance
1563
+ *
1564
+ * @see https://0.vuetifyjs.com/composables/registration/use-queue
1565
+ *
1566
+ * @example
1567
+ * ```ts
1568
+ * import { useQueue } from '@vuetify/v0'
1569
+ *
1570
+ * const queue = useQueue()
1571
+ *
1572
+ * // Register an ticket with default timeout (3000ms)
1573
+ * const ticket1 = queue.register({ value: 'Ticket 1' })
1574
+ *
1575
+ * // Register an ticket with custom timeout
1576
+ * const ticket2 = queue.register({ value: 'Ticket 2', timeout: 5000 })
1577
+ *
1578
+ * // Register a persistent ticket that must be manually dismissed
1579
+ * const ticket3 = queue.register({ value: 'Ticket 3', timeout: -1 })
1580
+ *
1581
+ * // Dismiss an ticket using the convenience method
1582
+ * ticket3.dismiss()
1583
+ *
1584
+ * console.log(queue.size) // 2
1585
+ * ```
1586
+ */
1587
+ function createQueue(_options = {}) {
1588
+ const { timeout: _timeout = 3e3,...options } = _options;
1589
+ const registry = useRegistry({
1590
+ ...options,
1591
+ events: true
1592
+ });
1593
+ const timeouts = /* @__PURE__ */ new Map();
1594
+ function startTimeout(ticket) {
1595
+ if (/* @__PURE__ */ isUndefined(ticket.timeout) || ticket.timeout < 0 || ticket.isPaused) return;
1596
+ const timeout = setTimeout(() => {
1597
+ timeouts.delete(ticket.id);
1598
+ registry.unregister(ticket.id);
1599
+ resume();
1600
+ }, ticket.timeout);
1601
+ timeouts.set(ticket.id, timeout);
1602
+ }
1603
+ function clearTimeout(id) {
1604
+ const timeout = timeouts.get(id);
1605
+ if (timeout) {
1606
+ globalThis.clearTimeout(timeout);
1607
+ timeouts.delete(id);
1608
+ }
1609
+ }
1610
+ function register(registration = {}) {
1611
+ const id = registration.id ?? /* @__PURE__ */ genId();
1612
+ const timeout = Object.prototype.hasOwnProperty.call(registration, "timeout") ? registration.timeout : _timeout;
1613
+ const ticket = {
1614
+ ...registration,
1615
+ id,
1616
+ timeout,
1617
+ isPaused: registry.size > 0,
1618
+ dismiss: () => unregister(id)
1619
+ };
1620
+ const registered = registry.register(ticket);
1621
+ startTimeout(registered);
1622
+ return registered;
1623
+ }
1624
+ function unregister(id) {
1625
+ const ticket = /* @__PURE__ */ isUndefined(id) ? registry.seek("first") : registry.get(id);
1626
+ if (!ticket) return void 0;
1627
+ const wasFirst = ticket.index === 0;
1628
+ clearTimeout(ticket.id);
1629
+ registry.unregister(ticket.id);
1630
+ if (wasFirst) resume();
1631
+ return ticket;
1632
+ }
1633
+ function offboard(ids) {
1634
+ let hadFirst = false;
1635
+ for (const id of ids) {
1636
+ const ticket = registry.get(id);
1637
+ if (!ticket) continue;
1638
+ if (ticket.index === 0) hadFirst = true;
1639
+ clearTimeout(ticket.id);
1640
+ }
1641
+ registry.offboard(ids);
1642
+ if (hadFirst) resume();
1643
+ }
1644
+ function pause() {
1645
+ const ticket = registry.seek("first");
1646
+ if (!ticket || ticket.isPaused) return void 0;
1647
+ clearTimeout(ticket.id);
1648
+ return registry.upsert(ticket.id, { isPaused: true });
1649
+ }
1650
+ function resume() {
1651
+ const ticket = registry.seek("first");
1652
+ if (!ticket || ticket.index !== 0 || !ticket.isPaused) return void 0;
1653
+ const updated = registry.upsert(ticket.id, { isPaused: false });
1654
+ startTimeout(updated);
1655
+ return updated;
1656
+ }
1657
+ function clear() {
1658
+ for (const id of timeouts.keys()) clearTimeout(id);
1659
+ registry.clear();
1660
+ }
1661
+ function dispose() {
1662
+ clear();
1663
+ registry.dispose();
1664
+ }
1665
+ onScopeDispose(dispose, true);
1666
+ return {
1667
+ ...registry,
1668
+ register,
1669
+ unregister,
1670
+ offboard,
1671
+ pause,
1672
+ resume,
1673
+ clear,
1674
+ dispose,
1675
+ get size() {
1676
+ return registry.size;
1677
+ }
1678
+ };
1679
+ }
1680
+ /**
1681
+ * Creates a new queue context.
1682
+ *
1683
+ * @param namespace The namespace for the queue context.
1684
+ * @param options The options for the queue context.
1685
+ * @template Z The type of the queue ticket.
1686
+ * @template E The type of the queue context.
1687
+ * @returns A new queue context.
1688
+ *
1689
+ * @see https://0.vuetifyjs.com/composables/registration/use-queue
1690
+ *
1691
+ * @example
1692
+ * ```ts
1693
+ * import { createQueueContext } from '@vuetify/v0'
1694
+ *
1695
+ * export const [useQueue, provideQueue] = createQueueContext('v0:queue', {
1696
+ * timeout: 5000,
1697
+ * })
1698
+ * ```
1699
+ */
1700
+ function createQueueContext(_options) {
1701
+ const { namespace,...options } = _options;
1702
+ const [useQueueContext, _provideQueueContext] = createContext(namespace);
1703
+ const context = createQueue(options);
1704
+ function provideQueueContext(_context = context, app) {
1705
+ return _provideQueueContext(_context, app);
1706
+ }
1707
+ return createTrinity(useQueueContext, provideQueueContext, context);
1708
+ }
1709
+ /**
1710
+ * Returns the current queue instance.
1711
+ *
1712
+ * @param namespace The namespace for the queue context. Defaults to `'v0:queue'`.
1713
+ * @returns The current queue instance.
1714
+ *
1715
+ * @see https://0.vuetifyjs.com/composables/registration/use-queue
1716
+ *
1717
+ * @example
1718
+ * ```vue
1719
+ * <script setup lang="ts">
1720
+ * import { useQueue } from '@vuetify/v0'
1721
+ *
1722
+ * const queue = useQueue()
1723
+ * <\/script>
1724
+ * ```
1725
+ */
1726
+ function useQueue(namespace = "v0:queue") {
1727
+ return useContext(namespace);
1728
+ }
1729
+
1730
+ //#endregion
1731
+ //#region src/composables/useStorage/adapters/memory.ts
1732
+ /**
1733
+ * In-memory storage adapter that implements the StorageAdapter interface.
1734
+ * This adapter provides temporary storage that persists only for the current
1735
+ * session and is useful for testing or when persistent storage is not available.
1736
+ */
1737
+ var MemoryAdapter = class {
1738
+ store = /* @__PURE__ */ new Map();
1739
+ get length() {
1740
+ return this.store.size;
1741
+ }
1742
+ getItem(key) {
1743
+ return this.store.get(key) ?? null;
1744
+ }
1745
+ setItem(key, value) {
1746
+ this.store.set(key, value);
1747
+ }
1748
+ removeItem(key) {
1749
+ this.store.delete(key);
1750
+ }
1751
+ key(index) {
1752
+ return String(Array.from(this.store.keys())[index] ?? "");
1753
+ }
1754
+ };
1755
+
1756
+ //#endregion
1757
+ //#region src/composables/useStorage/index.ts
1758
+ /**
1759
+ * @module useStorage
1760
+ *
1761
+ * @remarks
1762
+ * Reactive storage composable with adapter pattern for localStorage, sessionStorage, or memory.
1763
+ *
1764
+ * Key features:
1765
+ * - Reactive refs that sync with storage
1766
+ * - localStorage, sessionStorage, and memory adapters
1767
+ * - Custom serialization support
1768
+ * - SSR fallback to memory adapter
1769
+ * - Automatic cleanup on remove/clear
1770
+ *
1771
+ * Uses adapter pattern to abstract storage implementation details.
1772
+ */
1773
+ /**
1774
+ * Creates a new storage instance.
1775
+ *
1776
+ * @param options The options for the storage instance.
1777
+ * @template E The type of the storage context.
1778
+ * @returns A new storage instance.
1779
+ *
1780
+ * @see https://0.vuetifyjs.com/composables/plugins/use-storage
1781
+ *
1782
+ * @example
1783
+ * ```ts
1784
+ * import { createStorage } from '@vuetify/v0'
1785
+ *
1786
+ * const storage = createStorage()
1787
+ *
1788
+ * storage.set('username', 'MyUsername')
1789
+ *
1790
+ * const username = storage.get('username')
1791
+ *
1792
+ * console.log(username.value) // MyUsername
1793
+ *
1794
+ * storage.clear()
1795
+ * ```
1796
+ */
1797
+ function createStorage(options = {}) {
1798
+ const { adapter = IN_BROWSER ? window.localStorage : new MemoryAdapter(), prefix = "v0:", serializer = {
1799
+ read: JSON.parse,
1800
+ write: JSON.stringify
1801
+ } } = options;
1802
+ const cache = /* @__PURE__ */ new Map();
1803
+ const watchers = /* @__PURE__ */ new Map();
1804
+ function has(key) {
1805
+ const prefixedKey = `${prefix}${key}`;
1806
+ return cache.has(prefixedKey);
1807
+ }
1808
+ function get(key, defaultValue) {
1809
+ const prefixedKey = `${prefix}${key}`;
1810
+ if (cache.has(prefixedKey)) return cache.get(prefixedKey);
1811
+ const storedValue = adapter?.getItem(prefixedKey);
1812
+ let initialValue = defaultValue;
1813
+ if (storedValue) try {
1814
+ initialValue = serializer.read(storedValue);
1815
+ } catch (error) {
1816
+ console.error(`[v0:storage] Failed to parse stored value for key "${prefixedKey}":`, error);
1817
+ }
1818
+ const valueRef = ref(initialValue);
1819
+ const stop = watch(valueRef, (newValue) => {
1820
+ if (/* @__PURE__ */ isNullOrUndefined(newValue)) adapter?.removeItem(prefixedKey);
1821
+ else adapter?.setItem(prefixedKey, serializer.write(newValue));
1822
+ }, { deep: true });
1823
+ watchers.set(prefixedKey, stop);
1824
+ cache.set(prefixedKey, valueRef);
1825
+ return valueRef;
1826
+ }
1827
+ function set(key, value) {
1828
+ const valueRef = get(key);
1829
+ valueRef.value = value;
1830
+ }
1831
+ function remove(key) {
1832
+ const prefixedKey = `${prefix}${key}`;
1833
+ const stop = watchers.get(prefixedKey);
1834
+ if (!stop) return;
1835
+ stop();
1836
+ watchers.delete(prefixedKey);
1837
+ adapter?.removeItem(prefixedKey);
1838
+ cache.delete(prefixedKey);
1839
+ }
1840
+ function clear() {
1841
+ if (watchers.size > 0) {
1842
+ for (const stop of watchers.values()) stop();
1843
+ watchers.clear();
1844
+ }
1845
+ if (cache.size > 0) {
1846
+ for (const key of cache.keys()) adapter?.removeItem(key);
1847
+ cache.clear();
1848
+ }
1849
+ }
1850
+ return {
1851
+ has,
1852
+ get,
1853
+ set,
1854
+ remove,
1855
+ clear
1856
+ };
1857
+ }
1858
+ function createStorageContext(_options = {}) {
1859
+ const { namespace = "v0:storage",...options } = _options;
1860
+ const [useStorageContext, _provideStorageContext] = createContext(namespace);
1861
+ const context = createStorage(options);
1862
+ function provideStorageContext(_context = context, app) {
1863
+ return _provideStorageContext(_context, app);
1864
+ }
1865
+ return createTrinity(useStorageContext, provideStorageContext, context);
1866
+ }
1867
+ /**
1868
+ * Creates a new storage plugin.
1869
+ *
1870
+ * @param options The options for the storage plugin.
1871
+ * @returns A new storage plugin.
1872
+ *
1873
+ * @see https://0.vuetifyjs.com/composables/plugins/use-storage
1874
+ *
1875
+ * @example
1876
+ * ```ts
1877
+ * import { createApp } from 'vue'
1878
+ * import { createStoragePlugin } from '@vuetify/v0'
1879
+ * import App from './App.vue'
1880
+ *
1881
+ * const app = createApp(App)
1882
+ *
1883
+ * app.use(createStoragePlugin())
1884
+ *
1885
+ * app.mount('#app')
1886
+ * ```
1887
+ */
1888
+ function createStoragePlugin(_options = {}) {
1889
+ const { namespace = "v0:storage",...options } = _options;
1890
+ const [, provideStorageContext, context] = createStorageContext({
1891
+ ...options,
1892
+ namespace
1893
+ });
1894
+ return createPlugin({
1895
+ namespace,
1896
+ provide: (app) => {
1897
+ provideStorageContext(context, app);
1898
+ }
1899
+ });
1900
+ }
1901
+ /**
1902
+ * Returns the current storage instance.
1903
+ *
1904
+ * @param namespace The namespace for the storage context. Defaults to `'v0:storage'`.
1905
+ * @returns The current storage instance.
1906
+ *
1907
+ * @see https://0.vuetifyjs.com/composables/plugins/use-storage
1908
+ *
1909
+ * @example
1910
+ * ```vue
1911
+ * <script setup lang="ts">
1912
+ * import { useStorage } from '@vuetify/v0'
1913
+ *
1914
+ * const storage = useStorage()
1915
+ * const username = storage.get('username', 'Guest')
1916
+ * <\/script>
1917
+ *
1918
+ * <template>
1919
+ * <div>
1920
+ * <p>Username: {{ username }}</p>
1921
+ * </div>
1922
+ * </template>
1923
+ * ```
1924
+ */
1925
+ function useStorage(namespace = "v0:storage") {
1926
+ return useContext(namespace);
1927
+ }
1928
+
1929
+ //#endregion
1930
+ //#region src/composables/useTheme/adapters/adapter.ts
1931
+ var ThemeAdapter = class {
1932
+ stylesheetId = "v0-theme-stylesheet";
1933
+ prefix;
1934
+ constructor(prefix) {
1935
+ this.prefix = prefix;
1936
+ }
1937
+ generate(colors, isDark) {
1938
+ let css = "";
1939
+ for (const theme in colors) {
1940
+ const themeColors = colors[theme];
1941
+ if (!themeColors) continue;
1942
+ const vars = Object.entries(themeColors).map(([key, val]) => ` --${this.prefix}-${key}: ${val};`).join("\n");
1943
+ css += `[data-theme="${theme}"] {\n${vars}\n}\n`;
1944
+ }
1945
+ if (!/* @__PURE__ */ isUndefined(isDark)) css += `:root {\n color-scheme: ${isDark ? "dark" : "light"};\n}\n`;
1946
+ return css;
1947
+ }
1948
+ };
1949
+
1950
+ //#endregion
1951
+ //#region src/composables/useTheme/adapters/v0.ts
1952
+ /**
1953
+ * Theme adapter implementation for Vuetify v0 design system.
1954
+ * This adapter generates CSS custom properties and injects them into the DOM
1955
+ * as a stylesheet, allowing themes to be applied globally.
1956
+ */
1957
+ var Vuetify0ThemeAdapter = class extends ThemeAdapter {
1958
+ cspNonce;
1959
+ constructor(options = {}) {
1960
+ super(options.prefix ?? "v0");
1961
+ this.cspNonce = options.cspNonce;
1962
+ this.stylesheetId = options.stylesheetId ?? this.stylesheetId;
1963
+ }
1964
+ setup(app, context, target) {
1965
+ if (IN_BROWSER) {
1966
+ onScopeDispose(watch([context.colors, context.isDark], ([colors, isDark]) => {
1967
+ this.update(colors, isDark);
1968
+ }, { immediate: true }), true);
1969
+ if (/* @__PURE__ */ isNull(target)) return;
1970
+ const targetEl = target instanceof HTMLElement ? target : /* @__PURE__ */ isString(target) ? document.querySelector(target) : app._container || document.querySelector("#app") || document.body;
1971
+ if (!targetEl) return;
1972
+ onScopeDispose(watch(context.selectedId, (id) => {
1973
+ if (!id) return;
1974
+ targetEl.dataset.theme = String(id);
1975
+ }, { immediate: true }), true);
1976
+ } else {
1977
+ const head = app._context?.provides?.usehead ?? app._context?.provides?.head;
1978
+ if (head?.push) {
1979
+ const id = context.selectedId.value;
1980
+ head.push({
1981
+ htmlAttrs: { "data-theme": id ? String(id) : "" },
1982
+ style: [{
1983
+ innerHTML: this.generate(context.colors.value, context.isDark.value),
1984
+ id: this.stylesheetId
1985
+ }]
1986
+ });
1987
+ }
1988
+ }
1989
+ }
1990
+ update(colors, isDark) {
1991
+ if (!IN_BROWSER) return;
1992
+ this.upsert(this.generate(colors, isDark));
1993
+ }
1994
+ upsert(styles) {
1995
+ if (!IN_BROWSER) return;
1996
+ let styleEl = document.querySelector(`#${this.stylesheetId}`);
1997
+ if (!styleEl) {
1998
+ styleEl = document.createElement("style");
1999
+ styleEl.id = this.stylesheetId.startsWith("#") ? this.stylesheetId.slice(1) : this.stylesheetId;
2000
+ if (this.cspNonce) styleEl.setAttribute("nonce", this.cspNonce);
2001
+ document.head.append(styleEl);
2002
+ }
2003
+ styleEl.textContent = styles;
2004
+ }
2005
+ };
2006
+
2007
+ //#endregion
2008
+ //#region src/composables/useTheme/index.ts
2009
+ /**
2010
+ * @module useTheme
2011
+ *
2012
+ * @see https://0.vuetifyjs.com/composables/plugins/use-theme
2013
+ *
2014
+ * @remarks
2015
+ * Theme management composable with token resolution and CSS variable injection.
2016
+ *
2017
+ * Key features:
2018
+ * - Single-selection theme switching (extends createSingle)
2019
+ * - Token alias resolution via useTokens
2020
+ * - Lazy theme loading (compute colors only when selected)
2021
+ * - CSS variable generation via adapter pattern
2022
+ * - SSR support with head integration
2023
+ * - Theme cycling
2024
+ *
2025
+ * Integrates with createSingle for selection and useTokens for color resolution.
2026
+ */
2027
+ /**
2028
+ * Creates a new theme instance.
2029
+ *
2030
+ * @param options The options for the theme instance.
2031
+ * @template Z The type of the theme ticket.
2032
+ * @template E The type of the theme context.
2033
+ * @returns A new theme instance.
2034
+ *
2035
+ * @see https://0.vuetifyjs.com/composables/plugins/use-theme
2036
+ *
2037
+ * @example
2038
+ * ```ts
2039
+ * import { createTheme } from '@vuetify/v0'
2040
+ *
2041
+ * export const [useTheme, provideTheme] = createTheme({
2042
+ * namespace: 'v0:theme',
2043
+ * default: 'light',
2044
+ * themes: {
2045
+ * light: {
2046
+ * dark: false,
2047
+ * colors: {
2048
+ * primary: '#3b82f6',
2049
+ * },
2050
+ * },
2051
+ * dark: {
2052
+ * dark: true,
2053
+ * colors: {
2054
+ * primary: '#675496',
2055
+ * },
2056
+ * },
2057
+ * },
2058
+ * })
2059
+ * ```
2060
+ */
2061
+ function createTheme(_options = {}) {
2062
+ const { themes = {}, palette = {},...options } = _options;
2063
+ const tokens = createTokens({
2064
+ palette,
2065
+ ...themes
2066
+ }, { flat: true });
2067
+ const registry = createSingle(options);
2068
+ for (const id in themes) {
2069
+ const { colors: value,...theme } = themes[id];
2070
+ register({
2071
+ id,
2072
+ value,
2073
+ ...theme
2074
+ });
2075
+ if (id === options.default && !registry.selectedId.value) registry.select(id);
2076
+ }
2077
+ const names = computed(() => registry.keys());
2078
+ const colors = computed(() => {
2079
+ const resolved = {};
2080
+ for (const theme of registry.values()) {
2081
+ if (theme.lazy && theme.id !== registry.selectedId.value) continue;
2082
+ resolved[theme.id] = resolve(theme.value);
2083
+ }
2084
+ return resolved;
2085
+ });
2086
+ const isDark = toRef(() => registry.selectedItem.value?.dark ?? false);
2087
+ function cycle(themes$1 = names.value) {
2088
+ const current = themes$1.indexOf(registry.selectedId.value ?? "");
2089
+ const next = current === -1 ? 0 : (current + 1) % themes$1.length;
2090
+ registry.select(themes$1[next]);
2091
+ }
2092
+ function resolve(colors$1) {
2093
+ const resolved = {};
2094
+ for (const [key, value] of Object.entries(colors$1)) resolved[key] = tokens.isAlias(value) ? tokens.resolve(value) : value;
2095
+ return resolved;
2096
+ }
2097
+ function register(registration = {}) {
2098
+ const item = {
2099
+ lazy: false,
2100
+ dark: false,
2101
+ ...registration
2102
+ };
2103
+ return registry.register(item);
2104
+ }
2105
+ return {
2106
+ ...registry,
2107
+ colors,
2108
+ isDark,
2109
+ register,
2110
+ cycle,
2111
+ get size() {
2112
+ return registry.size;
2113
+ }
2114
+ };
2115
+ }
2116
+ /**
2117
+ * Creates a new theme context trinity.
2118
+ *
2119
+ * @param options The options for the theme context.
2120
+ * @template Z The type of the theme ticket.
2121
+ * @template E The type of the theme context.
2122
+ * @returns A new theme context trinity.
2123
+ *
2124
+ * @see https://0.vuetifyjs.com/composables/plugins/use-theme
2125
+ *
2126
+ * @example
2127
+ * ```ts
2128
+ * import { createThemeContext } from '@vuetify/v0'
2129
+ *
2130
+ * export const [useThemeContext, provideThemeContext, context] = createThemeContext({
2131
+ * namespace: 'v0:theme',
2132
+ * default: 'light',
2133
+ * themes: {
2134
+ * light: {
2135
+ * dark: false,
2136
+ * colors: {
2137
+ * primary: '#3b82f6',
2138
+ * },
2139
+ * },
2140
+ * dark: {
2141
+ * dark: true,
2142
+ * colors: {
2143
+ * primary: '#675496',
2144
+ * },
2145
+ * },
2146
+ * },
2147
+ * })
2148
+ * ```
2149
+ */
2150
+ function createThemeContext(_options = {}) {
2151
+ const { namespace = "v0:theme",...options } = _options;
2152
+ const [useThemeContext, _provideThemeContext] = createContext(namespace);
2153
+ const context = createTheme(options);
2154
+ function provideThemeContext(_context = context, app) {
2155
+ return _provideThemeContext(_context, app);
2156
+ }
2157
+ return createTrinity(useThemeContext, provideThemeContext, context);
2158
+ }
2159
+ /**
2160
+ * Creates a new theme plugin.
2161
+ *
2162
+ * @param options The options for the theme plugin.
2163
+ * @template Z The type of the theme ticket.
2164
+ * @template E The type of the theme context.
2165
+ * @returns A new theme plugin.
2166
+ *
2167
+ * @see https://0.vuetifyjs.com/composables/plugins/use-theme
2168
+ *
2169
+ * @example
2170
+ * ```ts
2171
+ * import { createApp } from 'vue'
2172
+ * import { createThemePlugin } from '@vuetify/v0'
2173
+ * import App from './App.vue'
2174
+ *
2175
+ * const app = createApp(App)
2176
+ *
2177
+ * app.use(
2178
+ * createThemePlugin({
2179
+ * default: 'light',
2180
+ * themes: {
2181
+ * light: {
2182
+ * dark: false,
2183
+ * colors: {
2184
+ * primary: '#3b82f6',
2185
+ * },
2186
+ * },
2187
+ * dark: {
2188
+ * dark: true,
2189
+ * colors: {
2190
+ * primary: '#675496',
2191
+ * },
2192
+ * },
2193
+ * },
2194
+ * })
2195
+ * )
2196
+ *
2197
+ * app.mount('#app')
2198
+ * ```
2199
+ */
2200
+ function createThemePlugin(_options = {}) {
2201
+ const { adapter = new Vuetify0ThemeAdapter(), namespace = "v0:theme", palette = {}, themes = {}, target,...options } = _options;
2202
+ const [, provideThemeContext, context] = createThemeContext({
2203
+ ...options,
2204
+ namespace,
2205
+ themes,
2206
+ palette
2207
+ });
2208
+ return createPlugin({
2209
+ namespace,
2210
+ provide: (app) => {
2211
+ provideThemeContext(context, app);
2212
+ },
2213
+ setup: (app) => {
2214
+ adapter.setup(app, context, target);
2215
+ }
2216
+ });
2217
+ }
2218
+ /**
2219
+ * Returns the current theme instance.
2220
+ *
2221
+ * @param namespace The namespace for the theme context. Defaults to `v0:theme`.
2222
+ * @returns The current theme instance.
2223
+ *
2224
+ * @see https://0.vuetifyjs.com/composables/plugins/use-theme
2225
+ *
2226
+ * @example
2227
+ * ```vue
2228
+ * <script setup lang="ts">
2229
+ * import { useTheme } from '@vuetify/v0'
2230
+ *
2231
+ * const theme = useTheme()
2232
+ * <\/script>
2233
+ *
2234
+ * <template>
2235
+ * <div>
2236
+ * <p>Current theme: {{ theme.selected.value }}</p>
2237
+ * </div>
2238
+ * </template>
2239
+ * ```
2240
+ */
2241
+ function useTheme(namespace = "v0:theme") {
2242
+ return useContext(namespace);
2243
+ }
2244
+
2245
+ //#endregion
2246
+ //#region src/composables/useTimeline/index.ts
2247
+ /**
2248
+ * @module useTimeline
2249
+ *
2250
+ * @remarks
2251
+ * Bounded undo/redo system with overflow management.
2252
+ *
2253
+ * Key features:
2254
+ * - Fixed-size history (default: 10 items)
2255
+ * - Undo/redo stack management
2256
+ * - Overflow queue (preserves oldest items)
2257
+ * - Automatic reindexing after operations
2258
+ * - Perfect for command pattern, history tracking
2259
+ *
2260
+ * Extends useRegistry with temporal navigation capabilities.
2261
+ */
2262
+ /**
2263
+ * Creates a new timeline instance.
2264
+ *
2265
+ * @param _options The options for the timeline instance.
2266
+ * @template Z The type of the timeline ticket.
2267
+ * @template E The type of the timeline context.
2268
+ * @returns A new timeline instance.
2269
+ *
2270
+ * @see https://0.vuetifyjs.com/composables/registration/use-timeline
2271
+ *
2272
+ * @example
2273
+ * ```ts
2274
+ * import { useTimeline } from '@vuetify/v0'
2275
+ *
2276
+ * const timeline = useTimeline({ size: 3 })
2277
+ *
2278
+ * timeline.onboard([{ id: 'one' }, { id: 'two' }, { id: 'three' }])
2279
+ *
2280
+ * console.log(timeline.values()) // [{ id: 'one' }, { id: 'two' }, { id: 'three' }]
2281
+ *
2282
+ * timeline.undo()
2283
+ * console.log(timeline.values()) // [{ id: 'one' }, { id: 'two' }]
2284
+ *
2285
+ * timeline.redo()
2286
+ * console.log(timeline.values()) // [{ id: 'one' }, { id: 'two' }, { id: 'three' }]
2287
+ * ```
2288
+ */
2289
+ function createTimeline(_options = {}) {
2290
+ const { size = 10,...options } = _options;
2291
+ const registry = useRegistry(options);
2292
+ const stack = [];
2293
+ const overflow = [];
2294
+ function register(item) {
2295
+ stack.length = 0;
2296
+ if (registry.size < size) return registry.register({ ...item });
2297
+ const removing = registry.seek("first");
2298
+ if (overflow.length === size) overflow.shift();
2299
+ overflow.push(removing);
2300
+ registry.unregister(removing.id);
2301
+ const ticket = registry.register({ ...item });
2302
+ registry.reindex();
2303
+ return ticket;
2304
+ }
2305
+ function undo() {
2306
+ const item = registry.seek("last");
2307
+ if (!item) return void 0;
2308
+ stack.push(item);
2309
+ registry.unregister(item.id);
2310
+ const restored = overflow.pop();
2311
+ if (restored) {
2312
+ const remaining = registry.values();
2313
+ registry.clear();
2314
+ registry.onboard([restored, ...remaining]);
2315
+ registry.reindex();
2316
+ }
2317
+ return item;
2318
+ }
2319
+ function redo() {
2320
+ if (stack.length === 0) return void 0;
2321
+ const item = stack.pop();
2322
+ const ticket = registry.register(item);
2323
+ registry.reindex();
2324
+ return ticket;
2325
+ }
2326
+ return {
2327
+ ...registry,
2328
+ register,
2329
+ undo,
2330
+ redo,
2331
+ get size() {
2332
+ return registry.size;
2333
+ }
2334
+ };
2335
+ }
2336
+ /**
2337
+ * Creates a new timeline context.
2338
+ *
2339
+ * @param options The options for the timeline context.
2340
+ * @template Z The type of the timeline ticket.
2341
+ * @template E The type of the timeline context.
2342
+ * @returns A new timeline context.
2343
+ *
2344
+ * @see https://0.vuetifyjs.com/composables/registration/use-timeline
2345
+ *
2346
+ * @example
2347
+ * ```ts
2348
+ * import { createTimelineContext } from '@vuetify/v0'
2349
+ *
2350
+ * // With default namespace 'v0:timeline'
2351
+ * export const [useTimeline, provideTimeline, context] = createTimelineContext({ size: 5 })
2352
+ *
2353
+ * context.register({ id: 'example' })
2354
+ *
2355
+ * // In a parent component
2356
+ * provideTimeline()
2357
+ *
2358
+ * // In a child component
2359
+ * const timeline = useTimeline()
2360
+ *
2361
+ * console.log(timeline.values()) // [{ id: 'example' }]
2362
+ * ```
2363
+ */
2364
+ function createTimelineContext(_options = {}) {
2365
+ const { namespace = "v0:timeline",...options } = _options;
2366
+ const [useTimelineContext, _provideTimelineContext] = createContext(namespace);
2367
+ const context = createTimeline(options);
2368
+ function provideTimelineContext(_context = context, app) {
2369
+ return _provideTimelineContext(_context, app);
2370
+ }
2371
+ return createTrinity(useTimelineContext, provideTimelineContext, context);
2372
+ }
2373
+ /**
2374
+ * Returns the current timeline instance.
2375
+ *
2376
+ * @param namespace The namespace for the timeline context. Defaults to `'v0:timeline'`.
2377
+ * @returns The current timeline instance.
2378
+ *
2379
+ * @see https://0.vuetifyjs.com/composables/registration/use-timeline
2380
+ *
2381
+ * @example
2382
+ * ```vue
2383
+ * <script setup lang="ts">
2384
+ * import { useTimeline } from '@vuetify/v0'
2385
+ *
2386
+ * const timeline = useTimeline()
2387
+ * <\/script>
2388
+ * ```
2389
+ */
2390
+ function useTimeline(namespace = "v0:timeline") {
2391
+ return useContext(namespace);
2392
+ }
2393
+
2394
+ //#endregion
2395
+ //#region src/composables/useToggleScope/index.ts
2396
+ /**
2397
+ * @module useToggleScope
2398
+ *
2399
+ * @remarks
2400
+ * Conditionally manages an effect scope based on a reactive boolean condition.
2401
+ * When the source becomes true, creates and runs an effect scope. When false, stops the scope.
2402
+ * All reactive effects created within the scoped function are automatically cleaned up on deactivation.
2403
+ *
2404
+ * Key features:
2405
+ * - Uses Vue's effectScope for efficient reactive effect lifecycle management
2406
+ * - Automatic cleanup when condition becomes false
2407
+ * - Supports optional reset callback for scope restart capability
2408
+ * - Handles rapid toggling and parent scope disposal safely
2409
+ * - SSR-safe (effectScope is part of Vue core)
2410
+ *
2411
+ * Perfect for conditional side effects, feature flags, and performance optimization
2412
+ * by only running reactive effects when needed.
2413
+ */
2414
+ /**
2415
+ * Conditionally manages an effect scope based on a reactive boolean source.
2416
+ *
2417
+ * @param source A reactive boolean value or getter that controls the scope lifecycle
2418
+ * @param fn The function to run within the effect scope. Can optionally receive controls for manual scope management.
2419
+ * @returns Controls object with isActive state and start/stop/reset methods
2420
+ *
2421
+ * @see https://vuejs.org/api/reactivity-advanced.html#effectscope
2422
+ * @see https://0.vuetifyjs.com/composables/system/use-toggle-scope
2423
+ *
2424
+ * @example
2425
+ * ```ts
2426
+ * import { ref } from 'vue'
2427
+ * import { useToggleScope } from '@vuetify/v0'
2428
+ *
2429
+ * const isEnabled = ref(false)
2430
+ *
2431
+ * const { isActive } = useToggleScope(isEnabled, () => {
2432
+ * // This code runs when isEnabled becomes true
2433
+ * const unwatch = watch(someRef, () => {
2434
+ * console.log('Watching...')
2435
+ * })
2436
+ *
2437
+ * // Cleanup happens automatically when isEnabled becomes false
2438
+ * })
2439
+ *
2440
+ * // Toggle the scope on/off
2441
+ * isEnabled.value = true // Starts the scope
2442
+ * console.log(isActive.value) // true
2443
+ * isEnabled.value = false // Stops and cleans up
2444
+ * console.log(isActive.value) // false
2445
+ * ```
2446
+ *
2447
+ * @example
2448
+ * With manual controls:
2449
+ * ```ts
2450
+ * const isEnabled = ref(true)
2451
+ *
2452
+ * useToggleScope(isEnabled, (controls) => {
2453
+ * // Access controls inside the scope
2454
+ * console.log('Active:', controls.isActive.value)
2455
+ *
2456
+ * // Manually reset the scope if needed
2457
+ * someEvent.on('reset', () => controls.reset())
2458
+ * })
2459
+ * ```
2460
+ */
2461
+ function useToggleScope(source, fn) {
2462
+ const scope = shallowRef();
2463
+ const isActive = toRef(() => !!scope.value);
2464
+ function start() {
2465
+ if (scope.value) return;
2466
+ scope.value = effectScope();
2467
+ scope.value.run(() => fn.length > 0 ? fn(controls) : fn());
2468
+ }
2469
+ function stop() {
2470
+ scope.value?.stop();
2471
+ scope.value = void 0;
2472
+ }
2473
+ function reset() {
2474
+ stop();
2475
+ start();
2476
+ }
2477
+ const controls = {
2478
+ isActive: shallowReadonly(isActive),
2479
+ start,
2480
+ stop,
2481
+ reset
2482
+ };
2483
+ watch(source, (active) => {
2484
+ if (active && !scope.value) start();
2485
+ else if (!active) stop();
2486
+ }, { immediate: true });
2487
+ onScopeDispose(() => {
2488
+ stop();
2489
+ });
2490
+ return controls;
2491
+ }
2492
+
2493
+ //#endregion
2494
+ //#region src/composables/useVirtual/index.ts
2495
+ /**
2496
+ * @module useVirtual
2497
+ *
2498
+ * @remarks
2499
+ * Virtual scrolling composable for efficiently rendering large lists.
2500
+ *
2501
+ * Key features:
2502
+ * - Renders only visible items (viewport + overscan)
2503
+ * - Dynamic or fixed item heights
2504
+ * - SSR-safe (checks IN_BROWSER)
2505
+ * - Bidirectional scrolling (forward/reverse for chat apps)
2506
+ * - Scroll anchoring (maintains position across data changes)
2507
+ * - Edge detection for infinite scroll
2508
+ * - iOS momentum and elastic scrolling
2509
+ * - Configurable overscan (extra items rendered for smooth scrolling)
2510
+ *
2511
+ * Perfect for large data sets, chat apps, and infinite scroll implementations.
2512
+ */
2513
+ /**
2514
+ * Virtual scrolling composable for efficiently rendering large lists
2515
+ *
2516
+ * @param items Reactive array of items to virtualize
2517
+ * @param options Configuration options
2518
+ * @returns Virtual scrolling context
2519
+ *
2520
+ * @see https://0.vuetifyjs.com/composables/utilities/use-virtual
2521
+ *
2522
+ * @example
2523
+ * ```vue
2524
+ * <script setup lang="ts">
2525
+ * import { useVirtual } from '@vuetify/v0'
2526
+ *
2527
+ * const items = ref(Array.from({ length: 10000 }, (_, i) => ({ id: i, name: `Item ${i}` })))
2528
+ * <\/script>
2529
+ *
2530
+ * <template>
2531
+ * <div
2532
+ * ref="element"
2533
+ * style="height: 600px; overflow-y: auto;"
2534
+ * @scroll="scroll"
2535
+ * >
2536
+ * <div :style="{ height: `${offset}px` }" />
2537
+ *
2538
+ * <div v-for="item in items" :key="item.index">
2539
+ * {{ item.raw.name }}
2540
+ * </div>
2541
+ *
2542
+ * <div :style="{ height: `${size}px` }" />
2543
+ * </div>
2544
+ * </template>
2545
+ * ```
2546
+ */
2547
+ function useVirtual(items, _options = {}) {
2548
+ const { itemHeight: _itemHeight, height, overscan = 5, direction = "forward", anchor = "auto", anchorSmooth = true, onStartReached, onEndReached, startThreshold = 0, endThreshold = 0, momentum: momentumOption, elastic: elasticOption } = _options;
2549
+ const element = ref();
2550
+ const itemHeight = shallowRef(Number.parseFloat(String(_itemHeight || 0)));
2551
+ const heights = shallowRef([]);
2552
+ const offsets = shallowRef([]);
2553
+ const first = shallowRef(0);
2554
+ const last = shallowRef(0);
2555
+ const offset = shallowRef(0);
2556
+ const size = shallowRef(0);
2557
+ const viewportHeight = shallowRef(0);
2558
+ const state = shallowRef("ok");
2559
+ const isIOS = IN_BROWSER && /iPad|iPhone|iPod/.test(navigator.userAgent);
2560
+ const momentum = momentumOption ?? isIOS;
2561
+ const elastic = elasticOption ?? isIOS;
2562
+ let raf = -1;
2563
+ let rebuildRaf = -1;
2564
+ let edgeRaf = -1;
2565
+ const cachedViewport = Number.parseInt(String(height)) || 0;
2566
+ let anchorIndex = -1;
2567
+ let anchorOffset = 0;
2568
+ const computedItems = computed(() => items.value.slice(first.value, last.value).map((item, i) => ({
2569
+ raw: item,
2570
+ index: i + first.value
2571
+ })));
2572
+ watch(element, (el) => {
2573
+ if (!IN_BROWSER || !el?.style) return;
2574
+ if (momentum) el.style.webkitOverflowScrolling = "touch";
2575
+ if (!elastic) el.style.overscrollBehavior = "none";
2576
+ });
2577
+ useResizeObserver(element, (entries) => {
2578
+ if (!entries[0]) return;
2579
+ viewportHeight.value = entries[0].contentRect.height;
2580
+ update();
2581
+ });
2582
+ watch(items, (newItems) => {
2583
+ captureAnchor();
2584
+ const length = newItems.length;
2585
+ const newHeights = heights.value.length === length ? heights.value : Array.from({ length }, () => null);
2586
+ if (heights.value !== newHeights) heights.value = newHeights;
2587
+ rebuild();
2588
+ }, { immediate: true });
2589
+ watch(element, () => {
2590
+ if (!element.value) return;
2591
+ if (direction === "reverse" && items.value.length > 0) {
2592
+ const lastIndex = items.value.length - 1;
2593
+ const totalHeight = (offsets.value[lastIndex] || 0) + (heights.value[lastIndex] || itemHeight.value);
2594
+ element.value.scrollTop = totalHeight;
2595
+ }
2596
+ update();
2597
+ });
2598
+ function captureAnchor() {
2599
+ if (!element.value || anchor === "auto") return;
2600
+ if (anchor === "start") {
2601
+ anchorIndex = 0;
2602
+ anchorOffset = 0;
2603
+ } else if (anchor === "end") {
2604
+ anchorIndex = items.value.length - 1;
2605
+ anchorOffset = 0;
2606
+ } else if (/* @__PURE__ */ isFunction(anchor)) {
2607
+ const result = anchor(items.value);
2608
+ if (/* @__PURE__ */ isNumber(result)) {
2609
+ anchorIndex = result;
2610
+ anchorOffset = element.value.scrollTop - (offsets.value[result] || 0);
2611
+ }
2612
+ } else {
2613
+ anchorIndex = first.value;
2614
+ anchorOffset = element.value.scrollTop - (offsets.value[first.value] || 0);
2615
+ }
2616
+ }
2617
+ function restoreAnchor() {
2618
+ if (!element.value || anchorIndex < 0) return;
2619
+ const newScrollTop = (offsets.value[anchorIndex] || 0) + anchorOffset;
2620
+ if (anchorSmooth && direction === "forward") element.value.scrollTo({
2621
+ top: newScrollTop,
2622
+ behavior: "smooth"
2623
+ });
2624
+ else element.value.scrollTop = newScrollTop;
2625
+ anchorIndex = -1;
2626
+ anchorOffset = 0;
2627
+ }
2628
+ function rebuild() {
2629
+ const length = items.value.length;
2630
+ const newOffsets = offsets.value.length === length ? offsets.value : Array.from({ length });
2631
+ let offset$1 = 0;
2632
+ for (let i = 0; i < length; i++) {
2633
+ newOffsets[i] = offset$1;
2634
+ offset$1 += heights.value[i] || itemHeight.value;
2635
+ }
2636
+ if (offsets.value !== newOffsets) offsets.value = newOffsets;
2637
+ update();
2638
+ restoreAnchor();
2639
+ }
2640
+ function findIndex(scrollTop) {
2641
+ const arr = offsets.value;
2642
+ if (arr.length === 0) return 0;
2643
+ let low = 0;
2644
+ let high = arr.length - 1;
2645
+ while (low <= high) {
2646
+ const mid = low + high >> 1;
2647
+ if (arr[mid] <= scrollTop) low = mid + 1;
2648
+ else high = mid - 1;
2649
+ }
2650
+ return Math.max(0, high);
2651
+ }
2652
+ function update() {
2653
+ if (!element.value) return;
2654
+ const viewport = viewportHeight.value || cachedViewport;
2655
+ if (!viewport || !itemHeight.value) return;
2656
+ const scrollTop = element.value.scrollTop || 0;
2657
+ const length = items.value.length;
2658
+ const visibleStart = findIndex(scrollTop);
2659
+ const visibleEnd = findIndex(scrollTop + viewport) + 1;
2660
+ const start = /* @__PURE__ */ clamp(visibleStart - overscan, 0, length);
2661
+ const end = /* @__PURE__ */ clamp(visibleEnd + overscan, start, length);
2662
+ first.value = start;
2663
+ last.value = end;
2664
+ offset.value = offsets.value[start] || 0;
2665
+ const lastIndex = length - 1;
2666
+ const totalHeight = (offsets.value[lastIndex] || 0) + (heights.value[lastIndex] || itemHeight.value);
2667
+ size.value = totalHeight - (offsets.value[end] || totalHeight);
2668
+ }
2669
+ function checkEdges() {
2670
+ if (!element.value) return;
2671
+ const scrollTop = element.value.scrollTop;
2672
+ const scrollHeight = element.value.scrollHeight;
2673
+ const clientHeight = element.value.clientHeight;
2674
+ const distanceFromStart = scrollTop;
2675
+ const distanceFromEnd = scrollHeight - (scrollTop + clientHeight);
2676
+ if (IN_BROWSER) {
2677
+ cancelAnimationFrame(edgeRaf);
2678
+ edgeRaf = requestAnimationFrame(() => {
2679
+ if (onStartReached && distanceFromStart <= startThreshold) onStartReached(distanceFromStart);
2680
+ if (onEndReached && distanceFromEnd <= endThreshold) onEndReached(distanceFromEnd);
2681
+ });
2682
+ } else {
2683
+ if (onStartReached && distanceFromStart <= startThreshold) onStartReached(distanceFromStart);
2684
+ if (onEndReached && distanceFromEnd <= endThreshold) onEndReached(distanceFromEnd);
2685
+ }
2686
+ }
2687
+ function resize(index, height$1) {
2688
+ if (heights.value[index] === height$1) return;
2689
+ heights.value[index] = height$1;
2690
+ if (!itemHeight.value) itemHeight.value = height$1;
2691
+ if (IN_BROWSER) {
2692
+ cancelAnimationFrame(rebuildRaf);
2693
+ rebuildRaf = requestAnimationFrame(rebuild);
2694
+ } else rebuild();
2695
+ }
2696
+ function scroll() {
2697
+ if (IN_BROWSER) {
2698
+ cancelAnimationFrame(raf);
2699
+ raf = requestAnimationFrame(update);
2700
+ checkEdges();
2701
+ } else update();
2702
+ }
2703
+ function scrollTo(index, scrollOptions) {
2704
+ if (!element.value) return;
2705
+ const targetOffset = offsets.value[index] || 0;
2706
+ const behavior = scrollOptions?.behavior ?? "auto";
2707
+ const block = scrollOptions?.block ?? "start";
2708
+ const extraOffset = scrollOptions?.offset ?? 0;
2709
+ let scrollTop = targetOffset + extraOffset;
2710
+ switch (block) {
2711
+ case "center": {
2712
+ const viewport = viewportHeight.value || cachedViewport;
2713
+ const itemH = heights.value[index] || itemHeight.value;
2714
+ scrollTop = targetOffset - viewport / 2 + itemH / 2 + extraOffset;
2715
+ break;
2716
+ }
2717
+ case "end": {
2718
+ const viewport = viewportHeight.value || cachedViewport;
2719
+ const itemH = heights.value[index] || itemHeight.value;
2720
+ scrollTop = targetOffset - viewport + itemH + extraOffset;
2721
+ break;
2722
+ }
2723
+ case "nearest": {
2724
+ const viewport = viewportHeight.value || cachedViewport;
2725
+ const currentScroll = element.value.scrollTop;
2726
+ const itemH = heights.value[index] || itemHeight.value;
2727
+ if (targetOffset < currentScroll) scrollTop = targetOffset + extraOffset;
2728
+ else if (targetOffset + itemH > currentScroll + viewport) scrollTop = targetOffset - viewport + itemH + extraOffset;
2729
+ else return;
2730
+ break;
2731
+ }
2732
+ }
2733
+ if (behavior === "smooth") element.value.scrollTo({
2734
+ top: scrollTop,
2735
+ behavior: "smooth"
2736
+ });
2737
+ else element.value.scrollTop = scrollTop;
2738
+ update();
2739
+ }
2740
+ function reset() {
2741
+ state.value = "ok";
2742
+ anchorIndex = -1;
2743
+ anchorOffset = 0;
2744
+ if (element.value && direction === "reverse" && items.value.length > 0) {
2745
+ const lastIndex = items.value.length - 1;
2746
+ const totalHeight = (offsets.value[lastIndex] || 0) + (heights.value[lastIndex] || itemHeight.value);
2747
+ element.value.scrollTop = totalHeight;
2748
+ }
2749
+ }
2750
+ return {
2751
+ element,
2752
+ items: computedItems,
2753
+ offset: readonly(offset),
2754
+ size: readonly(size),
2755
+ state,
2756
+ scrollTo,
2757
+ scroll,
2758
+ scrollend: scroll,
2759
+ resize,
2760
+ reset
2761
+ };
2762
+ }
2763
+
2764
+ //#endregion
2765
+ export { useForm as A, useDocumentEventListener as B, PermissionAdapter as C, useIntersectionObserver as D, useElementIntersection as E, useFeatures as F, useWindowEventListener as H, createBreakpoints as I, createBreakpointsContext as L, createFeatures as M, createFeaturesContext as N, createForm as O, createFeaturesPlugin as P, createBreakpointsPlugin as R, usePermissions as S, useKeydown as T, toReactive as U, useEventListener as V, createQueueContext as _, useTimeline as a, createPermissionsContext as b, createThemePlugin as c, createStorage as d, createStorageContext as f, createQueue as g, MemoryAdapter as h, createTimelineContext as i, useFilter as j, createFormContext as k, useTheme as l, useStorage as m, useToggleScope as n, createTheme as o, createStoragePlugin as p, createTimeline as r, createThemeContext as s, useVirtual as t, Vuetify0ThemeAdapter as u, useQueue as v, useMutationObserver as w, createPermissionsPlugin as x, createPermissions as y, useBreakpoints as z };