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