@vuetify/v0 0.0.21 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +34 -27
  2. package/dist/browser/index.js +7353 -4921
  3. package/dist/components/index.d.mts +4 -4
  4. package/dist/components/index.mjs +5 -5
  5. package/dist/{components-BomyjmT3.mjs → components-BI9xdoz5.mjs} +1171 -122
  6. package/dist/composables/index.d.mts +4 -4
  7. package/dist/composables/index.mjs +4 -5
  8. package/dist/constants/index.d.mts +1 -1
  9. package/dist/constants/index.mjs +2 -2
  10. package/dist/{globals-C3JrEDXZ.mjs → globals-WKdFmgwy.mjs} +1 -1
  11. package/dist/{index-MLb3LH9a.d.mts → index-Bby5ljat.d.mts} +55 -55
  12. package/dist/{index-CxeZr8jO.d.mts → index-C8YcMooA.d.mts} +6 -2
  13. package/dist/index-_y91DmIS.d.mts +4019 -0
  14. package/dist/{index-OU0IRbIS.d.mts → index-rcTqb9U3.d.mts} +610 -71
  15. package/dist/index.d.mts +7 -7
  16. package/dist/index.mjs +6 -7
  17. package/dist/json/attributes.json +898 -0
  18. package/dist/json/importMap.json +176 -0
  19. package/dist/json/tags.json +441 -0
  20. package/dist/json/web-types.json +2132 -0
  21. package/dist/types/index.d.mts +1 -1
  22. package/dist/useClickOutside-B47X8X6-.mjs +7656 -0
  23. package/dist/utilities/index.d.mts +3 -3
  24. package/dist/utilities/index.mjs +2 -2
  25. package/dist/{utilities-CjDz-Xvn.mjs → utilities-9i1hJnMd.mjs} +28 -1
  26. package/package.json +10 -4
  27. package/dist/composables-CbAPZabd.mjs +0 -3113
  28. package/dist/index-B0QJdwz9.d.mts +0 -2488
  29. package/dist/useStep-CfgBbrJB.mjs +0 -3192
  30. /package/dist/{constants-DypzAkYp.mjs → constants-3l8TGg5J.mjs} +0 -0
  31. /package/dist/{index-B9mKi4pr.d.mts → index-DMQJJUrv.d.mts} +0 -0
  32. /package/dist/{index-4jSy8KIt.d.mts → index-DYSwiS9k.d.mts} +0 -0
