@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.
@@ -1,4902 +0,0 @@
1
- import { a as isNull, c as isObject, d as isUndefined, f as mergeDeep, i as isFunction, n as isArray, o as isNullOrUndefined, r as isBoolean, s as isNumber, t as genId, u as isString } from "./utilities-C26nB74O.mjs";
2
- import { a as SUPPORTS_OBSERVER, i as SUPPORTS_MUTATION_OBSERVER, n as SUPPORTS_INTERSECTION_OBSERVER, s as __LOGGER_ENABLED__, t as IN_BROWSER } from "./globals-D7kNBw5Q.mjs";
3
- import { computed, getCurrentInstance, getCurrentScope, inject, isRef, onMounted, onScopeDispose, onUnmounted, provide, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRef, toValue, unref, watch } from "vue";
4
-
5
- //#region src/composables/createContext/index.ts
6
- /**
7
- * @module createContext
8
- *
9
- * @see https://0.vuetifyjs.com/composables/foundation/create-context
10
- *
11
- * @remarks
12
- * Factory for creating type-safe Vue dependency injection contexts.
13
- *
14
- * Provides a wrapper around Vue's provide/inject that throws errors when context is not found,
15
- * eliminating silent failures and improving developer experience. Supports both app-level and
16
- * component-level provision.
17
- */
18
- /**
19
- * Injects a context provided by an ancestor component.
20
- *
21
- * @param key The key of the context to inject.
22
- * @param defaultValue Optional default value if context is not found.
23
- * @template Z The type of the context.
24
- * @returns The injected context.
25
- * @throws An error if the context is not found and no default is provided.
26
- *
27
- * @see https://vuejs.org/api/composition-api-dependency-injection.html#inject
28
- * @see https://0.vuetifyjs.com/composables/foundation/create-context#use-context
29
- *
30
- * @example
31
- * ```ts
32
- * // Without default value
33
- * const context = useContext<MyContext>('my-context')
34
- *
35
- * // With default value
36
- * const context = useContext<MyContext>('my-context', defaultContext)
37
- * ```
38
- */
39
- function useContext(key, defaultValue) {
40
- const context = inject(key, defaultValue);
41
- if (context === void 0) throw new Error(`Context "${String(key)}" not found. Ensure it's provided by an ancestor.`);
42
- return context;
43
- }
44
- /**
45
- * Provides a context to all descendant components.
46
- *
47
- * @param key The key of the context to provide.
48
- * @param context The context to provide.
49
- * @param app Optional Vue app instance to provide the context at app level instead of component level.
50
- * @template Z The type of the context.
51
- * @returns The provided context.
52
- *
53
- * @remarks
54
- * When `app` parameter is provided, the context is made available to all components in the app.
55
- * When omitted, the context is provided at the current component level and available to descendants only.
56
- *
57
- * @see https://vuejs.org/api/composition-api-dependency-injection.html#provide
58
- * @see https://0.vuetifyjs.com/composables/foundation/create-context#provide-context
59
- *
60
- * @example
61
- * ```ts
62
- * // Component-level provision
63
- * provideContext<MyContext>('my-context', context)
64
- *
65
- * // App-level provision (typically used in plugins)
66
- * const app = createApp()
67
- * provideContext<MyContext>('my-context', context, app)
68
- * ```
69
- */
70
- function provideContext(key, context, app) {
71
- if (app) app.provide(key, context);
72
- else provide(key, context);
73
- return context;
74
- }
75
- /**
76
- * Creates a new context for providing and injecting data.
77
- *
78
- * @param key The key of the context to create.
79
- * @param defaultValue Optional default value if context is not found.
80
- * @template Z The type of the context.
81
- * @returns A tuple containing the `useContext` and `provideContext` functions.
82
- *
83
- * @see https://vuejs.org/api/composition-api-dependency-injection.html
84
- * @see https://0.vuetifyjs.com/composables/foundation/create-context#create-context
85
- *
86
- * @example
87
- * ```ts
88
- * // Without default value
89
- * const [useMyContext, provideMyContext] = createContext<MyContext>('my-context')
90
- *
91
- * // With default value
92
- * const [useMyContext, provideMyContext] = createContext<MyContext>('my-context', defaultContext)
93
- * ```
94
- */
95
- function createContext(_key, defaultValue) {
96
- function _provideContext(context, app) {
97
- return provideContext(_key, context, app);
98
- }
99
- function _useContext(key = _key) {
100
- return useContext(key, defaultValue);
101
- }
102
- return [_useContext, _provideContext];
103
- }
104
-
105
- //#endregion
106
- //#region src/composables/createPlugin/index.ts
107
- /**
108
- * Creates a new Vue plugin.
109
- *
110
- * @param options The plugin options.
111
- * @returns A new Vue plugin.
112
- *
113
- * @see https://0.vuetifyjs.com/composables/foundation/create-plugin#create-plugin
114
- *
115
- * @example
116
- * ```ts
117
- * export const [useContext, provideContext] = createContext<MyContext>('my-plugin')
118
- *
119
- * const context = {}
120
- *
121
- * export const MyPlugin = createPlugin({
122
- * namespace: 'my-plugin',
123
- * provide: (app) => {
124
- * provideContext(context, app)
125
- * },
126
- * setup: (app) => {
127
- * // Optional setup logic
128
- * },
129
- * })
130
- */
131
- function createPlugin(options) {
132
- return { install(app) {
133
- app.runWithContext(() => {
134
- options.provide(app);
135
- options.setup?.(app);
136
- });
137
- } };
138
- }
139
-
140
- //#endregion
141
- //#region src/composables/createTrinity/index.ts
142
- /**
143
- * Creates a new trinity for a context composable and its provider.
144
- *
145
- * @param useContext The function that retrieves/uses the context (typically named `useContext`).
146
- * @param provideContext The function that provides the context to descendants.
147
- * @param context The default context instance to use when no custom context is provided.
148
- * @template Z The type of the context.
149
- * @returns A readonly tuple containing: [useContext function, provideContext wrapper function, default context instance].
150
- *
151
- * @remarks The trinity pattern is a foundational pattern used throughout the codebase for creating reusable context systems. It provides three related elements:
152
- *
153
- * 1. A function to retrieve/use the context
154
- * 2. A function to provide the context (with default value support)
155
- * 3. The default context instance
156
- *
157
- * The returned tuple is readonly (using `as const`) to ensure proper type inference.
158
- *
159
- * @see https://0.vuetifyjs.com/composables/foundation/create-trinity#create-trinity
160
- *
161
- * @example
162
- * ```ts
163
- * interface MyContext {
164
- * foo: string
165
- * bar: number
166
- * }
167
- *
168
- * export function createMyFeature<E extends MyContext = MyContext>() {
169
- * const [useContext, _provideContext] = createContext<E>('my-context')
170
- *
171
- * const context = { foo: 'hello', bar: 42 }
172
- *
173
- * function provideContext (_context: E = context, app?: App): E {
174
- * return _provideContext(_context, app)
175
- * }
176
- *
177
- * return createTrinity<E>(useContext, provideContext, context)
178
- * }
179
- * ```
180
- */
181
- function createTrinity(useContext$1, provideContext$1, context) {
182
- return [
183
- useContext$1,
184
- (_context = context, app) => provideContext$1(_context, app),
185
- context
186
- ];
187
- }
188
-
189
- //#endregion
190
- //#region src/composables/toArray/index.ts
191
- /**
192
- * @module toArray
193
- *
194
- * @remarks
195
- * Utility function to normalize single values and arrays into arrays.
196
- *
197
- * Converts single values into single-element arrays, passes arrays through unchanged,
198
- * and handles null/undefined by returning empty arrays. Perfect for functions that
199
- * accept both single values and arrays as input (e.g., ID | ID[]).
200
- */
201
- /**
202
- * Converts a value to an array.
203
- *
204
- * @param value The value to convert.
205
- * @template Z The type of the value.
206
- * @returns The converted array.
207
- *
208
- * @see https://0.vuetifyjs.com/composables/transformers/to-array
209
- *
210
- * @example
211
- * ```ts
212
- * import { toArray } from '@vuetify/v0'
213
- *
214
- * const value = 'Example Value'
215
- * const valueAsArray = toArray(value)
216
- *
217
- * console.log(valueAsArray) // ['Example Value']
218
- * ```
219
- */
220
- function toArray(value) {
221
- return /* @__PURE__ */ isNullOrUndefined(value) ? [] : Array.isArray(value) ? value : [value];
222
- }
223
-
224
- //#endregion
225
- //#region src/composables/toReactive/index.ts
226
- /**
227
- * @module toReactive
228
- *
229
- * @remarks
230
- * Utility function to convert values and refs into reactive proxies with ref unwrapping.
231
- *
232
- * Key features:
233
- * - Automatic ref unwrapping
234
- * - Deep reactive proxying
235
- * - Map and Set support with ref unwrapping
236
- * - Nested object/array reactivity
237
- * - Type preservation
238
- *
239
- * Perfect for creating reactive versions of plain objects while automatically unwrapping refs.
240
- */
241
- /**
242
- * Converts a `MaybeRef` to a `UnwrapNestedRefs`.
243
- *
244
- * @param objectRef The object to convert.
245
- * @template Z The type of the object.
246
- * @returns The converted object.
247
- *
248
- * @see https://0.vuetifyjs.com/composables/transformers/to-reactive
249
- *
250
- * @example
251
- * ```ts
252
- * import { ref } from 'vue'
253
- * import { toReactive } from '@vuetify/v0'
254
- *
255
- * const state = ref({ name: 'John', age: 30 })
256
- * const rstate = toReactive(state)
257
- *
258
- * console.log(rstate.name) // John
259
- * ```
260
- */
261
- function toReactive(objectRef) {
262
- if (!isRef(objectRef)) return reactive(objectRef);
263
- const target = objectRef.value;
264
- if (target instanceof Map) {
265
- const mapProxy = new Proxy(/* @__PURE__ */ new Map(), { get(_, p) {
266
- const map = objectRef.value;
267
- if (p === "get") return (key) => unref(map.get(key));
268
- if (p === "set") return (key, value) => {
269
- const existingValue = map.get(key);
270
- if (isRef(existingValue)) existingValue.value = unref(value);
271
- else map.set(key, value);
272
- return mapProxy;
273
- };
274
- if (p === "has") return (key) => map.has(key);
275
- if (p === "delete") return (key) => map.delete(key);
276
- if (p === "clear") return () => map.clear();
277
- if (p === "size") return map.size;
278
- if (p === "keys") return () => map.keys();
279
- if (p === "values") return function* () {
280
- for (const value of map.values()) yield unref(value);
281
- };
282
- if (p === "entries") return function* () {
283
- for (const [key, value] of map.entries()) yield [key, unref(value)];
284
- };
285
- if (p === "forEach") return (callback, thisArg) => {
286
- for (const [key, value] of map.entries()) callback.call(thisArg, unref(value), key, mapProxy);
287
- };
288
- if (p === Symbol.iterator) return function* () {
289
- for (const [key, value] of map.entries()) yield [key, unref(value)];
290
- };
291
- return Reflect.get(map, p);
292
- } });
293
- return reactive(mapProxy);
294
- }
295
- if (target instanceof Set) {
296
- const setProxy = new Proxy(/* @__PURE__ */ new Set(), { get(_, p) {
297
- const set = objectRef.value;
298
- if (p === "add") return (value) => {
299
- set.add(value);
300
- return setProxy;
301
- };
302
- if (p === "has") return (value) => set.has(value);
303
- if (p === "delete") return (value) => set.delete(value);
304
- if (p === "clear") return () => set.clear();
305
- if (p === "size") return set.size;
306
- if (p === "keys" || p === "values") return function* () {
307
- for (const value of set.values()) yield unref(value);
308
- };
309
- if (p === "entries") return function* () {
310
- for (const value of set.values()) {
311
- const unreffedValue = unref(value);
312
- yield [unreffedValue, unreffedValue];
313
- }
314
- };
315
- if (p === "forEach") return (callback, thisArg) => {
316
- for (const value of set) {
317
- const unreffedValue = unref(value);
318
- callback.call(thisArg, unreffedValue, unreffedValue, setProxy);
319
- }
320
- };
321
- if (p === Symbol.iterator) return function* () {
322
- for (const value of set.values()) yield unref(value);
323
- };
324
- return Reflect.get(set, p);
325
- } });
326
- return reactive(setProxy);
327
- }
328
- return reactive(new Proxy({}, {
329
- get(_, p, receiver) {
330
- return unref(Reflect.get(objectRef.value, p, receiver));
331
- },
332
- set(_, p, value) {
333
- const currentTarget = objectRef.value;
334
- currentTarget[p] = value;
335
- return true;
336
- },
337
- deleteProperty(_, p) {
338
- return Reflect.deleteProperty(objectRef.value, p);
339
- },
340
- has(_, p) {
341
- return Reflect.has(objectRef.value, p);
342
- },
343
- ownKeys() {
344
- return Object.keys(objectRef.value);
345
- },
346
- getOwnPropertyDescriptor(_, p) {
347
- const desc = Reflect.getOwnPropertyDescriptor(objectRef.value, p);
348
- if (!desc) return;
349
- const newDesc = {
350
- ...desc,
351
- configurable: true
352
- };
353
- if ("value" in newDesc) newDesc.value = unref(newDesc.value);
354
- return newDesc;
355
- }
356
- }));
357
- }
358
-
359
- //#endregion
360
- //#region src/composables/useHydration/index.ts
361
- /**
362
- * @module useHydration
363
- *
364
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
365
- *
366
- * @remarks
367
- * SSR hydration state management composable.
368
- *
369
- * Key features:
370
- * - Hydration state detection (browser vs SSR)
371
- * - Root component detection
372
- * - Readonly hydration state refs
373
- * - Plugin installation support
374
- * - Perfect for hydration-safe rendering
375
- *
376
- * Essential for composables that need to behave differently during SSR vs client-side.
377
- */
378
- /**
379
- * Creates a new hydration instance.
380
- *
381
- * @returns A new hydration instance.
382
- *
383
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
384
- *
385
- * @example
386
- * ```ts
387
- * import { createHydration } from '@vuetify/v0'
388
- *
389
- * const hydration = createHydration()
390
- * console.log(hydration.isHydrated.value) // false
391
- * hydration.hydrate()
392
- * console.log(hydration.isHydrated.value) // true
393
- * ```
394
- */
395
- function createHydration() {
396
- const isHydrated = shallowRef(false);
397
- function hydrate() {
398
- isHydrated.value = true;
399
- }
400
- return {
401
- isHydrated: shallowReadonly(isHydrated),
402
- hydrate
403
- };
404
- }
405
- /**
406
- * Creates a new hydration context trinity.
407
- *
408
- * @param options Options for creating the hydration context.
409
- * @template E The type of the hydration context.
410
- * @returns A new hydration context trinity.
411
- *
412
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
413
- *
414
- * @example
415
- * ```ts
416
- * import { createHydrationContext } from '@vuetify/v0'
417
- *
418
- * export const [useHydrationContext, provideHydrationContext, context] = createHydrationContext({
419
- * namespace: 'app:hydration',
420
- * })
421
- * ```
422
- */
423
- function createHydrationContext(_options = {}) {
424
- const { namespace = "v0:hydration" } = _options;
425
- const [useHydrationContext, _provideHydrationContext] = createContext(namespace);
426
- const context = createHydration();
427
- function provideHydrationContext(_context = context, app) {
428
- return _provideHydrationContext(_context, app);
429
- }
430
- return createTrinity(useHydrationContext, provideHydrationContext, context);
431
- }
432
- /**
433
- * Creates a new hydration plugin.
434
- *
435
- * @param options The options for the hydration plugin.
436
- * @template E The type of the hydration context.
437
- * @returns A new hydration plugin.
438
- *
439
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
440
- *
441
- * @example
442
- * ```ts
443
- * import { createApp } from 'vue'
444
- * import { createHydrationPlugin } from '@vuetify/v0'
445
- * import App from './App.vue'
446
- *
447
- * const app = createApp(App)
448
- *
449
- * app.use(createHydrationPlugin())
450
- *
451
- * app.mount('#app')
452
- * ```
453
- */
454
- function createHydrationPlugin(_options = {}) {
455
- const { namespace = "v0:hydration",...options } = _options;
456
- const [, provideHydrationContext, context] = createHydrationContext({
457
- ...options,
458
- namespace
459
- });
460
- return createPlugin({
461
- namespace,
462
- provide: (app) => {
463
- provideHydrationContext(context, app);
464
- },
465
- setup: (app) => {
466
- app.mixin({ mounted() {
467
- if (this.$parent !== null) return;
468
- context.hydrate();
469
- } });
470
- }
471
- });
472
- }
473
- /**
474
- * Returns the current hydration instance.
475
- *
476
- * @param namespace The namespace for the hydration context. Defaults to `v0:hydration`.
477
- * @returns The current hydration instance.
478
- *
479
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
480
- *
481
- * @example
482
- * ```vue
483
- * <script setup lang="ts">
484
- * import { useHydration } from '@vuetify/v0'
485
- *
486
- * const hydration = useHydration()
487
- * <\/script>
488
- *
489
- * <template>
490
- * <div>
491
- * <p>Is hydrated: {{ hydration.isHydrated.value }}</p>
492
- * </div>
493
- * </template>
494
- * ```
495
- */
496
- function useHydration(namespace = "v0:hydration") {
497
- return useContext(namespace);
498
- }
499
-
500
- //#endregion
501
- //#region src/composables/useBreakpoints/index.ts
502
- /**
503
- * @module useBreakpoints
504
- *
505
- * @see https://0.vuetifyjs.com/composables/plugins/use-breakpoints
506
- *
507
- * @remarks
508
- * Responsive breakpoint detection composable with window resize handling.
509
- *
510
- * Key features:
511
- * - Window matchMedia integration
512
- * - Six built-in breakpoints (xs, sm, md, lg, xl, xxl)
513
- * - Automatic resize listener with cleanup
514
- * - SSR-safe (checks IN_BROWSER)
515
- * - Hydration-aware
516
- * - Custom breakpoint configuration
517
- *
518
- * Perfect for responsive layouts and conditional rendering based on screen size.
519
- */
520
- /**
521
- * Creates default breakpoint configuration.
522
- *
523
- * @returns The default breakpoint configuration object.
524
- */
525
- function createDefaultBreakpoints() {
526
- return {
527
- mobileBreakpoint: "md",
528
- breakpoints: {
529
- xs: 0,
530
- sm: 600,
531
- md: 960,
532
- lg: 1280,
533
- xl: 1920,
534
- xxl: 2560
535
- }
536
- };
537
- }
538
- /**
539
- * Creates a new breakpoints instance.
540
- *
541
- * @param options The options for the breakpoints instance.
542
- * @template E The type of the breakpoints context.
543
- * @returns A new breakpoints instance.
544
- *
545
- * @see https://0.vuetifyjs.com/composables/plugins/use-breakpoints
546
- *
547
- * @example
548
- * ```ts
549
- * import { createBreakpoints } from '@vuetify/v0'
550
- *
551
- * export const [useBreakpoints, provideBreakpoints] = createBreakpoints({
552
- * namespace: 'v0:breakpoints',
553
- * mobileBreakpoint: 'sm',
554
- * breakpoints: {
555
- * xs: 0,
556
- * sm: 680,
557
- * md: 1024,
558
- * lg: 1280,
559
- * xl: 1920,
560
- * xxl: 2560,
561
- * },
562
- * })
563
- * ```
564
- */
565
- function createBreakpoints(_options = {}) {
566
- const { mobileBreakpoint, breakpoints } = /* @__PURE__ */ mergeDeep(createDefaultBreakpoints(), _options);
567
- const sorted = Object.entries(breakpoints).toSorted((a, b) => a[1] - b[1]);
568
- const names = sorted.map(([n]) => n);
569
- const mb = /* @__PURE__ */ isNumber(mobileBreakpoint) ? mobileBreakpoint : breakpoints[mobileBreakpoint] ?? breakpoints.md;
570
- const name = shallowRef("xs");
571
- const width = shallowRef(0);
572
- const height = shallowRef(0);
573
- const isMobile = shallowRef(true);
574
- const xs = shallowRef(true);
575
- const sm = shallowRef(false);
576
- const md = shallowRef(false);
577
- const lg = shallowRef(false);
578
- const xl = shallowRef(false);
579
- const xxl = shallowRef(false);
580
- const smAndUp = shallowRef(false);
581
- const mdAndUp = shallowRef(false);
582
- const lgAndUp = shallowRef(false);
583
- const xlAndUp = shallowRef(false);
584
- const xxlAndUp = shallowRef(false);
585
- const smAndDown = shallowRef(true);
586
- const mdAndDown = shallowRef(true);
587
- const lgAndDown = shallowRef(true);
588
- const xlAndDown = shallowRef(true);
589
- const xxlAndDown = shallowRef(true);
590
- function update() {
591
- if (!IN_BROWSER) return;
592
- width.value = window.innerWidth;
593
- height.value = window.innerHeight;
594
- let current = "xs";
595
- for (let i = sorted.length - 1; i >= 0; i--) if (width.value >= sorted[i][1]) {
596
- current = sorted[i][0];
597
- break;
598
- }
599
- name.value = current;
600
- const index = names.indexOf(current);
601
- isMobile.value = width.value < mb;
602
- xs.value = index === 0;
603
- sm.value = index === 1;
604
- md.value = index === 2;
605
- lg.value = index === 3;
606
- xl.value = index === 4;
607
- xxl.value = index === 5;
608
- smAndUp.value = index >= 1;
609
- mdAndUp.value = index >= 2;
610
- lgAndUp.value = index >= 3;
611
- xlAndUp.value = index >= 4;
612
- xxlAndUp.value = index >= 5;
613
- smAndDown.value = index <= 1;
614
- mdAndDown.value = index <= 2;
615
- lgAndDown.value = index <= 3;
616
- xlAndDown.value = index <= 4;
617
- xxlAndDown.value = index <= 5;
618
- }
619
- return {
620
- breakpoints,
621
- name: readonly(name),
622
- width: readonly(width),
623
- height: readonly(height),
624
- isMobile: readonly(isMobile),
625
- xs: readonly(xs),
626
- sm: readonly(sm),
627
- md: readonly(md),
628
- lg: readonly(lg),
629
- xl: readonly(xl),
630
- xxl: readonly(xxl),
631
- smAndUp: readonly(smAndUp),
632
- mdAndUp: readonly(mdAndUp),
633
- lgAndUp: readonly(lgAndUp),
634
- xlAndUp: readonly(xlAndUp),
635
- xxlAndUp: readonly(xxlAndUp),
636
- smAndDown: readonly(smAndDown),
637
- mdAndDown: readonly(mdAndDown),
638
- lgAndDown: readonly(lgAndDown),
639
- xlAndDown: readonly(xlAndDown),
640
- xxlAndDown: readonly(xxlAndDown),
641
- update
642
- };
643
- }
644
- /**
645
- * Creates a new breakpoints context.
646
- *
647
- * @param options The options for the breakpoints context.
648
- * @template E The type of the breakpoints context.
649
- * @returns A new breakpoints context.
650
- *
651
- * @see https://0.vuetifyjs.com/composables/plugins/use-breakpoints
652
- *
653
- * @example
654
- * ```ts
655
- * import { createBreakpointsContext } from '@vuetify/v0'
656
- *
657
- * export const [useBreakpoints, provideBreakpoints, context] = createBreakpointsContext({
658
- * namespace: 'v0:breakpoints',
659
- * mobileBreakpoint: 'sm',
660
- * })
661
- * ```
662
- */
663
- function createBreakpointsContext(_options = {}) {
664
- const { namespace = "v0:breakpoints",...options } = _options;
665
- const [useBreakpointsContext, _provideBreakpointsContext] = createContext(namespace);
666
- const context = createBreakpoints(options);
667
- function provideBreakpointsContext(_context = context, app) {
668
- return _provideBreakpointsContext(_context, app);
669
- }
670
- return createTrinity(useBreakpointsContext, provideBreakpointsContext, context);
671
- }
672
- /**
673
- * Creates a new breakpoints plugin.
674
- *
675
- * @param options The options for the breakpoints plugin.
676
- * @template E The type of the breakpoints context.
677
- * @returns A new breakpoints plugin.
678
- *
679
- * @see https://0.vuetifyjs.com/composables/plugins/use-breakpoints
680
- *
681
- * @example
682
- * ```ts
683
- * import { createApp } from 'vue'
684
- * import { createBreakpointsPlugin } from '@vuetify/v0'
685
- * import App from './App.vue'
686
- *
687
- * const app = createApp(App)
688
- *
689
- * app.use(
690
- * createBreakpointsPlugin({
691
- * namespace: 'v0:breakpoints',
692
- * mobileBreakpoint: 'sm',
693
- * breakpoints: {
694
- * xs: 0,
695
- * sm: 680,
696
- * md: 1024,
697
- * lg: 1280,
698
- * xl: 1920,
699
- * xxl: 2560,
700
- * },
701
- * })
702
- * )
703
- *
704
- * app.mount('#app')
705
- * ```
706
- */
707
- function createBreakpointsPlugin(_options = {}) {
708
- const { namespace = "v0:breakpoints",...options } = _options;
709
- const [, provideBreakpointsContext, context] = createBreakpointsContext({
710
- ...options,
711
- namespace
712
- });
713
- return createPlugin({
714
- namespace,
715
- provide: (app) => {
716
- provideBreakpointsContext(context, app);
717
- },
718
- setup: (app) => {
719
- app.mixin({ mounted() {
720
- if (this.$parent !== null) return;
721
- const hydration = useHydration();
722
- function listener() {
723
- context.update();
724
- }
725
- const unwatch = watch(hydration.isHydrated, (hydrated) => {
726
- if (hydrated) listener();
727
- }, { immediate: true });
728
- window.addEventListener("resize", listener, { passive: true });
729
- onScopeDispose(() => {
730
- window.removeEventListener("resize", listener);
731
- unwatch();
732
- }, true);
733
- } });
734
- }
735
- });
736
- }
737
- /**
738
- * Returns the current breakpoints instance.
739
- *
740
- * @param namespace The namespace for the breakpoints context. Defaults to `v0:breakpoints`.
741
- * @returns The current breakpoints instance.
742
- *
743
- * @see https://0.vuetifyjs.com/composables/plugins/use-breakpoints
744
- *
745
- * @example
746
- * ```vue
747
- * <script setup lang="ts">
748
- * import { useBreakpoints } from '@vuetify/v0'
749
- *
750
- * const { isMobile, mdAndUp } = useBreakpoints()
751
- * <\/script>
752
- *
753
- * <template>
754
- * <div class="pa-4">
755
- * <p v-if="isMobile.value">Mobile layout active</p>
756
- * <p v-else-if="mdAndUp.value">Medium and up layout active</p>
757
- * </div>
758
- * </template>
759
- * ```
760
- */
761
- function useBreakpoints(namespace = "v0:breakpoints") {
762
- return useContext(namespace);
763
- }
764
-
765
- //#endregion
766
- //#region src/composables/useEventListener/index.ts
767
- /**
768
- * @module useEventListener
769
- *
770
- * @remarks
771
- * Event listener composable with automatic cleanup on scope disposal.
772
- *
773
- * Key features:
774
- * - Supports Window, Document, and HTMLElement targets
775
- * - Reactive targets, events, and listeners
776
- * - Event options support (capture, passive, once)
777
- * - Automatic removeEventListener on unmount
778
- * - Multiple overloads for type safety
779
- *
780
- * Perfect for safely managing event listeners in Vue components.
781
- */
782
- /**
783
- * Attaches an event listener to a target.
784
- *
785
- * @param target The target to attach the event listener to.
786
- * @param event The event to listen for.
787
- * @param listener The event listener.
788
- * @param options The event listener options.
789
- * @returns A function to remove the event listener.
790
- *
791
- * @see https://0.vuetifyjs.com/composables/system/use-event-listener
792
- */
793
- function useEventListener(target, event, listener, options) {
794
- const cleanups = [];
795
- function cleanup() {
796
- for (const fn of cleanups) fn();
797
- cleanups.length = 0;
798
- }
799
- function register(el, event$1, listener$1, options$1) {
800
- el.addEventListener(event$1, listener$1, options$1);
801
- return () => el.removeEventListener(event$1, listener$1, options$1);
802
- }
803
- const stopWatcher = watch(() => [
804
- toValue(target),
805
- toValue(event),
806
- unref(listener),
807
- toValue(options)
808
- ], ([el, events, listeners, opts]) => {
809
- cleanup();
810
- if (!el) return;
811
- const eventList = toArray(events);
812
- const listenerList = toArray(listeners);
813
- for (const event$1 of eventList) for (const listenerFn of listenerList) cleanups.push(register(el, event$1, listenerFn, opts));
814
- }, {
815
- immediate: true,
816
- flush: "post"
817
- });
818
- function stop() {
819
- stopWatcher();
820
- cleanup();
821
- }
822
- onScopeDispose(stop, true);
823
- return stop;
824
- }
825
- /**
826
- * Attaches an event listener to the window.
827
- *
828
- * @param event The event to listen for.
829
- * @param listener The event listener.
830
- * @param options The event listener options.
831
- * @template E The event type.
832
- * @returns A function to remove the event listener.
833
- *
834
- * @see https://0.vuetifyjs.com/composables/system/use-event-listener
835
- */
836
- function useWindowEventListener(event, listener, options) {
837
- return useEventListener(window, event, listener, options);
838
- }
839
- /**
840
- * Attaches an event listener to the document.
841
- *
842
- * @param event The event to listen for.
843
- * @param listener The event listener.
844
- * @param options The event listener options.
845
- * @template E The event type.
846
- * @returns A function to remove the event listener.
847
- *
848
- * @see https://0.vuetifyjs.com/composables/system/use-event-listener
849
- */
850
- function useDocumentEventListener(event, listener, options) {
851
- return useEventListener(document, event, listener, options);
852
- }
853
-
854
- //#endregion
855
- //#region src/composables/useLogger/adapters/consola.ts
856
- var ConsolaLoggerAdapter = class {
857
- consola;
858
- constructor(consolaInstance) {
859
- if (!consolaInstance) throw new Error("Consola instance is required for ConsolaLoggerAdapter");
860
- this.consola = consolaInstance;
861
- }
862
- debug(message, ...args) {
863
- this.consola.debug(message, ...args);
864
- }
865
- info(message, ...args) {
866
- this.consola.info(message, ...args);
867
- }
868
- warn(message, ...args) {
869
- this.consola.warn(message, ...args);
870
- }
871
- error(message, ...args) {
872
- this.consola.error(message, ...args);
873
- }
874
- trace(message, ...args) {
875
- if (this.consola.trace) this.consola.trace(message, ...args);
876
- else this.consola.debug(message, ...args);
877
- }
878
- fatal(message, ...args) {
879
- if (this.consola.fatal) this.consola.fatal(message, ...args);
880
- else this.consola.error("[FATAL]", message, ...args);
881
- }
882
- };
883
-
884
- //#endregion
885
- //#region src/composables/useLogger/adapters/pino.ts
886
- /**
887
- * Pino logger adapter implementation
888
- *
889
- * This adapter integrates with the Pino logging library,
890
- * providing high-performance structured logging optimized
891
- * for Node.js applications with minimal overhead.
892
- */
893
- var PinoLoggerAdapter = class {
894
- pino;
895
- constructor(pinoInstance) {
896
- if (!pinoInstance) throw new Error("Pino instance is required for PinoLoggerAdapter");
897
- this.pino = pinoInstance;
898
- }
899
- debug(message, ...args) {
900
- this.pino.debug(this.format(message, ...args));
901
- }
902
- info(message, ...args) {
903
- this.pino.info(this.format(message, ...args));
904
- }
905
- warn(message, ...args) {
906
- this.pino.warn(this.format(message, ...args));
907
- }
908
- error(message, ...args) {
909
- this.pino.error(this.format(message, ...args));
910
- }
911
- trace(message, ...args) {
912
- this.pino.trace(this.format(message, ...args));
913
- }
914
- fatal(message, ...args) {
915
- this.pino.fatal(this.format(message, ...args));
916
- }
917
- format(message, ...args) {
918
- if (args.length === 0) return { msg: message };
919
- if (args.length === 1 && /* @__PURE__ */ isObject(args[0])) return {
920
- ...args[0],
921
- msg: message
922
- };
923
- return {
924
- msg: message,
925
- args
926
- };
927
- }
928
- };
929
-
930
- //#endregion
931
- //#region src/composables/useLogger/adapters/v0.ts
932
- /**
933
- * Vuetify0.x logger adapter implementation
934
- *
935
- * This adapter provides console-based logging with proper formatting,
936
- * color coding, timestamps, and log level filtering for development
937
- * and production environments.
938
- */
939
- var Vuetify0LoggerAdapter = class {
940
- prefix;
941
- colors;
942
- timestamps;
943
- constructor(options = {}) {
944
- this.prefix = options.prefix || "v0";
945
- this.colors = options.colors !== false;
946
- this.timestamps = options.timestamps !== false;
947
- }
948
- debug(message, ...args) {
949
- this.log("debug", "debug", message, ...args);
950
- }
951
- info(message, ...args) {
952
- this.log("info", "info", message, ...args);
953
- }
954
- warn(message, ...args) {
955
- this.log("warn", "warn", message, ...args);
956
- }
957
- error(message, ...args) {
958
- this.log("error", "error", message, ...args);
959
- }
960
- trace(message, ...args) {
961
- this.log("trace", "trace", message, ...args);
962
- }
963
- fatal(message, ...args) {
964
- this.log("fatal", "error", message, ...args);
965
- }
966
- format(level, message, ...args) {
967
- return [[
968
- this.timestamps ? this.timestamp() : "",
969
- `[${this.prefix} ${level.toLowerCase()}]`,
970
- message
971
- ].filter(Boolean).join(" "), ...args];
972
- }
973
- timestamp() {
974
- if (!IN_BROWSER) return (/* @__PURE__ */ new Date()).toISOString();
975
- return (/* @__PURE__ */ new Date()).toTimeString().split(" ")[0] ?? "";
976
- }
977
- style(level) {
978
- if (!this.colors || !IN_BROWSER) return "";
979
- return {
980
- trace: "color: #64748b",
981
- debug: "color: #3b82f6",
982
- info: "color: #10b981",
983
- warn: "color: #f59e0b",
984
- error: "color: #ef4444",
985
- fatal: "color: #dc2626; font-weight: bold",
986
- silent: ""
987
- }[level] || "";
988
- }
989
- log(level, method, message, ...args) {
990
- const [formattedMessage, ...restArgs] = this.format(level, message, ...args);
991
- const style = this.style(level);
992
- if (IN_BROWSER && style && /* @__PURE__ */ isFunction(console[method])) console[method](`%c${formattedMessage}`, style, ...restArgs);
993
- else if (/* @__PURE__ */ isFunction(console[method])) console[method](formattedMessage, ...restArgs);
994
- }
995
- };
996
-
997
- //#endregion
998
- //#region src/composables/useLogger/index.ts
999
- /**
1000
- * @module useLogger
1001
- *
1002
- * @see https://0.vuetifyjs.com/composables/plugins/use-logger
1003
- *
1004
- * @remarks
1005
- * Logging composable with adapter pattern supporting console, consola, and pino.
1006
- *
1007
- * Key features:
1008
- * - Multiple log levels (trace, debug, info, warn, error, fatal)
1009
- * - Adapter pattern for console/consola/pino integration
1010
- * - Enable/disable logging
1011
- * - Fallback logger for undefined loggers
1012
- * - Context logging support
1013
- *
1014
- * Uses adapter pattern to abstract logging implementation.
1015
- */
1016
- /**
1017
- * Creates a new logger instance.
1018
- *
1019
- * @param options The options for the logger instance.
1020
- * @returns A new logger instance.
1021
- *
1022
- * @see https://0.vuetifyjs.com/composables/plugins/use-logger
1023
- *
1024
- * @example
1025
- * ```ts
1026
- * import { createLogger } from '@vuetify/v0'
1027
- *
1028
- * const logger = createLogger({
1029
- * level: 'debug',
1030
- * prefix: '[MyApp]',
1031
- * })
1032
- *
1033
- * logger.info('This is an info message')
1034
- * logger.debug('This is a debug message')
1035
- * logger.error('This is an error message')
1036
- * logger.level('debug')
1037
- * logger.debug('This debug message will now be logged')
1038
- * ```
1039
- */
1040
- function createLogger(options = {}) {
1041
- const { adapter = new Vuetify0LoggerAdapter({ prefix: options.prefix }), level: initialLevel = "info", enabled: initialEnabled = __LOGGER_ENABLED__ } = options;
1042
- const currentLevel = shallowRef(initialLevel);
1043
- const isEnabled = shallowRef(initialEnabled);
1044
- function value(level$1) {
1045
- return {
1046
- trace: 0,
1047
- debug: 1,
1048
- info: 2,
1049
- warn: 3,
1050
- error: 4,
1051
- fatal: 5,
1052
- silent: 6
1053
- }[level$1] ?? 2;
1054
- }
1055
- function can(level$1) {
1056
- if (!isEnabled.value) return false;
1057
- return value(level$1) >= value(currentLevel.value);
1058
- }
1059
- function format(message) {
1060
- return message;
1061
- }
1062
- function debug(message, ...args) {
1063
- if (can("debug")) adapter.debug(format(message), ...args);
1064
- }
1065
- function info(message, ...args) {
1066
- if (can("info")) adapter.info(format(message), ...args);
1067
- }
1068
- function warn(message, ...args) {
1069
- if (can("warn")) adapter.warn(format(message), ...args);
1070
- }
1071
- function error(message, ...args) {
1072
- if (can("error")) adapter.error(format(message), ...args);
1073
- }
1074
- function trace(message, ...args) {
1075
- if (can("trace")) adapter.trace?.(format(message), ...args);
1076
- }
1077
- function fatal(message, ...args) {
1078
- if (can("fatal")) adapter.fatal?.(format(message), ...args);
1079
- }
1080
- function level(newLevel) {
1081
- currentLevel.value = newLevel;
1082
- }
1083
- function current() {
1084
- return currentLevel.value;
1085
- }
1086
- function enabled() {
1087
- return isEnabled.value;
1088
- }
1089
- function enable() {
1090
- isEnabled.value = true;
1091
- }
1092
- function disable() {
1093
- isEnabled.value = false;
1094
- }
1095
- return {
1096
- debug,
1097
- info,
1098
- warn,
1099
- error,
1100
- trace,
1101
- fatal,
1102
- level,
1103
- current,
1104
- enabled,
1105
- enable,
1106
- disable
1107
- };
1108
- }
1109
- function createFallbackLogger(namespace = "v0:logger") {
1110
- function format(message, type) {
1111
- return `[${namespace} ${type}] ${message}`;
1112
- }
1113
- return {
1114
- debug: (message, ...args) => console.log(format(message, "debug"), ...args),
1115
- info: (message, ...args) => console.log(format(message, "info"), ...args),
1116
- warn: (message, ...args) => console.log(format(message, "warn"), ...args),
1117
- error: (message, ...args) => console.log(format(message, "error"), ...args),
1118
- trace: (message, ...args) => console.log(format(message, "trace"), ...args),
1119
- fatal: (message, ...args) => console.log(format(message, "fatal"), ...args),
1120
- level: () => {},
1121
- current: () => "info",
1122
- enabled: () => true,
1123
- enable: () => {},
1124
- disable: () => {}
1125
- };
1126
- }
1127
- /**
1128
- * Creates a new logger context.
1129
- *
1130
- * @param options The options for the logger context.
1131
- * @template E The type of the logger context.
1132
- * @returns A new logger context.
1133
- *
1134
- * @see https://0.vuetifyjs.com/composables/plugins/use-logger
1135
- *
1136
- * @example
1137
- * ```ts
1138
- * import { createLoggerContext } from '@vuetify/v0'
1139
- *
1140
- * export const [useAppLogger, provideAppLogger, appLogger] = createLoggerContext({
1141
- * namespace: 'app:logger',
1142
- * level: 'debug',
1143
- * })
1144
- * ```
1145
- */
1146
- function createLoggerContext(_options = {}) {
1147
- const { namespace = "v0:logger",...options } = _options;
1148
- const [useLoggerContext, _provideLoggerContext] = createContext(namespace);
1149
- const context = createLogger(options);
1150
- function provideLoggerContext(_context = context, app) {
1151
- return _provideLoggerContext(_context, app);
1152
- }
1153
- return createTrinity(useLoggerContext, provideLoggerContext, context);
1154
- }
1155
- /**
1156
- * Creates a new logger plugin.
1157
- *
1158
- * @param options The options for the logger plugin.
1159
- * @returns A new logger plugin.
1160
- *
1161
- * @see https://0.vuetifyjs.com/composables/plugins/use-logger
1162
- *
1163
- * @example
1164
- * ```ts
1165
- * import { createApp } from 'vue'
1166
- * import { createLoggerPlugin } from '@vuetify/v0'
1167
- * import App from './App.vue'
1168
- *
1169
- * const app = createApp(App)
1170
- *
1171
- * app.use(
1172
- * createLoggerPlugin({
1173
- * level: 'debug',
1174
- * prefix: '[MyApp]',
1175
- * })
1176
- * )
1177
- *
1178
- * app.mount('#app')
1179
- * ```
1180
- */
1181
- function createLoggerPlugin(_options = {}) {
1182
- const { namespace = "v0:logger",...options } = _options;
1183
- const [, provideLoggerContext, context] = createLoggerContext({
1184
- ...options,
1185
- namespace
1186
- });
1187
- return createPlugin({
1188
- namespace,
1189
- provide: (app) => {
1190
- provideLoggerContext(context, app);
1191
- },
1192
- setup: (_app) => {
1193
- if (process.env.NODE_ENV !== "production" && IN_BROWSER) window.__v0Logger__ = context;
1194
- }
1195
- });
1196
- }
1197
- /**
1198
- * Uses an existing or creates a new logger instance.
1199
- *
1200
- * @param namespace The namespace for the logger context. Defaults to `'v0:logger'`.
1201
- * @returns The logger instance.
1202
- *
1203
- * @see https://0.vuetifyjs.com/composables/plugins/use-logger
1204
- *
1205
- * @example
1206
- * ```ts
1207
- * import { useLogger } from '@vuetify/v0'
1208
- *
1209
- * const logger = useLogger()
1210
- *
1211
- * logger.info('This is an info message')
1212
- * logger.debug('This is a debug message')
1213
- * logger.error('This is an error message')
1214
- * logger.level('debug')
1215
- * logger.debug('This debug message will now be logged')
1216
- * ```
1217
- */
1218
- function useLogger(namespace = "v0:logger") {
1219
- const fallback = createFallbackLogger(namespace);
1220
- if (!getCurrentInstance()) return fallback;
1221
- try {
1222
- return useContext(namespace, fallback);
1223
- } catch {
1224
- return fallback;
1225
- }
1226
- }
1227
-
1228
- //#endregion
1229
- //#region src/composables/useRegistry/index.ts
1230
- /**
1231
- * @module useRegistry
1232
- *
1233
- * @remarks
1234
- * A foundational composable for managing collections of items (tickets) with:
1235
- * - Unique ID-based access
1236
- * - Index-based ordering
1237
- * - Value-based reverse lookup
1238
- * - Automatic reindexing
1239
- * - Optional event emission
1240
- * - Performance-optimized caching
1241
- *
1242
- * The registry serves as the base for many other composables in the system,
1243
- * including useSelection, useForm, useTimeline, and more.
1244
- */
1245
- /**
1246
- * Creates a new registry instance.
1247
- *
1248
- * @param options The options for the registry instance.
1249
- * @template Z The type of registry ticket that extends RegistryTicket. Use this to add custom properties to tickets.
1250
- * @template E The type of registry context that extends RegistryContext<Z>. Use this when extending the registry with additional methods.
1251
- * @returns A new registry instance.
1252
- *
1253
- * @see https://0.vuetifyjs.com/composables/registration/use-registry#use-registry
1254
- *
1255
- * @example
1256
- * ```ts
1257
- * import { useRegistry } from '@vuetify/v0'
1258
- *
1259
- * const registry = useRegistry()
1260
- *
1261
- * const ticket1 = registry.register({ id: 'user-1', value: { name: 'John' } })
1262
- * const ticket2 = registry.register({ id: 'user-2', value: { name: 'Jane' } })
1263
- *
1264
- * console.log(registry.size) // 2
1265
- * console.log(registry.get('user-1')) // { id: 'user-1', index: 0, value: { name: 'John' }, ... }
1266
- * ```
1267
- */
1268
- function useRegistry(options) {
1269
- const logger = useLogger();
1270
- const collection = /* @__PURE__ */ new Map();
1271
- const catalog = /* @__PURE__ */ new Map();
1272
- const directory = /* @__PURE__ */ new Map();
1273
- const cache = /* @__PURE__ */ new Map();
1274
- const listeners = /* @__PURE__ */ new Map();
1275
- const events = options?.events ?? false;
1276
- function emit(event, data = void 0) {
1277
- if (!events) return;
1278
- const cbs = listeners.get(event);
1279
- if (!cbs) return;
1280
- for (const cb of cbs) cb(data);
1281
- }
1282
- function on(event, cb) {
1283
- if (!events) {
1284
- logger.warn(`Attempted to register event listener for "${event}" but events are disabled.`);
1285
- return;
1286
- }
1287
- if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
1288
- listeners.get(event).add(cb);
1289
- }
1290
- function off(event, cb) {
1291
- listeners.get(event)?.delete(cb);
1292
- }
1293
- function dispose() {
1294
- if (listeners.size > 0) listeners.clear();
1295
- clear();
1296
- }
1297
- function get(id) {
1298
- return collection.get(id);
1299
- }
1300
- function upsert(id, patch = {}) {
1301
- const existing = get(id);
1302
- if (!existing) return register({
1303
- ...patch,
1304
- id
1305
- });
1306
- const hasValue = Object.prototype.hasOwnProperty.call(patch, "value");
1307
- let value = existing.value;
1308
- let valueIsIndex = existing.valueIsIndex;
1309
- if (hasValue) {
1310
- if (patch.value === void 0) {
1311
- value = existing.index;
1312
- valueIsIndex = true;
1313
- } else {
1314
- value = patch.value;
1315
- valueIsIndex = false;
1316
- }
1317
- if (!Object.is(value, existing.value)) {
1318
- unassign(existing.value, id);
1319
- assign(value, id);
1320
- }
1321
- }
1322
- const updated = {
1323
- ...existing,
1324
- ...patch,
1325
- id,
1326
- index: existing.index,
1327
- value,
1328
- valueIsIndex
1329
- };
1330
- collection.set(id, updated);
1331
- invalidate();
1332
- emit("update:ticket", updated);
1333
- return updated;
1334
- }
1335
- function browse(value) {
1336
- return catalog.get(value);
1337
- }
1338
- function lookup(index) {
1339
- return directory.get(index);
1340
- }
1341
- function has(id) {
1342
- return collection.has(id);
1343
- }
1344
- function assign(value, id) {
1345
- const bucket = catalog.get(value);
1346
- if (bucket) {
1347
- if (/* @__PURE__ */ isArray(bucket)) {
1348
- if (!bucket.includes(id)) bucket.push(id);
1349
- } else if (bucket !== id) catalog.set(value, [bucket, id]);
1350
- } else catalog.set(value, id);
1351
- }
1352
- function unassign(value, id) {
1353
- const bucket = catalog.get(value);
1354
- if (!bucket) return;
1355
- if (/* @__PURE__ */ isArray(bucket)) {
1356
- const next = bucket.filter((v) => v !== id);
1357
- if (next.length === 0) catalog.delete(value);
1358
- else if (next.length === 1) catalog.set(value, next[0]);
1359
- else catalog.set(value, next);
1360
- } else if (bucket === id) catalog.delete(value);
1361
- }
1362
- function keys() {
1363
- const cached = cache.get("keys");
1364
- if (cached != void 0) return cached;
1365
- const keys$1 = Array.from(collection.keys());
1366
- cache.set("keys", keys$1);
1367
- return keys$1;
1368
- }
1369
- function values() {
1370
- const cached = cache.get("values");
1371
- if (!/* @__PURE__ */ isUndefined(cached)) return cached;
1372
- const values$1 = Array.from(collection.values());
1373
- cache.set("values", values$1);
1374
- return values$1;
1375
- }
1376
- function entries() {
1377
- const cached = cache.get("entries");
1378
- if (!/* @__PURE__ */ isUndefined(cached)) return cached;
1379
- const entries$1 = Array.from(collection.entries());
1380
- cache.set("entries", entries$1);
1381
- return entries$1;
1382
- }
1383
- function clear() {
1384
- if (collection.size > 0) collection.clear();
1385
- if (catalog.size > 0) catalog.clear();
1386
- if (directory.size > 0) directory.clear();
1387
- invalidate();
1388
- emit("clear:registry");
1389
- }
1390
- function invalidate() {
1391
- if (cache.size > 0) cache.clear();
1392
- }
1393
- function reindex() {
1394
- if (catalog.size > 0) catalog.clear();
1395
- if (directory.size > 0) directory.clear();
1396
- invalidate();
1397
- let index = 0;
1398
- for (const ticket of values()) {
1399
- if (ticket.index !== index) {
1400
- ticket.index = index;
1401
- if (ticket.valueIsIndex) ticket.value = index;
1402
- }
1403
- directory.set(index, ticket.id);
1404
- assign(ticket.value, ticket.id);
1405
- index++;
1406
- }
1407
- emit("reindex:registry");
1408
- }
1409
- function register(registration = {}) {
1410
- const size = collection.size;
1411
- const id = registration.id ?? /* @__PURE__ */ genId();
1412
- if (has(id)) {
1413
- logger.warn(`Ticket with id "${id}" already exists in the registry. Skipping registration.`);
1414
- return get(id);
1415
- }
1416
- const valueIsUndefined = /* @__PURE__ */ isUndefined(registration.value);
1417
- const index = registration.index ?? size;
1418
- const value = valueIsUndefined ? index : registration.value;
1419
- const valueIsIndex = valueIsUndefined;
1420
- const ticket = {
1421
- ...registration,
1422
- id,
1423
- index,
1424
- value,
1425
- valueIsIndex
1426
- };
1427
- collection.set(ticket.id, ticket);
1428
- directory.set(ticket.index, ticket.id);
1429
- assign(ticket.value, ticket.id);
1430
- invalidate();
1431
- emit("register:ticket", ticket);
1432
- return ticket;
1433
- }
1434
- function unregister(id) {
1435
- const ticket = collection.get(id);
1436
- if (!ticket) return;
1437
- collection.delete(ticket.id);
1438
- directory.delete(ticket.index);
1439
- unassign(ticket.value, ticket.id);
1440
- invalidate();
1441
- emit("unregister:ticket", ticket);
1442
- reindex();
1443
- }
1444
- function seek(direction = "first", from, predicate) {
1445
- if (collection.size === 0) return void 0;
1446
- const tickets = values();
1447
- const index = /* @__PURE__ */ isUndefined(from) ? void 0 : Math.max(0, Math.min(from, tickets.length - 1));
1448
- if (direction === "last") {
1449
- const start = /* @__PURE__ */ isUndefined(index) ? tickets.length - 1 : index;
1450
- for (let i = start; i >= 0; i--) {
1451
- const ticket = tickets[i];
1452
- if (!predicate || predicate(ticket)) return ticket;
1453
- }
1454
- } else {
1455
- const start = /* @__PURE__ */ isUndefined(index) ? 0 : index;
1456
- for (let i = start; i < tickets.length; i++) {
1457
- const ticket = tickets[i];
1458
- if (!predicate || predicate(ticket)) return ticket;
1459
- }
1460
- }
1461
- }
1462
- return {
1463
- collection,
1464
- emit,
1465
- on,
1466
- off,
1467
- dispose,
1468
- has,
1469
- keys,
1470
- clear,
1471
- browse,
1472
- entries,
1473
- values,
1474
- lookup,
1475
- get,
1476
- upsert,
1477
- register,
1478
- unregister,
1479
- reindex,
1480
- seek,
1481
- onboard(registrations) {
1482
- return registrations.map((registration) => this.register(registration));
1483
- },
1484
- get size() {
1485
- return collection.size;
1486
- }
1487
- };
1488
- }
1489
- /**
1490
- * Creates a new registry context.
1491
- *
1492
- * @param namespace The namespace for the registry context.
1493
- * @param options The options for the registry context.
1494
- * @template Z The type of registry ticket that extends RegistryTicket. Use this to add custom properties to tickets.
1495
- * @template E The type of registry context that extends RegistryContext<Z>. Use this when extending the registry with additional methods.
1496
- * @returns A new registry context.
1497
- *
1498
- * @see https://0.vuetifyjs.com/composables/registration/use-registry#create-registry-context
1499
- *
1500
- * @example
1501
- * ```ts
1502
- * import { createRegistryContext } from '@vuetify/v0'
1503
- *
1504
- * export const [useItems, provideItems, items] = createRegistryContext('items')
1505
- *
1506
- * // In a parent component:
1507
- * provideItems()
1508
- *
1509
- * // In a child component:
1510
- * const items = useItems()
1511
- * items.register({ id: 'item-1', value: 'Value 1' })
1512
- * ```
1513
- */
1514
- function createRegistryContext(_options) {
1515
- const { namespace,...options } = _options;
1516
- const [useRegistryContext, _provideRegistryContext] = createContext(namespace);
1517
- const context = useRegistry(options);
1518
- function provideRegistryContext(_context = context, app) {
1519
- return _provideRegistryContext(_context, app);
1520
- }
1521
- return createTrinity(useRegistryContext, provideRegistryContext, context);
1522
- }
1523
-
1524
- //#endregion
1525
- //#region src/composables/useSelection/index.ts
1526
- /**
1527
- * @module useSelection
1528
- *
1529
- * @remarks
1530
- * Base composable for managing selected items in a collection with Set-based tracking.
1531
- *
1532
- * Key features:
1533
- * - Set-based selectedIds for O(1) selection checks
1534
- * - Mandatory selection mode (prevents deselecting last item)
1535
- * - Auto-enrollment option (selects non-disabled items on register)
1536
- * - Disabled item filtering
1537
- * - Computed selectedItems and selectedValues Sets
1538
- *
1539
- * Extends useRegistry and serves as the base for useSingle, useGroup, useStep, and useFeatures.
1540
- */
1541
- /**
1542
- * Creates a new selection instance for managing multiple selected items.
1543
- *
1544
- * Extends `useRegistry` with selection tracking via a reactive `Set` of selected IDs.
1545
- * Supports disabled items, mandatory selection enforcement, and auto-enrollment.
1546
- *
1547
- * @param options The options for the selection instance.
1548
- * @template Z The type of the selection ticket.
1549
- * @template E The type of the selection context.
1550
- * @returns A new selection instance with selection management methods.
1551
- *
1552
- * @remarks
1553
- * **Key Features:**
1554
- * - Multi-selection support (unlike `useSingle` which enforces single selection)
1555
- * - Set-based `selectedIds` tracking for efficient lookups
1556
- * - Computed `selectedItems` and `selectedValues` for reactive access
1557
- * - Each ticket gets `isSelected`, `select()`, `unselect()`, and `toggle()` methods
1558
- * - Disabled items cannot be selected
1559
- * - Mandatory mode prevents deselecting the last item
1560
- * - Force mode auto-selects first non-disabled item on registration
1561
- * - Enroll option auto-selects all non-disabled items on registration
1562
- *
1563
- * **Inheritance Chain:**
1564
- * `useRegistry` → `createSelection` → `createSingle`/`createGroup` → `createStep`
1565
- *
1566
- * @see https://0.vuetifyjs.com/composables/selection/use-selection
1567
- *
1568
- * @example
1569
- * ```ts
1570
- * import { createSelection } from '@vuetify/v0'
1571
- *
1572
- * const selection = createSelection({ mandatory: true })
1573
- *
1574
- * selection.onboard([
1575
- * { id: 'item-1', value: 'Item 1' },
1576
- * { id: 'item-2', value: 'Item 2', disabled: true },
1577
- * { id: 'item-3', value: 'Item 3' },
1578
- * ])
1579
- *
1580
- * selection.select('item-1')
1581
- * selection.select('item-3')
1582
- *
1583
- * console.log(selection.selectedIds) // Set { 'item-1', 'item-3' }
1584
- * console.log(Array.from(selection.selectedValues.value)) // ['Item 1', 'Item 3']
1585
- * ```
1586
- */
1587
- function createSelection(_options = {}) {
1588
- const { disabled = false, enroll = false, mandatory = false, multiple = false,...options } = _options;
1589
- const registry = useRegistry(options);
1590
- const selectedIds = shallowReactive(/* @__PURE__ */ new Set());
1591
- const selectedItems = computed(() => {
1592
- return new Set(Array.from(selectedIds).map((id) => registry.get(id)));
1593
- });
1594
- const selectedValues = computed(() => {
1595
- return new Set(Array.from(selectedItems.value).map((item) => item?.value));
1596
- });
1597
- function seek(direction = "first", from) {
1598
- return registry.seek(direction, from, (ticket) => !toValue(ticket.disabled));
1599
- }
1600
- function mandate() {
1601
- if (!mandatory || registry.size === 0 || selectedIds.size > 0) return;
1602
- const ticket = seek("first");
1603
- if (ticket) select(ticket.id);
1604
- }
1605
- function select(id) {
1606
- const item = registry.get(id);
1607
- if (!item || toValue(item.disabled)) return;
1608
- if (!multiple) selectedIds.clear();
1609
- selectedIds.add(id);
1610
- }
1611
- function unselect(id) {
1612
- if (mandatory && selectedIds.size === 1) return;
1613
- selectedIds.delete(id);
1614
- }
1615
- function toggle(id) {
1616
- if (selected(id)) unselect(id);
1617
- else select(id);
1618
- }
1619
- function selected(id) {
1620
- return selectedIds.has(id);
1621
- }
1622
- function register(registration = {}) {
1623
- const id = registration.id ?? /* @__PURE__ */ genId();
1624
- const item = {
1625
- disabled: false,
1626
- ...registration,
1627
- id,
1628
- isSelected: toRef(() => selected(id)),
1629
- select: () => select(id),
1630
- unselect: () => unselect(id),
1631
- toggle: () => toggle(id)
1632
- };
1633
- const ticket = registry.register(item);
1634
- if (enroll && !toValue(item.disabled)) selectedIds.add(ticket.id);
1635
- if (mandatory === "force") mandate();
1636
- return ticket;
1637
- }
1638
- function unregister(id) {
1639
- selectedIds.delete(id);
1640
- registry.unregister(id);
1641
- }
1642
- function reset() {
1643
- registry.clear();
1644
- selectedIds.clear();
1645
- mandate();
1646
- }
1647
- return {
1648
- ...registry,
1649
- disabled,
1650
- selectedIds,
1651
- selectedItems,
1652
- selectedValues,
1653
- register,
1654
- unregister,
1655
- reset,
1656
- mandate,
1657
- seek,
1658
- select,
1659
- unselect,
1660
- toggle,
1661
- selected,
1662
- get size() {
1663
- return registry.size;
1664
- }
1665
- };
1666
- }
1667
- /**
1668
- * Creates a new selection context.
1669
- *
1670
- * @param namespace The namespace for the selection context.
1671
- * @param options The options for the selection context.
1672
- * @template Z The type of the selection ticket.
1673
- * @template E The type of the selection context.
1674
- * @returns A new selection context.
1675
- *
1676
- * @see https://0.vuetifyjs.com/composables/selection/use-selection
1677
- *
1678
- * @example
1679
- * ```ts
1680
- * import { createSelectionContext } from '@vuetify/v0'
1681
- *
1682
- * export const [useCheckboxes, provideCheckboxes, checkboxes] = createSelectionContext('checkboxes')
1683
- *
1684
- * // In a parent component:
1685
- * provideCheckboxes()
1686
- *
1687
- * // In a child component:
1688
- * const checkboxes = useCheckboxes()
1689
- * checkboxes.select('checkbox-1')
1690
- * ```
1691
- */
1692
- function createSelectionContext(_options) {
1693
- const { namespace,...options } = _options;
1694
- const [useSelectionContext, _provideSelectionContext] = createContext(namespace);
1695
- const context = createSelection(options);
1696
- function provideSelectionContext(_context = context, app) {
1697
- return _provideSelectionContext(_context, app);
1698
- }
1699
- return createTrinity(useSelectionContext, provideSelectionContext, context);
1700
- }
1701
- /**
1702
- * Returns the current selection instance.
1703
- *
1704
- * @param namespace The namespace for the selection context. Defaults to `'v0:selection'`.
1705
- * @returns The current selection instance.
1706
- *
1707
- * @see https://0.vuetifyjs.com/composables/selection/use-selection
1708
- *
1709
- * @example
1710
- * ```vue
1711
- * <script setup lang="ts">
1712
- * import { useSelection } from '@vuetify/v0'
1713
- *
1714
- * const selection = useSelection()
1715
- * <\/script>
1716
- *
1717
- * <template>
1718
- * <div>
1719
- * <p>Selected: {{ selection.selectedIds.size }}</p>
1720
- * </div>
1721
- * </template>
1722
- * ```
1723
- */
1724
- function useSelection(namespace = "v0:selection") {
1725
- return useContext(namespace);
1726
- }
1727
-
1728
- //#endregion
1729
- //#region src/composables/useGroup/index.ts
1730
- /**
1731
- * @module useGroup
1732
- *
1733
- * @remarks
1734
- * Multi-selection composable that extends useSelection with batch operation support.
1735
- *
1736
- * Key features:
1737
- * - Batch operations (select/unselect/toggle accept ID | ID[])
1738
- * - selectedIndexes computed Set for position-based tracking
1739
- * - Perfect for checkboxes, multi-select dropdowns, filter panels
1740
- *
1741
- * Inheritance chain: useRegistry → useSelection → useGroup
1742
- * Extended by: useFeatures
1743
- */
1744
- /**
1745
- * Creates a new group instance with batch selection operations.
1746
- *
1747
- * Extends `createSelection` to support selecting, unselecting, and toggling multiple items
1748
- * at once by passing an array of IDs. Adds `selectedIndexes` computed property.
1749
- *
1750
- * @param options The options for the group instance.
1751
- * @template Z The type of the group ticket.
1752
- * @template E The type of the group context.
1753
- * @returns A new group instance with batch selection support.
1754
- *
1755
- * @remarks
1756
- * **Key Differences from `createSelection`:**
1757
- * - `select()` accepts `ID | ID[]` for batch operations
1758
- * - `unselect()` accepts `ID | ID[]` for batch operations
1759
- * - `toggle()` accepts `ID | ID[]` for batch operations
1760
- * - Adds `selectedIndexes` computed Set for getting selected item indexes
1761
- * - Perfect for checkboxes, multi-select dropdowns, and bulk operations
1762
- *
1763
- * **Batch Operations:**
1764
- * - Single ID: `group.select('item-1')`
1765
- * - Array of IDs: `group.select(['item-1', 'item-2', 'item-3'])`
1766
- * - Uses `toArray()` utility internally to normalize input
1767
- * - Disabled items are automatically skipped in batch operations
1768
- * - Non-existent IDs are silently ignored
1769
- *
1770
- * **Inheritance Chain:**
1771
- * `useRegistry` → `createSelection` → `createGroup`
1772
- *
1773
- * **Used By:**
1774
- * - `createFeatures` for feature flag management with multiple selections
1775
- *
1776
- * @see https://0.vuetifyjs.com/composables/selection/use-group
1777
- *
1778
- * @example
1779
- * ```ts
1780
- * import { createGroup } from '@vuetify/v0'
1781
- *
1782
- * const checkboxes = createGroup()
1783
- *
1784
- * checkboxes.onboard([
1785
- * { id: 'option-a', value: 'Option A' },
1786
- * { id: 'option-b', value: 'Option B' },
1787
- * { id: 'option-c', value: 'Option C' },
1788
- * ])
1789
- *
1790
- * // Select multiple items at once
1791
- * checkboxes.select(['option-a', 'option-c'])
1792
- *
1793
- * console.log(checkboxes.selectedIds) // Set { 'option-a', 'option-c' }
1794
- * console.log(Array.from(checkboxes.selectedIndexes.value)) // [0, 2]
1795
- *
1796
- * // Toggle operations
1797
- * checkboxes.toggle(['option-a', 'option-b'])
1798
- * console.log(checkboxes.selectedIds) // Set { 'option-b', 'option-c' }
1799
- * ```
1800
- */
1801
- function createGroup(_options = {}) {
1802
- const { mandatory = false, multiple = true,...options } = _options;
1803
- const registry = createSelection({
1804
- ...options,
1805
- mandatory,
1806
- multiple
1807
- });
1808
- const selectedIndexes = computed(() => {
1809
- return new Set(Array.from(registry.selectedItems.value).map((item) => item?.index));
1810
- });
1811
- function select(ids) {
1812
- for (const id of toArray(ids)) registry.select(id);
1813
- }
1814
- function unselect(ids) {
1815
- for (const id of toArray(ids)) registry.unselect(id);
1816
- }
1817
- function toggle(ids) {
1818
- for (const id of toArray(ids)) registry.toggle(id);
1819
- }
1820
- return {
1821
- ...registry,
1822
- select,
1823
- unselect,
1824
- toggle,
1825
- selectedIndexes,
1826
- get size() {
1827
- return registry.size;
1828
- }
1829
- };
1830
- }
1831
- /**
1832
- * Creates a new group context.
1833
- *
1834
- * @param namespace The namespace for the group context.
1835
- * @param options The options for the group context.
1836
- * @template Z The type of the group ticket.
1837
- * @template E The type of the group context.
1838
- * @returns A new group context.
1839
- *
1840
- * @see https://0.vuetifyjs.com/composables/selection/use-group
1841
- *
1842
- * @example
1843
- * ```ts
1844
- * import { createGroupContext } from '@vuetify/v0'
1845
- *
1846
- * export const [useMyGroup, provideMyGroup, myGroup] = createGroupContext('my-group')
1847
- *
1848
- * // In a parent component:
1849
- * provideMyGroup()
1850
- *
1851
- * // In a child component:
1852
- * const group = useMyGroup()
1853
- * ```
1854
- */
1855
- function createGroupContext(_options) {
1856
- const { namespace,...options } = _options;
1857
- const [useGroupContext, _provideGroupContext] = createContext(namespace);
1858
- const context = createGroup(options);
1859
- function provideGroupContext(_context = context, app) {
1860
- return _provideGroupContext(_context, app);
1861
- }
1862
- return createTrinity(useGroupContext, provideGroupContext, context);
1863
- }
1864
- /**
1865
- * Returns the current group instance.
1866
- *
1867
- * @param namespace The namespace for the group context. Defaults to `'v0:group'`.
1868
- * @returns The current group instance.
1869
- *
1870
- * @see https://0.vuetifyjs.com/composables/selection/use-group
1871
- *
1872
- * @example
1873
- * ```vue
1874
- * <script setup lang="ts">
1875
- * import { useGroup } from '@vuetify/v0'
1876
- *
1877
- * const group = useGroup()
1878
- * <\/script>
1879
- *
1880
- * <template>
1881
- * <div>
1882
- * <p>Selected: {{ group.selectedIds.size }}</p>
1883
- * </div>
1884
- * </template>
1885
- * ```
1886
- */
1887
- function useGroup(namespace) {
1888
- return useContext(namespace);
1889
- }
1890
-
1891
- //#endregion
1892
- //#region src/composables/useTokens/index.ts
1893
- /**
1894
- * @module useTokens
1895
- *
1896
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
1897
- *
1898
- * @remarks
1899
- * Design token registry with alias resolution and W3C Design Tokens format support.
1900
- *
1901
- * Key features:
1902
- * - Alias resolution with circular reference detection
1903
- * - Nested token flattening with dot notation
1904
- * - W3C Design Tokens format ($value, $type, $description, $extensions)
1905
- * - Path-based resolution (e.g., {colors}.blue.500)
1906
- * - Resolution caching for performance (~28,590 ops/sec)
1907
- *
1908
- * Used by useTheme, useLocale, and useFeatures for token-based configuration.
1909
- */
1910
- /**
1911
- * Creates a new token instance.
1912
- *
1913
- * @param tokens The tokens to use.
1914
- * @param options The options for the token instance.
1915
- * @template Z The type of the token ticket.
1916
- * @template E The type of the token context.
1917
- * @returns A new token instance.
1918
- *
1919
- * @see https://www.designtokens.org/tr/drafts/format/
1920
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
1921
- *
1922
- * @example
1923
- * ```ts
1924
- * import { useTokens } from '@vuetify/v0'
1925
- *
1926
- * const tokens = useTokens({
1927
- * colors: {
1928
- * primary: '#3b82f6',
1929
- * secondary: '{colors.primary}', // Alias reference
1930
- * },
1931
- * })
1932
- *
1933
- * console.log(tokens.resolve('{colors.primary}')) // '#3b82f6'
1934
- * console.log(tokens.resolve('{colors.secondary}')) // '#3b82f6'
1935
- * ```
1936
- */
1937
- function createTokens(tokens = {}, options = {}) {
1938
- const logger = useLogger();
1939
- const registry = useRegistry(options);
1940
- const cache = /* @__PURE__ */ new Map();
1941
- registry.onboard(flatten(tokens, options.prefix, !!options.flat));
1942
- function isAlias(token) {
1943
- return /* @__PURE__ */ isString(token) && token.length > 2 && token[0] === "{" && token.at(-1) === "}";
1944
- }
1945
- function isTokenAlias(value) {
1946
- return /* @__PURE__ */ isObject(value) && "$value" in value;
1947
- }
1948
- function resolve(token, visited = /* @__PURE__ */ new Set()) {
1949
- const cacheKey = /* @__PURE__ */ isString(token) ? token : JSON.stringify(token);
1950
- const cached = cache.get(cacheKey);
1951
- if (cached !== void 0) return cached;
1952
- const reference = isTokenAlias(token) ? token.$value : token;
1953
- const clean = /* @__PURE__ */ isString(reference) && isAlias(reference) ? reference.slice(1, -1) : String(reference);
1954
- if (visited.has(clean)) {
1955
- logger.warn(`Circular alias detected for "${clean}"`);
1956
- cache.set(cacheKey, void 0);
1957
- return;
1958
- }
1959
- visited.add(clean);
1960
- let found = registry.get(clean);
1961
- let segments = [];
1962
- if (!found && clean.includes(".")) {
1963
- const parts = clean.split(".");
1964
- for (let i = parts.length - 1; i > 0; i--) {
1965
- const prefix = parts.slice(0, i).join(".");
1966
- const suffix = parts.slice(i);
1967
- const candidate = registry.get(prefix);
1968
- if (candidate?.value !== void 0) {
1969
- found = candidate;
1970
- segments = suffix;
1971
- break;
1972
- }
1973
- }
1974
- }
1975
- if (found?.value === void 0) {
1976
- logger.warn(`Alias not found for "${String(reference)}"`);
1977
- cache.set(cacheKey, void 0);
1978
- return;
1979
- }
1980
- let result;
1981
- let current = found.value;
1982
- if (segments.length > 0) {
1983
- if (isTokenAlias(current)) current = current.$value;
1984
- for (const segment of segments) {
1985
- if (!/* @__PURE__ */ isObject(current) || !(segment in current)) {
1986
- current = void 0;
1987
- break;
1988
- }
1989
- current = current[segment];
1990
- if (isTokenAlias(current)) current = current.$value;
1991
- }
1992
- if (current === void 0) {
1993
- logger.warn(`Path not found inside "${clean}": ${segments.join(".")}`);
1994
- cache.set(cacheKey, void 0);
1995
- return;
1996
- }
1997
- result = current;
1998
- } else if (isTokenAlias(current)) {
1999
- const inner = current.$value;
2000
- if (/* @__PURE__ */ isString(inner) && isAlias(inner)) return resolve(inner, visited);
2001
- result = inner;
2002
- } else if (/* @__PURE__ */ isString(current) && isAlias(current)) return resolve(current, visited);
2003
- else result = current;
2004
- cache.set(cacheKey, result);
2005
- return result;
2006
- }
2007
- return {
2008
- ...registry,
2009
- resolve,
2010
- isAlias,
2011
- get size() {
2012
- return registry.size;
2013
- }
2014
- };
2015
- }
2016
- /**
2017
- * Creates a new token context.
2018
- *
2019
- * @param namespace The namespace for the token context.
2020
- * @param tokens The tokens to use.
2021
- * @template Z The type of the token ticket.
2022
- * @template E The type of the token context.
2023
- * @returns A new token context.
2024
- *
2025
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
2026
- *
2027
- * @example
2028
- * ```ts
2029
- * import { createTokensContext } from '@vuetify/v0'
2030
- *
2031
- * export const [useTokens, provideTokens, context] = createTokensContext({
2032
- * namespace: 'v0:tokens',
2033
- * tokens: {
2034
- * colors: {
2035
- * primary: '#3b82f6',
2036
- * secondary: '{colors.primary}', // Alias reference
2037
- * },
2038
- * },
2039
- * })
2040
- * ```
2041
- */
2042
- function createTokensContext(_options) {
2043
- const { namespace, tokens = {},...options } = _options;
2044
- const [useTokensContext, _provideTokensContext] = createContext(namespace);
2045
- const context = createTokens(tokens, options);
2046
- function provideTokensContext(_context = context, app) {
2047
- return _provideTokensContext(_context, app);
2048
- }
2049
- return createTrinity(useTokensContext, provideTokensContext, context);
2050
- }
2051
- /**
2052
- * Returns the current tokens instance.
2053
- *
2054
- * @param namespace The namespace for the tokens context. Defaults to `'v0:tokens'`.
2055
- * @returns The current tokens instance.
2056
- *
2057
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
2058
- *
2059
- * @example
2060
- * ```vue
2061
- * <script setup lang="ts">
2062
- * import { useTokens } from '@vuetify/v0'
2063
- *
2064
- * const tokens = useTokens()
2065
- * <\/script>
2066
- * ```
2067
- */
2068
- function useTokens(namespace = "v0:tokens") {
2069
- return useContext(namespace);
2070
- }
2071
- /**
2072
- * Flattens a nested collection of tokens into a flat array of tokens.
2073
- * Each token is represented by an object containing its ID & value.
2074
- * @param tokens The collection of tokens to flatten.
2075
- * @param prefix An optional prefix to prepend to each token ID.
2076
- * @returns An array of flattened tokens, each with an ID and value.
2077
- */
2078
- function flatten(tokens, prefix = "", flat = false) {
2079
- const flattened = [];
2080
- const stack = [{
2081
- tokens,
2082
- prefix,
2083
- flat
2084
- }];
2085
- while (stack.length > 0) {
2086
- const { tokens: currentTokens, prefix: currentPrefix, flat: flat$1 } = stack.pop();
2087
- const meta = {};
2088
- for (const k in currentTokens) if (k.startsWith("$")) meta[k] = currentTokens[k];
2089
- if (Object.keys(meta).length > 0 && currentPrefix) flattened.push({
2090
- id: currentPrefix,
2091
- value: meta
2092
- });
2093
- for (const key in currentTokens) {
2094
- if (key.startsWith("$")) continue;
2095
- const value = currentTokens[key];
2096
- const id = currentPrefix ? `${currentPrefix}.${key}` : key;
2097
- if (!/* @__PURE__ */ isObject(value)) {
2098
- flattened.push({
2099
- id,
2100
- value
2101
- });
2102
- continue;
2103
- }
2104
- if ("$value" in value) {
2105
- flattened.push({
2106
- id,
2107
- value
2108
- });
2109
- const inner = value.$value;
2110
- if (/* @__PURE__ */ isObject(inner) && !flat$1) for (const innerKey in inner) {
2111
- if (innerKey.startsWith("$")) continue;
2112
- const child = inner[innerKey];
2113
- const childId = `${id}.${innerKey}`;
2114
- if (!/* @__PURE__ */ isObject(child)) flattened.push({
2115
- id: childId,
2116
- value: child
2117
- });
2118
- else if ("$value" in child) flattened.push({
2119
- id: childId,
2120
- value: child
2121
- });
2122
- else stack.push({
2123
- tokens: child,
2124
- prefix: childId,
2125
- flat: flat$1
2126
- });
2127
- }
2128
- continue;
2129
- }
2130
- if (flat$1) {
2131
- flattened.push({
2132
- id,
2133
- value
2134
- });
2135
- continue;
2136
- }
2137
- stack.push({
2138
- tokens: value,
2139
- prefix: id,
2140
- flat: flat$1
2141
- });
2142
- }
2143
- }
2144
- return flattened;
2145
- }
2146
-
2147
- //#endregion
2148
- //#region src/composables/useFeatures/index.ts
2149
- /**
2150
- * @module useFeatures
2151
- *
2152
- * @see https://0.vuetifyjs.com/composables/plugins/use-features
2153
- *
2154
- * @remarks
2155
- * Feature flag system with boolean and token-based features.
2156
- *
2157
- * Key features:
2158
- * - Boolean features (true/false activation)
2159
- * - Token features with $variation support
2160
- * - Auto-selection of enabled features
2161
- * - Multi-select support for feature combinations
2162
- * - Perfect for A/B testing, progressive rollout, feature toggles
2163
- *
2164
- * Inheritance chain: useRegistry → createSelection → createGroup → createFeatures
2165
- * Integrates with useTokens for token-based features.
2166
- */
2167
- /**
2168
- * Creates a new features instance.
2169
- *
2170
- * @param options The options for the features instance.
2171
- * @template Z The type of the feature ticket.
2172
- * @template E The type of the feature context.
2173
- * @returns A new features instance.
2174
- *
2175
- * @see https://0.vuetifyjs.com/composables/plugins/use-features
2176
- *
2177
- * @example
2178
- * ```ts
2179
- * import { createFeatures } from '@vuetify/v0'
2180
- *
2181
- * const [useFeatures, provideFeaturesContext, context] = createFeatures({
2182
- * namespace: 'v0:features',
2183
- * features: {
2184
- * 'dark-mode': true,
2185
- * 'theme-color': { $variation: 'blue' },
2186
- * },
2187
- * })
2188
- * ```
2189
- */
2190
- function createFeatures(_options = {}) {
2191
- const { features,...options } = _options;
2192
- const tokens = createTokens(features, { flat: true });
2193
- const registry = createGroup(options);
2194
- for (const [id, { value }] of tokens.entries()) register({
2195
- id,
2196
- value
2197
- });
2198
- function variation(id, fallback = null) {
2199
- const ticket = registry.get(id);
2200
- if (!ticket) return fallback;
2201
- return /* @__PURE__ */ isObject(ticket.value) ? ticket.value.$variation ?? fallback : ticket.value ?? fallback;
2202
- }
2203
- function register(registration = {}) {
2204
- const item = {
2205
- value: false,
2206
- ...registration
2207
- };
2208
- const ticket = registry.register(item);
2209
- 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);
2210
- return ticket;
2211
- }
2212
- return {
2213
- ...registry,
2214
- variation,
2215
- register,
2216
- get size() {
2217
- return registry.size;
2218
- }
2219
- };
2220
- }
2221
- /**
2222
- * Creates a new features context.
2223
- *
2224
- * @param options The options for the features context.
2225
- * @template Z The type of the feature ticket.
2226
- * @template E The type of the feature context.
2227
- * @returns A new features context.
2228
- *
2229
- * @see https://0.vuetifyjs.com/composables/plugins/use-features
2230
- *
2231
- * @example
2232
- * ```ts
2233
- * import { createFeaturesContext } from '@vuetify/v0'
2234
- *
2235
- * export const [useFeatures, provideFeatures, context] = createFeaturesContext({
2236
- * namespace: 'app:features',
2237
- * features: {
2238
- * 'dark-mode': true,
2239
- * 'theme-color': { $variation: 'blue' },
2240
- * },
2241
- * })
2242
- * ```
2243
- */
2244
- function createFeaturesContext(_options = {}) {
2245
- const { namespace = "v0:features",...options } = _options;
2246
- const [useFeaturesContext, _provideFeaturesContext] = createContext(namespace);
2247
- const context = createFeatures(options);
2248
- function provideFeaturesContext(_context = context, app) {
2249
- return _provideFeaturesContext(_context, app);
2250
- }
2251
- return createTrinity(useFeaturesContext, provideFeaturesContext, context);
2252
- }
2253
- /**
2254
- * Creates a new features plugin.
2255
- *
2256
- * @param options The options for the features plugin.
2257
- * @template Z The type of the feature ticket.
2258
- * @template E The type of the feature context.
2259
- * @returns A new features plugin.
2260
- *
2261
- * @see https://0.vuetifyjs.com/composables/plugins/use-features
2262
- *
2263
- * @example
2264
- * ```ts
2265
- * import { createApp } from 'vue'
2266
- * import { createFeaturesPlugin } from '@vuetify/v0'
2267
- * import App from './App.vue'
2268
- *
2269
- * const app = createApp(App)
2270
- *
2271
- * app.use(
2272
- * createFeaturesPlugin({
2273
- * features: {
2274
- * 'dark-mode': true,
2275
- * 'theme-color': { $variation: 'blue' },
2276
- * },
2277
- * })
2278
- * )
2279
- *
2280
- * app.mount('#app')
2281
- * ```
2282
- */
2283
- function createFeaturesPlugin(_options = {}) {
2284
- const { namespace = "v0:features",...options } = _options;
2285
- const [, provideFeaturesContext, context] = createFeaturesContext({
2286
- ...options,
2287
- namespace
2288
- });
2289
- return createPlugin({
2290
- namespace,
2291
- provide: (app) => {
2292
- provideFeaturesContext(context, app);
2293
- }
2294
- });
2295
- }
2296
- /**
2297
- * Returns the current features instance.
2298
- *
2299
- * @param namespace The namespace for the features context. Defaults to `v0:features`.
2300
- * @template Z The type of the feature ticket.
2301
- * @returns The current features instance.
2302
- *
2303
- * @see https://0.vuetifyjs.com/composables/plugins/use-features
2304
- *
2305
- * @example
2306
- * ```vue
2307
- * <script setup lang="ts">
2308
- * import { useFeatures } from '@vuetify/v0'
2309
- *
2310
- * const features = useFeatures()
2311
- * <\/script>
2312
- *
2313
- * <template>
2314
- * <div>
2315
- * <p>Features: {{ features.get('dark-mode') }}</p>
2316
- * <p>Theme Color: {{ features.variation('theme-color') }}</p>
2317
- * </div>
2318
- * </template>
2319
- * ```
2320
- */
2321
- function useFeatures(namespace = "v0:features") {
2322
- return useContext(namespace);
2323
- }
2324
-
2325
- //#endregion
2326
- //#region src/composables/useFilter/index.ts
2327
- /**
2328
- * @module useFilter
2329
- *
2330
- * @remarks
2331
- * Reactive array filtering composable with multiple filter modes.
2332
- *
2333
- * Key features:
2334
- * - Four filter modes: some, every, union, intersection
2335
- * - Case-insensitive filtering
2336
- * - Custom filter functions
2337
- * - Reactive updates
2338
- * - Perfect for search, multi-criteria filtering
2339
- *
2340
- * Filters arrays based on query strings with configurable matching strategies.
2341
- */
2342
- function defaultFilter(query, item, keys, mode = "some") {
2343
- const queries = Array.isArray(query) ? query.map((q) => String(q).toLowerCase()) : [String(query).toLowerCase()];
2344
- function match(value, q) {
2345
- return String(value).toLowerCase().includes(q);
2346
- }
2347
- const stringValues = (/* @__PURE__ */ isObject(item) ? keys?.length ? keys.map((k) => item[k]) : Object.values(item) : [item]).map((v) => String(v).toLowerCase());
2348
- if (mode === "some") return stringValues.some((val) => match(val, queries[0]));
2349
- if (mode === "every") return stringValues.every((val) => match(val, queries[0]));
2350
- if (mode === "union") return queries.some((q) => stringValues.some((val) => match(val, q)));
2351
- if (mode === "intersection") return queries.every((q) => stringValues.some((val) => match(val, q)));
2352
- return false;
2353
- }
2354
- /**
2355
- * A reusable function for filtering an array of items.
2356
- *
2357
- * @param query The query to filter by.
2358
- * @param items The items to filter.
2359
- * @param options The filter options.
2360
- * @template Z The type of the items.
2361
- * @returns The filtered items.
2362
- *
2363
- * @see https://0.vuetifyjs.com/composables/selection/use-filter
2364
- *
2365
- * @example
2366
- * ```ts
2367
- * import { ref } from 'vue'
2368
- * import { useFilter } from '@vuetify/v0'
2369
- *
2370
- * const items = ref([
2371
- * { name: 'John Doe', age: 30 },
2372
- * { name: 'Jane Doe', age: 25 },
2373
- * { name: 'Peter Jones', age: 40 },
2374
- * ])
2375
- *
2376
- * const query = ref('doe')
2377
- * const { items: filtered } = useFilter(query, items, { keys: ['name'] })
2378
- *
2379
- * console.log(filtered.value) // [ { name: 'John Doe', age: 30 }, { name: 'Jane Doe', age: 25 } ]
2380
- * ```
2381
- */
2382
- function useFilter(query, items, options = {}) {
2383
- const { customFilter, keys, mode = "some" } = options;
2384
- const filterFunction = customFilter ?? ((q, i) => defaultFilter(q, i, keys, mode));
2385
- const itemsRef = isRef(items) ? items : toRef(() => items);
2386
- const queryRef = toRef(query);
2387
- return { items: computed(() => {
2388
- const q = toValue(queryRef);
2389
- const queries = (Array.isArray(q) ? q : [q]).filter((q$1) => String(q$1).trim());
2390
- if (queries.length === 0) return itemsRef.value;
2391
- const queryParam = queries.length === 1 ? queries[0] : queries;
2392
- return itemsRef.value.filter((item) => filterFunction(queryParam, item));
2393
- }) };
2394
- }
2395
-
2396
- //#endregion
2397
- //#region src/composables/useForm/index.ts
2398
- /**
2399
- * @module useForm
2400
- *
2401
- * @remarks
2402
- * Form validation composable with async rule support and multiple validation modes.
2403
- *
2404
- * Key features:
2405
- * - Sync and async validation rules
2406
- * - Multiple validation modes (submit, change, combined)
2407
- * - Tri-state isValid (null/true/false)
2408
- * - isPristine tracking
2409
- * - Silent validation mode
2410
- * - Form-level validation and reset
2411
- *
2412
- * Each field is registered with validation rules and tracks its own state independently.
2413
- */
2414
- /**
2415
- * Creates a new form instance.
2416
- *
2417
- * @param options The options for the form instance.
2418
- * @template Z The type of the form ticket.
2419
- * @template E The type of the form context.
2420
- * @returns A new form instance.
2421
- *
2422
- * @see https://0.vuetifyjs.com/composables/forms/use-form
2423
- *
2424
- * @example
2425
- * ```ts
2426
- * import { createForm } from '@vuetify/v0'
2427
- *
2428
- * const form = createForm()
2429
- *
2430
- * const username = form.register({
2431
- * id: 'username',
2432
- * value: '',
2433
- * rules: [(v) => v.length > 0 || 'Username is required'],
2434
- * })
2435
- *
2436
- * await form.submit()
2437
- *
2438
- * console.log(username.errors.value) // ['Username is required']
2439
- *
2440
- * form.reset()
2441
- * ```
2442
- */
2443
- function createForm(options) {
2444
- const registry = useRegistry(options);
2445
- const validateOn = options?.validateOn || "submit";
2446
- function parse(value) {
2447
- return value.toLowerCase().split(/\s+/);
2448
- }
2449
- function validatesOn(event) {
2450
- return parse(validateOn).includes(event);
2451
- }
2452
- const isValidating = computed(() => {
2453
- for (const ticket of registry.collection.values()) if (ticket.isValidating.value) return true;
2454
- return false;
2455
- });
2456
- const isValid = computed(() => {
2457
- let hasFields = false;
2458
- for (const ticket of registry.values()) {
2459
- hasFields = true;
2460
- if (ticket.isValid.value === false) return false;
2461
- if (ticket.isValid.value === null) return null;
2462
- }
2463
- return hasFields || null;
2464
- });
2465
- function reset() {
2466
- for (const ticket of registry.values()) ticket.reset();
2467
- }
2468
- async function submit() {
2469
- return validate(registry.keys());
2470
- }
2471
- async function validate(id) {
2472
- const validating = toArray(id);
2473
- if (validatesOn("submit")) return (await Promise.all(validating.map(async (id$1) => await registry.get(id$1)?.validate() ?? true))).every(Boolean);
2474
- return validating.map((id$1) => registry.get(id$1)).filter(Boolean).every((ticket) => ticket.isValid.value === true);
2475
- }
2476
- function register(registration) {
2477
- const model = shallowRef(registration.value == null ? "" : toValue(registration.value));
2478
- const rules = registration.rules || [];
2479
- const errors = shallowRef([]);
2480
- const isValidating$1 = shallowRef(false);
2481
- const initialValue = model.value;
2482
- const triggers = registration.validateOn || validateOn;
2483
- const isPristine = shallowRef(true);
2484
- const isValid$1 = shallowRef(null);
2485
- function _validatesOn(event) {
2486
- return parse(triggers).includes(event);
2487
- }
2488
- function _reset() {
2489
- model.value = initialValue;
2490
- errors.value = [];
2491
- isPristine.value = true;
2492
- isValid$1.value = null;
2493
- }
2494
- async function validate$1(silent = false) {
2495
- if (rules.length === 0) return isValid$1.value = true;
2496
- isValidating$1.value = true;
2497
- try {
2498
- const errorMessages = (await Promise.all(rules.map((rule) => rule(model.value)))).filter((result) => /* @__PURE__ */ isString(result));
2499
- if (!silent) {
2500
- errors.value = errorMessages;
2501
- isValid$1.value = errorMessages.length === 0;
2502
- isPristine.value = toValue(model) === initialValue;
2503
- }
2504
- return errorMessages.length === 0;
2505
- } finally {
2506
- isValidating$1.value = false;
2507
- }
2508
- }
2509
- const item = {
2510
- ...registration,
2511
- rules,
2512
- errors,
2513
- disabled: registration.disabled || false,
2514
- validateOn: triggers,
2515
- isValidating: isValidating$1,
2516
- isPristine,
2517
- isValid: isValid$1,
2518
- reset: _reset,
2519
- validate: validate$1
2520
- };
2521
- const ticket = registry.register(item);
2522
- Object.defineProperty(ticket, "value", {
2523
- get() {
2524
- return model.value;
2525
- },
2526
- set(val) {
2527
- model.value = val;
2528
- isPristine.value = val === initialValue;
2529
- isValid$1.value = null;
2530
- if (_validatesOn("change")) validate$1();
2531
- },
2532
- enumerable: true,
2533
- configurable: true
2534
- });
2535
- return ticket;
2536
- }
2537
- return {
2538
- ...registry,
2539
- register,
2540
- reset,
2541
- submit,
2542
- validateOn,
2543
- isValid,
2544
- isValidating,
2545
- get size() {
2546
- return registry.size;
2547
- }
2548
- };
2549
- }
2550
- /**
2551
- * Creates a new form context.
2552
- *
2553
- * @param namespace The namespace for the form context.
2554
- * @param options The options for the form context.
2555
- * @template Z The type of the form ticket.
2556
- * @template E The type of the form context.
2557
- * @returns A new form context.
2558
- *
2559
- * @see https://0.vuetifyjs.com/composables/forms/use-form
2560
- *
2561
- * @example
2562
- * ```ts
2563
- * import { createFormContext } from '@vuetify/v0'
2564
- *
2565
- * export const [useMyForm, provideMyForm, myForm] = createFormContext('my-form', {
2566
- * validateOn: 'change',
2567
- * })
2568
- *
2569
- * // In a parent component:
2570
- * provideMyForm()
2571
- *
2572
- * // In a child component:
2573
- * const form = useMyForm()
2574
- * form.register({ id: 'field', value: ref(''), rules: [...] })
2575
- * ```
2576
- */
2577
- function createFormContext(_options) {
2578
- const { namespace,...options } = _options;
2579
- const [useFormContext, _provideFormContext] = createContext(namespace);
2580
- const context = createForm(options);
2581
- function provideFormContext(_context = context, app) {
2582
- return _provideFormContext(_context, app);
2583
- }
2584
- return createTrinity(useFormContext, provideFormContext, context);
2585
- }
2586
- /**
2587
- * Returns the current form instance.
2588
- *
2589
- * @param namespace The namespace for the form context. Defaults to `'v0:form'`.
2590
- * @returns The current form instance.
2591
- *
2592
- * @see https://0.vuetifyjs.com/composables/forms/use-form
2593
- *
2594
- * @example
2595
- * ```vue
2596
- * <script setup lang="ts">
2597
- * import { useForm } from '@vuetify/v0'
2598
- *
2599
- * const form = useForm()
2600
- * <\/script>
2601
- *
2602
- * <template>
2603
- * <div>
2604
- * <p>Form is {{ form.isValid.value ? 'valid' : 'invalid' }}</p>
2605
- * </div>
2606
- * </template>
2607
- * ```
2608
- */
2609
- function useForm(namespace = "v0:form") {
2610
- return useContext(namespace);
2611
- }
2612
-
2613
- //#endregion
2614
- //#region src/composables/useIntersectionObserver/index.ts
2615
- /**
2616
- * @module useIntersectionObserver
2617
- *
2618
- * @remarks
2619
- * IntersectionObserver composable with lifecycle management.
2620
- *
2621
- * Key features:
2622
- * - IntersectionObserver API wrapper
2623
- * - Pause/resume/stop functionality
2624
- * - Automatic cleanup on unmount
2625
- * - SSR-safe (checks SUPPORTS_INTERSECTION_OBSERVER)
2626
- * - Hydration-aware
2627
- * - Immediate callback option
2628
- *
2629
- * Perfect for lazy loading, infinite scroll, and visibility detection.
2630
- */
2631
- /**
2632
- * A composable that uses the Intersection Observer API to detect when an element
2633
- * is visible in the viewport.
2634
- *
2635
- * @param target The element to observe.
2636
- * @param callback The callback to execute when the element's intersection changes.
2637
- * @param options The options for the Intersection Observer.
2638
- * @returns An object with methods to control the observer.
2639
- *
2640
- * @see https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver
2641
- * @see https://0.vuetifyjs.com/composables/system/use-intersection-observer
2642
- *
2643
- * @example
2644
- * ```ts
2645
- * import { ref } from 'vue'
2646
- * import { useIntersectionObserver } from '@vuetify/v0'
2647
- *
2648
- * const target = ref<HTMLElement>()
2649
- * const isVisible = ref(false)
2650
- *
2651
- * const { isIntersecting, pause, resume } = useIntersectionObserver(
2652
- * target,
2653
- * (entries) => {
2654
- * const entry = entries[0]
2655
- * if (entry) {
2656
- * isVisible.value = entry.isIntersecting
2657
- * console.log('Element is visible:', entry.isIntersecting)
2658
- * }
2659
- * },
2660
- * { threshold: 0.5 }
2661
- * )
2662
- *
2663
- * // Pause observation
2664
- * pause()
2665
- *
2666
- * // Resume observation
2667
- * resume()
2668
- * ```
2669
- */
2670
- function useIntersectionObserver(target, callback, options = {}) {
2671
- const { isHydrated } = useHydration();
2672
- const observer = shallowRef();
2673
- const isPaused = shallowRef(false);
2674
- const isIntersecting = shallowRef(false);
2675
- function setup() {
2676
- if (!isHydrated.value || !SUPPORTS_INTERSECTION_OBSERVER || !target.value || isPaused.value) return;
2677
- observer.value = new IntersectionObserver((entries) => {
2678
- const transformedEntries = entries.map((entry) => ({
2679
- boundingClientRect: entry.boundingClientRect,
2680
- intersectionRatio: entry.intersectionRatio,
2681
- intersectionRect: entry.intersectionRect,
2682
- isIntersecting: entry.isIntersecting,
2683
- rootBounds: entry.rootBounds,
2684
- target: entry.target,
2685
- time: entry.time
2686
- }));
2687
- const latestEntry = transformedEntries.at(-1);
2688
- if (latestEntry) isIntersecting.value = latestEntry.isIntersecting;
2689
- callback(transformedEntries);
2690
- }, {
2691
- root: options.root || null,
2692
- rootMargin: options.rootMargin || "0px",
2693
- threshold: options.threshold || 0
2694
- });
2695
- observer.value.observe(target.value);
2696
- if (options.immediate) callback([{
2697
- boundingClientRect: target.value.getBoundingClientRect(),
2698
- intersectionRatio: 0,
2699
- intersectionRect: new DOMRect(0, 0, 0, 0),
2700
- isIntersecting: false,
2701
- rootBounds: null,
2702
- target: target.value,
2703
- time: performance.now()
2704
- }]);
2705
- }
2706
- watch([isHydrated, target], () => {
2707
- cleanup();
2708
- setup();
2709
- }, { immediate: true });
2710
- function cleanup() {
2711
- if (observer.value) {
2712
- observer.value.disconnect();
2713
- observer.value = void 0;
2714
- }
2715
- }
2716
- function pause() {
2717
- isPaused.value = true;
2718
- isIntersecting.value = false;
2719
- observer.value?.disconnect();
2720
- }
2721
- function resume() {
2722
- isPaused.value = false;
2723
- setup();
2724
- }
2725
- function stop() {
2726
- cleanup();
2727
- }
2728
- onUnmounted(stop);
2729
- return {
2730
- isIntersecting: readonly(isIntersecting),
2731
- isPaused: readonly(isPaused),
2732
- pause,
2733
- resume,
2734
- stop
2735
- };
2736
- }
2737
- /**
2738
- * A convenience composable that uses the Intersection Observer API to detect
2739
- * when an element is visible in the viewport.
2740
- *
2741
- * @param target The element to observe.
2742
- * @param options The options for the Intersection Observer.
2743
- * @returns An object with the intersection state.
2744
- *
2745
- * @see https://0.vuetifyjs.com/composables/system/use-intersection-observer
2746
- *
2747
- * @example
2748
- * ```ts
2749
- * import { ref } from 'vue'
2750
- * import { useElementIntersection } from '@vuetify/v0'
2751
- *
2752
- * const myElement = ref<HTMLElement>()
2753
- * const { isIntersecting, intersectionRatio } = useElementIntersection(myElement, {
2754
- * threshold: 0.5
2755
- * })
2756
- *
2757
- * // Use in template to conditionally render or animate
2758
- * watchEffect(() => {
2759
- * if (isIntersecting.value) {
2760
- * console.log('Element is visible!', intersectionRatio.value)
2761
- * }
2762
- * })
2763
- * ```
2764
- */
2765
- function useElementIntersection(target, options = {}) {
2766
- const isIntersecting = shallowRef(false);
2767
- const intersectionRatio = shallowRef(0);
2768
- const { pause: _pause, resume, stop, isPaused } = useIntersectionObserver(target, (entries) => {
2769
- const entry = entries.at(-1);
2770
- if (entry) {
2771
- isIntersecting.value = entry.isIntersecting;
2772
- intersectionRatio.value = entry.intersectionRatio;
2773
- }
2774
- }, {
2775
- immediate: true,
2776
- ...options
2777
- });
2778
- function pause() {
2779
- isIntersecting.value = false;
2780
- intersectionRatio.value = 0;
2781
- _pause();
2782
- }
2783
- return {
2784
- isIntersecting: readonly(isIntersecting),
2785
- intersectionRatio: readonly(intersectionRatio),
2786
- isPaused,
2787
- pause,
2788
- resume,
2789
- stop
2790
- };
2791
- }
2792
-
2793
- //#endregion
2794
- //#region src/composables/useKeydown/index.ts
2795
- /**
2796
- * @module useKeydown
2797
- *
2798
- * @remarks
2799
- * Keydown event listener composable with key filtering.
2800
- *
2801
- * Key features:
2802
- * - Key-specific event handling
2803
- * - preventDefault and stopPropagation options
2804
- * - Automatic cleanup on scope disposal
2805
- * - Auto-starts when in component scope
2806
- *
2807
- * Simplified wrapper around useEventListener for keyboard interactions.
2808
- */
2809
- /**
2810
- * A composable that adds a keydown event listener to the document.
2811
- *
2812
- * @param handlers The key handlers to add.
2813
- * @returns An object with methods to start and stop listening.
2814
- *
2815
- * @see https://0.vuetifyjs.com/composables/system/use-keydown
2816
- *
2817
- * @example
2818
- * ```ts
2819
- * import { useKeydown } from '@vuetify/v0'
2820
- *
2821
- * const { startListening, stopListening } = useKeydown([
2822
- * { key: 'Enter', handler: () => console.log('Enter pressed') },
2823
- * { key: 'Escape', handler: () => console.log('Escape pressed'), preventDefault: true },
2824
- * ])
2825
- *
2826
- * startListening()
2827
- * stopListening()
2828
- * ```
2829
- */
2830
- function useKeydown(handlers) {
2831
- const keyHandlers = Array.isArray(handlers) ? handlers : [handlers];
2832
- function onKeydown(event) {
2833
- const handler = keyHandlers.find((h$1) => h$1.key === event.key);
2834
- if (handler) {
2835
- if (handler.preventDefault) event.preventDefault();
2836
- if (handler.stopPropagation) event.stopPropagation();
2837
- handler.handler(event);
2838
- }
2839
- }
2840
- function startListening() {
2841
- document.addEventListener("keydown", onKeydown);
2842
- }
2843
- function stopListening() {
2844
- document.removeEventListener("keydown", onKeydown);
2845
- }
2846
- if (getCurrentScope()) onMounted(startListening);
2847
- onScopeDispose(stopListening, true);
2848
- return {
2849
- startListening,
2850
- stopListening
2851
- };
2852
- }
2853
-
2854
- //#endregion
2855
- //#region src/composables/useSingle/index.ts
2856
- /**
2857
- * @module useSingle
2858
- *
2859
- * @remarks
2860
- * Single-selection composable that extends useSelection to enforce only one selected item.
2861
- *
2862
- * Key features:
2863
- * - Auto-clears previous selection when selecting new item
2864
- * - Singular computed properties (selectedId, selectedItem, selectedIndex, selectedValue)
2865
- * - Perfect for tabs, radio buttons, theme selectors
2866
- *
2867
- * Inheritance chain: useRegistry → useSelection → useSingle
2868
- */
2869
- /**
2870
- * Creates a new single selection instance that enforces only one selected item at a time.
2871
- *
2872
- * Extends `createSelection` by automatically clearing previous selections when a new item is selected.
2873
- * Adds computed singular properties: `selectedId`, `selectedItem`, `selectedIndex`, `selectedValue`.
2874
- *
2875
- * @param options The options for the single selection instance.
2876
- * @template Z The type of the single selection ticket.
2877
- * @template E The type of the single selection context.
2878
- * @returns A new single selection instance with single-selection enforcement.
2879
- *
2880
- * @remarks
2881
- * **Key Differences from `createSelection`:**
2882
- * - Automatically clears `selectedIds` before selecting a new item (enforces single selection)
2883
- * - Provides singular computed properties instead of plural sets
2884
- * - Perfect for tabs, radio buttons, theme selectors, and other single-choice UI components
2885
- *
2886
- * **Computed Properties:**
2887
- * - `selectedId`: The ID of the selected item (undefined if none selected)
2888
- * - `selectedItem`: The selected ticket object (undefined if none selected)
2889
- * - `selectedIndex`: The index of the selected item (-1 if none selected)
2890
- * - `selectedValue`: The value of the selected item (undefined if none selected)
2891
- *
2892
- * **Inheritance Chain:**
2893
- * `useRegistry` → `createSelection` → `createSingle` → `createStep`
2894
- *
2895
- * @see https://0.vuetifyjs.com/composables/selection/use-single
2896
- *
2897
- * @example
2898
- * ```ts
2899
- * import { createSingle } from '@vuetify/v0'
2900
- *
2901
- * const tabs = createSingle({ mandatory: true })
2902
- *
2903
- * tabs.onboard([
2904
- * { id: 'home', value: 'Home' },
2905
- * { id: 'about', value: 'About' },
2906
- * { id: 'contact', value: 'Contact' },
2907
- * ])
2908
- *
2909
- * tabs.first() // Select first tab
2910
- *
2911
- * console.log(tabs.selectedId.value) // 'home'
2912
- * console.log(tabs.selectedIndex.value) // 0
2913
- *
2914
- * tabs.select('about') // Switch to about tab
2915
- * console.log(tabs.selectedId.value) // 'about'
2916
- * console.log(tabs.selectedIds.size) // 1 (always enforces single selection)
2917
- * ```
2918
- */
2919
- function createSingle(_options = {}) {
2920
- const { mandatory = false, multiple = false,...options } = _options;
2921
- const registry = createSelection({
2922
- ...options,
2923
- mandatory,
2924
- multiple
2925
- });
2926
- const selectedId = computed(() => registry.selectedIds.values().next().value);
2927
- const selectedItem = computed(() => registry.selectedItems.value.values().next().value);
2928
- const selectedIndex = computed(() => selectedItem.value?.index ?? -1);
2929
- const selectedValue = computed(() => selectedItem.value?.value);
2930
- function unselect(id) {
2931
- if (mandatory && registry.selectedIds.size === 1) return;
2932
- registry.selectedIds.delete(id);
2933
- }
2934
- function toggle(id) {
2935
- if (registry.selectedIds.has(id)) unselect(id);
2936
- else registry.select(id);
2937
- }
2938
- return {
2939
- ...registry,
2940
- selectedId,
2941
- selectedItem,
2942
- selectedIndex,
2943
- selectedValue,
2944
- unselect,
2945
- toggle,
2946
- get size() {
2947
- return registry.size;
2948
- }
2949
- };
2950
- }
2951
- /**
2952
- * Creates a new single selection context.
2953
- *
2954
- * @param namespace The namespace for the single selection context.
2955
- * @param options The options for the single selection context.
2956
- * @template Z The type of the single selection ticket.
2957
- * @template E The type of the single selection context.
2958
- * @returns A new single selection context.
2959
- *
2960
- * @see https://0.vuetifyjs.com/composables/selection/use-single
2961
- *
2962
- * @example
2963
- * ```ts
2964
- * import { createSingleContext } from '@vuetify/v0'
2965
- *
2966
- * export const [useTabs, provideTabs, tabs] = createSingleContext('tabs', { mandatory: true })
2967
- *
2968
- * // In a parent component:
2969
- * provideTabs()
2970
- *
2971
- * // In a child component:
2972
- * const tabs = useTabs()
2973
- * tabs.select('tab-1')
2974
- * ```
2975
- */
2976
- function createSingleContext(_options) {
2977
- const { namespace,...options } = _options;
2978
- const [useSingleContext, _provideSingleContext] = createContext(namespace);
2979
- const context = createSingle(options);
2980
- function provideSingleContext(_context = context, app) {
2981
- return _provideSingleContext(_context, app);
2982
- }
2983
- return createTrinity(useSingleContext, provideSingleContext, context);
2984
- }
2985
- /**
2986
- * Returns the current single selection instance.
2987
- *
2988
- * @param namespace The namespace for the single selection context. Defaults to `'v0:single'`.
2989
- * @returns The current single selection instance.
2990
- *
2991
- * @see https://0.vuetifyjs.com/composables/selection/use-single
2992
- *
2993
- * @example
2994
- * ```vue
2995
- * <script setup lang="ts">
2996
- * import { useSingle } from '@vuetify/v0'
2997
- *
2998
- * const tabs = useSingle()
2999
- * <\/script>
3000
- *
3001
- * <template>
3002
- * <div>
3003
- * <p>Selected: {{ tabs.selectedId }}</p>
3004
- * </div>
3005
- * </template>
3006
- * ```
3007
- */
3008
- function useSingle(namespace = "v0:single") {
3009
- return useContext(namespace);
3010
- }
3011
-
3012
- //#endregion
3013
- //#region src/composables/useLocale/adapters/v0.ts
3014
- /**
3015
- * Vuetify0.x locale adapter implementation
3016
- *
3017
- * This adapter provides translation and number formatting
3018
- * capabilities using the Intl API and supports both
3019
- * numbered and named variables in translation strings.
3020
- */
3021
- var Vuetify0LocaleAdapter = class {
3022
- t(message, ...params) {
3023
- let resolvedMessage = message;
3024
- if (params.length > 0 && /* @__PURE__ */ isObject(params[0])) {
3025
- const variables = params[0];
3026
- resolvedMessage = resolvedMessage.replace(/{([a-zA-Z][a-zA-Z0-9_]*)}/g, (match, name) => {
3027
- return variables[name] === void 0 ? match : String(variables[name]);
3028
- });
3029
- params = params.slice(1);
3030
- }
3031
- resolvedMessage = resolvedMessage.replace(/\{(\d+)\}/g, (match, index) => {
3032
- const idx = Number.parseInt(index, 10);
3033
- if (params[idx] !== void 0) return String(params[idx]);
3034
- return match;
3035
- });
3036
- return resolvedMessage;
3037
- }
3038
- n(value, locale, ...params) {
3039
- if (!IN_BROWSER || !locale) return value.toString();
3040
- const options = params[0];
3041
- return new Intl.NumberFormat(String(locale), options).format(value);
3042
- }
3043
- };
3044
-
3045
- //#endregion
3046
- //#region src/composables/useLocale/index.ts
3047
- /**
3048
- * @module useLocale
3049
- *
3050
- * @remarks
3051
- * Internationalization (i18n) composable with adapter pattern for message translation.
3052
- *
3053
- * Key features:
3054
- * - Locale selection with createSingle
3055
- * - Token-based message storage with useTokens
3056
- * - Numbered and named placeholder support ({0}, {name})
3057
- * - Number formatting with Intl.NumberFormat
3058
- * - Adapter pattern for integration with i18n providers
3059
- *
3060
- * Integrates with createSingle for locale selection and useTokens for message resolution.
3061
- */
3062
- /**
3063
- * Creates a new locale instance.
3064
- *
3065
- * @param options The options for the locale instance.
3066
- * @template Z The type of the locale ticket.
3067
- * @template E The type of the locale context.
3068
- * @returns A new locale instance.
3069
- *
3070
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
3071
- */
3072
- function createLocale(_options = {}) {
3073
- const { adapter = new Vuetify0LocaleAdapter(), messages = {},...options } = _options;
3074
- const tokens = createTokens(messages, { flat: true });
3075
- const registry = createSingle(options);
3076
- for (const id in messages) {
3077
- registry.register({
3078
- id,
3079
- value: messages[id]
3080
- });
3081
- if (id === options.default && !registry.selectedId.value) registry.select(id);
3082
- }
3083
- function t(key, ...params) {
3084
- const locale = registry.selectedId.value;
3085
- if (!locale) return key;
3086
- const message = (registry.get(locale)?.value)?.[key];
3087
- const template = /* @__PURE__ */ isString(message) ? resolve(locale, message) : key;
3088
- return adapter.t(template, ...params);
3089
- }
3090
- function n(value, ...params) {
3091
- return adapter.n(value, registry.selectedId.value, ...params);
3092
- }
3093
- function resolve(locale, str) {
3094
- return str.replace(/{([a-zA-Z0-9.-_]+)}/g, (match, key) => {
3095
- const [prefix, ...rest] = key.split(".");
3096
- const path = rest.join(".");
3097
- const prefixTicket = registry.get(prefix);
3098
- const target = prefixTicket ? prefix : locale;
3099
- const name = prefixTicket ? path : key;
3100
- const resolved = (registry.get(target)?.value)?.[name];
3101
- if (/* @__PURE__ */ isString(resolved)) return resolve(target, resolved);
3102
- const alias = `{${key}}`;
3103
- if (tokens.isAlias(alias)) {
3104
- const result = tokens.resolve(alias);
3105
- return /* @__PURE__ */ isString(result) ? result : match;
3106
- }
3107
- return match;
3108
- });
3109
- }
3110
- return {
3111
- ...registry,
3112
- t,
3113
- n,
3114
- get size() {
3115
- return registry.size;
3116
- }
3117
- };
3118
- }
3119
- /**
3120
- * Creates a new locale context.
3121
- *
3122
- * @param options The options for the locale context.
3123
- * @template Z The type of the locale ticket.
3124
- * @template E The type of the locale context.
3125
- * @returns A new locale context.
3126
- *
3127
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
3128
- *
3129
- * @example
3130
- * ```ts
3131
- * import { createLocaleContext } from '@vuetify/v0'
3132
- *
3133
- * export const [useAppLocale, provideAppLocale, appLocale] = createLocaleContext({
3134
- * namespace: 'app:locale',
3135
- * messages: {
3136
- * en: { hello: 'Hello' },
3137
- * es: { hello: 'Hola' },
3138
- * },
3139
- * })
3140
- *
3141
- * // In a parent component:
3142
- * provideAppLocale()
3143
- *
3144
- * // In a child component:
3145
- * const locale = useAppLocale()
3146
- * locale.select('es')
3147
- * ```
3148
- */
3149
- function createLocaleContext(_options = {}) {
3150
- const { namespace = "v0:locale",...options } = _options;
3151
- const [useLocaleContext, _provideLocaleContext] = createContext(namespace);
3152
- const context = createLocale(options);
3153
- function provideLocaleContext(_context = context, app) {
3154
- return _provideLocaleContext(_context, app);
3155
- }
3156
- return createTrinity(useLocaleContext, provideLocaleContext, context);
3157
- }
3158
- /**
3159
- * Creates a new locale plugin.
3160
- *
3161
- * @param options The options for the locale plugin.
3162
- * @template Z The type of the locale ticket.
3163
- * @template E The type of the locale context.
3164
- * @template R The type of the token ticket.
3165
- * @template O The type of the token context.
3166
- * @returns A new locale plugin.
3167
- *
3168
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
3169
- */
3170
- function createLocalePlugin(_options = {}) {
3171
- const { namespace = "v0:locale", adapter = new Vuetify0LocaleAdapter(), messages = {},...options } = _options;
3172
- const [, provideLocaleContext, context] = createLocaleContext({
3173
- ...options,
3174
- namespace,
3175
- adapter,
3176
- messages
3177
- });
3178
- return createPlugin({
3179
- namespace,
3180
- provide: (app) => {
3181
- provideLocaleContext(context, app);
3182
- }
3183
- });
3184
- }
3185
- /**
3186
- * Returns the current locale instance.
3187
- *
3188
- * @returns The current locale instance.
3189
- *
3190
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
3191
- */
3192
- function useLocale(namespace = "v0:locale") {
3193
- return useContext(namespace);
3194
- }
3195
-
3196
- //#endregion
3197
- //#region src/composables/useMutationObserver/index.ts
3198
- /**
3199
- * @module useMutationObserver
3200
- *
3201
- * @remarks
3202
- * MutationObserver composable with lifecycle management.
3203
- *
3204
- * Key features:
3205
- * - MutationObserver API wrapper
3206
- * - Pause/resume/stop functionality
3207
- * - Automatic cleanup on unmount
3208
- * - SSR-safe (checks SUPPORTS_MUTATION_OBSERVER)
3209
- * - Hydration-aware
3210
- * - Configurable observation options (childList, attributes, characterData, etc.)
3211
- *
3212
- * Perfect for detecting DOM changes and responding to mutations.
3213
- */
3214
- /**
3215
- * A composable that uses the Mutation Observer API to detect changes in the DOM.
3216
- *
3217
- * @param target The element to observe.
3218
- * @param callback The callback to execute when a mutation is observed.
3219
- * @param options The options for the Mutation Observer.
3220
- * @returns An object with methods to control the observer.
3221
- *
3222
- * @see https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
3223
- * @see https://0.vuetifyjs.com/composables/system/use-mutation-observer
3224
- *
3225
- * @example
3226
- * ```ts
3227
- * import { ref } from 'vue'
3228
- * import { useMutationObserver } from '@vuetify/v0'
3229
- *
3230
- * const container = ref<HTMLElement>()
3231
- *
3232
- * const { pause, resume, isPaused } = useMutationObserver(
3233
- * container,
3234
- * (mutations) => {
3235
- * mutations.forEach((mutation) => {
3236
- * if (mutation.type === 'childList') {
3237
- * console.log('Children changed:', mutation.addedNodes, mutation.removedNodes)
3238
- * } else if (mutation.type === 'attributes') {
3239
- * console.log('Attribute changed:', mutation.attributeName)
3240
- * }
3241
- * })
3242
- * },
3243
- * {
3244
- * childList: true,
3245
- * attributes: true,
3246
- * subtree: true
3247
- * }
3248
- * )
3249
- *
3250
- * // Pause observation
3251
- * pause()
3252
- *
3253
- * // Resume observation
3254
- * resume()
3255
- * ```
3256
- */
3257
- function useMutationObserver(target, callback, options = {}) {
3258
- const { isHydrated } = useHydration();
3259
- const observer = shallowRef();
3260
- const isPaused = shallowRef(false);
3261
- const observerOptions = {
3262
- childList: options.childList ?? true,
3263
- attributes: options.attributes ?? false,
3264
- characterData: options.characterData ?? false,
3265
- subtree: options.subtree ?? false,
3266
- attributeOldValue: options.attributeOldValue ?? false,
3267
- characterDataOldValue: options.characterDataOldValue ?? false,
3268
- attributeFilter: options.attributeFilter
3269
- };
3270
- function setup() {
3271
- if (!isHydrated.value || !SUPPORTS_MUTATION_OBSERVER || !target.value || isPaused.value) return;
3272
- observer.value = new MutationObserver((mutations) => {
3273
- callback(mutations.map((mutation) => ({
3274
- type: mutation.type,
3275
- target: mutation.target,
3276
- addedNodes: mutation.addedNodes,
3277
- removedNodes: mutation.removedNodes,
3278
- previousSibling: mutation.previousSibling,
3279
- nextSibling: mutation.nextSibling,
3280
- attributeName: mutation.attributeName,
3281
- attributeNamespace: mutation.attributeNamespace,
3282
- oldValue: mutation.oldValue
3283
- })));
3284
- });
3285
- observer.value.observe(target.value, observerOptions);
3286
- if (options.immediate) {
3287
- const emptyNodeList = {
3288
- length: 0,
3289
- item: () => null,
3290
- forEach: () => {},
3291
- *[Symbol.iterator]() {}
3292
- };
3293
- callback([{
3294
- type: "childList",
3295
- target: target.value,
3296
- addedNodes: emptyNodeList,
3297
- removedNodes: emptyNodeList,
3298
- previousSibling: null,
3299
- nextSibling: null,
3300
- attributeName: null,
3301
- attributeNamespace: null,
3302
- oldValue: null
3303
- }]);
3304
- }
3305
- }
3306
- watch([isHydrated, target], () => {
3307
- cleanup();
3308
- setup();
3309
- }, { immediate: true });
3310
- function cleanup() {
3311
- if (observer.value) {
3312
- observer.value.disconnect();
3313
- observer.value = void 0;
3314
- }
3315
- }
3316
- function pause() {
3317
- isPaused.value = true;
3318
- observer.value?.disconnect();
3319
- }
3320
- function resume() {
3321
- isPaused.value = false;
3322
- setup();
3323
- }
3324
- function stop() {
3325
- cleanup();
3326
- }
3327
- onUnmounted(stop);
3328
- return {
3329
- isPaused: readonly(isPaused),
3330
- pause,
3331
- resume,
3332
- stop
3333
- };
3334
- }
3335
-
3336
- //#endregion
3337
- //#region src/composables/usePermissions/adapters/adapter.ts
3338
- var PermissionAdapter = class {};
3339
-
3340
- //#endregion
3341
- //#region src/composables/usePermissions/adapters/v0.ts
3342
- var Vuetify0PermissionAdapter = class extends PermissionAdapter {
3343
- constructor() {
3344
- super();
3345
- }
3346
- can(role, action, subject, context, permissions) {
3347
- const access = `${role}.${action}.${subject}`;
3348
- const ticket = permissions.get(access);
3349
- if (!ticket || !ticket.value) return false;
3350
- return /* @__PURE__ */ isFunction(ticket.value) ? ticket.value(context) : ticket.value;
3351
- }
3352
- };
3353
-
3354
- //#endregion
3355
- //#region src/composables/usePermissions/index.ts
3356
- /**
3357
- * @module usePermissions
3358
- *
3359
- * @see https://0.vuetifyjs.com/composables/plugins/use-permissions
3360
- *
3361
- * @remarks
3362
- * Permission management composable with support for RBAC and ABAC patterns.
3363
- *
3364
- * Key features:
3365
- * - Role-Based Access Control (RBAC) support
3366
- * - Attribute-Based Access Control (ABAC) with context
3367
- * - Functional permission conditions
3368
- * - Token-based permission storage
3369
- * - Adapter pattern for custom permission systems
3370
- *
3371
- * Built on useTokens for flexible permission configuration.
3372
- */
3373
- /**
3374
- * Creates a new permissions instance.
3375
- *
3376
- * @param options The options for the permissions instance.
3377
- * @template Z The type of the permission ticket.
3378
- * @template E The type of the permission context.
3379
- * @returns A new permissions instance.
3380
- *
3381
- * @see https://0.vuetifyjs.com/composables/plugins/use-permissions
3382
- *
3383
- * @example
3384
- * ```ts
3385
- * import { createPermissions } from '@vuetify/v0'
3386
- *
3387
- * const [usePermissions, providePermissions] = createPermissions({
3388
- * namespace: 'v0:permissions',
3389
- * permissions: {
3390
- * admin: [['read', 'users']],
3391
- * editor: [['edit', 'posts']],
3392
- * },
3393
- * })
3394
- * ```
3395
- */
3396
- function createPermissions(_options = {}) {
3397
- const { adapter = new Vuetify0PermissionAdapter(), permissions = {},...options } = _options;
3398
- const record = {};
3399
- for (const role in permissions) {
3400
- if (!record[role]) record[role] = {};
3401
- for (const [actions, subjects, condition = true] of permissions[role]) for (const action of toArray(actions)) for (const subject of toArray(subjects)) {
3402
- if (!record[role][action]) record[role][action] = {};
3403
- record[role][action][subject] = condition;
3404
- }
3405
- }
3406
- const tokens = createTokens(record, options);
3407
- function can(id, action, subject, context = {}) {
3408
- return adapter.can(id, action, subject, context, tokens);
3409
- }
3410
- return {
3411
- ...tokens,
3412
- can
3413
- };
3414
- }
3415
- /**
3416
- * Creates a new permissions context.
3417
- *
3418
- * @param options The options for the permissions context.
3419
- * @template Z The type of the permission ticket.
3420
- * @template E The type of the permission context.
3421
- * @returns A new permissions context.
3422
- *
3423
- * @see https://0.vuetifyjs.com/composables/plugins/use-permissions
3424
- *
3425
- * @example
3426
- * ```ts
3427
- * import { createPermissionsContext } from '@vuetify/v0'
3428
- *
3429
- * export const [usePermissions, providePermissions, context] = createPermissionsContext({
3430
- * namespace: 'app:permissions',
3431
- * permissions: {
3432
- * admin: [['read', 'users'], ['edit', 'users']],
3433
- * editor: [['edit', 'posts']],
3434
- * },
3435
- * })
3436
- * ```
3437
- */
3438
- function createPermissionsContext(_options = {}) {
3439
- const { namespace = "v0:permissions",...options } = _options;
3440
- const [usePermissionsContext, _providePermissionsContext] = createContext(namespace);
3441
- const context = createPermissions(options);
3442
- function providePermissionsContext(_context = context, app) {
3443
- return _providePermissionsContext(_context, app);
3444
- }
3445
- return createTrinity(usePermissionsContext, providePermissionsContext, context);
3446
- }
3447
- /**
3448
- * Creates a new permissions plugin.
3449
- *
3450
- * @param options The options for the permissions plugin.
3451
- * @template Z The type of the permission ticket.
3452
- * @template E The type of the permission context.
3453
- * @returns A new permissions plugin.
3454
- *
3455
- * @see https://0.vuetifyjs.com/composables/plugins/use-permissions
3456
- *
3457
- * @example
3458
- * ```ts
3459
- * import { createApp } from 'vue'
3460
- * import { createPermissionsPlugin } from '@vuetify/v0'
3461
- * import App from './App.vue'
3462
- *
3463
- * const app = createApp(App)
3464
- *
3465
- * app.use(
3466
- * createPermissionsPlugin({
3467
- * permissions: {
3468
- * admin: [['read', 'users']],
3469
- * editor: [['edit', 'posts']],
3470
- * },
3471
- * })
3472
- * )
3473
- *
3474
- * app.mount('#app')
3475
- * ```
3476
- */
3477
- function createPermissionsPlugin(_options = {}) {
3478
- const { namespace = "v0:permissions",...options } = _options;
3479
- const [, providePermissionContext, context] = createPermissionsContext({
3480
- ...options,
3481
- namespace
3482
- });
3483
- return createPlugin({
3484
- namespace,
3485
- provide: (app) => {
3486
- providePermissionContext(context, app);
3487
- }
3488
- });
3489
- }
3490
- /**
3491
- * Returns the current permissions instance.
3492
- *
3493
- * @template Z The type of the permission ticket.
3494
- * @returns The current permissions instance.
3495
- *
3496
- * @see https://0.vuetifyjs.com/composables/plugins/use-permissions
3497
- *
3498
- * @example
3499
- * ```vue
3500
- * <script setup lang="ts">
3501
- * import { usePermissions } from '@vuetify/v0'
3502
- *
3503
- * const { can } = usePermissions()
3504
- * <\/script>
3505
- *
3506
- * <template>
3507
- * <div>
3508
- * <p v-if="can('admin', 'read', 'users')">Admin access</p>
3509
- * </div>
3510
- * </template>
3511
- * ```
3512
- */
3513
- function usePermissions(namespace = "v0:permissions") {
3514
- return useContext(namespace);
3515
- }
3516
-
3517
- //#endregion
3518
- //#region src/composables/useProxyModel/index.ts
3519
- /**
3520
- * @module useProxyModel
3521
- *
3522
- * @remarks
3523
- * Proxy composable for bidirectional sync between selection registry and v-model.
3524
- *
3525
- * Key features:
3526
- * - Bidirectional synchronization
3527
- * - Array and single-value modes
3528
- * - Automatic cleanup on scope disposal
3529
- * - Perfect for form controls with selection backing
3530
- *
3531
- * Bridges the gap between selection composables and Vue's v-model.
3532
- */
3533
- /**
3534
- * Syncs a ref with a selection registry bidirectionally.
3535
- *
3536
- * @param registry The selection registry to bind to.
3537
- * @param model The ref to sync.
3538
- * @param options The options for the proxy model.
3539
- * @template Z The type of the selection ticket.
3540
- * @returns A function to stop the sync.
3541
- *
3542
- * @see https://0.vuetifyjs.com/composables/forms/use-proxy-model
3543
- *
3544
- * @example
3545
- * ```ts
3546
- * import { createSelection, useProxyModel } from '@vuetify/v0'
3547
- *
3548
- * const model = ref()
3549
- * const registry = createSelection({ events: true })
3550
- * registry.onboard([
3551
- * { id: 'item-1', value: 'Item 1' },
3552
- * { id: 'item-2', value: 'Item 2' },
3553
- * ])
3554
- *
3555
- * const stop = useProxyModel(registry, model)
3556
- * ```
3557
- */
3558
- function useProxyModel(registry, model, options) {
3559
- const multiple = options?.multiple ?? false;
3560
- const _transformIn = options?.transformIn;
3561
- const _transformOut = options?.transformOut;
3562
- function transformIn(val) {
3563
- const value = toValue(val);
3564
- return toArray(/* @__PURE__ */ isFunction(_transformIn) ? _transformIn(value) : value);
3565
- }
3566
- function transformOut(val) {
3567
- if (/* @__PURE__ */ isFunction(_transformOut)) return _transformOut(val);
3568
- return multiple ? val : val[0];
3569
- }
3570
- const modelAsArray = transformIn(model);
3571
- const pending = new Set(modelAsArray);
3572
- for (const value of modelAsArray) {
3573
- const ids = registry.browse(value);
3574
- if (/* @__PURE__ */ isArray(ids)) {
3575
- for (const id of ids) registry.select(id);
3576
- pending.delete(value);
3577
- } else if (ids) {
3578
- registry.select(ids);
3579
- pending.delete(value);
3580
- }
3581
- }
3582
- const registryWatch = watch(registry.selectedValues, (val) => {
3583
- modelWatch.pause();
3584
- model.value = transformOut(Array.from(toValue(val)));
3585
- modelWatch.resume();
3586
- }, { flush: "sync" });
3587
- const modelWatch = watch(model, (val) => {
3588
- registryWatch.pause();
3589
- const currentIds = new Set(toValue(registry.selectedIds));
3590
- const targetIds = /* @__PURE__ */ new Set();
3591
- for (const value of transformIn(val)) {
3592
- const ids = registry.browse(value);
3593
- if (/* @__PURE__ */ isArray(ids)) for (const single of ids) targetIds.add(single);
3594
- else if (ids) targetIds.add(ids);
3595
- }
3596
- if (multiple) {
3597
- for (const id of currentIds.difference(targetIds)) registry.selectedIds.delete(id);
3598
- for (const id of targetIds.difference(currentIds)) registry.selectedIds.add(id);
3599
- } else {
3600
- const next = targetIds.values().next().value;
3601
- const last = currentIds.values().next().value;
3602
- if (last !== void 0) registry.unselect(last);
3603
- if (next !== void 0) registry.select(next);
3604
- }
3605
- registryWatch.resume();
3606
- }, {
3607
- flush: "sync",
3608
- deep: multiple
3609
- });
3610
- function onRegister(ticket) {
3611
- if (!pending.has(ticket.value) || ticket.disabled) return;
3612
- registryWatch.pause();
3613
- modelWatch.pause();
3614
- registry.select(ticket.id);
3615
- pending.delete(ticket.value);
3616
- modelWatch.resume();
3617
- registryWatch.resume();
3618
- }
3619
- registry.on("register:ticket", onRegister);
3620
- function stop() {
3621
- registryWatch();
3622
- modelWatch();
3623
- registry.off("register:ticket", onRegister);
3624
- }
3625
- onScopeDispose(stop, true);
3626
- return stop;
3627
- }
3628
-
3629
- //#endregion
3630
- //#region src/composables/useProxyRegistry/index.ts
3631
- /**
3632
- * @module useProxyRegistry
3633
- *
3634
- * @remarks
3635
- * Proxy composable for reactive registry keys, values, entries, and size.
3636
- *
3637
- * Key features:
3638
- * - Reactive proxy for registry data
3639
- * - Deep or shallow reactivity options
3640
- * - Event-based updates
3641
- * - Automatic cleanup on scope disposal
3642
- * - Transforms Map-based registry into reactive refs
3643
- *
3644
- * Perfect for exposing registry data as reactive computed properties.
3645
- */
3646
- /**
3647
- * Creates a proxy registry that provides reactive objects for registry data.
3648
- *
3649
- * @param registry The registry instance to proxy.
3650
- * @param options The options for the proxy registry.
3651
- * @template Z The type of the registry ticket.
3652
- * @returns A proxy registry with reactive objects.
3653
- *
3654
- * @see https://0.vuetifyjs.com/composables/registration/use-proxy-registry
3655
- *
3656
- * @example
3657
- * ```ts
3658
- * import { useRegistry, useProxyRegistry } from '@vuetify/v0'
3659
- *
3660
- * const registry = useRegistry({ events: true })
3661
- * const proxy = useProxyRegistry(registry)
3662
- *
3663
- * registry.register({ value: 'Item 1' })
3664
- * console.log(proxy.size) // 1
3665
- * ```
3666
- */
3667
- function useProxyRegistry(registry, options) {
3668
- const state = (options?.deep ? reactive : shallowReactive)({
3669
- keys: registry.keys(),
3670
- values: registry.values(),
3671
- entries: registry.entries(),
3672
- size: registry.size
3673
- });
3674
- function update() {
3675
- state.keys = registry.keys();
3676
- state.values = registry.values();
3677
- state.entries = registry.entries();
3678
- state.size = registry.size;
3679
- }
3680
- registry.on("register:ticket", update);
3681
- registry.on("unregister:ticket", update);
3682
- registry.on("update:ticket", update);
3683
- registry.on("clear:registry", update);
3684
- onScopeDispose(() => {
3685
- registry.off("register:ticket", update);
3686
- registry.off("unregister:ticket", update);
3687
- registry.off("update:ticket", update);
3688
- registry.off("clear:registry", update);
3689
- }, true);
3690
- return state;
3691
- }
3692
-
3693
- //#endregion
3694
- //#region src/composables/useQueue/index.ts
3695
- /**
3696
- * @module useQueue
3697
- *
3698
- * @remarks
3699
- * A queue composable for managing time-based collections with:
3700
- * - Automatic timeout-based removal
3701
- * - Pause/resume functionality
3702
- * - FIFO (First In, First Out) ordering
3703
- * - Manual dismissal support
3704
- * - Queue progression management
3705
- *
3706
- * Built on top of useRegistry, the queue automatically manages timeouts for tickets,
3707
- * ensuring only the first ticket in the queue is active at any time. When an ticket
3708
- * expires or is removed, the next ticket in the queue automatically becomes active.
3709
- */
3710
- /**
3711
- * Creates a new queue instance
3712
- *
3713
- * @param options The options for the queue instance
3714
- * @template Z The type of queue ticket that extends QueueTicket. Use this to add custom properties to tickets.
3715
- * @template E The type of queue context that extends QueueContext<Z>. Use this when extending the queue with additional methods.
3716
- * @returns A new queue instance
3717
- *
3718
- * @see https://0.vuetifyjs.com/composables/registration/use-queue
3719
- *
3720
- * @example
3721
- * ```ts
3722
- * import { useQueue } from '@vuetify/v0'
3723
- *
3724
- * const queue = useQueue()
3725
- *
3726
- * // Register an ticket with default timeout (3000ms)
3727
- * const ticket1 = queue.register({ value: 'Ticket 1' })
3728
- *
3729
- * // Register an ticket with custom timeout
3730
- * const ticket2 = queue.register({ value: 'Ticket 2', timeout: 5000 })
3731
- *
3732
- * // Register a persistent ticket that must be manually dismissed
3733
- * const ticket3 = queue.register({ value: 'Ticket 3', timeout: -1 })
3734
- *
3735
- * // Dismiss an ticket using the convenience method
3736
- * ticket3.dismiss()
3737
- *
3738
- * console.log(queue.size) // 2
3739
- * ```
3740
- */
3741
- function createQueue(_options = {}) {
3742
- const { timeout: _timeout = 3e3,...options } = _options;
3743
- const registry = useRegistry({
3744
- ...options,
3745
- events: true
3746
- });
3747
- const timeouts = /* @__PURE__ */ new Map();
3748
- function startTimeout(ticket) {
3749
- if (ticket.timeout === void 0 || ticket.timeout === -1 || ticket.isPaused) return;
3750
- const timeout = setTimeout(() => {
3751
- timeouts.delete(ticket.id);
3752
- registry.unregister(ticket.id);
3753
- resume();
3754
- }, ticket.timeout);
3755
- timeouts.set(ticket.id, timeout);
3756
- }
3757
- function clearTimeout(id) {
3758
- const timeout = timeouts.get(id);
3759
- if (timeout) {
3760
- globalThis.clearTimeout(timeout);
3761
- timeouts.delete(id);
3762
- }
3763
- }
3764
- function register(registration = {}) {
3765
- const id = registration.id ?? /* @__PURE__ */ genId();
3766
- const timeout = Object.prototype.hasOwnProperty.call(registration, "timeout") ? registration.timeout : _timeout;
3767
- const ticket = {
3768
- ...registration,
3769
- id,
3770
- timeout,
3771
- isPaused: registry.size > 0,
3772
- dismiss: () => unregister(id)
3773
- };
3774
- const registered = registry.register(ticket);
3775
- startTimeout(registered);
3776
- return registered;
3777
- }
3778
- function unregister(id) {
3779
- const ticket = id === void 0 ? registry.seek("first") : registry.get(id);
3780
- if (!ticket) return void 0;
3781
- const wasFirst = ticket.index === 0;
3782
- clearTimeout(ticket.id);
3783
- registry.unregister(ticket.id);
3784
- if (wasFirst) resume();
3785
- return ticket;
3786
- }
3787
- function pause() {
3788
- const ticket = registry.seek("first");
3789
- if (!ticket || ticket.isPaused) return void 0;
3790
- clearTimeout(ticket.id);
3791
- registry.upsert(ticket.id, { isPaused: true });
3792
- return ticket;
3793
- }
3794
- function resume() {
3795
- const ticket = registry.seek("first");
3796
- if (!ticket || ticket.index !== 0 || !ticket.isPaused) return void 0;
3797
- registry.upsert(ticket.id, { isPaused: false });
3798
- startTimeout(ticket);
3799
- return ticket;
3800
- }
3801
- function clear() {
3802
- for (const id of timeouts.keys()) clearTimeout(id);
3803
- registry.clear();
3804
- }
3805
- function dispose() {
3806
- clear();
3807
- registry.dispose();
3808
- }
3809
- onScopeDispose(dispose, true);
3810
- return {
3811
- ...registry,
3812
- register,
3813
- unregister,
3814
- pause,
3815
- resume,
3816
- clear,
3817
- dispose,
3818
- get size() {
3819
- return registry.size;
3820
- }
3821
- };
3822
- }
3823
- /**
3824
- * Creates a new queue context.
3825
- *
3826
- * @param namespace The namespace for the queue context.
3827
- * @param options The options for the queue context.
3828
- * @template Z The type of the queue ticket.
3829
- * @template E The type of the queue context.
3830
- * @returns A new queue context.
3831
- *
3832
- * @see https://0.vuetifyjs.com/composables/registration/use-queue
3833
- *
3834
- * @example
3835
- * ```ts
3836
- * import { createQueueContext } from '@vuetify/v0'
3837
- *
3838
- * export const [useQueue, provideQueue] = createQueueContext('v0:queue', {
3839
- * timeout: 5000,
3840
- * })
3841
- * ```
3842
- */
3843
- function createQueueContext(_options) {
3844
- const { namespace,...options } = _options;
3845
- const [useQueueContext, _provideQueueContext] = createContext(namespace);
3846
- const context = createQueue(options);
3847
- function provideQueueContext(_context = context, app) {
3848
- return _provideQueueContext(_context, app);
3849
- }
3850
- return createTrinity(useQueueContext, provideQueueContext, context);
3851
- }
3852
- /**
3853
- * Returns the current queue instance.
3854
- *
3855
- * @param namespace The namespace for the queue context. Defaults to `'v0:queue'`.
3856
- * @returns The current queue instance.
3857
- *
3858
- * @see https://0.vuetifyjs.com/composables/registration/use-queue
3859
- *
3860
- * @example
3861
- * ```vue
3862
- * <script setup lang="ts">
3863
- * import { useQueue } from '@vuetify/v0'
3864
- *
3865
- * const queue = useQueue()
3866
- * <\/script>
3867
- * ```
3868
- */
3869
- function useQueue(namespace = "v0:queue") {
3870
- return useContext(namespace);
3871
- }
3872
-
3873
- //#endregion
3874
- //#region src/composables/useResizeObserver/index.ts
3875
- /**
3876
- * @module useResizeObserver
3877
- *
3878
- * @remarks
3879
- * ResizeObserver composable with lifecycle management.
3880
- *
3881
- * Key features:
3882
- * - ResizeObserver API wrapper
3883
- * - Pause/resume/stop functionality
3884
- * - Automatic cleanup on unmount
3885
- * - SSR-safe (checks SUPPORTS_OBSERVER)
3886
- * - Hydration-aware
3887
- * - Box model options (content-box/border-box)
3888
- *
3889
- * Perfect for responsive components and size-based rendering.
3890
- */
3891
- /**
3892
- * A composable that uses the Resize Observer API to detect when an element's
3893
- * size changes.
3894
- *
3895
- * @param target The element to observe.
3896
- * @param callback The callback to execute when the element's size changes.
3897
- * @param options The options for the Resize Observer.
3898
- * @returns An object with methods to control the observer.
3899
- *
3900
- * @see https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
3901
- * @see https://0.vuetifyjs.com/composables/system/use-resize-observer
3902
- *
3903
- * @example
3904
- * ```ts
3905
- * import { ref } from 'vue'
3906
- * import { useResizeObserver } from '@vuetify/v0'
3907
- *
3908
- * const el = ref<HTMLElement>()
3909
- * const width = ref(0)
3910
- * const height = ref(0)
3911
- *
3912
- * const { pause, resume, isPaused } = useResizeObserver(
3913
- * el,
3914
- * (entries) => {
3915
- * const entry = entries[0]
3916
- * if (entry) {
3917
- * width.value = entry.contentRect.width
3918
- * height.value = entry.contentRect.height
3919
- * console.log('Size changed:', width.value, 'x', height.value)
3920
- * }
3921
- * },
3922
- * { immediate: true }
3923
- * )
3924
- *
3925
- * // Pause observation
3926
- * pause()
3927
- *
3928
- * // Resume observation
3929
- * resume()
3930
- * ```
3931
- */
3932
- function useResizeObserver(target, callback, options = {}) {
3933
- const { isHydrated } = useHydration();
3934
- const observer = shallowRef();
3935
- const isPaused = shallowRef(false);
3936
- function setup() {
3937
- if (!isHydrated.value || !SUPPORTS_OBSERVER || !target.value || isPaused.value) return;
3938
- observer.value = new ResizeObserver((entries) => {
3939
- callback(entries.map((entry) => ({
3940
- contentRect: {
3941
- width: entry.contentRect.width,
3942
- height: entry.contentRect.height,
3943
- top: entry.contentRect.top,
3944
- left: entry.contentRect.left
3945
- },
3946
- target: entry.target
3947
- })));
3948
- });
3949
- observer.value.observe(target.value, { box: options.box || "content-box" });
3950
- if (options.immediate) {
3951
- const rect = target.value.getBoundingClientRect();
3952
- callback([{
3953
- contentRect: {
3954
- width: rect.width,
3955
- height: rect.height,
3956
- top: rect.top,
3957
- left: rect.left
3958
- },
3959
- target: target.value
3960
- }]);
3961
- }
3962
- }
3963
- watch([isHydrated, target], () => {
3964
- cleanup();
3965
- setup();
3966
- }, { immediate: true });
3967
- function cleanup() {
3968
- if (observer.value) {
3969
- observer.value.disconnect();
3970
- observer.value = void 0;
3971
- }
3972
- }
3973
- function pause() {
3974
- isPaused.value = true;
3975
- observer.value?.disconnect();
3976
- }
3977
- function resume() {
3978
- isPaused.value = false;
3979
- setup();
3980
- }
3981
- function stop() {
3982
- cleanup();
3983
- }
3984
- onUnmounted(stop);
3985
- return {
3986
- isPaused: readonly(isPaused),
3987
- pause,
3988
- resume,
3989
- stop
3990
- };
3991
- }
3992
- /**
3993
- * A convenience composable that uses the Resize Observer API to track an
3994
- * element's size.
3995
- *
3996
- * @param target The element to observe.
3997
- * @returns An object with the element's width and height.
3998
- *
3999
- * @see https://0.vuetifyjs.com/composables/system/use-resize-observer#use-element-size
4000
- *
4001
- * @example
4002
- * ```ts
4003
- * import { ref, watchEffect } from 'vue'
4004
- * import { useElementSize } from '@vuetify/v0'
4005
- *
4006
- * const box = ref<HTMLElement>()
4007
- * const { width, height } = useElementSize(box)
4008
- *
4009
- * // Width and height are reactive refs
4010
- * watchEffect(() => {
4011
- * console.log('Box size:', width.value, 'x', height.value)
4012
- * })
4013
- * ```
4014
- */
4015
- function useElementSize(target) {
4016
- const width = shallowRef(0);
4017
- const height = shallowRef(0);
4018
- const { pause: _pause, resume, stop, isPaused } = useResizeObserver(target, (entries) => {
4019
- const entry = entries[0];
4020
- if (entry) {
4021
- width.value = entry.contentRect.width;
4022
- height.value = entry.contentRect.height;
4023
- }
4024
- }, { immediate: true });
4025
- function pause() {
4026
- width.value = 0;
4027
- height.value = 0;
4028
- _pause();
4029
- }
4030
- return {
4031
- width,
4032
- height,
4033
- isPaused,
4034
- pause,
4035
- resume,
4036
- stop
4037
- };
4038
- }
4039
-
4040
- //#endregion
4041
- //#region src/composables/useStep/index.ts
4042
- /**
4043
- * @module useStep
4044
- *
4045
- * @remarks
4046
- * Navigation composable that extends useSingle with first/last/next/prev/step methods.
4047
- *
4048
- * Key features:
4049
- * - Configurable circular or bounded navigation
4050
- * - Automatic disabled item skipping
4051
- * - Arbitrary step counts (positive/negative)
4052
- * - Perfect for wizards, carousels, pagination, onboarding flows
4053
- *
4054
- * Inheritance chain: useRegistry → useSelection → useSingle → useStep
4055
- */
4056
- /**
4057
- * Creates a new step instance with navigation through items.
4058
- *
4059
- * Extends `createSingle` with `first()`, `last()`, `next()`, `prev()`, and `step(count)` methods
4060
- * for sequential navigation. Supports both circular (wrapping) and bounded (stopping at edges) modes.
4061
- *
4062
- * @param options The options for the step instance.
4063
- * @template Z The type of the step ticket.
4064
- * @template E The type of the step context.
4065
- * @returns A new step instance with navigation methods.
4066
- *
4067
- * @remarks
4068
- * **Key Features:**
4069
- * - **Configurable Navigation**: `circular: true` for wrapping, `false` for bounded (default: false)
4070
- * - **Disabled Item Skipping**: Automatically skips disabled items during navigation
4071
- * - **Bidirectional**: Forward (`next`, positive `step`) and backward (`prev`, negative `step`)
4072
- * - **Safe Edge Cases**: Handles empty registries and all-disabled scenarios gracefully
4073
- *
4074
- * **Navigation Methods:**
4075
- * - `first()`: Select first non-disabled item
4076
- * - `last()`: Select last non-disabled item
4077
- * - `next()`: Move to next item (wraps if circular, stops at end if bounded)
4078
- * - `prev()`: Move to previous item (wraps if circular, stops at start if bounded)
4079
- * - `step(count)`: Move by `count` positions (negative for backward)
4080
- *
4081
- * **Circular Mode (`circular: true`):**
4082
- * - Uses modulo arithmetic for wrapping: `((index % length) + length) % length`
4083
- * - Works correctly with negative indexes and large step counts
4084
- * - Perfect for carousels, theme switchers, infinite scrolling
4085
- *
4086
- * **Bounded Mode (`circular: false`, default):**
4087
- * - Navigation stops at boundaries (no wrapping)
4088
- * - `next()` on last item does nothing
4089
- * - `prev()` on first item does nothing
4090
- * - Perfect for pagination, wizards with explicit completion, forms
4091
- *
4092
- * **Inheritance Chain:**
4093
- * `useRegistry` → `createSelection` → `createSingle` → `createStep`
4094
- *
4095
- * @see https://0.vuetifyjs.com/composables/selection/use-step
4096
- *
4097
- * @example
4098
- * ```ts
4099
- * import { createStep } from '@vuetify/v0'
4100
- *
4101
- * // Bounded navigation (default) - for pagination
4102
- * const pagination = createStep({ circular: false })
4103
- * pagination.onboard([
4104
- * { id: 'page-1', value: 1 },
4105
- * { id: 'page-2', value: 2 },
4106
- * { id: 'page-3', value: 3 },
4107
- * ])
4108
- * pagination.first() // Select page 1
4109
- * pagination.prev() // Does nothing (already at first)
4110
- * pagination.next() // Select page 2
4111
- *
4112
- * // Circular navigation - for carousels
4113
- * const carousel = createStep({ circular: true })
4114
- * carousel.onboard([
4115
- * { id: 'slide-1', value: 'First' },
4116
- * { id: 'slide-2', value: 'Second' },
4117
- * { id: 'slide-3', value: 'Third' },
4118
- * ])
4119
- * carousel.first()
4120
- * carousel.prev() // Wraps to 'slide-3'
4121
- * carousel.next() // Wraps to 'slide-1'
4122
- * ```
4123
- */
4124
- function createStep(_options = {}) {
4125
- const { circular = false,...options } = _options;
4126
- const registry = createSingle(options);
4127
- function first() {
4128
- const ticket = registry.seek("first");
4129
- if (ticket) registry.select(ticket.id);
4130
- }
4131
- function last() {
4132
- const ticket = registry.seek("last");
4133
- if (ticket) registry.select(ticket.id);
4134
- }
4135
- function next() {
4136
- step(1);
4137
- }
4138
- function prev() {
4139
- step(-1);
4140
- }
4141
- function wrapped(length, index) {
4142
- return (index % length + length) % length;
4143
- }
4144
- function step(count = 1) {
4145
- const length = registry.size;
4146
- if (!length) return;
4147
- const currentIndex = registry.selectedIndex.value;
4148
- const direction = Math.sign(count || 1);
4149
- let hops = 0;
4150
- let index = circular ? wrapped(length, currentIndex + count) : currentIndex + count;
4151
- if (!circular && (index < 0 || index >= length)) return;
4152
- let id = registry.lookup(index);
4153
- while (id !== void 0 && toValue(registry.get(id)?.disabled) && hops < length) {
4154
- index = circular ? wrapped(length, index + direction) : index + direction;
4155
- if (!circular && (index < 0 || index >= length)) return;
4156
- id = registry.lookup(index);
4157
- hops++;
4158
- }
4159
- if (id === void 0 || hops === length) return;
4160
- registry.selectedIds.clear();
4161
- registry.select(id);
4162
- }
4163
- return {
4164
- ...registry,
4165
- first,
4166
- last,
4167
- next,
4168
- prev,
4169
- step,
4170
- get size() {
4171
- return registry.size;
4172
- }
4173
- };
4174
- }
4175
- /**
4176
- * Creates a new step context.
4177
- *
4178
- * @param namespace The namespace for the step context.
4179
- * @param options The options for the step context.
4180
- * @template Z The type of the step ticket.
4181
- * @template E The type of the step context.
4182
- * @returns A new step context.
4183
- *
4184
- * @see https://0.vuetifyjs.com/composables/selection/use-step
4185
- *
4186
- * @example
4187
- * ```ts
4188
- * import { createStepContext } from '@vuetify/v0'
4189
- *
4190
- * export const [useWizard, provideWizard, wizard] = createStepContext('wizard')
4191
- *
4192
- * // In a parent component:
4193
- * provideWizard()
4194
- *
4195
- * // In a child component:
4196
- * const wizard = useWizard()
4197
- * wizard.next() // Progress to next step
4198
- * ```
4199
- */
4200
- function createStepContext(_options) {
4201
- const { namespace,...options } = _options;
4202
- const [useStepContext, _provideStepContext] = createContext(namespace);
4203
- const context = createStep(options);
4204
- function provideStepContext(_context = context, app) {
4205
- return _provideStepContext(_context, app);
4206
- }
4207
- return createTrinity(useStepContext, provideStepContext, context);
4208
- }
4209
- /**
4210
- * Returns the current step instance.
4211
- *
4212
- * @param namespace The namespace for the step context. Defaults to `'v0:step'`.
4213
- * @returns The current step instance.
4214
- *
4215
- * @see https://0.vuetifyjs.com/composables/selection/use-step
4216
- *
4217
- * @example
4218
- * ```vue
4219
- * <script setup lang="ts">
4220
- * import { useStep } from '@vuetify/v0'
4221
- *
4222
- * const wizard = useStep()
4223
- * <\/script>
4224
- *
4225
- * <template>
4226
- * <div>
4227
- * <p>Current step: {{ wizard.selectedIndex }}</p>
4228
- * <button @click="wizard.next()">Next</button>
4229
- * </div>
4230
- * </template>
4231
- * ```
4232
- */
4233
- function useStep(namespace = "v0:step") {
4234
- return useContext(namespace);
4235
- }
4236
-
4237
- //#endregion
4238
- //#region src/composables/useStorage/adapters/memory.ts
4239
- /**
4240
- * In-memory storage adapter that implements the StorageAdapter interface.
4241
- * This adapter provides temporary storage that persists only for the current
4242
- * session and is useful for testing or when persistent storage is not available.
4243
- */
4244
- var MemoryAdapter = class {
4245
- store = /* @__PURE__ */ new Map();
4246
- get length() {
4247
- return this.store.size;
4248
- }
4249
- getItem(key) {
4250
- return this.store.get(key) ?? null;
4251
- }
4252
- setItem(key, value) {
4253
- this.store.set(key, value);
4254
- }
4255
- removeItem(key) {
4256
- this.store.delete(key);
4257
- }
4258
- key(index) {
4259
- return String(Array.from(this.store.keys())[index] ?? "");
4260
- }
4261
- };
4262
-
4263
- //#endregion
4264
- //#region src/composables/useStorage/index.ts
4265
- /**
4266
- * @module useStorage
4267
- *
4268
- * @remarks
4269
- * Reactive storage composable with adapter pattern for localStorage, sessionStorage, or memory.
4270
- *
4271
- * Key features:
4272
- * - Reactive refs that sync with storage
4273
- * - localStorage, sessionStorage, and memory adapters
4274
- * - Custom serialization support
4275
- * - SSR fallback to memory adapter
4276
- * - Automatic cleanup on remove/clear
4277
- *
4278
- * Uses adapter pattern to abstract storage implementation details.
4279
- */
4280
- const [useStorageContext, provideStorageContext] = createContext("v0:storage");
4281
- /**
4282
- * Creates a new storage instance.
4283
- *
4284
- * @param options The options for the storage instance.
4285
- * @template E The type of the storage context.
4286
- * @returns A new storage instance.
4287
- *
4288
- * @see https://0.vuetifyjs.com/composables/plugins/use-storage
4289
- *
4290
- * @example
4291
- * ```ts
4292
- * import { createStorage } from '@vuetify/v0'
4293
- *
4294
- * const storage = createStorage()
4295
- *
4296
- * storage.set('username', 'MyUsername')
4297
- *
4298
- * const username = storage.get('username')
4299
- *
4300
- * console.log(username.value) // MyUsername
4301
- *
4302
- * storage.clear()
4303
- * ```
4304
- */
4305
- function createStorage(options = {}) {
4306
- const { adapter = IN_BROWSER ? window.localStorage : new MemoryAdapter(), prefix = "v0:", serializer = {
4307
- read: JSON.parse,
4308
- write: JSON.stringify
4309
- } } = options;
4310
- const cache = /* @__PURE__ */ new Map();
4311
- const watchers = /* @__PURE__ */ new Map();
4312
- function has(key) {
4313
- const prefixedKey = `${prefix}${key}`;
4314
- return cache.has(prefixedKey);
4315
- }
4316
- function get(key, defaultValue) {
4317
- const prefixedKey = `${prefix}${key}`;
4318
- if (cache.has(prefixedKey)) return cache.get(prefixedKey);
4319
- const storedValue = adapter?.getItem(prefixedKey);
4320
- let initialValue = defaultValue;
4321
- if (storedValue) try {
4322
- initialValue = serializer.read(storedValue);
4323
- } catch (error) {
4324
- console.error(`[v0:storage] Failed to parse stored value for key "${prefixedKey}":`, error);
4325
- }
4326
- const valueRef = ref(initialValue);
4327
- const stop = watch(valueRef, (newValue) => {
4328
- if (newValue === void 0 || newValue === null) adapter?.removeItem(prefixedKey);
4329
- else adapter?.setItem(prefixedKey, serializer.write(newValue));
4330
- }, { deep: true });
4331
- watchers.set(prefixedKey, stop);
4332
- cache.set(prefixedKey, valueRef);
4333
- return valueRef;
4334
- }
4335
- function set(key, value) {
4336
- const valueRef = get(key);
4337
- valueRef.value = value;
4338
- }
4339
- function remove(key) {
4340
- const prefixedKey = `${prefix}${key}`;
4341
- const stop = watchers.get(prefixedKey);
4342
- if (!stop) return;
4343
- stop();
4344
- watchers.delete(prefixedKey);
4345
- adapter?.removeItem(prefixedKey);
4346
- cache.delete(prefixedKey);
4347
- }
4348
- function clear() {
4349
- if (watchers.size > 0) {
4350
- for (const stop of watchers.values()) stop();
4351
- watchers.clear();
4352
- }
4353
- if (cache.size > 0) {
4354
- for (const key of cache.keys()) adapter?.removeItem(key);
4355
- cache.clear();
4356
- }
4357
- }
4358
- return {
4359
- has,
4360
- get,
4361
- set,
4362
- remove,
4363
- clear
4364
- };
4365
- }
4366
- function createStorageContext(_options = {}) {
4367
- const { namespace = "v0:storage",...options } = _options;
4368
- const [useStorageContext$1, _provideStorageContext] = createContext(namespace);
4369
- const context = createStorage(options);
4370
- function provideStorageContext$1(_context = context, app) {
4371
- return _provideStorageContext(_context, app);
4372
- }
4373
- return createTrinity(useStorageContext$1, provideStorageContext$1, context);
4374
- }
4375
- /**
4376
- * Creates a new storage plugin.
4377
- *
4378
- * @param options The options for the storage plugin.
4379
- * @returns A new storage plugin.
4380
- *
4381
- * @see https://0.vuetifyjs.com/composables/plugins/use-storage
4382
- *
4383
- * @example
4384
- * ```ts
4385
- * import { createApp } from 'vue'
4386
- * import { createStoragePlugin } from '@vuetify/v0'
4387
- * import App from './App.vue'
4388
- *
4389
- * const app = createApp(App)
4390
- *
4391
- * app.use(createStoragePlugin())
4392
- *
4393
- * app.mount('#app')
4394
- * ```
4395
- */
4396
- function createStoragePlugin(_options = {}) {
4397
- const { namespace = "v0:storage",...options } = _options;
4398
- const [, provideStorageContext$1, context] = createStorageContext({
4399
- ...options,
4400
- namespace
4401
- });
4402
- return createPlugin({
4403
- namespace,
4404
- provide: (app) => {
4405
- provideStorageContext$1(context, app);
4406
- }
4407
- });
4408
- }
4409
- /**
4410
- * Returns the current storage instance.
4411
- *
4412
- * @param namespace The namespace for the storage context. Defaults to `'v0:storage'`.
4413
- * @returns The current storage instance.
4414
- *
4415
- * @see https://0.vuetifyjs.com/composables/plugins/use-storage
4416
- *
4417
- * @example
4418
- * ```vue
4419
- * <script setup lang="ts">
4420
- * import { useStorage } from '@vuetify/v0'
4421
- *
4422
- * const storage = useStorage()
4423
- * const username = storage.get('username', 'Guest')
4424
- * <\/script>
4425
- *
4426
- * <template>
4427
- * <div>
4428
- * <p>Username: {{ username }}</p>
4429
- * </div>
4430
- * </template>
4431
- * ```
4432
- */
4433
- function useStorage(namespace = "v0:storage") {
4434
- return useContext(namespace);
4435
- }
4436
-
4437
- //#endregion
4438
- //#region src/composables/useTheme/adapters/adapter.ts
4439
- var ThemeAdapter = class {
4440
- stylesheetId = "v0-theme-stylesheet";
4441
- prefix;
4442
- constructor(prefix) {
4443
- this.prefix = prefix;
4444
- }
4445
- generate(colors, isDark) {
4446
- let css = "";
4447
- for (const theme in colors) {
4448
- const themeColors = colors[theme];
4449
- if (!themeColors) continue;
4450
- const vars = Object.entries(themeColors).map(([key, val]) => ` --${this.prefix}-${key}: ${val};`).join("\n");
4451
- css += `[data-theme="${theme}"] {\n${vars}\n}\n`;
4452
- }
4453
- if (isDark !== void 0) css += `:root {\n color-scheme: ${isDark ? "dark" : "light"};\n}\n`;
4454
- return css;
4455
- }
4456
- };
4457
-
4458
- //#endregion
4459
- //#region src/composables/useTheme/adapters/v0.ts
4460
- /**
4461
- * Theme adapter implementation for Vuetify v0 design system.
4462
- * This adapter generates CSS custom properties and injects them into the DOM
4463
- * as a stylesheet, allowing themes to be applied globally.
4464
- */
4465
- var Vuetify0ThemeAdapter = class extends ThemeAdapter {
4466
- cspNonce;
4467
- constructor(options = {}) {
4468
- super(options.prefix ?? "v0");
4469
- this.cspNonce = options.cspNonce;
4470
- this.stylesheetId = options.stylesheetId ?? this.stylesheetId;
4471
- }
4472
- setup(app, context, target) {
4473
- if (IN_BROWSER) {
4474
- onScopeDispose(watch([context.colors, context.isDark], ([colors, isDark]) => {
4475
- this.update(colors, isDark);
4476
- }, { immediate: true }), true);
4477
- if (/* @__PURE__ */ isNull(target)) return;
4478
- const targetEl = target instanceof HTMLElement ? target : /* @__PURE__ */ isString(target) ? document.querySelector(target) : app._container || document.querySelector("#app") || document.body;
4479
- if (!targetEl) return;
4480
- onScopeDispose(watch(context.selectedId, (id) => {
4481
- if (!id) return;
4482
- targetEl.dataset.theme = String(id);
4483
- }, { immediate: true }), true);
4484
- } else {
4485
- const head = app._context?.provides?.usehead ?? app._context?.provides?.head;
4486
- if (head?.push) {
4487
- const id = context.selectedId.value;
4488
- head.push({
4489
- htmlAttrs: { "data-theme": id ? String(id) : "" },
4490
- style: [{
4491
- innerHTML: this.generate(context.colors.value, context.isDark.value),
4492
- id: this.stylesheetId
4493
- }]
4494
- });
4495
- }
4496
- }
4497
- }
4498
- update(colors, isDark) {
4499
- if (!IN_BROWSER) return;
4500
- this.upsert(this.generate(colors, isDark));
4501
- }
4502
- upsert(styles) {
4503
- if (!IN_BROWSER) return;
4504
- let styleEl = document.querySelector(`#${this.stylesheetId}`);
4505
- if (!styleEl) {
4506
- styleEl = document.createElement("style");
4507
- styleEl.id = this.stylesheetId.startsWith("#") ? this.stylesheetId.slice(1) : this.stylesheetId;
4508
- if (this.cspNonce) styleEl.setAttribute("nonce", this.cspNonce);
4509
- document.head.append(styleEl);
4510
- }
4511
- styleEl.textContent = styles;
4512
- }
4513
- };
4514
-
4515
- //#endregion
4516
- //#region src/composables/useTheme/index.ts
4517
- /**
4518
- * @module useTheme
4519
- *
4520
- * @see https://0.vuetifyjs.com/composables/plugins/use-theme
4521
- *
4522
- * @remarks
4523
- * Theme management composable with token resolution and CSS variable injection.
4524
- *
4525
- * Key features:
4526
- * - Single-selection theme switching (extends createSingle)
4527
- * - Token alias resolution via useTokens
4528
- * - Lazy theme loading (compute colors only when selected)
4529
- * - CSS variable generation via adapter pattern
4530
- * - SSR support with head integration
4531
- * - Theme cycling
4532
- *
4533
- * Integrates with createSingle for selection and useTokens for color resolution.
4534
- */
4535
- /**
4536
- * Creates a new theme instance.
4537
- *
4538
- * @param options The options for the theme instance.
4539
- * @template Z The type of the theme ticket.
4540
- * @template E The type of the theme context.
4541
- * @returns A new theme instance.
4542
- *
4543
- * @see https://0.vuetifyjs.com/composables/plugins/use-theme
4544
- *
4545
- * @example
4546
- * ```ts
4547
- * import { createTheme } from '@vuetify/v0'
4548
- *
4549
- * export const [useTheme, provideTheme] = createTheme({
4550
- * namespace: 'v0:theme',
4551
- * default: 'light',
4552
- * themes: {
4553
- * light: {
4554
- * dark: false,
4555
- * colors: {
4556
- * primary: '#3b82f6',
4557
- * },
4558
- * },
4559
- * dark: {
4560
- * dark: true,
4561
- * colors: {
4562
- * primary: '#675496',
4563
- * },
4564
- * },
4565
- * },
4566
- * })
4567
- * ```
4568
- */
4569
- function createTheme(_options = {}) {
4570
- const { themes = {}, palette = {},...options } = _options;
4571
- const tokens = createTokens({
4572
- palette,
4573
- ...themes
4574
- }, { flat: true });
4575
- const registry = createSingle(options);
4576
- for (const id in themes) {
4577
- const { colors: value,...theme } = themes[id];
4578
- register({
4579
- id,
4580
- value,
4581
- ...theme
4582
- });
4583
- if (id === options.default && !registry.selectedId.value) registry.select(id);
4584
- }
4585
- const names = computed(() => registry.keys());
4586
- const colors = computed(() => {
4587
- const resolved = {};
4588
- for (const theme of registry.values()) {
4589
- if (theme.lazy && theme.id !== registry.selectedId.value) continue;
4590
- resolved[theme.id] = resolve(theme.value);
4591
- }
4592
- return resolved;
4593
- });
4594
- const isDark = toRef(() => registry.selectedItem.value?.dark ?? false);
4595
- function cycle(themes$1 = names.value) {
4596
- const current = themes$1.indexOf(registry.selectedId.value ?? "");
4597
- const next = current === -1 ? 0 : (current + 1) % themes$1.length;
4598
- registry.select(themes$1[next]);
4599
- }
4600
- function resolve(colors$1) {
4601
- const resolved = {};
4602
- for (const [key, value] of Object.entries(colors$1)) resolved[key] = tokens.isAlias(value) ? tokens.resolve(value) : value;
4603
- return resolved;
4604
- }
4605
- function register(registration = {}) {
4606
- const item = {
4607
- lazy: false,
4608
- dark: false,
4609
- ...registration
4610
- };
4611
- return registry.register(item);
4612
- }
4613
- return {
4614
- ...registry,
4615
- colors,
4616
- isDark,
4617
- register,
4618
- cycle,
4619
- get size() {
4620
- return registry.size;
4621
- }
4622
- };
4623
- }
4624
- /**
4625
- * Creates a new theme context trinity.
4626
- *
4627
- * @param options The options for the theme context.
4628
- * @template Z The type of the theme ticket.
4629
- * @template E The type of the theme context.
4630
- * @returns A new theme context trinity.
4631
- *
4632
- * @see https://0.vuetifyjs.com/composables/plugins/use-theme
4633
- *
4634
- * @example
4635
- * ```ts
4636
- * import { createThemeContext } from '@vuetify/v0'
4637
- *
4638
- * export const [useThemeContext, provideThemeContext, context] = createThemeContext({
4639
- * namespace: 'v0:theme',
4640
- * default: 'light',
4641
- * themes: {
4642
- * light: {
4643
- * dark: false,
4644
- * colors: {
4645
- * primary: '#3b82f6',
4646
- * },
4647
- * },
4648
- * dark: {
4649
- * dark: true,
4650
- * colors: {
4651
- * primary: '#675496',
4652
- * },
4653
- * },
4654
- * },
4655
- * })
4656
- * ```
4657
- */
4658
- function createThemeContext(_options = {}) {
4659
- const { namespace = "v0:theme",...options } = _options;
4660
- const [useThemeContext, _provideThemeContext] = createContext(namespace);
4661
- const context = createTheme(options);
4662
- function provideThemeContext(_context = context, app) {
4663
- return _provideThemeContext(_context, app);
4664
- }
4665
- return createTrinity(useThemeContext, provideThemeContext, context);
4666
- }
4667
- /**
4668
- * Creates a new theme plugin.
4669
- *
4670
- * @param options The options for the theme plugin.
4671
- * @template Z The type of the theme ticket.
4672
- * @template E The type of the theme context.
4673
- * @returns A new theme plugin.
4674
- *
4675
- * @see https://0.vuetifyjs.com/composables/plugins/use-theme
4676
- *
4677
- * @example
4678
- * ```ts
4679
- * import { createApp } from 'vue'
4680
- * import { createThemePlugin } from '@vuetify/v0'
4681
- * import App from './App.vue'
4682
- *
4683
- * const app = createApp(App)
4684
- *
4685
- * app.use(
4686
- * createThemePlugin({
4687
- * default: 'light',
4688
- * themes: {
4689
- * light: {
4690
- * dark: false,
4691
- * colors: {
4692
- * primary: '#3b82f6',
4693
- * },
4694
- * },
4695
- * dark: {
4696
- * dark: true,
4697
- * colors: {
4698
- * primary: '#675496',
4699
- * },
4700
- * },
4701
- * },
4702
- * })
4703
- * )
4704
- *
4705
- * app.mount('#app')
4706
- * ```
4707
- */
4708
- function createThemePlugin(_options = {}) {
4709
- const { adapter = new Vuetify0ThemeAdapter(), namespace = "v0:theme", palette = {}, themes = {}, target,...options } = _options;
4710
- const [, provideThemeContext, context] = createThemeContext({
4711
- ...options,
4712
- namespace,
4713
- themes,
4714
- palette
4715
- });
4716
- return createPlugin({
4717
- namespace,
4718
- provide: (app) => {
4719
- provideThemeContext(context, app);
4720
- },
4721
- setup: (app) => {
4722
- adapter.setup(app, context, target);
4723
- }
4724
- });
4725
- }
4726
- /**
4727
- * Returns the current theme instance.
4728
- *
4729
- * @param namespace The namespace for the theme context. Defaults to `v0:theme`.
4730
- * @returns The current theme instance.
4731
- *
4732
- * @see https://0.vuetifyjs.com/composables/plugins/use-theme
4733
- *
4734
- * @example
4735
- * ```vue
4736
- * <script setup lang="ts">
4737
- * import { useTheme } from '@vuetify/v0'
4738
- *
4739
- * const theme = useTheme()
4740
- * <\/script>
4741
- *
4742
- * <template>
4743
- * <div>
4744
- * <p>Current theme: {{ theme.selected.value }}</p>
4745
- * </div>
4746
- * </template>
4747
- * ```
4748
- */
4749
- function useTheme(namespace = "v0:theme") {
4750
- return useContext(namespace);
4751
- }
4752
-
4753
- //#endregion
4754
- //#region src/composables/useTimeline/index.ts
4755
- /**
4756
- * @module useTimeline
4757
- *
4758
- * @remarks
4759
- * Bounded undo/redo system with overflow management.
4760
- *
4761
- * Key features:
4762
- * - Fixed-size history (default: 10 items)
4763
- * - Undo/redo stack management
4764
- * - Overflow queue (preserves oldest items)
4765
- * - Automatic reindexing after operations
4766
- * - Perfect for command pattern, history tracking
4767
- *
4768
- * Extends useRegistry with temporal navigation capabilities.
4769
- */
4770
- /**
4771
- * Creates a new timeline instance.
4772
- *
4773
- * @param _options The options for the timeline instance.
4774
- * @template Z The type of the timeline ticket.
4775
- * @template E The type of the timeline context.
4776
- * @returns A new timeline instance.
4777
- *
4778
- * @see https://0.vuetifyjs.com/composables/registration/use-timeline
4779
- *
4780
- * @example
4781
- * ```ts
4782
- * import { useTimeline } from '@vuetify/v0'
4783
- *
4784
- * const timeline = useTimeline({ size: 3 })
4785
- *
4786
- * timeline.onboard([{ id: 'one' }, { id: 'two' }, { id: 'three' }])
4787
- *
4788
- * console.log(timeline.values()) // [{ id: 'one' }, { id: 'two' }, { id: 'three' }]
4789
- *
4790
- * timeline.undo()
4791
- * console.log(timeline.values()) // [{ id: 'one' }, { id: 'two' }]
4792
- *
4793
- * timeline.redo()
4794
- * console.log(timeline.values()) // [{ id: 'one' }, { id: 'two' }, { id: 'three' }]
4795
- * ```
4796
- */
4797
- function createTimeline(_options = {}) {
4798
- const { size = 10,...options } = _options;
4799
- const registry = useRegistry(options);
4800
- const stack = [];
4801
- const overflow = [];
4802
- function register(item) {
4803
- stack.length = 0;
4804
- if (registry.size < size) return registry.register({ ...item });
4805
- const removing = registry.seek("first");
4806
- if (overflow.length === size) overflow.shift();
4807
- overflow.push(removing);
4808
- registry.unregister(removing.id);
4809
- const ticket = registry.register({ ...item });
4810
- registry.reindex();
4811
- return ticket;
4812
- }
4813
- function undo() {
4814
- const item = registry.seek("last");
4815
- if (!item) return void 0;
4816
- stack.push(item);
4817
- registry.unregister(item.id);
4818
- const restored = overflow.pop();
4819
- if (restored) {
4820
- const remaining = registry.values();
4821
- registry.clear();
4822
- registry.onboard([restored, ...remaining]);
4823
- registry.reindex();
4824
- }
4825
- return item;
4826
- }
4827
- function redo() {
4828
- if (stack.length === 0) return void 0;
4829
- const item = stack.pop();
4830
- const ticket = registry.register(item);
4831
- registry.reindex();
4832
- return ticket;
4833
- }
4834
- return {
4835
- ...registry,
4836
- register,
4837
- undo,
4838
- redo,
4839
- get size() {
4840
- return registry.size;
4841
- }
4842
- };
4843
- }
4844
- /**
4845
- * Creates a new timeline plugin.
4846
- *
4847
- * @param namespace The namespace for the timeline plugin.
4848
- * @param options The options for the timeline plugin.
4849
- * @template Z The type of the timeline ticket.
4850
- * @template E The type of the timeline context.
4851
- * @returns A new timeline plugin.
4852
- *
4853
- * @see https://0.vuetifyjs.com/composables/registration/use-timeline
4854
- *
4855
- * @example
4856
- * ```ts
4857
- * import { createTimelineContext } from '@vuetify/v0'
4858
- *
4859
- * export const [useTimeline, provideTimeline, context] = createTimelineContext('v0:timeline', { size: 5 })
4860
- * context.register({ id: 'example' })
4861
- *
4862
- * // In a parent component
4863
- * provideTimeline()
4864
- *
4865
- * // In a child component
4866
- * const timeline = useTimeline()
4867
- *
4868
- * console.log(timeline.values()) // [{ id: 'example' }]
4869
- * ```
4870
- */
4871
- function createTimelineContext(_options) {
4872
- const { namespace,...options } = _options;
4873
- const [useTimelineContext, _provideTimelineContext] = createContext(namespace);
4874
- const context = createTimeline(options);
4875
- function provideTimelineContext(_context = context, app) {
4876
- return _provideTimelineContext(_context, app);
4877
- }
4878
- return createTrinity(useTimelineContext, provideTimelineContext, context);
4879
- }
4880
- /**
4881
- * Returns the current timeline instance.
4882
- *
4883
- * @param namespace The namespace for the timeline context. Defaults to `'v0:timeline'`.
4884
- * @returns The current timeline instance.
4885
- *
4886
- * @see https://0.vuetifyjs.com/composables/registration/use-timeline
4887
- *
4888
- * @example
4889
- * ```vue
4890
- * <script setup lang="ts">
4891
- * import { useTimeline } from '@vuetify/v0'
4892
- *
4893
- * const timeline = useTimeline()
4894
- * <\/script>
4895
- * ```
4896
- */
4897
- function useTimeline(namespace = "v0:timeline") {
4898
- return useContext(namespace);
4899
- }
4900
-
4901
- //#endregion
4902
- export { useTokens as $, PermissionAdapter as A, createContext as At, useKeydown as B, useQueue as C, createHydrationContext as Ct, createPermissionsContext as D, toArray as Dt, createPermissions as E, toReactive as Et, useLocale as F, useForm as G, useIntersectionObserver as H, Vuetify0LocaleAdapter as I, createFeaturesContext as J, useFilter as K, createSingle as L, createLocale as M, useContext as Mt, createLocaleContext as N, createPermissionsPlugin as O, createTrinity as Ot, createLocalePlugin as P, createTokensContext as Q, createSingleContext as R, createQueueContext as S, createHydration as St, useProxyModel as T, useHydration as Tt, createForm as U, useElementIntersection as V, createFormContext as W, useFeatures as X, createFeaturesPlugin as Y, createTokens as Z, createStepContext as _, useWindowEventListener as _t, createThemeContext as a, useSelection as at, useResizeObserver as b, createBreakpointsPlugin as bt, Vuetify0ThemeAdapter as c, createLogger as ct, createStoragePlugin as d, useLogger as dt, createGroup as et, provideStorageContext as f, Vuetify0LoggerAdapter as ft, createStep as g, useEventListener as gt, MemoryAdapter as h, useDocumentEventListener as ht, createTheme as i, createSelectionContext as it, useMutationObserver as j, provideContext as jt, usePermissions as k, createPlugin as kt, createStorage as l, createLoggerContext as lt, useStorageContext as m, ConsolaLoggerAdapter as mt, createTimelineContext as n, useGroup as nt, createThemePlugin as o, createRegistryContext as ot, useStorage as p, PinoLoggerAdapter as pt, createFeatures as q, useTimeline as r, createSelection as rt, useTheme as s, useRegistry as st, createTimeline as t, createGroupContext as tt, createStorageContext as u, createLoggerPlugin as ut, useStep as v, createBreakpoints as vt, useProxyRegistry as w, createHydrationPlugin as wt, createQueue as x, useBreakpoints as xt, useElementSize as y, createBreakpointsContext as yt, useSingle as z };