@@ -1,3192 +0,0 @@
1
- import { _ as range, c as isNull, d as isObject, h as isUndefined, i as isArray, l as isNullOrUndefined, m as isSymbol, o as isFunction, p as isString, r as genId, s as isNaN, t as clamp } from "./utilities-CjDz-Xvn.mjs";
2
- import { a as SUPPORTS_OBSERVER, s as __LOGGER_ENABLED__, t as IN_BROWSER } from "./globals-C3JrEDXZ.mjs";
3
- import { computed, getCurrentInstance, inject, isRef, onScopeDispose, provide, reactive, shallowReactive, shallowReadonly, shallowRef, toRef, toValue, watch, watchEffect } 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
- * Supports two modes:
19
- * - **Static key**: `createContext('my-key')` - key is fixed at creation time
20
- * - **Dynamic key**: `createContext()` or `createContext({ suffix: 'item' })` - key provided at runtime
21
- */
22
- /**
23
- * Injects a context provided by an ancestor component.
24
- *
25
- * @param key The key of the context to inject.
26
- * @param defaultValue Optional default value if context is not found.
27
- * @template Z The type of the context.
28
- * @returns The injected context.
29
- * @throws An error if the context is not found and no default is provided.
30
- *
31
- * @see https://vuejs.org/api/composition-api-dependency-injection.html#inject
32
- * @see https://0.vuetifyjs.com/composables/foundation/create-context#use-context
33
- *
34
- * @example
35
- * ```ts
36
- * // Without default value
37
- * const context = useContext<MyContext>('my-context')
38
- *
39
- * // With default value
40
- * const context = useContext<MyContext>('my-context', defaultContext)
41
- * ```
42
- */
43
- function useContext(key, defaultValue) {
44
- const context = inject(key, defaultValue);
45
- if (/* @__PURE__ */ isUndefined(context)) throw new Error(`Context "${String(key)}" not found. Ensure it's provided by an ancestor.`);
46
- return context;
47
- }
48
- /**
49
- * Provides a context to all descendant components.
50
- *
51
- * @param key The key of the context to provide.
52
- * @param context The context to provide.
53
- * @param app Optional Vue app instance to provide the context at app level instead of component level.
54
- * @template Z The type of the context.
55
- * @returns The provided context.
56
- *
57
- * @remarks
58
- * When `app` parameter is provided, the context is made available to all components in the app.
59
- * When omitted, the context is provided at the current component level and available to descendants only.
60
- *
61
- * @see https://vuejs.org/api/composition-api-dependency-injection.html#provide
62
- * @see https://0.vuetifyjs.com/composables/foundation/create-context#provide-context
63
- *
64
- * @example
65
- * ```ts
66
- * // Component-level provision
67
- * provideContext<MyContext>('my-context', context)
68
- *
69
- * // App-level provision (typically used in plugins)
70
- * const app = createApp()
71
- * provideContext<MyContext>('my-context', context, app)
72
- * ```
73
- */
74
- function provideContext(key, context, app) {
75
- if (app) app.provide(key, context);
76
- else provide(key, context);
77
- return context;
78
- }
79
- function createContext(keyOrOptions, defaultValue) {
80
- if (/* @__PURE__ */ isString(keyOrOptions) || /* @__PURE__ */ isSymbol(keyOrOptions)) {
81
- const _key = keyOrOptions;
82
- function _provideContext$1(context, app) {
83
- return provideContext(_key, context, app);
84
- }
85
- function _useContext$1() {
86
- return useContext(_key, defaultValue);
87
- }
88
- return [_useContext$1, _provideContext$1];
89
- }
90
- const suffix = /* @__PURE__ */ isObject(keyOrOptions) ? keyOrOptions.suffix : void 0;
91
- function _provideContext(key, context, app) {
92
- return provideContext(suffix ? `${key}:${suffix}` : key, context, app);
93
- }
94
- function _useContext(key, defaultValue$1) {
95
- return useContext(suffix ? `${key}:${suffix}` : key, defaultValue$1);
96
- }
97
- return [_useContext, _provideContext];
98
- }
99
-
100
- //#endregion
101
- //#region src/composables/createTrinity/index.ts
102
- /**
103
- * Creates a new trinity for a context composable and its provider.
104
- *
105
- * @param useContext The function that retrieves/uses the context (typically named `useContext`).
106
- * @param provideContext The function that provides the context to descendants.
107
- * @param context The default context instance to use when no custom context is provided.
108
- * @template Z The type of the context.
109
- * @returns A readonly tuple containing: [useContext function, provideContext wrapper function, default context instance].
110
- *
111
- * @remarks The trinity pattern is a foundational pattern used throughout the codebase for creating reusable context systems. It provides three related elements:
112
- *
113
- * 1. A function to retrieve/use the context
114
- * 2. A function to provide the context (with default value support)
115
- * 3. The default context instance
116
- *
117
- * The returned tuple is readonly (using `as const`) to ensure proper type inference.
118
- *
119
- * @see https://0.vuetifyjs.com/composables/foundation/create-trinity#create-trinity
120
- *
121
- * @example
122
- * ```ts
123
- * interface MyContext {
124
- * foo: string
125
- * bar: number
126
- * }
127
- *
128
- * export function createMyFeature<E extends MyContext = MyContext>() {
129
- * const [useContext, _provideContext] = createContext<E>('my-context')
130
- *
131
- * const context = { foo: 'hello', bar: 42 }
132
- *
133
- * function provideContext (_context: E = context, app?: App): E {
134
- * return _provideContext(_context, app)
135
- * }
136
- *
137
- * return createTrinity<E>(useContext, provideContext, context)
138
- * }
139
- * ```
140
- */
141
- function createTrinity(useContext$1, provideContext$1, context) {
142
- return [
143
- useContext$1,
144
- (_context = context, app) => provideContext$1(_context, app),
145
- context
146
- ];
147
- }
148
-
149
- //#endregion
150
- //#region src/composables/createPlugin/index.ts
151
- /**
152
- * Creates a new Vue plugin.
153
- *
154
- * @param options The plugin options.
155
- * @returns A new Vue plugin.
156
- *
157
- * @see https://0.vuetifyjs.com/composables/foundation/create-plugin#create-plugin
158
- *
159
- * @example
160
- * ```ts
161
- * export const [useContext, provideContext] = createContext<MyContext>('my-plugin')
162
- *
163
- * const context = {}
164
- *
165
- * export const MyPlugin = createPlugin({
166
- * namespace: 'my-plugin',
167
- * provide: (app) => {
168
- * provideContext(context, app)
169
- * },
170
- * setup: (app) => {
171
- * // Optional setup logic
172
- * },
173
- * })
174
- */
175
- function createPlugin(options) {
176
- return { install(app) {
177
- app.runWithContext(() => {
178
- options.provide(app);
179
- options.setup?.(app);
180
- });
181
- } };
182
- }
183
-
184
- //#endregion
185
- //#region src/composables/useLogger/adapters/consola.ts
186
- var ConsolaLoggerAdapter = class {
187
- consola;
188
- constructor(consolaInstance) {
189
- if (!consolaInstance) throw new Error("Consola instance is required for ConsolaLoggerAdapter");
190
- this.consola = consolaInstance;
191
- }
192
- debug(message, ...args) {
193
- this.consola.debug(message, ...args);
194
- }
195
- info(message, ...args) {
196
- this.consola.info(message, ...args);
197
- }
198
- warn(message, ...args) {
199
- this.consola.warn(message, ...args);
200
- }
201
- error(message, ...args) {
202
- this.consola.error(message, ...args);
203
- }
204
- trace(message, ...args) {
205
- if (this.consola.trace) this.consola.trace(message, ...args);
206
- else this.consola.debug(message, ...args);
207
- }
208
- fatal(message, ...args) {
209
- if (this.consola.fatal) this.consola.fatal(message, ...args);
210
- else this.consola.error("[FATAL]", message, ...args);
211
- }
212
- };
213
-
214
- //#endregion
215
- //#region src/composables/useLogger/adapters/pino.ts
216
- /**
217
- * Pino logger adapter implementation
218
- *
219
- * This adapter integrates with the Pino logging library,
220
- * providing high-performance structured logging optimized
221
- * for Node.js applications with minimal overhead.
222
- */
223
- var PinoLoggerAdapter = class {
224
- pino;
225
- constructor(pinoInstance) {
226
- if (!pinoInstance) throw new Error("Pino instance is required for PinoLoggerAdapter");
227
- this.pino = pinoInstance;
228
- }
229
- debug(message, ...args) {
230
- this.pino.debug(this.format(message, ...args));
231
- }
232
- info(message, ...args) {
233
- this.pino.info(this.format(message, ...args));
234
- }
235
- warn(message, ...args) {
236
- this.pino.warn(this.format(message, ...args));
237
- }
238
- error(message, ...args) {
239
- this.pino.error(this.format(message, ...args));
240
- }
241
- trace(message, ...args) {
242
- this.pino.trace(this.format(message, ...args));
243
- }
244
- fatal(message, ...args) {
245
- this.pino.fatal(this.format(message, ...args));
246
- }
247
- format(message, ...args) {
248
- if (args.length === 0) return { msg: message };
249
- if (args.length === 1 && /* @__PURE__ */ isObject(args[0])) return {
250
- ...args[0],
251
- msg: message
252
- };
253
- return {
254
- msg: message,
255
- args
256
- };
257
- }
258
- };
259
-
260
- //#endregion
261
- //#region src/composables/useLogger/adapters/v0.ts
262
- /**
263
- * Vuetify0.x logger adapter implementation
264
- *
265
- * This adapter provides console-based logging with proper formatting,
266
- * color coding, timestamps, and log level filtering for development
267
- * and production environments.
268
- */
269
- var Vuetify0LoggerAdapter = class {
270
- prefix;
271
- colors;
272
- timestamps;
273
- constructor(options = {}) {
274
- this.prefix = options.prefix || "v0";
275
- this.colors = options.colors !== false;
276
- this.timestamps = options.timestamps !== false;
277
- }
278
- debug(message, ...args) {
279
- this.log("debug", "debug", message, ...args);
280
- }
281
- info(message, ...args) {
282
- this.log("info", "info", message, ...args);
283
- }
284
- warn(message, ...args) {
285
- this.log("warn", "warn", message, ...args);
286
- }
287
- error(message, ...args) {
288
- this.log("error", "error", message, ...args);
289
- }
290
- trace(message, ...args) {
291
- this.log("trace", "trace", message, ...args);
292
- }
293
- fatal(message, ...args) {
294
- this.log("fatal", "error", message, ...args);
295
- }
296
- format(level, message, ...args) {
297
- return [[
298
- this.timestamps ? this.timestamp() : "",
299
- `[${this.prefix} ${level.toLowerCase()}]`,
300
- message
301
- ].filter(Boolean).join(" "), ...args];
302
- }
303
- timestamp() {
304
- if (!IN_BROWSER) return (/* @__PURE__ */ new Date()).toISOString();
305
- /* v8 ignore next -- defensive fallback, toTimeString always returns valid format */
306
- return (/* @__PURE__ */ new Date()).toTimeString().split(" ")[0] ?? "";
307
- }
308
- style(level) {
309
- if (!this.colors || !IN_BROWSER) return "";
310
- /* v8 ignore next -- LogLevel union is exhaustive */
311
- return {
312
- trace: "color: #64748b",
313
- debug: "color: #3b82f6",
314
- info: "color: #10b981",
315
- warn: "color: #f59e0b",
316
- error: "color: #ef4444",
317
- fatal: "color: #dc2626; font-weight: bold",
318
- silent: ""
319
- }[level] || "";
320
- }
321
- log(level, method, message, ...args) {
322
- const [formattedMessage, ...restArgs] = this.format(level, message, ...args);
323
- const style = this.style(level);
324
- if (IN_BROWSER && style && /* @__PURE__ */ isFunction(console[method])) console[method](`%c${formattedMessage}`, style, ...restArgs);
325
- else if (/* @__PURE__ */ isFunction(console[method])) console[method](formattedMessage, ...restArgs);
326
- }
327
- };
328
-
329
- //#endregion
330
- //#region src/composables/useLogger/index.ts
331
- /**
332
- * @module useLogger
333
- *
334
- * @see https://0.vuetifyjs.com/composables/plugins/use-logger
335
- *
336
- * @remarks
337
- * Logging composable with adapter pattern supporting console, consola, and pino.
338
- *
339
- * Key features:
340
- * - Multiple log levels (trace, debug, info, warn, error, fatal)
341
- * - Adapter pattern for console/consola/pino integration
342
- * - Enable/disable logging
343
- * - Fallback logger for undefined loggers
344
- * - Context logging support
345
- *
346
- * Uses adapter pattern to abstract logging implementation.
347
- */
348
- /**
349
- * Creates a new logger instance.
350
- *
351
- * @param options The options for the logger instance.
352
- * @returns A new logger instance.
353
- *
354
- * @see https://0.vuetifyjs.com/composables/plugins/use-logger
355
- *
356
- * @example
357
- * ```ts
358
- * import { createLogger } from '@vuetify/v0'
359
- *
360
- * const logger = createLogger({
361
- * level: 'debug',
362
- * prefix: '[MyApp]',
363
- * })
364
- *
365
- * logger.info('This is an info message')
366
- * logger.debug('This is a debug message')
367
- * logger.error('This is an error message')
368
- * logger.level('debug')
369
- * logger.debug('This debug message will now be logged')
370
- * ```
371
- */
372
- function createLogger(options = {}) {
373
- const { adapter = new Vuetify0LoggerAdapter({ prefix: options.prefix }), level: initialLevel = "info", enabled: initialEnabled = __LOGGER_ENABLED__ } = options;
374
- const currentLevel = shallowRef(initialLevel);
375
- const isEnabled = shallowRef(initialEnabled);
376
- function value(level$1) {
377
- return {
378
- trace: 0,
379
- debug: 1,
380
- info: 2,
381
- warn: 3,
382
- error: 4,
383
- fatal: 5,
384
- silent: 6
385
- }[level$1] ?? 2;
386
- }
387
- function can(level$1) {
388
- if (!isEnabled.value) return false;
389
- return value(level$1) >= value(currentLevel.value);
390
- }
391
- function format(message) {
392
- return message;
393
- }
394
- function debug(message, ...args) {
395
- if (can("debug")) adapter.debug(format(message), ...args);
396
- }
397
- function info(message, ...args) {
398
- if (can("info")) adapter.info(format(message), ...args);
399
- }
400
- function warn(message, ...args) {
401
- if (can("warn")) adapter.warn(format(message), ...args);
402
- }
403
- function error(message, ...args) {
404
- if (can("error")) adapter.error(format(message), ...args);
405
- }
406
- function trace(message, ...args) {
407
- if (can("trace")) adapter.trace?.(format(message), ...args);
408
- }
409
- function fatal(message, ...args) {
410
- if (can("fatal")) adapter.fatal?.(format(message), ...args);
411
- }
412
- function level(newLevel) {
413
- currentLevel.value = newLevel;
414
- }
415
- function current() {
416
- return currentLevel.value;
417
- }
418
- function enabled() {
419
- return isEnabled.value;
420
- }
421
- function enable() {
422
- isEnabled.value = true;
423
- }
424
- function disable() {
425
- isEnabled.value = false;
426
- }
427
- return {
428
- debug,
429
- info,
430
- warn,
431
- error,
432
- trace,
433
- fatal,
434
- level,
435
- current,
436
- enabled,
437
- enable,
438
- disable
439
- };
440
- }
441
- function createFallbackLogger(namespace = "v0:logger") {
442
- function format(message, type) {
443
- return `[${namespace} ${type}] ${message}`;
444
- }
445
- return {
446
- debug: (message, ...args) => console.debug(format(message, "debug"), ...args),
447
- info: (message, ...args) => console.info(format(message, "info"), ...args),
448
- warn: (message, ...args) => console.warn(format(message, "warn"), ...args),
449
- error: (message, ...args) => console.error(format(message, "error"), ...args),
450
- trace: (message, ...args) => console.trace(format(message, "trace"), ...args),
451
- fatal: (message, ...args) => console.error(format(message, "fatal"), ...args),
452
- level: () => {},
453
- current: () => "info",
454
- enabled: () => true,
455
- enable: () => {},
456
- disable: () => {}
457
- };
458
- }
459
- /**
460
- * Creates a new logger context.
461
- *
462
- * @param options The options for the logger context.
463
- * @template E The type of the logger context.
464
- * @returns A new logger context.
465
- *
466
- * @see https://0.vuetifyjs.com/composables/plugins/use-logger
467
- *
468
- * @example
469
- * ```ts
470
- * import { createLoggerContext } from '@vuetify/v0'
471
- *
472
- * export const [useAppLogger, provideAppLogger, appLogger] = createLoggerContext({
473
- * namespace: 'app:logger',
474
- * level: 'debug',
475
- * })
476
- * ```
477
- */
478
- function createLoggerContext(_options = {}) {
479
- const { namespace = "v0:logger", ...options } = _options;
480
- const [useLoggerContext, _provideLoggerContext] = createContext(namespace);
481
- const context = createLogger(options);
482
- function provideLoggerContext(_context = context, app) {
483
- return _provideLoggerContext(_context, app);
484
- }
485
- return createTrinity(useLoggerContext, provideLoggerContext, context);
486
- }
487
- /**
488
- * Creates a new logger plugin.
489
- *
490
- * @param options The options for the logger plugin.
491
- * @returns A new logger plugin.
492
- *
493
- * @see https://0.vuetifyjs.com/composables/plugins/use-logger
494
- *
495
- * @example
496
- * ```ts
497
- * import { createApp } from 'vue'
498
- * import { createLoggerPlugin } from '@vuetify/v0'
499
- * import App from './App.vue'
500
- *
501
- * const app = createApp(App)
502
- *
503
- * app.use(
504
- * createLoggerPlugin({
505
- * level: 'debug',
506
- * prefix: '[MyApp]',
507
- * })
508
- * )
509
- *
510
- * app.mount('#app')
511
- * ```
512
- */
513
- function createLoggerPlugin(_options = {}) {
514
- const { namespace = "v0:logger", ...options } = _options;
515
- const [, provideLoggerContext, context] = createLoggerContext({
516
- ...options,
517
- namespace
518
- });
519
- return createPlugin({
520
- namespace,
521
- provide: (app) => {
522
- provideLoggerContext(context, app);
523
- },
524
- setup: (_app) => {
525
- if (process.env.NODE_ENV !== "production" && IN_BROWSER) window.__v0Logger__ = context;
526
- }
527
- });
528
- }
529
- /**
530
- * Uses an existing or creates a new logger instance.
531
- *
532
- * @param namespace The namespace for the logger context. Defaults to `'v0:logger'`.
533
- * @returns The logger instance.
534
- *
535
- * @see https://0.vuetifyjs.com/composables/plugins/use-logger
536
- *
537
- * @example
538
- * ```ts
539
- * import { useLogger } from '@vuetify/v0'
540
- *
541
- * const logger = useLogger()
542
- *
543
- * logger.info('This is an info message')
544
- * logger.debug('This is a debug message')
545
- * logger.error('This is an error message')
546
- * logger.level('debug')
547
- * logger.debug('This debug message will now be logged')
548
- * ```
549
- */
550
- function useLogger(namespace = "v0:logger") {
551
- const fallback = createFallbackLogger(namespace);
552
- if (!getCurrentInstance()) return fallback;
553
- try {
554
- return useContext(namespace, fallback);
555
- } catch {
556
- return fallback;
557
- }
558
- }
559
-
560
- //#endregion
561
- //#region src/composables/useRegistry/index.ts
562
- /**
563
- * @module useRegistry
564
- *
565
- * @see https://0.vuetifyjs.com/composables/registration/use-registry
566
- *
567
- * @remarks
568
- * A foundational composable for managing collections of items (tickets) with:
569
- * - Unique ID-based access
570
- * - Index-based ordering
571
- * - Value-based reverse lookup
572
- * - Automatic reindexing
573
- * - Optional event emission
574
- * - Performance-optimized caching
575
- *
576
- * The registry serves as the base for many other composables in the system,
577
- * including useSelection, useForm, useTimeline, and more.
578
- */
579
- /**
580
- * Creates a new registry instance.
581
- *
582
- * @param options The options for the registry instance.
583
- * @template Z The type of registry ticket that extends RegistryTicket. Use this to add custom properties to tickets.
584
- * @template E The type of registry context that extends RegistryContext<Z>. Use this when extending the registry with additional methods.
585
- * @returns A new registry instance.
586
- *
587
- * @see https://0.vuetifyjs.com/composables/registration/use-registry#use-registry
588
- *
589
- * @example
590
- * ```ts
591
- * import { useRegistry } from '@vuetify/v0'
592
- *
593
- * const registry = useRegistry()
594
- *
595
- * const ticket1 = registry.register({ id: 'user-1', value: { name: 'John' } })
596
- * const ticket2 = registry.register({ id: 'user-2', value: { name: 'Jane' } })
597
- *
598
- * console.log(registry.size) // 2
599
- * console.log(registry.get('user-1')) // { id: 'user-1', index: 0, value: { name: 'John' }, ... }
600
- * ```
601
- */
602
- function useRegistry(options) {
603
- const logger = useLogger();
604
- const collection = /* @__PURE__ */ new Map();
605
- const catalog = /* @__PURE__ */ new Map();
606
- const directory = /* @__PURE__ */ new Map();
607
- const cache = /* @__PURE__ */ new Map();
608
- const listeners = /* @__PURE__ */ new Map();
609
- const events = options?.events ?? false;
610
- let indexDependentCount = 0;
611
- let needsReindex = false;
612
- let minDirtyIndex = Infinity;
613
- let batching = false;
614
- let pendingEmits = [];
615
- function emit(event, data = void 0) {
616
- if (!events) return;
617
- const cbs = listeners.get(event);
618
- if (!cbs) return;
619
- for (const cb of cbs) cb(data);
620
- }
621
- function on(event, cb) {
622
- if (!events) {
623
- logger.warn(`Events are disabled. Initialize with \`useRegistry({ events: true })\` to enable.`);
624
- return;
625
- }
626
- if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
627
- listeners.get(event).add(cb);
628
- }
629
- function off(event, cb) {
630
- if (!events) {
631
- logger.warn(`Events are disabled. Initialize with \`useRegistry({ events: true })\` to enable.`);
632
- return;
633
- }
634
- listeners.get(event)?.delete(cb);
635
- }
636
- function dispose() {
637
- listeners.clear();
638
- clear();
639
- }
640
- function get(id) {
641
- return collection.get(id);
642
- }
643
- function upsert(id, patch = {}) {
644
- const existing = get(id);
645
- if (!existing) return register({
646
- ...patch,
647
- id
648
- });
649
- const hasValue = Object.prototype.hasOwnProperty.call(patch, "value");
650
- let value = existing.value;
651
- let valueIsIndex = existing.valueIsIndex;
652
- if (hasValue) {
653
- if (/* @__PURE__ */ isUndefined(patch.value)) {
654
- value = existing.index;
655
- valueIsIndex = true;
656
- } else {
657
- value = patch.value;
658
- valueIsIndex = false;
659
- }
660
- if (valueIsIndex !== existing.valueIsIndex) if (valueIsIndex) indexDependentCount++;
661
- else indexDependentCount--;
662
- if (!Object.is(value, existing.value)) {
663
- unassign(existing.value, id);
664
- assign(value, id);
665
- }
666
- }
667
- const updated = {
668
- ...existing,
669
- ...patch,
670
- id,
671
- index: existing.index,
672
- value,
673
- valueIsIndex
674
- };
675
- collection.set(id, updated);
676
- invalidate();
677
- emit("update:ticket", updated);
678
- return updated;
679
- }
680
- function browse(value) {
681
- if (indexDependentCount > 0 && needsReindex) reindex();
682
- return catalog.get(value);
683
- }
684
- function lookup(index) {
685
- if (needsReindex) reindex();
686
- return directory.get(index);
687
- }
688
- function has(id) {
689
- return collection.has(id);
690
- }
691
- function assign(value, id) {
692
- const bucket = catalog.get(value);
693
- if (bucket) {
694
- if (!bucket.includes(id)) bucket.push(id);
695
- } else catalog.set(value, [id]);
696
- }
697
- function unassign(value, id) {
698
- const bucket = catalog.get(value);
699
- if (!bucket) return;
700
- const next = bucket.filter((v) => v !== id);
701
- if (next.length === 0) catalog.delete(value);
702
- else catalog.set(value, next);
703
- }
704
- function keys() {
705
- const cached = cache.get("keys");
706
- if (!/* @__PURE__ */ isUndefined(cached)) return cached;
707
- const keys$1 = Array.from(collection.keys());
708
- cache.set("keys", keys$1);
709
- return keys$1;
710
- }
711
- function values() {
712
- const cached = cache.get("values");
713
- if (!/* @__PURE__ */ isUndefined(cached)) return cached;
714
- const values$1 = Array.from(collection.values());
715
- cache.set("values", values$1);
716
- return values$1;
717
- }
718
- function entries() {
719
- const cached = cache.get("entries");
720
- if (!/* @__PURE__ */ isUndefined(cached)) return cached;
721
- const entries$1 = Array.from(collection.entries());
722
- cache.set("entries", entries$1);
723
- return entries$1;
724
- }
725
- function clear() {
726
- collection.clear();
727
- catalog.clear();
728
- directory.clear();
729
- invalidate();
730
- indexDependentCount = 0;
731
- needsReindex = false;
732
- minDirtyIndex = Infinity;
733
- emit("clear:registry");
734
- }
735
- function invalidate() {
736
- if (batching) return;
737
- cache.clear();
738
- }
739
- function queueEmit(event, data) {
740
- if (batching) pendingEmits.push({
741
- event,
742
- data
743
- });
744
- else emit(event, data);
745
- }
746
- function batch(fn) {
747
- if (batching) return fn();
748
- batching = true;
749
- pendingEmits = [];
750
- try {
751
- const result = fn();
752
- cache.clear();
753
- for (const { event, data } of pendingEmits) emit(event, data);
754
- return result;
755
- } finally {
756
- batching = false;
757
- pendingEmits = [];
758
- }
759
- }
760
- function reindex() {
761
- const startIndex = minDirtyIndex === Infinity ? 0 : minDirtyIndex;
762
- if (startIndex === 0) {
763
- catalog.clear();
764
- directory.clear();
765
- }
766
- invalidate();
767
- let index = 0;
768
- for (const ticket of collection.values()) {
769
- if (index < startIndex) {
770
- index++;
771
- continue;
772
- }
773
- if (startIndex > 0) directory.delete(ticket.index);
774
- if (ticket.valueIsIndex) {
775
- if (startIndex > 0) unassign(ticket.value, ticket.id);
776
- ticket.value = index;
777
- assign(ticket.value, ticket.id);
778
- } else if (startIndex === 0) assign(ticket.value, ticket.id);
779
- ticket.index = index;
780
- directory.set(index, ticket.id);
781
- index++;
782
- }
783
- needsReindex = false;
784
- minDirtyIndex = Infinity;
785
- emit("reindex:registry");
786
- }
787
- function register(registration = {}) {
788
- const size = collection.size;
789
- const id = registration.id ?? /* @__PURE__ */ genId();
790
- if (has(id)) {
791
- logger.warn(`Ticket "${id}" already exists. Use \`upsert()\` to update or check \`has()\` before registering.`);
792
- return get(id);
793
- }
794
- const valueIsUndefined = /* @__PURE__ */ isUndefined(registration.value);
795
- const index = registration.index ?? size;
796
- const value = valueIsUndefined ? index : registration.value;
797
- const valueIsIndex = valueIsUndefined;
798
- if (valueIsIndex) indexDependentCount++;
799
- const ticket = {
800
- ...registration,
801
- id,
802
- index,
803
- value,
804
- valueIsIndex
805
- };
806
- collection.set(ticket.id, ticket);
807
- directory.set(ticket.index, ticket.id);
808
- assign(ticket.value, ticket.id);
809
- invalidate();
810
- queueEmit("register:ticket", ticket);
811
- return ticket;
812
- }
813
- function unregister(id) {
814
- const ticket = collection.get(id);
815
- if (!ticket) return;
816
- if (ticket.valueIsIndex) indexDependentCount--;
817
- collection.delete(ticket.id);
818
- directory.delete(ticket.index);
819
- unassign(ticket.value, ticket.id);
820
- const willReindex = indexDependentCount > 0 && ticket.index < collection.size;
821
- if (!willReindex) invalidate();
822
- emit("unregister:ticket", ticket);
823
- minDirtyIndex = Math.min(minDirtyIndex, ticket.index);
824
- if (willReindex) reindex();
825
- else needsReindex = true;
826
- }
827
- function offboard(ids) {
828
- const removed = [];
829
- for (const id of ids) {
830
- const ticket = collection.get(id);
831
- if (!ticket) continue;
832
- if (ticket.valueIsIndex) indexDependentCount--;
833
- minDirtyIndex = Math.min(minDirtyIndex, ticket.index);
834
- collection.delete(ticket.id);
835
- directory.delete(ticket.index);
836
- unassign(ticket.value, ticket.id);
837
- removed.push(ticket);
838
- }
839
- if (removed.length === 0) return;
840
- invalidate();
841
- for (const ticket of removed) queueEmit("unregister:ticket", ticket);
842
- needsReindex = true;
843
- }
844
- function seek(direction = "first", from, predicate) {
845
- if (collection.size === 0) return void 0;
846
- if (needsReindex) reindex();
847
- if (!predicate && /* @__PURE__ */ isUndefined(from)) {
848
- const tickets$1 = values();
849
- return direction === "first" ? tickets$1[0] : tickets$1.at(-1);
850
- }
851
- const tickets = values();
852
- const index = /* @__PURE__ */ isUndefined(from) ? void 0 : /* @__PURE__ */ clamp(from, 0, tickets.length - 1);
853
- if (direction === "last") {
854
- const start = /* @__PURE__ */ isUndefined(index) ? tickets.length - 1 : index;
855
- for (let i = start; i >= 0; i--) {
856
- const ticket = tickets[i];
857
- if (!predicate || predicate(ticket)) return ticket;
858
- }
859
- } else {
860
- const start = /* @__PURE__ */ isUndefined(index) ? 0 : index;
861
- for (let i = start; i < tickets.length; i++) {
862
- const ticket = tickets[i];
863
- if (!predicate || predicate(ticket)) return ticket;
864
- }
865
- }
866
- }
867
- return {
868
- collection,
869
- emit,
870
- on,
871
- off,
872
- dispose,
873
- has,
874
- keys,
875
- clear,
876
- browse,
877
- entries,
878
- values,
879
- lookup,
880
- get,
881
- upsert,
882
- register,
883
- unregister,
884
- reindex,
885
- seek,
886
- batch,
887
- onboard(registrations) {
888
- return batch(() => registrations.map((registration) => register(registration)));
889
- },
890
- offboard,
891
- get size() {
892
- return collection.size;
893
- }
894
- };
895
- }
896
- /**
897
- * Creates a new registry context.
898
- *
899
- * @param options The options for the registry context, including `namespace` (defaults to `'v0:registry'`) and `events`.
900
- * @template Z The type of registry ticket that extends RegistryTicket. Use this to add custom properties to tickets.
901
- * @template E The type of registry context that extends RegistryContext<Z>. Use this when extending the registry with additional methods.
902
- * @returns A new registry context.
903
- *
904
- * @see https://0.vuetifyjs.com/composables/registration/use-registry#create-registry-context
905
- *
906
- * @example
907
- * ```ts
908
- * import { createRegistryContext } from '@vuetify/v0'
909
- *
910
- * // With default namespace 'v0:registry'
911
- * export const [useItems, provideItems, items] = createRegistryContext()
912
- *
913
- * // Or with custom namespace
914
- * export const [useItems, provideItems, items] = createRegistryContext({ namespace: 'my-items' })
915
- *
916
- * // In a parent component:
917
- * provideItems()
918
- *
919
- * // In a child component:
920
- * const items = useItems()
921
- * items.register({ id: 'item-1', value: 'Value 1' })
922
- * ```
923
- */
924
- function createRegistryContext(_options = {}) {
925
- const { namespace = "v0:registry", ...options } = _options;
926
- const [useRegistryContext, _provideRegistryContext] = createContext(namespace);
927
- const context = useRegistry(options);
928
- function provideRegistryContext(_context = context, app) {
929
- return _provideRegistryContext(_context, app);
930
- }
931
- return createTrinity(useRegistryContext, provideRegistryContext, context);
932
- }
933
-
934
- //#endregion
935
- //#region src/composables/useSelection/index.ts
936
- /**
937
- * @module useSelection
938
- *
939
- * @remarks
940
- * Base composable for managing selected items in a collection with Set-based tracking.
941
- *
942
- * Key features:
943
- * - Set-based selectedIds for O(1) selection checks
944
- * - Mandatory selection mode (prevents deselecting last item)
945
- * - Auto-enrollment option (selects non-disabled items on register)
946
- * - Disabled item filtering
947
- * - Computed selectedItems and selectedValues Sets
948
- *
949
- * Extends useRegistry and serves as the base for useSingle, useGroup, useStep, and useFeatures.
950
- */
951
- /**
952
- * Creates a new selection instance for managing multiple selected items.
953
- *
954
- * Extends `useRegistry` with selection tracking via a reactive `Set` of selected IDs.
955
- * Supports disabled items, mandatory selection enforcement, and auto-enrollment.
956
- *
957
- * @param options The options for the selection instance.
958
- * @template Z The type of the selection ticket.
959
- * @template E The type of the selection context.
960
- * @returns A new selection instance with selection management methods.
961
- *
962
- * @remarks
963
- * **Key Features:**
964
- * - Multi-selection support (unlike `useSingle` which enforces single selection)
965
- * - Set-based `selectedIds` tracking for efficient lookups
966
- * - Computed `selectedItems` and `selectedValues` for reactive access
967
- * - Each ticket gets `isSelected`, `select()`, `unselect()`, and `toggle()` methods
968
- * - Disabled items cannot be selected
969
- * - Mandatory mode prevents deselecting the last item
970
- * - Force mode auto-selects first non-disabled item on registration
971
- * - Enroll option auto-selects all non-disabled items on registration
972
- *
973
- * **Inheritance Chain:**
974
- * `useRegistry` → `createSelection` → `createSingle`/`createGroup` → `createStep`
975
- *
976
- * @see https://0.vuetifyjs.com/composables/selection/use-selection
977
- *
978
- * @example
979
- * ```ts
980
- * import { createSelection } from '@vuetify/v0'
981
- *
982
- * const selection = createSelection({ mandatory: true })
983
- *
984
- * selection.onboard([
985
- * { id: 'item-1', value: 'Item 1' },
986
- * { id: 'item-2', value: 'Item 2', disabled: true },
987
- * { id: 'item-3', value: 'Item 3' },
988
- * ])
989
- *
990
- * selection.select('item-1')
991
- * selection.select('item-3')
992
- *
993
- * console.log(selection.selectedIds) // Set { 'item-1', 'item-3' }
994
- * console.log(Array.from(selection.selectedValues.value)) // ['Item 1', 'Item 3']
995
- * ```
996
- */
997
- function createSelection(_options = {}) {
998
- const { disabled = false, enroll = false, mandatory = false, multiple = false, ...options } = _options;
999
- const registry = useRegistry(options);
1000
- const selectedIds = shallowReactive(/* @__PURE__ */ new Set());
1001
- const selectedItems = computed(() => {
1002
- return new Set(Array.from(selectedIds).map((id) => registry.get(id)).filter((item) => !/* @__PURE__ */ isUndefined(item)));
1003
- });
1004
- const selectedValues = computed(() => {
1005
- return new Set(Array.from(selectedItems.value).map((item) => item.value));
1006
- });
1007
- function seek(direction = "first", from) {
1008
- return registry.seek(direction, from, (ticket) => !toValue(ticket.disabled));
1009
- }
1010
- function mandate() {
1011
- if (!mandatory || registry.size === 0 || selectedIds.size > 0) return;
1012
- const ticket = seek("first");
1013
- if (ticket) select(ticket.id);
1014
- }
1015
- function select(id) {
1016
- if (toValue(disabled)) return;
1017
- const item = registry.get(id);
1018
- if (!item || toValue(item.disabled)) return;
1019
- if (!multiple) selectedIds.clear();
1020
- selectedIds.add(id);
1021
- }
1022
- function unselect(id) {
1023
- if (toValue(disabled)) return;
1024
- if (mandatory && selectedIds.size === 1) return;
1025
- selectedIds.delete(id);
1026
- }
1027
- function toggle(id) {
1028
- if (toValue(disabled)) return;
1029
- if (selected(id)) unselect(id);
1030
- else select(id);
1031
- }
1032
- function selected(id) {
1033
- return selectedIds.has(id);
1034
- }
1035
- function register(registration = {}) {
1036
- const id = registration.id ?? /* @__PURE__ */ genId();
1037
- const item = {
1038
- disabled: false,
1039
- select: () => select(id),
1040
- unselect: () => unselect(id),
1041
- toggle: () => toggle(id),
1042
- isSelected: toRef(() => selected(id)),
1043
- ...registration,
1044
- id
1045
- };
1046
- const ticket = registry.register(item);
1047
- if (enroll && !toValue(disabled) && !toValue(item.disabled)) selectedIds.add(ticket.id);
1048
- if (mandatory === "force") mandate();
1049
- return ticket;
1050
- }
1051
- function unregister(id) {
1052
- selectedIds.delete(id);
1053
- registry.unregister(id);
1054
- }
1055
- function offboard(ids) {
1056
- for (const id of ids) selectedIds.delete(id);
1057
- registry.offboard(ids);
1058
- }
1059
- function onboard(registrations) {
1060
- return registrations.map((registration) => register(registration));
1061
- }
1062
- function reset() {
1063
- registry.clear();
1064
- selectedIds.clear();
1065
- mandate();
1066
- }
1067
- return {
1068
- ...registry,
1069
- disabled,
1070
- selectedIds,
1071
- selectedItems,
1072
- selectedValues,
1073
- register,
1074
- unregister,
1075
- onboard,
1076
- offboard,
1077
- reset,
1078
- mandate,
1079
- seek,
1080
- select,
1081
- unselect,
1082
- toggle,
1083
- selected,
1084
- get size() {
1085
- return registry.size;
1086
- }
1087
- };
1088
- }
1089
- /**
1090
- * Creates a new selection context.
1091
- *
1092
- * @param options The options for the selection context.
1093
- * @template Z The type of the selection ticket.
1094
- * @template E The type of the selection context.
1095
- * @returns A new selection context.
1096
- *
1097
- * @see https://0.vuetifyjs.com/composables/selection/use-selection
1098
- *
1099
- * @example
1100
- * ```ts
1101
- * import { createSelectionContext } from '@vuetify/v0'
1102
- *
1103
- * // With default namespace 'v0:selection'
1104
- * export const [useCheckboxes, provideCheckboxes, checkboxes] = createSelectionContext()
1105
- *
1106
- * // Or with custom namespace
1107
- * export const [useCheckboxes, provideCheckboxes, checkboxes] = createSelectionContext({ namespace: 'checkboxes' })
1108
- *
1109
- * // In a parent component:
1110
- * provideCheckboxes()
1111
- *
1112
- * // In a child component:
1113
- * const checkboxes = useCheckboxes()
1114
- * checkboxes.select('checkbox-1')
1115
- * ```
1116
- */
1117
- function createSelectionContext(_options = {}) {
1118
- const { namespace = "v0:selection", ...options } = _options;
1119
- const [useSelectionContext, _provideSelectionContext] = createContext(namespace);
1120
- const context = createSelection(options);
1121
- function provideSelectionContext(_context = context, app) {
1122
- return _provideSelectionContext(_context, app);
1123
- }
1124
- return createTrinity(useSelectionContext, provideSelectionContext, context);
1125
- }
1126
- /**
1127
- * Returns the current selection instance.
1128
- *
1129
- * @param namespace The namespace for the selection context. Defaults to `'v0:selection'`.
1130
- * @returns The current selection instance.
1131
- *
1132
- * @see https://0.vuetifyjs.com/composables/selection/use-selection
1133
- *
1134
- * @example
1135
- * ```vue
1136
- * <script setup lang="ts">
1137
- * import { useSelection } from '@vuetify/v0'
1138
- *
1139
- * const selection = useSelection()
1140
- * <\/script>
1141
- *
1142
- * <template>
1143
- * <div>
1144
- * <p>Selected: {{ selection.selectedIds.size }}</p>
1145
- * </div>
1146
- * </template>
1147
- * ```
1148
- */
1149
- function useSelection(namespace = "v0:selection") {
1150
- return useContext(namespace);
1151
- }
1152
-
1153
- //#endregion
1154
- //#region src/composables/toArray/index.ts
1155
- /**
1156
- * @module toArray
1157
- *
1158
- * @remarks
1159
- * Utility function to normalize single values and arrays into arrays.
1160
- *
1161
- * Converts single values into single-element arrays, passes arrays through unchanged,
1162
- * and handles null/undefined by returning empty arrays. Perfect for functions that
1163
- * accept both single values and arrays as input (e.g., ID | ID[]).
1164
- */
1165
- /**
1166
- * Converts a value to an array.
1167
- *
1168
- * @param value The value to convert.
1169
- * @template Z The type of the value.
1170
- * @returns The converted array.
1171
- *
1172
- * @see https://0.vuetifyjs.com/composables/transformers/to-array
1173
- *
1174
- * @example
1175
- * ```ts
1176
- * import { toArray } from '@vuetify/v0'
1177
- *
1178
- * const value = 'Example Value'
1179
- * const valueAsArray = toArray(value)
1180
- *
1181
- * console.log(valueAsArray) // ['Example Value']
1182
- * ```
1183
- */
1184
- function toArray(value) {
1185
- return /* @__PURE__ */ isNullOrUndefined(value) ? [] : /* @__PURE__ */ isArray(value) ? value : [value];
1186
- }
1187
-
1188
- //#endregion
1189
- //#region src/composables/useProxyModel/index.ts
1190
- /**
1191
- * @module useProxyModel
1192
- *
1193
- * @remarks
1194
- * Proxy composable for bidirectional sync between selection registry and v-model.
1195
- *
1196
- * Key features:
1197
- * - Bidirectional synchronization
1198
- * - Array and single-value modes
1199
- * - Automatic cleanup on scope disposal
1200
- * - Perfect for form controls with selection backing
1201
- *
1202
- * Bridges the gap between selection composables and Vue's v-model.
1203
- */
1204
- /**
1205
- * Syncs a ref with a selection registry bidirectionally.
1206
- *
1207
- * @param registry The selection registry to bind to.
1208
- * @param model The ref to sync.
1209
- * @param options The options for the proxy model.
1210
- * @template Z The type of the selection ticket.
1211
- * @returns A function to stop the sync.
1212
- *
1213
- * @see https://0.vuetifyjs.com/composables/forms/use-proxy-model
1214
- *
1215
- * @example
1216
- * ```ts
1217
- * import { createSelection, useProxyModel } from '@vuetify/v0'
1218
- *
1219
- * const model = ref()
1220
- * const registry = createSelection({ events: true })
1221
- * registry.onboard([
1222
- * { id: 'item-1', value: 'Item 1' },
1223
- * { id: 'item-2', value: 'Item 2' },
1224
- * ])
1225
- *
1226
- * const stop = useProxyModel(registry, model)
1227
- * ```
1228
- */
1229
- function useProxyModel(registry, model, options) {
1230
- const multiple = options?.multiple ?? false;
1231
- const _transformIn = options?.transformIn;
1232
- const _transformOut = options?.transformOut;
1233
- function transformIn(val) {
1234
- const value = toValue(val);
1235
- return toArray(/* @__PURE__ */ isFunction(_transformIn) ? _transformIn(value) : value);
1236
- }
1237
- function transformOut(val) {
1238
- if (/* @__PURE__ */ isFunction(_transformOut)) return _transformOut(val);
1239
- return multiple ? val : val[0];
1240
- }
1241
- const modelAsArray = transformIn(model);
1242
- const pending = new Set(modelAsArray);
1243
- for (const value of modelAsArray) {
1244
- const ids = registry.browse(value);
1245
- if (ids) {
1246
- for (const id of ids) registry.select(id);
1247
- pending.delete(value);
1248
- }
1249
- }
1250
- const registryWatch = watch(registry.selectedValues, (val) => {
1251
- modelWatch.pause();
1252
- model.value = transformOut(Array.from(toValue(val)));
1253
- modelWatch.resume();
1254
- }, { flush: "sync" });
1255
- const modelWatch = watch(model, (val) => {
1256
- registryWatch.pause();
1257
- const currentIds = new Set(toValue(registry.selectedIds));
1258
- const targetIds = /* @__PURE__ */ new Set();
1259
- for (const value of transformIn(val)) {
1260
- const ids = registry.browse(value);
1261
- if (ids) for (const id of ids) targetIds.add(id);
1262
- }
1263
- if (multiple) {
1264
- for (const id of currentIds.difference(targetIds)) registry.selectedIds.delete(id);
1265
- for (const id of targetIds.difference(currentIds)) registry.selectedIds.add(id);
1266
- } else {
1267
- const next = targetIds.values().next().value;
1268
- const last = currentIds.values().next().value;
1269
- if (!/* @__PURE__ */ isUndefined(last)) registry.unselect(last);
1270
- if (!/* @__PURE__ */ isUndefined(next)) registry.select(next);
1271
- }
1272
- registryWatch.resume();
1273
- }, {
1274
- flush: "sync",
1275
- deep: multiple
1276
- });
1277
- function onRegister(data) {
1278
- const ticket = data;
1279
- if (!pending.has(ticket.value) || ticket.disabled) return;
1280
- registryWatch.pause();
1281
- modelWatch.pause();
1282
- registry.select(ticket.id);
1283
- pending.delete(ticket.value);
1284
- modelWatch.resume();
1285
- registryWatch.resume();
1286
- }
1287
- registry.on("register:ticket", onRegister);
1288
- function stop() {
1289
- registryWatch();
1290
- modelWatch();
1291
- registry.off("register:ticket", onRegister);
1292
- }
1293
- onScopeDispose(stop, true);
1294
- return stop;
1295
- }
1296
-
1297
- //#endregion
1298
- //#region src/composables/useProxyRegistry/index.ts
1299
- /**
1300
- * @module useProxyRegistry
1301
- *
1302
- * @remarks
1303
- * Proxy composable for reactive registry keys, values, entries, and size.
1304
- *
1305
- * Key features:
1306
- * - Reactive proxy for registry data
1307
- * - Deep or shallow reactivity options
1308
- * - Event-based updates
1309
- * - Automatic cleanup on scope disposal
1310
- * - Transforms Map-based registry into reactive refs
1311
- *
1312
- * Perfect for exposing registry data as reactive computed properties.
1313
- */
1314
- /**
1315
- * Creates a proxy registry that provides reactive objects for registry data.
1316
- *
1317
- * @param registry The registry instance to proxy.
1318
- * @param options The options for the proxy registry.
1319
- * @template Z The type of the registry ticket.
1320
- * @returns A proxy registry with reactive objects.
1321
- *
1322
- * @see https://0.vuetifyjs.com/composables/registration/use-proxy-registry
1323
- *
1324
- * @example
1325
- * ```ts
1326
- * import { useRegistry, useProxyRegistry } from '@vuetify/v0'
1327
- *
1328
- * const registry = useRegistry({ events: true })
1329
- * const proxy = useProxyRegistry(registry)
1330
- *
1331
- * registry.register({ value: 'Item 1' })
1332
- * console.log(proxy.size) // 1
1333
- * ```
1334
- */
1335
- function useProxyRegistry(registry, options) {
1336
- const state = (options?.deep ? reactive : shallowReactive)({
1337
- keys: registry.keys(),
1338
- values: registry.values(),
1339
- entries: registry.entries(),
1340
- size: registry.size
1341
- });
1342
- function update() {
1343
- state.keys = registry.keys();
1344
- state.values = registry.values();
1345
- state.entries = registry.entries();
1346
- state.size = registry.size;
1347
- }
1348
- registry.on("register:ticket", update);
1349
- registry.on("unregister:ticket", update);
1350
- registry.on("update:ticket", update);
1351
- registry.on("clear:registry", update);
1352
- onScopeDispose(() => {
1353
- registry.off("register:ticket", update);
1354
- registry.off("unregister:ticket", update);
1355
- registry.off("update:ticket", update);
1356
- registry.off("clear:registry", update);
1357
- }, true);
1358
- return state;
1359
- }
1360
-
1361
- //#endregion
1362
- //#region src/composables/useGroup/index.ts
1363
- /**
1364
- * @module useGroup
1365
- *
1366
- * @remarks
1367
- * Multi-selection composable that extends useSelection with batch operations and tri-state support.
1368
- *
1369
- * Key features:
1370
- * - Batch operations (select/unselect/toggle accept ID | ID[])
1371
- * - Tri-state support via mixed/indeterminate state (mix/unmix)
1372
- * - selectedIndexes computed Set for position-based tracking
1373
- * - Perfect for checkbox trees, multi-select dropdowns, filter panels
1374
- *
1375
- * Tri-state behavior:
1376
- * - Items can be selected, mixed (indeterminate), or unselected
1377
- * - select() clears mixed state, mix() clears selected state (mutually exclusive)
1378
- * - toggle() on a mixed item selects it (resolves positively)
1379
- *
1380
- * Inheritance chain: useRegistry → useSelection → useGroup
1381
- * Extended by: useFeatures
1382
- */
1383
- /**
1384
- * Creates a new group instance with batch selection and tri-state support.
1385
- *
1386
- * Extends `createSelection` to support selecting, unselecting, and toggling multiple items
1387
- * at once by passing an array of IDs. Adds tri-state (mixed/indeterminate) support for
1388
- * checkbox trees and similar use cases.
1389
- *
1390
- * @param options The options for the group instance.
1391
- * @template Z The type of the group ticket.
1392
- * @template E The type of the group context.
1393
- * @returns A new group instance with batch selection and tri-state support.
1394
- *
1395
- * @remarks
1396
- * **Key Differences from `createSelection`:**
1397
- * - `select()` accepts `ID | ID[]` for batch operations
1398
- * - `unselect()` accepts `ID | ID[]` for batch operations
1399
- * - `toggle()` accepts `ID | ID[]` for batch operations
1400
- * - Adds `selectedIndexes` computed Set for getting selected item indexes
1401
- * - Adds tri-state support via `mix()`, `unmix()`, `mixed()`, `mixedIds`, `mixedItems`
1402
- * - Perfect for checkbox trees, multi-select dropdowns, and bulk operations
1403
- *
1404
- * **Tri-State Support:**
1405
- * - Items can be in one of three states: selected, mixed (indeterminate), or unselected
1406
- * - `mix(id)` sets item to mixed state (clears selected if set)
1407
- * - `unmix(id)` clears mixed state
1408
- * - `select(id)` clears mixed state before selecting
1409
- * - `toggle(id)` on a mixed item selects it (resolves the indeterminate state positively)
1410
- * - Mixed state works on disabled items (it's a computed state, not user action)
1411
- *
1412
- * **Batch Operations:**
1413
- * - Single ID: `group.select('item-1')`
1414
- * - Array of IDs: `group.select(['item-1', 'item-2', 'item-3'])`
1415
- * - Uses `toArray()` utility internally to normalize input
1416
- * - Disabled items are automatically skipped in select operations
1417
- * - Non-existent IDs are silently ignored
1418
- *
1419
- * **Inheritance Chain:**
1420
- * `useRegistry` → `createSelection` → `createGroup`
1421
- *
1422
- * **Used By:**
1423
- * - `createFeatures` for feature flag management with multiple selections
1424
- *
1425
- * @see https://0.vuetifyjs.com/composables/selection/use-group
1426
- *
1427
- * @example
1428
- * ```ts
1429
- * import { createGroup } from '@vuetify/v0'
1430
- *
1431
- * const checkboxes = createGroup()
1432
- *
1433
- * checkboxes.onboard([
1434
- * { id: 'option-a', value: 'Option A' },
1435
- * { id: 'option-b', value: 'Option B' },
1436
- * { id: 'option-c', value: 'Option C' },
1437
- * ])
1438
- *
1439
- * // Select multiple items at once
1440
- * checkboxes.select(['option-a', 'option-c'])
1441
- *
1442
- * console.log(checkboxes.selectedIds) // Set { 'option-a', 'option-c' }
1443
- * console.log(Array.from(checkboxes.selectedIndexes.value)) // [0, 2]
1444
- *
1445
- * // Set item to mixed/indeterminate state
1446
- * checkboxes.mix('option-a')
1447
- * console.log(checkboxes.mixedIds) // Set { 'option-a' }
1448
- * console.log(checkboxes.selectedIds) // Set { 'option-c' } (option-a removed)
1449
- *
1450
- * // Toggle a mixed item selects it
1451
- * checkboxes.toggle('option-a')
1452
- * console.log(checkboxes.selectedIds) // Set { 'option-a', 'option-c' }
1453
- * console.log(checkboxes.mixedIds) // Set {} (cleared)
1454
- * ```
1455
- */
1456
- function createGroup(_options = {}) {
1457
- const { mandatory = false, multiple = true, ...options } = _options;
1458
- const selection = createSelection({
1459
- ...options,
1460
- mandatory,
1461
- multiple,
1462
- events: true
1463
- });
1464
- const proxy = useProxyRegistry(selection);
1465
- const mixedIds = shallowReactive(/* @__PURE__ */ new Set());
1466
- const selectedIndexes = computed(() => {
1467
- return new Set(Array.from(selection.selectedItems.value).map((item) => item?.index).filter((index) => !/* @__PURE__ */ isUndefined(index)));
1468
- });
1469
- const mixedItems = computed(() => {
1470
- return new Set(Array.from(mixedIds).map((id) => selection.get(id)).filter((item) => !/* @__PURE__ */ isUndefined(item)));
1471
- });
1472
- function mixed(id) {
1473
- return mixedIds.has(id);
1474
- }
1475
- function mix(ids) {
1476
- for (const id of toArray(ids)) {
1477
- if (!selection.has(id)) continue;
1478
- selection.selectedIds.delete(id);
1479
- mixedIds.add(id);
1480
- }
1481
- }
1482
- function unmix(ids) {
1483
- for (const id of toArray(ids)) mixedIds.delete(id);
1484
- }
1485
- function select(ids) {
1486
- for (const id of toArray(ids)) {
1487
- mixedIds.delete(id);
1488
- selection.select(id);
1489
- }
1490
- }
1491
- function unselect(ids) {
1492
- for (const id of toArray(ids)) selection.unselect(id);
1493
- }
1494
- function toggle(ids) {
1495
- for (const id of toArray(ids)) if (mixed(id)) select(id);
1496
- else selection.toggle(id);
1497
- }
1498
- function register(registration = {}) {
1499
- const id = registration.id ?? /* @__PURE__ */ genId();
1500
- const item = {
1501
- ...registration,
1502
- id,
1503
- isMixed: toRef(() => mixed(id)),
1504
- select: () => select(id),
1505
- unselect: () => unselect(id),
1506
- toggle: () => toggle(id),
1507
- mix: () => mix(id),
1508
- unmix: () => unmix(id)
1509
- };
1510
- const ticket = selection.register(item);
1511
- if (toValue(registration.indeterminate)) mix(id);
1512
- return ticket;
1513
- }
1514
- function unregister(id) {
1515
- mixedIds.delete(id);
1516
- selection.unregister(id);
1517
- }
1518
- function offboard(ids) {
1519
- for (const id of ids) mixedIds.delete(id);
1520
- selection.offboard(ids);
1521
- }
1522
- function onboard(registrations) {
1523
- return registrations.map((registration) => register(registration));
1524
- }
1525
- function reset() {
1526
- mixedIds.clear();
1527
- selection.reset();
1528
- }
1529
- const selectableItems = computed(() => {
1530
- return proxy.values.filter((item) => !toValue(item.disabled));
1531
- });
1532
- const isAllSelected = computed(() => {
1533
- const items = selectableItems.value;
1534
- if (items.length === 0) return false;
1535
- return items.every((item) => selection.selectedIds.has(item.id));
1536
- });
1537
- const isNoneSelected = computed(() => selection.selectedIds.size === 0);
1538
- const isMixed = computed(() => {
1539
- return mixedIds.size > 0 || !isNoneSelected.value && !isAllSelected.value;
1540
- });
1541
- function selectAll() {
1542
- for (const item of selectableItems.value) {
1543
- mixedIds.delete(item.id);
1544
- selection.select(item.id);
1545
- }
1546
- }
1547
- function unselectAll() {
1548
- const first = selection.selectedIds.values().next().value;
1549
- selection.selectedIds.clear();
1550
- if (!mandatory || !first) return;
1551
- selection.select(first);
1552
- }
1553
- function toggleAll() {
1554
- if (isAllSelected.value) unselectAll();
1555
- else selectAll();
1556
- }
1557
- return {
1558
- ...selection,
1559
- mixed,
1560
- mix,
1561
- unmix,
1562
- select,
1563
- unselect,
1564
- toggle,
1565
- register,
1566
- unregister,
1567
- offboard,
1568
- onboard,
1569
- reset,
1570
- selectAll,
1571
- unselectAll,
1572
- toggleAll,
1573
- mixedIds,
1574
- mixedItems,
1575
- selectedIndexes,
1576
- isNoneSelected,
1577
- isAllSelected,
1578
- isMixed,
1579
- get size() {
1580
- return selection.size;
1581
- }
1582
- };
1583
- }
1584
- /**
1585
- * Creates a new group context.
1586
- *
1587
- * @param options The options for the group context.
1588
- * @template Z The type of the group ticket.
1589
- * @template E The type of the group context.
1590
- * @returns A new group context.
1591
- *
1592
- * @see https://0.vuetifyjs.com/composables/selection/use-group
1593
- *
1594
- * @example
1595
- * ```ts
1596
- * import { createGroupContext } from '@vuetify/v0'
1597
- *
1598
- * // With default namespace 'v0:group'
1599
- * export const [useMyGroup, provideMyGroup, myGroup] = createGroupContext()
1600
- *
1601
- * // Or with custom namespace
1602
- * export const [useMyGroup, provideMyGroup, myGroup] = createGroupContext({ namespace: 'my-group' })
1603
- *
1604
- * // In a parent component:
1605
- * provideMyGroup()
1606
- *
1607
- * // In a child component:
1608
- * const group = useMyGroup()
1609
- * ```
1610
- */
1611
- function createGroupContext(_options = {}) {
1612
- const { namespace = "v0:group", ...options } = _options;
1613
- const [useGroupContext, _provideGroupContext] = createContext(namespace);
1614
- const context = createGroup(options);
1615
- function provideGroupContext(_context = context, app) {
1616
- return _provideGroupContext(_context, app);
1617
- }
1618
- return createTrinity(useGroupContext, provideGroupContext, context);
1619
- }
1620
- /**
1621
- * Returns the current group instance.
1622
- *
1623
- * @param namespace The namespace for the group context. Defaults to `'v0:group'`.
1624
- * @returns The current group instance.
1625
- *
1626
- * @see https://0.vuetifyjs.com/composables/selection/use-group
1627
- *
1628
- * @example
1629
- * ```vue
1630
- * <script setup lang="ts">
1631
- * import { useGroup } from '@vuetify/v0'
1632
- *
1633
- * const group = useGroup()
1634
- * <\/script>
1635
- *
1636
- * <template>
1637
- * <div>
1638
- * <p>Selected: {{ group.selectedIds.size }}</p>
1639
- * </div>
1640
- * </template>
1641
- * ```
1642
- */
1643
- function useGroup(namespace = "v0:group") {
1644
- return useContext(namespace);
1645
- }
1646
-
1647
- //#endregion
1648
- //#region src/composables/useSingle/index.ts
1649
- /**
1650
- * @module useSingle
1651
- *
1652
- * @remarks
1653
- * Single-selection composable that extends useSelection to enforce only one selected item.
1654
- *
1655
- * Key features:
1656
- * - Auto-clears previous selection when selecting new item
1657
- * - Singular computed properties (selectedId, selectedItem, selectedIndex, selectedValue)
1658
- * - Perfect for tabs, radio buttons, theme selectors
1659
- *
1660
- * Inheritance chain: useRegistry → useSelection → useSingle
1661
- */
1662
- /**
1663
- * Creates a new single selection instance that enforces only one selected item at a time.
1664
- *
1665
- * Extends `createSelection` by automatically clearing previous selections when a new item is selected.
1666
- * Adds computed singular properties: `selectedId`, `selectedItem`, `selectedIndex`, `selectedValue`.
1667
- *
1668
- * @param options The options for the single selection instance.
1669
- * @template Z The type of the single selection ticket.
1670
- * @template E The type of the single selection context.
1671
- * @returns A new single selection instance with single-selection enforcement.
1672
- *
1673
- * @remarks
1674
- * **Key Differences from `createSelection`:**
1675
- * - Automatically clears `selectedIds` before selecting a new item (enforces single selection)
1676
- * - Provides singular computed properties instead of plural sets
1677
- * - Perfect for tabs, radio buttons, theme selectors, and other single-choice UI components
1678
- *
1679
- * **Computed Properties:**
1680
- * - `selectedId`: The ID of the selected item (undefined if none selected)
1681
- * - `selectedItem`: The selected ticket object (undefined if none selected)
1682
- * - `selectedIndex`: The index of the selected item (-1 if none selected)
1683
- * - `selectedValue`: The value of the selected item (undefined if none selected)
1684
- *
1685
- * **Inheritance Chain:**
1686
- * `useRegistry` → `createSelection` → `createSingle` → `createStep`
1687
- *
1688
- * @see https://0.vuetifyjs.com/composables/selection/use-single
1689
- *
1690
- * @example
1691
- * ```ts
1692
- * import { createSingle } from '@vuetify/v0'
1693
- *
1694
- * const tabs = createSingle({ mandatory: true })
1695
- *
1696
- * tabs.onboard([
1697
- * { id: 'home', value: 'Home' },
1698
- * { id: 'about', value: 'About' },
1699
- * { id: 'contact', value: 'Contact' },
1700
- * ])
1701
- *
1702
- * tabs.first() // Select first tab
1703
- *
1704
- * console.log(tabs.selectedId.value) // 'home'
1705
- * console.log(tabs.selectedIndex.value) // 0
1706
- *
1707
- * tabs.select('about') // Switch to about tab
1708
- * console.log(tabs.selectedId.value) // 'about'
1709
- * console.log(tabs.selectedIds.size) // 1 (always enforces single selection)
1710
- * ```
1711
- */
1712
- function createSingle(_options = {}) {
1713
- const { mandatory = false, multiple = false, ...options } = _options;
1714
- const registry = createSelection({
1715
- ...options,
1716
- mandatory,
1717
- multiple
1718
- });
1719
- const selectedId = computed(() => registry.selectedIds.values().next().value);
1720
- const selectedItem = computed(() => registry.selectedItems.value.values().next().value);
1721
- const selectedIndex = computed(() => selectedItem.value?.index ?? -1);
1722
- const selectedValue = computed(() => selectedItem.value?.value);
1723
- function unselect(id) {
1724
- if (mandatory && registry.selectedIds.size === 1) return;
1725
- registry.selectedIds.delete(id);
1726
- }
1727
- function toggle(id) {
1728
- if (registry.selectedIds.has(id)) unselect(id);
1729
- else registry.select(id);
1730
- }
1731
- return {
1732
- ...registry,
1733
- selectedId,
1734
- selectedItem,
1735
- selectedIndex,
1736
- selectedValue,
1737
- unselect,
1738
- toggle,
1739
- get size() {
1740
- return registry.size;
1741
- }
1742
- };
1743
- }
1744
- /**
1745
- * Creates a new single selection context.
1746
- *
1747
- * @param options The options for the single selection context.
1748
- * @template Z The type of the single selection ticket.
1749
- * @template E The type of the single selection context.
1750
- * @returns A new single selection context.
1751
- *
1752
- * @see https://0.vuetifyjs.com/composables/selection/use-single
1753
- *
1754
- * @example
1755
- * ```ts
1756
- * import { createSingleContext } from '@vuetify/v0'
1757
- *
1758
- * // With default namespace 'v0:single'
1759
- * export const [useSingle, provideSingle, context] = createSingleContext()
1760
- *
1761
- * // In a parent component:
1762
- * provideSingle()
1763
- *
1764
- * // In a child component:
1765
- * const single = useSingle()
1766
- * single.select('tab-1')
1767
- * ```
1768
- */
1769
- function createSingleContext(_options = {}) {
1770
- const { namespace = "v0:single", ...options } = _options;
1771
- const [useSingleContext, _provideSingleContext] = createContext(namespace);
1772
- const context = createSingle(options);
1773
- function provideSingleContext(_context = context, app) {
1774
- return _provideSingleContext(_context, app);
1775
- }
1776
- return createTrinity(useSingleContext, provideSingleContext, context);
1777
- }
1778
- /**
1779
- * Returns the current single selection instance.
1780
- *
1781
- * @param namespace The namespace for the single selection context. Defaults to `'v0:single'`.
1782
- * @returns The current single selection instance.
1783
- *
1784
- * @see https://0.vuetifyjs.com/composables/selection/use-single
1785
- *
1786
- * @example
1787
- * ```vue
1788
- * <script setup lang="ts">
1789
- * import { useSingle } from '@vuetify/v0'
1790
- *
1791
- * const tabs = useSingle()
1792
- * <\/script>
1793
- *
1794
- * <template>
1795
- * <div>
1796
- * <p>Selected: {{ tabs.selectedId }}</p>
1797
- * </div>
1798
- * </template>
1799
- * ```
1800
- */
1801
- function useSingle(namespace = "v0:single") {
1802
- return useContext(namespace);
1803
- }
1804
-
1805
- //#endregion
1806
- //#region src/composables/useTokens/index.ts
1807
- /**
1808
- * @module useTokens
1809
- *
1810
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
1811
- *
1812
- * @remarks
1813
- * Design token registry with alias resolution and W3C Design Tokens format support.
1814
- *
1815
- * Key features:
1816
- * - Alias resolution with circular reference detection
1817
- * - Nested token flattening with dot notation
1818
- * - W3C Design Tokens format ($value, $type, $description, $extensions)
1819
- * - Path-based resolution (e.g., {colors}.blue.500)
1820
- * - Resolution caching for performance (~28,590 ops/sec)
1821
- *
1822
- * Used by useTheme, useLocale, and useFeatures for token-based configuration.
1823
- */
1824
- /**
1825
- * Creates a new token instance.
1826
- *
1827
- * @param tokens The tokens to use.
1828
- * @param options The options for the token instance.
1829
- * @template Z The type of the token ticket.
1830
- * @template E The type of the token context.
1831
- * @returns A new token instance.
1832
- *
1833
- * @see https://www.designtokens.org/tr/drafts/format/
1834
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
1835
- *
1836
- * @example
1837
- * ```ts
1838
- * import { useTokens } from '@vuetify/v0'
1839
- *
1840
- * const tokens = useTokens({
1841
- * colors: {
1842
- * primary: '#3b82f6',
1843
- * secondary: '{colors.primary}', // Alias reference
1844
- * },
1845
- * })
1846
- *
1847
- * console.log(tokens.resolve('{colors.primary}')) // '#3b82f6'
1848
- * console.log(tokens.resolve('{colors.secondary}')) // '#3b82f6'
1849
- * ```
1850
- */
1851
- function createTokens(tokens = {}, options = {}) {
1852
- const logger = useLogger();
1853
- const registry = useRegistry(options);
1854
- const cache = /* @__PURE__ */ new Map();
1855
- registry.onboard(flatten(tokens, options.prefix, !!options.flat));
1856
- function isAlias(token) {
1857
- return /* @__PURE__ */ isString(token) && token.length > 2 && token[0] === "{" && token.at(-1) === "}";
1858
- }
1859
- function isTokenAlias(value) {
1860
- return /* @__PURE__ */ isObject(value) && "$value" in value;
1861
- }
1862
- function resolve(token, visited = /* @__PURE__ */ new Set()) {
1863
- const cacheKey = /* @__PURE__ */ isString(token) ? token : JSON.stringify(token);
1864
- const cached = cache.get(cacheKey);
1865
- if (!/* @__PURE__ */ isUndefined(cached)) return cached;
1866
- const reference = isTokenAlias(token) ? token.$value : token;
1867
- const isAliasReference = /* @__PURE__ */ isString(reference) && isAlias(reference);
1868
- const clean = isAliasReference ? reference.slice(1, -1) : String(reference);
1869
- if (visited.has(clean)) {
1870
- logger.warn(`Circular alias detected for "${clean}"`);
1871
- cache.set(cacheKey, void 0);
1872
- return;
1873
- }
1874
- visited.add(clean);
1875
- let found = registry.get(clean);
1876
- let segments = [];
1877
- if (!found && clean.includes(".")) {
1878
- const parts = clean.split(".");
1879
- for (let i = parts.length - 1; i > 0; i--) {
1880
- const prefix = parts.slice(0, i).join(".");
1881
- const suffix = parts.slice(i);
1882
- const candidate = registry.get(prefix);
1883
- if (!/* @__PURE__ */ isUndefined(candidate?.value)) {
1884
- found = candidate;
1885
- segments = suffix;
1886
- break;
1887
- }
1888
- }
1889
- }
1890
- if (/* @__PURE__ */ isUndefined(found?.value)) {
1891
- if (isAliasReference) logger.warn(`Alias not found for "${String(reference)}"`);
1892
- cache.set(cacheKey, void 0);
1893
- return;
1894
- }
1895
- let result;
1896
- let current = found.value;
1897
- if (segments.length > 0) {
1898
- if (isTokenAlias(current)) current = current.$value;
1899
- for (const segment of segments) {
1900
- if (!/* @__PURE__ */ isObject(current) || !(segment in current)) {
1901
- current = void 0;
1902
- break;
1903
- }
1904
- current = current[segment];
1905
- if (isTokenAlias(current)) current = current.$value;
1906
- }
1907
- if (/* @__PURE__ */ isUndefined(current)) {
1908
- logger.warn(`Path not found inside "${clean}": ${segments.join(".")}`);
1909
- cache.set(cacheKey, void 0);
1910
- return;
1911
- }
1912
- result = current;
1913
- } else if (isTokenAlias(current)) {
1914
- const inner = current.$value;
1915
- if (/* @__PURE__ */ isString(inner) && isAlias(inner)) return resolve(inner, visited);
1916
- result = inner;
1917
- } else if (/* @__PURE__ */ isString(current) && isAlias(current)) return resolve(current, visited);
1918
- else result = current;
1919
- cache.set(cacheKey, result);
1920
- return result;
1921
- }
1922
- return {
1923
- ...registry,
1924
- resolve,
1925
- isAlias,
1926
- get size() {
1927
- return registry.size;
1928
- }
1929
- };
1930
- }
1931
- /**
1932
- * Creates a new token context.
1933
- *
1934
- * @param namespace The namespace for the token context.
1935
- * @param tokens The tokens to use.
1936
- * @template Z The type of the token ticket.
1937
- * @template E The type of the token context.
1938
- * @returns A new token context.
1939
- *
1940
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
1941
- *
1942
- * @example
1943
- * ```ts
1944
- * import { createTokensContext } from '@vuetify/v0'
1945
- *
1946
- * export const [useTokens, provideTokens, context] = createTokensContext({
1947
- * namespace: 'v0:tokens',
1948
- * tokens: {
1949
- * colors: {
1950
- * primary: '#3b82f6',
1951
- * secondary: '{colors.primary}', // Alias reference
1952
- * },
1953
- * },
1954
- * })
1955
- * ```
1956
- */
1957
- function createTokensContext(_options) {
1958
- const { namespace = "v0:tokens", tokens = {}, ...options } = _options;
1959
- const [useTokensContext, _provideTokensContext] = createContext(namespace);
1960
- const context = createTokens(tokens, options);
1961
- function provideTokensContext(_context = context, app) {
1962
- return _provideTokensContext(_context, app);
1963
- }
1964
- return createTrinity(useTokensContext, provideTokensContext, context);
1965
- }
1966
- /**
1967
- * Returns the current tokens instance.
1968
- *
1969
- * @param namespace The namespace for the tokens context. Defaults to `'v0:tokens'`.
1970
- * @returns The current tokens instance.
1971
- *
1972
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
1973
- *
1974
- * @example
1975
- * ```vue
1976
- * <script setup lang="ts">
1977
- * import { useTokens } from '@vuetify/v0'
1978
- *
1979
- * const tokens = useTokens()
1980
- * <\/script>
1981
- * ```
1982
- */
1983
- function useTokens(namespace = "v0:tokens") {
1984
- return useContext(namespace);
1985
- }
1986
- /**
1987
- * Flattens a nested collection of tokens into a flat array of tokens.
1988
- * Each token is represented by an object containing its ID & value.
1989
- * @param tokens The collection of tokens to flatten.
1990
- * @param prefix An optional prefix to prepend to each token ID.
1991
- * @returns An array of flattened tokens, each with an ID and value.
1992
- */
1993
- function flatten(tokens, prefix = "", flat = false) {
1994
- const flattened = [];
1995
- const stack = [{
1996
- tokens,
1997
- prefix,
1998
- flat
1999
- }];
2000
- while (stack.length > 0) {
2001
- const { tokens: currentTokens, prefix: currentPrefix, flat: flat$1 } = stack.pop();
2002
- const meta = {};
2003
- for (const k in currentTokens) if (k.startsWith("$")) meta[k] = currentTokens[k];
2004
- if (Object.keys(meta).length > 0 && currentPrefix) flattened.push({
2005
- id: currentPrefix,
2006
- value: meta
2007
- });
2008
- for (const key in currentTokens) {
2009
- if (key.startsWith("$")) continue;
2010
- const value = currentTokens[key];
2011
- const id = currentPrefix ? `${currentPrefix}.${key}` : key;
2012
- if (!/* @__PURE__ */ isObject(value)) {
2013
- flattened.push({
2014
- id,
2015
- value
2016
- });
2017
- continue;
2018
- }
2019
- if ("$value" in value) {
2020
- flattened.push({
2021
- id,
2022
- value
2023
- });
2024
- const inner = value.$value;
2025
- if (/* @__PURE__ */ isObject(inner) && !flat$1) for (const innerKey in inner) {
2026
- if (innerKey.startsWith("$")) continue;
2027
- const child = inner[innerKey];
2028
- const childId = `${id}.${innerKey}`;
2029
- if (!/* @__PURE__ */ isObject(child)) flattened.push({
2030
- id: childId,
2031
- value: child
2032
- });
2033
- else if ("$value" in child) flattened.push({
2034
- id: childId,
2035
- value: child
2036
- });
2037
- else stack.push({
2038
- tokens: child,
2039
- prefix: childId,
2040
- flat: flat$1
2041
- });
2042
- }
2043
- continue;
2044
- }
2045
- if (flat$1) {
2046
- flattened.push({
2047
- id,
2048
- value
2049
- });
2050
- continue;
2051
- }
2052
- stack.push({
2053
- tokens: value,
2054
- prefix: id,
2055
- flat: flat$1
2056
- });
2057
- }
2058
- }
2059
- return flattened;
2060
- }
2061
-
2062
- //#endregion
2063
- //#region src/composables/useLocale/adapters/v0.ts
2064
- /**
2065
- * Vuetify0.x locale adapter implementation
2066
- *
2067
- * This adapter provides translation and number formatting
2068
- * capabilities using the Intl API and supports both
2069
- * numbered ({0}, {1}) and named ({name}) variables in translation strings.
2070
- */
2071
- var Vuetify0LocaleAdapter = class {
2072
- t(message, ...params) {
2073
- let resolvedMessage = message;
2074
- if (params.length > 0 && /* @__PURE__ */ isObject(params[0])) {
2075
- const variables = params[0];
2076
- resolvedMessage = resolvedMessage.replace(/{([a-zA-Z][a-zA-Z0-9_]*)}/g, (match, name) => {
2077
- return /* @__PURE__ */ isUndefined(variables[name]) ? match : String(variables[name]);
2078
- });
2079
- params = params.slice(1);
2080
- }
2081
- resolvedMessage = resolvedMessage.replace(/\{(\d+)\}/g, (match, index) => {
2082
- const idx = Number.parseInt(index, 10);
2083
- if (!/* @__PURE__ */ isUndefined(params[idx])) return String(params[idx]);
2084
- return match;
2085
- });
2086
- return resolvedMessage;
2087
- }
2088
- n(value, locale, ...params) {
2089
- if (!IN_BROWSER || !locale) return value.toString();
2090
- const options = params[0];
2091
- return new Intl.NumberFormat(String(locale), options).format(value);
2092
- }
2093
- };
2094
-
2095
- //#endregion
2096
- //#region src/composables/useLocale/index.ts
2097
- /**
2098
- * @module useLocale
2099
- *
2100
- * @remarks
2101
- * Internationalization (i18n) composable with adapter pattern for message translation.
2102
- *
2103
- * Key features:
2104
- * - Locale selection with createSingle
2105
- * - Token-based message storage with useTokens
2106
- * - Numbered and named placeholder support ({0}, {name})
2107
- * - Number formatting with Intl.NumberFormat
2108
- * - Adapter pattern for integration with i18n providers
2109
- *
2110
- * Integrates with createSingle for locale selection and useTokens for message resolution.
2111
- */
2112
- /**
2113
- * Creates a new locale instance.
2114
- *
2115
- * @param options The options for the locale instance.
2116
- * @template Z The type of the locale ticket.
2117
- * @template E The type of the locale context.
2118
- * @returns A new locale instance.
2119
- *
2120
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2121
- */
2122
- function createLocale(_options = {}) {
2123
- const { adapter = new Vuetify0LocaleAdapter(), messages = {}, ...options } = _options;
2124
- const tokens = createTokens(messages);
2125
- const registry = createSingle(options);
2126
- for (const id in messages) {
2127
- registry.register({ id });
2128
- if (id === options.default && !registry.selectedId.value) registry.select(id);
2129
- }
2130
- function t(key, params, fallback) {
2131
- const locale = registry.selectedId.value;
2132
- const args = toArray(params);
2133
- if (!locale) return adapter.t(fallback ?? key, ...args);
2134
- const path = `${locale}.${key}`;
2135
- const message = tokens.get(path)?.value;
2136
- const template = /* @__PURE__ */ isString(message) ? resolve(locale, message) : fallback ?? key;
2137
- return adapter.t(template, ...args);
2138
- }
2139
- function n(value, ...params) {
2140
- return adapter.n(value, registry.selectedId.value, ...params);
2141
- }
2142
- function resolve(locale, str) {
2143
- return str.replace(/{([a-zA-Z0-9.-_]+)}/g, (match, key) => {
2144
- const [prefix, ...rest] = key.split(".");
2145
- const target = registry.has(prefix) ? prefix : locale;
2146
- const path = `${target}.${registry.has(prefix) ? rest.join(".") : key}`;
2147
- const resolved = tokens.get(path)?.value;
2148
- if (/* @__PURE__ */ isString(resolved)) return resolve(target, resolved);
2149
- return match;
2150
- });
2151
- }
2152
- return {
2153
- ...registry,
2154
- t,
2155
- n,
2156
- get size() {
2157
- return registry.size;
2158
- }
2159
- };
2160
- }
2161
- function createLocaleFallback() {
2162
- return {
2163
- size: 0,
2164
- t: (key, _params, fallback) => fallback ?? key,
2165
- n: String
2166
- };
2167
- }
2168
- /**
2169
- * Creates a new locale context.
2170
- *
2171
- * @param options The options for the locale context.
2172
- * @template Z The type of the locale ticket.
2173
- * @template E The type of the locale context.
2174
- * @returns A new locale context.
2175
- *
2176
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2177
- *
2178
- * @example
2179
- * ```ts
2180
- * import { createLocaleContext } from '@vuetify/v0'
2181
- *
2182
- * export const [useAppLocale, provideAppLocale, appLocale] = createLocaleContext({
2183
- * namespace: 'app:locale',
2184
- * messages: {
2185
- * en: { hello: 'Hello' },
2186
- * es: { hello: 'Hola' },
2187
- * },
2188
- * })
2189
- *
2190
- * // In a parent component:
2191
- * provideAppLocale()
2192
- *
2193
- * // In a child component:
2194
- * const locale = useAppLocale()
2195
- * locale.select('es')
2196
- * ```
2197
- */
2198
- function createLocaleContext(_options = {}) {
2199
- const { namespace = "v0:locale", ...options } = _options;
2200
- const [useLocaleContext, _provideLocaleContext] = createContext(namespace);
2201
- const context = createLocale(options);
2202
- function provideLocaleContext(_context = context, app) {
2203
- return _provideLocaleContext(_context, app);
2204
- }
2205
- return createTrinity(useLocaleContext, provideLocaleContext, context);
2206
- }
2207
- /**
2208
- * Creates a new locale plugin.
2209
- *
2210
- * @param options The options for the locale plugin.
2211
- * @template Z The type of the locale ticket.
2212
- * @template E The type of the locale context.
2213
- * @template R The type of the token ticket.
2214
- * @template O The type of the token context.
2215
- * @returns A new locale plugin.
2216
- *
2217
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2218
- */
2219
- function createLocalePlugin(_options = {}) {
2220
- const { namespace = "v0:locale", adapter = new Vuetify0LocaleAdapter(), messages = {}, ...options } = _options;
2221
- const [, provideLocaleContext, context] = createLocaleContext({
2222
- ...options,
2223
- namespace,
2224
- adapter,
2225
- messages
2226
- });
2227
- return createPlugin({
2228
- namespace,
2229
- provide: (app) => {
2230
- provideLocaleContext(context, app);
2231
- }
2232
- });
2233
- }
2234
- /**
2235
- * Returns the current locale instance.
2236
- *
2237
- * @returns The current locale instance.
2238
- *
2239
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2240
- */
2241
- function useLocale(namespace = "v0:locale") {
2242
- const fallback = createLocaleFallback();
2243
- if (!getCurrentInstance()) return fallback;
2244
- try {
2245
- return useContext(namespace, fallback);
2246
- } catch {
2247
- return fallback;
2248
- }
2249
- }
2250
-
2251
- //#endregion
2252
- //#region src/composables/useHydration/index.ts
2253
- /**
2254
- * @module useHydration
2255
- *
2256
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2257
- *
2258
- * @remarks
2259
- * SSR hydration state management composable.
2260
- *
2261
- * Key features:
2262
- * - Hydration state detection (browser vs SSR)
2263
- * - Root component detection
2264
- * - Readonly hydration state refs
2265
- * - Plugin installation support
2266
- * - Perfect for hydration-safe rendering
2267
- *
2268
- * Essential for composables that need to behave differently during SSR vs client-side.
2269
- */
2270
- /**
2271
- * Creates a new hydration instance.
2272
- *
2273
- * @returns A new hydration instance.
2274
- *
2275
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2276
- *
2277
- * @example
2278
- * ```ts
2279
- * import { createHydration } from '@vuetify/v0'
2280
- *
2281
- * const hydration = createHydration()
2282
- * console.log(hydration.isHydrated.value) // false
2283
- * hydration.hydrate()
2284
- * console.log(hydration.isHydrated.value) // true
2285
- * ```
2286
- */
2287
- function createHydration() {
2288
- const isHydrated = shallowRef(false);
2289
- function hydrate() {
2290
- isHydrated.value = true;
2291
- }
2292
- return {
2293
- isHydrated: shallowReadonly(isHydrated),
2294
- hydrate
2295
- };
2296
- }
2297
- function createFallbackHydration() {
2298
- return {
2299
- isHydrated: shallowReadonly(shallowRef(true)),
2300
- hydrate: () => {}
2301
- };
2302
- }
2303
- /**
2304
- * Creates a new hydration context trinity.
2305
- *
2306
- * @param options Options for creating the hydration context.
2307
- * @template E The type of the hydration context.
2308
- * @returns A new hydration context trinity.
2309
- *
2310
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2311
- *
2312
- * @example
2313
- * ```ts
2314
- * import { createHydrationContext } from '@vuetify/v0'
2315
- *
2316
- * export const [useHydrationContext, provideHydrationContext, context] = createHydrationContext({
2317
- * namespace: 'app:hydration',
2318
- * })
2319
- * ```
2320
- */
2321
- function createHydrationContext(_options = {}) {
2322
- const { namespace = "v0:hydration" } = _options;
2323
- const [useHydrationContext, _provideHydrationContext] = createContext(namespace);
2324
- const context = createHydration();
2325
- function provideHydrationContext(_context = context, app) {
2326
- return _provideHydrationContext(_context, app);
2327
- }
2328
- return createTrinity(useHydrationContext, provideHydrationContext, context);
2329
- }
2330
- /**
2331
- * Creates a new hydration plugin.
2332
- *
2333
- * @param options The options for the hydration plugin.
2334
- * @template E The type of the hydration context.
2335
- * @returns A new hydration plugin.
2336
- *
2337
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2338
- *
2339
- * @example
2340
- * ```ts
2341
- * import { createApp } from 'vue'
2342
- * import { createHydrationPlugin } from '@vuetify/v0'
2343
- * import App from './App.vue'
2344
- *
2345
- * const app = createApp(App)
2346
- *
2347
- * app.use(createHydrationPlugin())
2348
- *
2349
- * app.mount('#app')
2350
- * ```
2351
- */
2352
- function createHydrationPlugin(_options = {}) {
2353
- const { namespace = "v0:hydration", ...options } = _options;
2354
- const [, provideHydrationContext, context] = createHydrationContext({
2355
- ...options,
2356
- namespace
2357
- });
2358
- return createPlugin({
2359
- namespace,
2360
- provide: (app) => {
2361
- provideHydrationContext(context, app);
2362
- },
2363
- setup: (app) => {
2364
- app.mixin({ mounted() {
2365
- if (!/* @__PURE__ */ isNull(this.$parent)) return;
2366
- context.hydrate();
2367
- } });
2368
- }
2369
- });
2370
- }
2371
- /**
2372
- * Returns the current hydration instance.
2373
- *
2374
- * @param namespace The namespace for the hydration context. Defaults to `v0:hydration`.
2375
- * @returns The current hydration instance.
2376
- *
2377
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2378
- *
2379
- * @example
2380
- * ```vue
2381
- * <script setup lang="ts">
2382
- * import { useHydration } from '@vuetify/v0'
2383
- *
2384
- * const hydration = useHydration()
2385
- * <\/script>
2386
- *
2387
- * <template>
2388
- * <div>
2389
- * <p>Is hydrated: {{ hydration.isHydrated.value }}</p>
2390
- * </div>
2391
- * </template>
2392
- * ```
2393
- */
2394
- function useHydration(namespace = "v0:hydration") {
2395
- const fallback = createFallbackHydration();
2396
- if (!getCurrentInstance()) return fallback;
2397
- try {
2398
- return useContext(namespace, fallback);
2399
- } catch {
2400
- return fallback;
2401
- }
2402
- }
2403
-
2404
- //#endregion
2405
- //#region src/composables/useResizeObserver/index.ts
2406
- /**
2407
- * @module useResizeObserver
2408
- *
2409
- * @remarks
2410
- * ResizeObserver composable with lifecycle management.
2411
- *
2412
- * Key features:
2413
- * - ResizeObserver API wrapper
2414
- * - Pause/resume/stop functionality
2415
- * - Automatic cleanup on unmount
2416
- * - SSR-safe (checks SUPPORTS_OBSERVER)
2417
- * - Hydration-aware
2418
- * - Box model options (content-box/border-box)
2419
- *
2420
- * Perfect for responsive components and size-based rendering.
2421
- */
2422
- /**
2423
- * A composable that uses the Resize Observer API to detect when an element's
2424
- * size changes.
2425
- *
2426
- * @param target The element to observe.
2427
- * @param callback The callback to execute when the element's size changes.
2428
- * @param options The options for the Resize Observer.
2429
- * @returns An object with methods to control the observer.
2430
- *
2431
- * @see https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
2432
- * @see https://0.vuetifyjs.com/composables/system/use-resize-observer
2433
- *
2434
- * @example
2435
- * ```ts
2436
- * import { ref } from 'vue'
2437
- * import { useResizeObserver } from '@vuetify/v0'
2438
- *
2439
- * const el = ref<HTMLElement>()
2440
- * const width = ref(0)
2441
- * const height = ref(0)
2442
- *
2443
- * const { pause, resume, isPaused } = useResizeObserver(
2444
- * el,
2445
- * (entries) => {
2446
- * const entry = entries[0]
2447
- * if (entry) {
2448
- * width.value = entry.contentRect.width
2449
- * height.value = entry.contentRect.height
2450
- * console.log('Size changed:', width.value, 'x', height.value)
2451
- * }
2452
- * },
2453
- * { immediate: true }
2454
- * )
2455
- *
2456
- * // Pause observation
2457
- * pause()
2458
- *
2459
- * // Resume observation
2460
- * resume()
2461
- * ```
2462
- */
2463
- function useResizeObserver(target, callback, options = {}) {
2464
- const { isHydrated } = useHydration();
2465
- const observer = shallowRef();
2466
- const isPaused = shallowRef(false);
2467
- const isActive = toRef(() => !!observer.value);
2468
- function setup() {
2469
- if (/* @__PURE__ */ isNull(observer.value)) return;
2470
- if (!isHydrated.value || !SUPPORTS_OBSERVER || !target.value || isPaused.value) return;
2471
- observer.value = new ResizeObserver((entries) => {
2472
- callback(entries.map((entry) => ({
2473
- contentRect: {
2474
- width: entry.contentRect.width,
2475
- height: entry.contentRect.height,
2476
- top: entry.contentRect.top,
2477
- left: entry.contentRect.left
2478
- },
2479
- target: entry.target
2480
- })));
2481
- if (options.once) stop();
2482
- });
2483
- observer.value.observe(target.value, { box: options.box || "content-box" });
2484
- if (options.immediate) {
2485
- const rect = target.value.getBoundingClientRect();
2486
- callback([{
2487
- contentRect: {
2488
- width: rect.width,
2489
- height: rect.height,
2490
- top: rect.top,
2491
- left: rect.left
2492
- },
2493
- target: target.value
2494
- }]);
2495
- }
2496
- }
2497
- watchEffect(() => {
2498
- const hydrated = isHydrated.value;
2499
- const el = target.value;
2500
- cleanup();
2501
- if (hydrated && el) setup();
2502
- });
2503
- function cleanup() {
2504
- if (observer.value) {
2505
- observer.value.disconnect();
2506
- observer.value = void 0;
2507
- }
2508
- }
2509
- function pause() {
2510
- isPaused.value = true;
2511
- observer.value?.disconnect();
2512
- }
2513
- function resume() {
2514
- isPaused.value = false;
2515
- setup();
2516
- }
2517
- function stop() {
2518
- cleanup();
2519
- observer.value = null;
2520
- }
2521
- onScopeDispose(stop, true);
2522
- return {
2523
- isActive: shallowReadonly(isActive),
2524
- isPaused: shallowReadonly(isPaused),
2525
- pause,
2526
- resume,
2527
- stop
2528
- };
2529
- }
2530
- /**
2531
- * A convenience composable that uses the Resize Observer API to track an
2532
- * element's size.
2533
- *
2534
- * @param target The element to observe.
2535
- * @returns An object with the element's width and height.
2536
- *
2537
- * @see https://0.vuetifyjs.com/composables/system/use-resize-observer#use-element-size
2538
- *
2539
- * @example
2540
- * ```ts
2541
- * import { ref, watchEffect } from 'vue'
2542
- * import { useElementSize } from '@vuetify/v0'
2543
- *
2544
- * const box = ref<HTMLElement>()
2545
- * const { width, height } = useElementSize(box)
2546
- *
2547
- * // Width and height are reactive refs
2548
- * watchEffect(() => {
2549
- * console.log('Box size:', width.value, 'x', height.value)
2550
- * })
2551
- * ```
2552
- */
2553
- function useElementSize(target) {
2554
- const width = shallowRef(0);
2555
- const height = shallowRef(0);
2556
- const { pause: _pause, resume, stop, isActive, isPaused } = useResizeObserver(target, (entries) => {
2557
- const entry = entries[0];
2558
- if (entry) {
2559
- width.value = entry.contentRect.width;
2560
- height.value = entry.contentRect.height;
2561
- }
2562
- }, { immediate: true });
2563
- function pause() {
2564
- width.value = 0;
2565
- height.value = 0;
2566
- _pause();
2567
- }
2568
- return {
2569
- width,
2570
- height,
2571
- isActive,
2572
- isPaused,
2573
- pause,
2574
- resume,
2575
- stop
2576
- };
2577
- }
2578
-
2579
- //#endregion
2580
- //#region src/composables/useOverflow/index.ts
2581
- /**
2582
- * @module useOverflow
2583
- *
2584
- * @remarks
2585
- * Composable for computing how many items fit in a container based on available width.
2586
- * Enables responsive truncation logic for Pagination, Breadcrumbs, and similar components.
2587
- *
2588
- * Key features:
2589
- * - Container width tracking via ResizeObserver
2590
- * - Two modes: variable-width (per-item) or uniform-width (sample-based)
2591
- * - Computes capacity (how many items fit)
2592
- * - SSR-safe with Infinity fallback
2593
- * - Supports reserved space for nav buttons, ellipsis, etc.
2594
- *
2595
- * Use variable mode (default) for items with different widths like Breadcrumbs.
2596
- * Use uniform mode (itemWidth option) for same-width items like Pagination buttons.
2597
- */
2598
- /**
2599
- * Creates a new overflow context for computing how many items fit in a container.
2600
- *
2601
- * @param options Configuration options
2602
- * @returns Overflow context with container ref, capacity, and measurement functions
2603
- *
2604
- * @example Variable-width mode (Breadcrumbs)
2605
- * ```vue
2606
- * <script lang="ts" setup>
2607
- * import { useTemplateRef } from 'vue'
2608
- * import { createOverflow } from '@vuetify/v0'
2609
- *
2610
- * const containerRef = useTemplateRef('container')
2611
- * const overflow = createOverflow({
2612
- * container: containerRef,
2613
- * gap: 8,
2614
- * reserved: 40,
2615
- * })
2616
- * <\/script>
2617
- *
2618
- * <template>
2619
- * <div ref="container">
2620
- * <span
2621
- * v-for="(item, i) in items.slice(0, overflow.capacity.value)"
2622
- * :key="i"
2623
- * :ref="el => overflow.measure(i, el)"
2624
- * >
2625
- * {{ item }}
2626
- * </span>
2627
- * <span v-if="overflow.isOverflowing.value">...</span>
2628
- * </div>
2629
- * </template>
2630
- * ```
2631
- *
2632
- * @example Uniform-width mode (Pagination)
2633
- * ```ts
2634
- * const overflow = createOverflow({
2635
- * container: () => atom.value?.element,
2636
- * itemWidth: buttonWidth,
2637
- * reserved: () => buttonWidth.value * 4,
2638
- * })
2639
- * ```
2640
- */
2641
- function createOverflow(options = {}) {
2642
- const { container: _container, gap = 0, reserved = 0, itemWidth, reverse } = options;
2643
- const container = /* @__PURE__ */ isUndefined(_container) ? shallowRef() : toRef(_container);
2644
- const widths = shallowRef(/* @__PURE__ */ new Map());
2645
- const { width } = useElementSize(container);
2646
- function measure(index, el) {
2647
- if (!el) {
2648
- if (widths.value.has(index)) {
2649
- const next = new Map(widths.value);
2650
- next.delete(index);
2651
- widths.value = next;
2652
- }
2653
- return;
2654
- }
2655
- const style = getComputedStyle(el);
2656
- const marginX = Number.parseFloat(style.marginLeft) + Number.parseFloat(style.marginRight);
2657
- const w = el.offsetWidth + marginX;
2658
- if (widths.value.get(index) !== w) widths.value = new Map(widths.value).set(index, w);
2659
- }
2660
- function reset() {
2661
- widths.value = /* @__PURE__ */ new Map();
2662
- }
2663
- const total = computed(() => {
2664
- const g = toValue(gap);
2665
- let sum = 0;
2666
- let count = 0;
2667
- for (const w of widths.value.values()) {
2668
- sum += w + (count > 0 ? g : 0);
2669
- count++;
2670
- }
2671
- return sum;
2672
- });
2673
- return {
2674
- container,
2675
- width,
2676
- capacity: computed(() => {
2677
- const available = width.value - toValue(reserved);
2678
- if (width.value === 0) return Infinity;
2679
- if (available <= 0) return 0;
2680
- const g = toValue(gap);
2681
- const uniformWidth = toValue(itemWidth);
2682
- if (uniformWidth && uniformWidth > 0) {
2683
- const first = uniformWidth;
2684
- const subsequent = uniformWidth + g;
2685
- if (available < first) return 0;
2686
- return Math.max(1, Math.floor((available - first) / subsequent) + 1);
2687
- }
2688
- const entries = [...widths.value.entries()].toSorted((a, b) => a[0] - b[0]);
2689
- if (toValue(reverse)) entries.reverse();
2690
- let sum = 0;
2691
- let count = 0;
2692
- for (const [, w] of entries) {
2693
- const next = sum + w + (count > 0 ? g : 0);
2694
- if (next > available) break;
2695
- sum = next;
2696
- count++;
2697
- }
2698
- return count;
2699
- }),
2700
- total,
2701
- isOverflowing: toRef(() => {
2702
- return total.value > width.value - toValue(reserved);
2703
- }),
2704
- measure,
2705
- reset
2706
- };
2707
- }
2708
- /**
2709
- * Creates an overflow context with dependency injection support.
2710
- *
2711
- * @param options Configuration options including namespace
2712
- * @returns Trinity tuple: [useContext, provideContext, defaultContext]
2713
- *
2714
- * @example
2715
- * ```ts
2716
- * // Create injectable context
2717
- * const [useOverflow, provideOverflow, overflow] = createOverflowContext({
2718
- * namespace: 'my-overflow',
2719
- * gap: 8,
2720
- * reserved: 160,
2721
- * })
2722
- *
2723
- * // In parent component
2724
- * provideOverflow()
2725
- *
2726
- * // In child component
2727
- * const overflow = useOverflow()
2728
- * ```
2729
- */
2730
- function createOverflowContext(_options = {}) {
2731
- const { namespace = "v0:overflow", ...options } = _options;
2732
- const [useOverflowContext, _provideOverflowContext] = createContext(namespace);
2733
- const context = createOverflow(options);
2734
- function provideOverflowContext(_context = context, app) {
2735
- return _provideOverflowContext(_context, app);
2736
- }
2737
- return createTrinity(useOverflowContext, provideOverflowContext, context);
2738
- }
2739
- /**
2740
- * Returns the current overflow context from dependency injection.
2741
- *
2742
- * @param namespace The namespace for the overflow context. Defaults to `v0:overflow`.
2743
- * @returns The current overflow context.
2744
- *
2745
- * @example
2746
- * ```vue
2747
- * <script lang="ts" setup>
2748
- * import { useOverflow } from '@vuetify/v0'
2749
- *
2750
- * // Inject overflow context provided by parent
2751
- * const overflow = useOverflow()
2752
- * <\/script>
2753
- *
2754
- * <template>
2755
- * <div>
2756
- * <p>Capacity: {{ overflow.capacity.value }}</p>
2757
- * </div>
2758
- * </template>
2759
- * ```
2760
- */
2761
- function useOverflow(namespace = "v0:overflow") {
2762
- return useContext(namespace);
2763
- }
2764
-
2765
- //#endregion
2766
- //#region src/composables/usePagination/index.ts
2767
- /**
2768
- * @module usePagination
2769
- *
2770
- * @remarks
2771
- * Lightweight pagination composable for navigating through pages.
2772
- *
2773
- * Key features:
2774
- * - No registry overhead - just a bounded integer
2775
- * - Direct ref support for v-model compatibility
2776
- * - Navigation methods: next, prev, first, last
2777
- * - Computed visible items with ellipsis
2778
- * - Trinity pattern for dependency injection
2779
- *
2780
- * Unlike registry-based composables, pagination tracks a single number
2781
- * within a range, making it efficient for large page counts.
2782
- */
2783
- /**
2784
- * Creates a pagination instance.
2785
- *
2786
- * @param options The options for the pagination instance.
2787
- * @returns A pagination context with navigation methods.
2788
- *
2789
- * @example
2790
- * ```ts
2791
- * import { createPagination } from '@vuetify/v0'
2792
- *
2793
- * // Basic usage
2794
- * const pagination = createPagination({ size: 100 })
2795
- * pagination.next()
2796
- * pagination.items.value // [{ type: 'page', value: 1 }, { type: 'page', value: 2 }, ...]
2797
- *
2798
- * // With v-model (pass a ref)
2799
- * const page = ref(1)
2800
- * const pagination = createPagination({ page, size: 100 })
2801
- * // Mutating pagination.page or the passed ref syncs both
2802
- * ```
2803
- */
2804
- function createPagination(_options = {}) {
2805
- const { page: _page = 1, itemsPerPage: _itemsPerPage = 10, size: _size = 0, visible: _visible = 7, ellipsis = "..." } = _options;
2806
- const page = isRef(_page) ? _page : shallowRef(_page);
2807
- const pages = computed(() => {
2808
- const size = toValue(_size);
2809
- const perPage = toValue(_itemsPerPage);
2810
- if (size <= 0 || /* @__PURE__ */ isNaN(size)) return 0;
2811
- return Math.ceil(size / perPage);
2812
- });
2813
- function first() {
2814
- page.value = 1;
2815
- }
2816
- function last() {
2817
- page.value = Math.max(1, pages.value);
2818
- }
2819
- function next() {
2820
- if (page.value < pages.value) page.value++;
2821
- }
2822
- function prev() {
2823
- if (page.value > 1) page.value--;
2824
- }
2825
- function select(value) {
2826
- if (value < 1) page.value = 1;
2827
- else if (value > pages.value) page.value = Math.max(1, pages.value);
2828
- else page.value = value;
2829
- }
2830
- const isFirst = computed(() => page.value <= 1);
2831
- const isLast = computed(() => page.value >= pages.value);
2832
- const pageStart = computed(() => (page.value - 1) * toValue(_itemsPerPage));
2833
- const pageStop = computed(() => Math.min(pageStart.value + toValue(_itemsPerPage), toValue(_size)));
2834
- function toPage(value) {
2835
- return {
2836
- type: "page",
2837
- value
2838
- };
2839
- }
2840
- function toEllipsis() {
2841
- return ellipsis === false ? false : {
2842
- type: "ellipsis",
2843
- value: ellipsis
2844
- };
2845
- }
2846
- function filter(array) {
2847
- return array.filter((item) => item !== false);
2848
- }
2849
- return {
2850
- page,
2851
- ellipsis,
2852
- items: computed(() => {
2853
- const pageCount = pages.value;
2854
- const visible = toValue(_visible);
2855
- const current = page.value;
2856
- if (pageCount <= 0 || /* @__PURE__ */ isNaN(pageCount) || pageCount > Number.MAX_SAFE_INTEGER) return [];
2857
- if (visible <= 0) return [];
2858
- if (visible <= 2) return [toPage(current)];
2859
- if (pageCount <= visible) return (/* @__PURE__ */ range(pageCount, 1)).map(toPage);
2860
- if (visible === 3) {
2861
- const mid = current <= 1 ? 2 : current >= pageCount ? pageCount - 1 : current;
2862
- return [
2863
- toPage(1),
2864
- toPage(mid),
2865
- toPage(pageCount)
2866
- ];
2867
- }
2868
- const boundary = visible - 2;
2869
- const middle = visible - 4;
2870
- if (middle <= 0) {
2871
- if (current <= boundary) return filter([
2872
- ...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
2873
- toEllipsis(),
2874
- toPage(pageCount)
2875
- ]);
2876
- if (current > pageCount - boundary) return filter([
2877
- toPage(1),
2878
- toEllipsis(),
2879
- ...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
2880
- ]);
2881
- return current <= Math.ceil(pageCount / 2) ? filter([
2882
- toPage(1),
2883
- toPage(current),
2884
- toEllipsis(),
2885
- toPage(pageCount)
2886
- ]) : filter([
2887
- toPage(1),
2888
- toEllipsis(),
2889
- toPage(current),
2890
- toPage(pageCount)
2891
- ]);
2892
- }
2893
- const leftThreshold = boundary - 1;
2894
- const rightThreshold = pageCount - boundary + 2;
2895
- if (current <= leftThreshold) return filter([
2896
- ...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
2897
- toEllipsis(),
2898
- toPage(pageCount)
2899
- ]);
2900
- else if (current >= rightThreshold) return filter([
2901
- toPage(1),
2902
- toEllipsis(),
2903
- ...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
2904
- ]);
2905
- else {
2906
- const start = current - Math.floor(middle / 2);
2907
- return filter([
2908
- toPage(1),
2909
- toEllipsis(),
2910
- ...(/* @__PURE__ */ range(middle, start)).map(toPage),
2911
- toEllipsis(),
2912
- toPage(pageCount)
2913
- ]);
2914
- }
2915
- }),
2916
- pageStart,
2917
- pageStop,
2918
- isFirst,
2919
- isLast,
2920
- first,
2921
- last,
2922
- next,
2923
- prev,
2924
- select,
2925
- get itemsPerPage() {
2926
- return toValue(_itemsPerPage);
2927
- },
2928
- get size() {
2929
- return toValue(_size);
2930
- },
2931
- get pages() {
2932
- return pages.value;
2933
- }
2934
- };
2935
- }
2936
- /**
2937
- * Creates a pagination context for dependency injection.
2938
- *
2939
- * @param options The options including namespace.
2940
- * @returns A trinity: [usePagination, providePagination, defaultContext]
2941
- *
2942
- * @example
2943
- * ```ts
2944
- * // With default namespace 'v0:pagination'
2945
- * const [usePagination, providePaginationContext] = createPaginationContext({ size: 50 })
2946
- *
2947
- * // Or with custom namespace
2948
- * const [usePagination, providePaginationContext] = createPaginationContext({
2949
- * namespace: 'my-pagination',
2950
- * size: 50,
2951
- * })
2952
- *
2953
- * // Parent component
2954
- * providePaginationContext()
2955
- *
2956
- * // Child component
2957
- * const pagination = usePagination()
2958
- * pagination.next()
2959
- * ```
2960
- */
2961
- function createPaginationContext(_options = {}) {
2962
- const { namespace = "v0:pagination", ...options } = _options;
2963
- const [usePaginationContext, _providePaginationContext] = createContext(namespace);
2964
- const context = createPagination(options);
2965
- function providePaginationContext(_context = context, app) {
2966
- return _providePaginationContext(_context, app);
2967
- }
2968
- return createTrinity(usePaginationContext, providePaginationContext, context);
2969
- }
2970
- /**
2971
- * Returns the current pagination instance from context.
2972
- *
2973
- * @param namespace The namespace. @default 'v0:pagination'
2974
- * @returns The pagination context.
2975
- *
2976
- * @example
2977
- * ```vue
2978
- * <script setup lang="ts">
2979
- * import { usePagination } from '@vuetify/v0'
2980
- *
2981
- * const pagination = usePagination()
2982
- * <\/script>
2983
- *
2984
- * <template>
2985
- * <button @click="pagination.prev()" :disabled="pagination.isFirst.value">Prev</button>
2986
- * <button @click="pagination.next()" :disabled="pagination.isLast.value">Next</button>
2987
- * </template>
2988
- * ```
2989
- */
2990
- function usePagination(namespace = "v0:pagination") {
2991
- return useContext(namespace);
2992
- }
2993
-
2994
- //#endregion
2995
- //#region src/composables/useStep/index.ts
2996
- /**
2997
- * @module useStep
2998
- *
2999
- * @remarks
3000
- * Navigation composable that extends useSingle with first/last/next/prev/step methods.
3001
- *
3002
- * Key features:
3003
- * - Configurable circular or bounded navigation
3004
- * - Automatic disabled item skipping
3005
- * - Arbitrary step counts (positive/negative)
3006
- * - Perfect for wizards, carousels, pagination, onboarding flows
3007
- *
3008
- * Inheritance chain: useRegistry → useSelection → useSingle → useStep
3009
- */
3010
- /**
3011
- * Creates a new step instance with navigation through items.
3012
- *
3013
- * Extends `createSingle` with `first()`, `last()`, `next()`, `prev()`, and `step(count)` methods
3014
- * for sequential navigation. Supports both circular (wrapping) and bounded (stopping at edges) modes.
3015
- *
3016
- * @param options The options for the step instance.
3017
- * @template Z The type of the step ticket.
3018
- * @template E The type of the step context.
3019
- * @returns A new step instance with navigation methods.
3020
- *
3021
- * @remarks
3022
- * **Key Features:**
3023
- * - **Configurable Navigation**: `circular: true` for wrapping, `false` for bounded (default: false)
3024
- * - **Disabled Item Skipping**: Automatically skips disabled items during navigation
3025
- * - **Bidirectional**: Forward (`next`, positive `step`) and backward (`prev`, negative `step`)
3026
- * - **Safe Edge Cases**: Handles empty registries and all-disabled scenarios gracefully
3027
- *
3028
- * **Navigation Methods:**
3029
- * - `first()`: Select first non-disabled item
3030
- * - `last()`: Select last non-disabled item
3031
- * - `next()`: Move to next item (wraps if circular, stops at end if bounded)
3032
- * - `prev()`: Move to previous item (wraps if circular, stops at start if bounded)
3033
- * - `step(count)`: Move by `count` positions (negative for backward)
3034
- *
3035
- * **Circular Mode (`circular: true`):**
3036
- * - Uses modulo arithmetic for wrapping: `((index % length) + length) % length`
3037
- * - Works correctly with negative indexes and large step counts
3038
- * - Perfect for carousels, theme switchers, infinite scrolling
3039
- *
3040
- * **Bounded Mode (`circular: false`, default):**
3041
- * - Navigation stops at boundaries (no wrapping)
3042
- * - `next()` on last item does nothing
3043
- * - `prev()` on first item does nothing
3044
- * - Perfect for pagination, wizards with explicit completion, forms
3045
- *
3046
- * **Inheritance Chain:**
3047
- * `useRegistry` → `createSelection` → `createSingle` → `createStep`
3048
- *
3049
- * @see https://0.vuetifyjs.com/composables/selection/use-step
3050
- *
3051
- * @example
3052
- * ```ts
3053
- * import { createStep } from '@vuetify/v0'
3054
- *
3055
- * // Bounded navigation (default) - for pagination
3056
- * const pagination = createStep({ circular: false })
3057
- * pagination.onboard([
3058
- * { id: 'page-1', value: 1 },
3059
- * { id: 'page-2', value: 2 },
3060
- * { id: 'page-3', value: 3 },
3061
- * ])
3062
- * pagination.first() // Select page 1
3063
- * pagination.prev() // Does nothing (already at first)
3064
- * pagination.next() // Select page 2
3065
- *
3066
- * // Circular navigation - for carousels
3067
- * const carousel = createStep({ circular: true })
3068
- * carousel.onboard([
3069
- * { id: 'slide-1', value: 'First' },
3070
- * { id: 'slide-2', value: 'Second' },
3071
- * { id: 'slide-3', value: 'Third' },
3072
- * ])
3073
- * carousel.first()
3074
- * carousel.prev() // Wraps to 'slide-3'
3075
- * carousel.next() // Wraps to 'slide-1'
3076
- * ```
3077
- */
3078
- function createStep(_options = {}) {
3079
- const { circular = false, ...options } = _options;
3080
- const registry = createSingle(options);
3081
- function first() {
3082
- const ticket = registry.seek("first");
3083
- if (ticket) registry.select(ticket.id);
3084
- }
3085
- function last() {
3086
- const ticket = registry.seek("last");
3087
- if (ticket) registry.select(ticket.id);
3088
- }
3089
- function next() {
3090
- step(1);
3091
- }
3092
- function prev() {
3093
- step(-1);
3094
- }
3095
- function wrapped(length, index) {
3096
- return (index % length + length) % length;
3097
- }
3098
- function step(count = 1) {
3099
- const length = registry.size;
3100
- if (!length) return;
3101
- const currentIndex = registry.selectedIndex.value;
3102
- const direction = Math.sign(count || 1);
3103
- let hops = 0;
3104
- let index = circular ? wrapped(length, currentIndex + count) : currentIndex + count;
3105
- if (!circular && (index < 0 || index >= length)) return;
3106
- let id = registry.lookup(index);
3107
- while (!/* @__PURE__ */ isUndefined(id) && toValue(registry.get(id)?.disabled) && hops < length) {
3108
- index = circular ? wrapped(length, index + direction) : index + direction;
3109
- if (!circular && (index < 0 || index >= length)) return;
3110
- id = registry.lookup(index);
3111
- hops++;
3112
- }
3113
- if (/* @__PURE__ */ isUndefined(id) || hops === length) return;
3114
- registry.selectedIds.clear();
3115
- registry.select(id);
3116
- }
3117
- return {
3118
- ...registry,
3119
- first,
3120
- last,
3121
- next,
3122
- prev,
3123
- step,
3124
- get size() {
3125
- return registry.size;
3126
- }
3127
- };
3128
- }
3129
- /**
3130
- * Creates a new step context.
3131
- *
3132
- * @param options The options for the step context.
3133
- * @template Z The type of the step ticket.
3134
- * @template E The type of the step context.
3135
- * @returns A new step context.
3136
- *
3137
- * @see https://0.vuetifyjs.com/composables/selection/use-step
3138
- *
3139
- * @example
3140
- * ```ts
3141
- * import { createStepContext } from '@vuetify/v0'
3142
- *
3143
- * // With default namespace 'v0:step'
3144
- * export const [useStep, provideStep, context] = createStepContext()
3145
- *
3146
- * // In a parent component:
3147
- * provideStep()
3148
- *
3149
- * // In a child component:
3150
- * const context = useStep()
3151
- * context.next() // Progress to next step
3152
- * ```
3153
- */
3154
- function createStepContext(_options = {}) {
3155
- const { namespace = "v0:step", ...options } = _options;
3156
- const [useStepContext, _provideStepContext] = createContext(namespace);
3157
- const context = createStep(options);
3158
- function provideStepContext(_context = context, app) {
3159
- return _provideStepContext(_context, app);
3160
- }
3161
- return createTrinity(useStepContext, provideStepContext, context);
3162
- }
3163
- /**
3164
- * Returns the current step instance.
3165
- *
3166
- * @param namespace The namespace for the step context. Defaults to `'v0:step'`.
3167
- * @returns The current step instance.
3168
- *
3169
- * @see https://0.vuetifyjs.com/composables/selection/use-step
3170
- *
3171
- * @example
3172
- * ```vue
3173
- * <script setup lang="ts">
3174
- * import { useStep } from '@vuetify/v0'
3175
- *
3176
- * const wizard = useStep()
3177
- * <\/script>
3178
- *
3179
- * <template>
3180
- * <div>
3181
- * <p>Current step: {{ wizard.selectedIndex }}</p>
3182
- * <button @click="wizard.next()">Next</button>
3183
- * </div>
3184
- * </template>
3185
- * ```
3186
- */
3187
- function useStep(namespace = "v0:step") {
3188
- return useContext(namespace);
3189
- }
3190
-
3191
- //#endregion
3192
- export { createGroupContext as A, createLogger as B, createTokens as C, createSingleContext as D, createSingle as E, createSelection as F, PinoLoggerAdapter as G, createLoggerPlugin as H, createSelectionContext as I, createTrinity as J, ConsolaLoggerAdapter as K, useSelection as L, useProxyRegistry as M, useProxyModel as N, useSingle as O, toArray as P, createRegistryContext as R, Vuetify0LocaleAdapter as S, useTokens as T, useLogger as U, createLoggerContext as V, Vuetify0LoggerAdapter as W, provideContext as X, createContext as Y, useContext as Z, createLocale as _, createPaginationContext as a, createLocalePlugin as b, createOverflowContext as c, useResizeObserver as d, createFallbackHydration as f, useHydration as g, createHydrationPlugin as h, createPagination as i, useGroup as j, createGroup as k, useOverflow as l, createHydrationContext as m, createStepContext as n, usePagination as o, createHydration as p, createPlugin as q, useStep as r, createOverflow as s, createStep as t, useElementSize as u, createLocaleContext as v, createTokensContext as w, useLocale as x, createLocaleFallback as y, useRegistry as z };