@vuetify/v0 0.0.18 → 0.0.21
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/README.md +128 -47
- package/dist/browser/index.js +1645 -1128
- package/dist/components/index.d.mts +3 -3
- package/dist/components/index.mjs +4 -4
- package/dist/{components-DRdenC8b.mjs → components-BomyjmT3.mjs} +24 -3
- package/dist/composables/index.d.mts +4 -4
- package/dist/composables/index.mjs +5 -5
- package/dist/{composables-DG1MHp-R.mjs → composables-CbAPZabd.mjs} +276 -29
- package/dist/constants/index.mjs +1 -1
- package/dist/{globals-BGVqrlN7.mjs → globals-C3JrEDXZ.mjs} +2 -1
- package/dist/index-4jSy8KIt.d.mts +73 -0
- package/dist/{index-oQuVnpOj.d.mts → index-B0QJdwz9.d.mts} +254 -208
- package/dist/index-CxeZr8jO.d.mts +270 -0
- package/dist/{index-DaX9-kmY.d.mts → index-MLb3LH9a.d.mts} +9 -7
- package/dist/{index-CH7DpIK8.d.mts → index-OU0IRbIS.d.mts} +127 -10
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +6 -6
- package/dist/types/index.d.mts +1 -1
- package/dist/{useStep-DC_JvZpy.mjs → useStep-CfgBbrJB.mjs} +1088 -1070
- package/dist/utilities/index.d.mts +2 -2
- package/dist/utilities/index.mjs +1 -1
- package/dist/utilities-CjDz-Xvn.mjs +369 -0
- package/package.json +3 -21
- package/dist/index-B-jVvwsm.d.mts +0 -71
- package/dist/index-C5LQZd3v.d.mts +0 -11
- package/dist/utilities-JxCe-nF2.mjs +0 -139
package/dist/browser/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { computed, createBlock, createCommentVNode, createPropsRestProxy, createTextVNode, defineComponent, effectScope, getCurrentInstance, guardReactiveProps, inject, isRef, mergeModels, mergeProps, normalizeProps, onBeforeUnmount, onMounted, onScopeDispose, onUnmounted, openBlock, provide, reactive, readonly, ref, renderSlot, resolveDynamicComponent, shallowReactive, shallowReadonly, shallowRef, toDisplayString, toRef, toValue, unref, useAttrs, useId, useModel, useTemplateRef, vShow, watch, withCtx, withDirectives } from "vue";
|
|
1
|
+
import { computed, createBlock, createCommentVNode, createPropsRestProxy, createTextVNode, defineComponent, effectScope, getCurrentInstance, guardReactiveProps, inject, isRef, mergeModels, mergeProps, normalizeProps, onBeforeUnmount, onMounted, onScopeDispose, onUnmounted, openBlock, provide, reactive, readonly, ref, renderSlot, resolveDynamicComponent, shallowReactive, shallowReadonly, shallowRef, toDisplayString, toRef, toValue, unref, useAttrs, useId, useModel, useTemplateRef, vShow, watch, watchEffect, withCtx, withDirectives } from "vue";
|
|
2
2
|
|
|
3
3
|
//#region src/constants/htmlElements.ts
|
|
4
4
|
const selfClosingTags = [
|
|
@@ -78,69 +78,299 @@ function isSelfClosingTag(tag) {
|
|
|
78
78
|
|
|
79
79
|
//#endregion
|
|
80
80
|
//#region src/utilities/helpers.ts
|
|
81
|
+
/**
|
|
82
|
+
* Checks if a value is a function
|
|
83
|
+
*
|
|
84
|
+
* @param item The value to check
|
|
85
|
+
* @returns True if the value is a function
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* isFunction(() => {}) // true
|
|
90
|
+
* isFunction('string') // false
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
81
93
|
/* @__NO_SIDE_EFFECTS__ */
|
|
82
94
|
function isFunction(item) {
|
|
83
95
|
return typeof item === "function";
|
|
84
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Checks if a value is a string
|
|
99
|
+
*
|
|
100
|
+
* @param item The value to check
|
|
101
|
+
* @returns True if the value is a string
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* isString('hello') // true
|
|
106
|
+
* isString(123) // false
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
85
109
|
/* @__NO_SIDE_EFFECTS__ */
|
|
86
110
|
function isString(item) {
|
|
87
111
|
return typeof item === "string";
|
|
88
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Checks if a value is a number
|
|
115
|
+
*
|
|
116
|
+
* @param item The value to check
|
|
117
|
+
* @returns True if the value is a number (including NaN)
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* isNumber(123) // true
|
|
122
|
+
* isNumber(NaN) // true
|
|
123
|
+
* isNumber('123') // false
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
* @see {@link isNaN} to check for NaN specifically
|
|
127
|
+
*/
|
|
89
128
|
/* @__NO_SIDE_EFFECTS__ */
|
|
90
129
|
function isNumber(item) {
|
|
91
130
|
return typeof item === "number";
|
|
92
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Checks if a value is a boolean
|
|
134
|
+
*
|
|
135
|
+
* @param item The value to check
|
|
136
|
+
* @returns True if the value is a boolean
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* isBoolean(true) // true
|
|
141
|
+
* isBoolean(false) // true
|
|
142
|
+
* isBoolean(0) // false
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
93
145
|
/* @__NO_SIDE_EFFECTS__ */
|
|
94
146
|
function isBoolean(item) {
|
|
95
147
|
return typeof item === "boolean";
|
|
96
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Checks if a value is a plain object (excludes null and arrays)
|
|
151
|
+
*
|
|
152
|
+
* @param item The value to check
|
|
153
|
+
* @returns True if the value is a plain object
|
|
154
|
+
*
|
|
155
|
+
* @remarks
|
|
156
|
+
* Returns false for null and arrays, even though `typeof null === 'object'`
|
|
157
|
+
* and `typeof [] === 'object'` in JavaScript.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```ts
|
|
161
|
+
* isObject({}) // true
|
|
162
|
+
* isObject({ a: 1 }) // true
|
|
163
|
+
* isObject(null) // false
|
|
164
|
+
* isObject([]) // false
|
|
165
|
+
* ```
|
|
166
|
+
*
|
|
167
|
+
* @see {@link isArray} to check for arrays
|
|
168
|
+
* @see {@link isNull} to check for null
|
|
169
|
+
*/
|
|
97
170
|
/* @__NO_SIDE_EFFECTS__ */
|
|
98
171
|
function isObject(item) {
|
|
99
172
|
return typeof item === "object" && item !== null && !Array.isArray(item);
|
|
100
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Checks if a value is an array
|
|
176
|
+
*
|
|
177
|
+
* @param item The value to check
|
|
178
|
+
* @returns True if the value is an array
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```ts
|
|
182
|
+
* isArray([]) // true
|
|
183
|
+
* isArray([1, 2, 3]) // true
|
|
184
|
+
* isArray('string') // false
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
101
187
|
/* @__NO_SIDE_EFFECTS__ */
|
|
102
188
|
function isArray(item) {
|
|
103
189
|
return Array.isArray(item);
|
|
104
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Checks if a value is null
|
|
193
|
+
*
|
|
194
|
+
* @param item The value to check
|
|
195
|
+
* @returns True if the value is null
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* isNull(null) // true
|
|
200
|
+
* isNull(undefined) // false
|
|
201
|
+
* ```
|
|
202
|
+
*
|
|
203
|
+
* @see {@link isUndefined} to check for undefined
|
|
204
|
+
* @see {@link isNullOrUndefined} to check for either
|
|
205
|
+
*/
|
|
105
206
|
/* @__NO_SIDE_EFFECTS__ */
|
|
106
207
|
function isNull(item) {
|
|
107
208
|
return item === null;
|
|
108
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Checks if a value is null or undefined
|
|
212
|
+
*
|
|
213
|
+
* @param item The value to check
|
|
214
|
+
* @returns True if the value is null or undefined
|
|
215
|
+
*
|
|
216
|
+
* @remarks
|
|
217
|
+
* Uses loose equality (`== null`) which matches both null and undefined.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```ts
|
|
221
|
+
* isNullOrUndefined(null) // true
|
|
222
|
+
* isNullOrUndefined(undefined) // true
|
|
223
|
+
* isNullOrUndefined(0) // false
|
|
224
|
+
* isNullOrUndefined('') // false
|
|
225
|
+
* ```
|
|
226
|
+
*
|
|
227
|
+
* @see {@link isNull} to check for null only
|
|
228
|
+
* @see {@link isUndefined} to check for undefined only
|
|
229
|
+
*/
|
|
109
230
|
/* @__NO_SIDE_EFFECTS__ */
|
|
110
231
|
function isNullOrUndefined(item) {
|
|
111
232
|
return item == null;
|
|
112
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Checks if a value is undefined
|
|
236
|
+
*
|
|
237
|
+
* @param item The value to check
|
|
238
|
+
* @returns True if the value is undefined
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```ts
|
|
242
|
+
* isUndefined(undefined) // true
|
|
243
|
+
* isUndefined(null) // false
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* @see {@link isNull} to check for null
|
|
247
|
+
* @see {@link isNullOrUndefined} to check for either
|
|
248
|
+
*/
|
|
113
249
|
/* @__NO_SIDE_EFFECTS__ */
|
|
114
250
|
function isUndefined(item) {
|
|
115
251
|
return item === void 0;
|
|
116
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Checks if a value is a primitive (string, number, or boolean)
|
|
255
|
+
*
|
|
256
|
+
* @param item The value to check
|
|
257
|
+
* @returns True if the value is a string, number, or boolean
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```ts
|
|
261
|
+
* isPrimitive('hello') // true
|
|
262
|
+
* isPrimitive(123) // true
|
|
263
|
+
* isPrimitive(true) // true
|
|
264
|
+
* isPrimitive({}) // false
|
|
265
|
+
* isPrimitive(null) // false
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
117
268
|
/* @__NO_SIDE_EFFECTS__ */
|
|
118
269
|
function isPrimitive(item) {
|
|
119
270
|
return typeof item === "string" || typeof item === "number" || typeof item === "boolean";
|
|
120
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Checks if a value is a symbol
|
|
274
|
+
*
|
|
275
|
+
* @param item The value to check
|
|
276
|
+
* @returns True if the value is a symbol
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```ts
|
|
280
|
+
* isSymbol(Symbol('test')) // true
|
|
281
|
+
* isSymbol('symbol') // false
|
|
282
|
+
* ```
|
|
283
|
+
*/
|
|
121
284
|
/* @__NO_SIDE_EFFECTS__ */
|
|
122
285
|
function isSymbol(item) {
|
|
123
286
|
return typeof item === "symbol";
|
|
124
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Checks if a value is NaN (Not a Number)
|
|
290
|
+
*
|
|
291
|
+
* @param item The value to check
|
|
292
|
+
* @returns True if the value is NaN
|
|
293
|
+
*
|
|
294
|
+
* @remarks
|
|
295
|
+
* Uses `Number.isNaN()` which only returns true for the actual NaN value,
|
|
296
|
+
* unlike the global `isNaN()` which coerces the argument to a number first.
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* ```ts
|
|
300
|
+
* isNaN(NaN) // true
|
|
301
|
+
* isNaN(123) // false
|
|
302
|
+
* isNaN('hello') // false (unlike global isNaN)
|
|
303
|
+
* isNaN(undefined) // false (unlike global isNaN)
|
|
304
|
+
* ```
|
|
305
|
+
*
|
|
306
|
+
* @see {@link isNumber} to check if a value is a number type
|
|
307
|
+
*/
|
|
125
308
|
/* @__NO_SIDE_EFFECTS__ */
|
|
126
309
|
function isNaN(item) {
|
|
127
310
|
return /* @__PURE__ */ isNumber(item) && Number.isNaN(item);
|
|
128
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* Deeply merges source objects into a target object
|
|
314
|
+
*
|
|
315
|
+
* @param target The target object to merge into (will be mutated)
|
|
316
|
+
* @param sources One or more source objects to merge from
|
|
317
|
+
* @returns The mutated target object
|
|
318
|
+
*
|
|
319
|
+
* @remarks
|
|
320
|
+
* - Mutates the target object in place
|
|
321
|
+
* - Nested objects are recursively merged
|
|
322
|
+
* - Arrays are replaced, not merged
|
|
323
|
+
* - Primitives from sources overwrite target values
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* ```ts
|
|
327
|
+
* const target = { a: 1, b: { c: 2 } }
|
|
328
|
+
* mergeDeep(target, { b: { d: 3 } })
|
|
329
|
+
* // target is now { a: 1, b: { c: 2, d: 3 } }
|
|
330
|
+
*
|
|
331
|
+
* // Multiple sources
|
|
332
|
+
* mergeDeep({}, { a: 1 }, { b: 2 }) // { a: 1, b: 2 }
|
|
333
|
+
*
|
|
334
|
+
* // Arrays are replaced
|
|
335
|
+
* mergeDeep({ arr: [1, 2] }, { arr: [3] }) // { arr: [3] }
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
338
|
+
const UNSAFE_KEYS = new Set([
|
|
339
|
+
"__proto__",
|
|
340
|
+
"constructor",
|
|
341
|
+
"prototype"
|
|
342
|
+
]);
|
|
129
343
|
/* @__NO_SIDE_EFFECTS__ */
|
|
130
344
|
function mergeDeep(target, ...sources) {
|
|
131
345
|
if (sources.length === 0) return target;
|
|
132
346
|
const source = sources.shift();
|
|
133
|
-
if (/* @__PURE__ */ isObject(target) && /* @__PURE__ */ isObject(source)) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
347
|
+
if (/* @__PURE__ */ isObject(target) && /* @__PURE__ */ isObject(source)) for (const key in source) {
|
|
348
|
+
if (UNSAFE_KEYS.has(key)) continue;
|
|
349
|
+
if (!Object.prototype.hasOwnProperty.call(source, key)) continue;
|
|
350
|
+
const sourceValue = source[key];
|
|
351
|
+
const targetValue = target[key];
|
|
352
|
+
if (/* @__PURE__ */ isObject(sourceValue)) {
|
|
353
|
+
if (!/* @__PURE__ */ isObject(targetValue)) Object.assign(target, { [key]: {} });
|
|
354
|
+
target[key];
|
|
355
|
+
} else Object.assign(target, { [key]: sourceValue });
|
|
141
356
|
}
|
|
142
357
|
return /* @__PURE__ */ mergeDeep(target, ...sources);
|
|
143
358
|
}
|
|
359
|
+
/**
|
|
360
|
+
* Generates a random 7-character alphanumeric ID
|
|
361
|
+
*
|
|
362
|
+
* @returns A random string of 7 characters (a-z, 0-9)
|
|
363
|
+
*
|
|
364
|
+
* @remarks
|
|
365
|
+
* Uses `Math.random()` converted to base-36. Not cryptographically secure.
|
|
366
|
+
* Suitable for unique keys in UI components, not for security purposes.
|
|
367
|
+
*
|
|
368
|
+
* @example
|
|
369
|
+
* ```ts
|
|
370
|
+
* genId() // 'k7x9m2p'
|
|
371
|
+
* genId() // 'a3b8c1d'
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
144
374
|
/* @__NO_SIDE_EFFECTS__ */
|
|
145
375
|
function genId() {
|
|
146
376
|
return Math.random().toString(36).slice(2, 9);
|
|
@@ -216,6 +446,27 @@ function debounce(fn, delay) {
|
|
|
216
446
|
|
|
217
447
|
//#endregion
|
|
218
448
|
//#region src/components/Atom/Atom.vue
|
|
449
|
+
/**
|
|
450
|
+
* @module Atom
|
|
451
|
+
*
|
|
452
|
+
* @remarks
|
|
453
|
+
* Foundation component providing polymorphic rendering with three modes:
|
|
454
|
+
* 1. **Element Mode** (default): Renders as any HTML element via the `as` prop
|
|
455
|
+
* 2. **Renderless Mode**: Renders slot content directly without wrapper
|
|
456
|
+
* 3. **Null Mode**: Equivalent to renderless when `as={null}`
|
|
457
|
+
*
|
|
458
|
+
* Key features:
|
|
459
|
+
* - Polymorphic element rendering (div, button, span, etc.)
|
|
460
|
+
* - Self-closing tag detection (img, input, br, hr, etc.)
|
|
461
|
+
* - Automatic attribute forwarding to rendered element
|
|
462
|
+
* - Generic slot props typing for type-safe attribute passing
|
|
463
|
+
* - Template ref exposure for DOM element access
|
|
464
|
+
* - Renderless mode for maximum flexibility
|
|
465
|
+
*
|
|
466
|
+
* The Atom component is the lowest-level primitive in the component system,
|
|
467
|
+
* serving as the foundation for all other components that need polymorphic
|
|
468
|
+
* rendering capabilities.
|
|
469
|
+
*/
|
|
219
470
|
const _sfc_main$27 = /* @__PURE__ */ defineComponent({
|
|
220
471
|
name: "Atom",
|
|
221
472
|
__name: "Atom",
|
|
@@ -394,6 +645,18 @@ function createTrinity(useContext$1, provideContext$1, context) {
|
|
|
394
645
|
];
|
|
395
646
|
}
|
|
396
647
|
|
|
648
|
+
//#endregion
|
|
649
|
+
//#region src/constants/globals.ts
|
|
650
|
+
const IN_BROWSER = typeof window !== "undefined";
|
|
651
|
+
const SUPPORTS_TOUCH = IN_BROWSER && ("ontouchstart" in window || window.navigator.maxTouchPoints > 0);
|
|
652
|
+
const SUPPORTS_MATCH_MEDIA = IN_BROWSER && "matchMedia" in window && typeof window.matchMedia === "function";
|
|
653
|
+
const SUPPORTS_OBSERVER = IN_BROWSER && "ResizeObserver" in window;
|
|
654
|
+
const SUPPORTS_INTERSECTION_OBSERVER = IN_BROWSER && "IntersectionObserver" in window;
|
|
655
|
+
const SUPPORTS_MUTATION_OBSERVER = IN_BROWSER && "MutationObserver" in window;
|
|
656
|
+
const version = "0.0.21";
|
|
657
|
+
/* v8 ignore next -- build-time constant, __DEV__ short-circuits in tests */
|
|
658
|
+
const __LOGGER_ENABLED__ = false;
|
|
659
|
+
|
|
397
660
|
//#endregion
|
|
398
661
|
//#region src/composables/createPlugin/index.ts
|
|
399
662
|
/**
|
|
@@ -505,17 +768,6 @@ var PinoLoggerAdapter = class {
|
|
|
505
768
|
}
|
|
506
769
|
};
|
|
507
770
|
|
|
508
|
-
//#endregion
|
|
509
|
-
//#region src/constants/globals.ts
|
|
510
|
-
const IN_BROWSER = typeof window !== "undefined";
|
|
511
|
-
const SUPPORTS_TOUCH = IN_BROWSER && ("ontouchstart" in window || window.navigator.maxTouchPoints > 0);
|
|
512
|
-
const SUPPORTS_MATCH_MEDIA = IN_BROWSER && "matchMedia" in window && typeof window.matchMedia === "function";
|
|
513
|
-
const SUPPORTS_OBSERVER = IN_BROWSER && "ResizeObserver" in window;
|
|
514
|
-
const SUPPORTS_INTERSECTION_OBSERVER = IN_BROWSER && "IntersectionObserver" in window;
|
|
515
|
-
const SUPPORTS_MUTATION_OBSERVER = IN_BROWSER && "MutationObserver" in window;
|
|
516
|
-
const version = "0.0.18";
|
|
517
|
-
const __LOGGER_ENABLED__ = false;
|
|
518
|
-
|
|
519
771
|
//#endregion
|
|
520
772
|
//#region src/composables/useLogger/adapters/v0.ts
|
|
521
773
|
/**
|
|
@@ -561,10 +813,12 @@ var Vuetify0LoggerAdapter = class {
|
|
|
561
813
|
}
|
|
562
814
|
timestamp() {
|
|
563
815
|
if (!IN_BROWSER) return (/* @__PURE__ */ new Date()).toISOString();
|
|
816
|
+
/* v8 ignore next -- defensive fallback, toTimeString always returns valid format */
|
|
564
817
|
return (/* @__PURE__ */ new Date()).toTimeString().split(" ")[0] ?? "";
|
|
565
818
|
}
|
|
566
819
|
style(level) {
|
|
567
820
|
if (!this.colors || !IN_BROWSER) return "";
|
|
821
|
+
/* v8 ignore next -- LogLevel union is exhaustive */
|
|
568
822
|
return {
|
|
569
823
|
trace: "color: #64748b",
|
|
570
824
|
debug: "color: #3b82f6",
|
|
@@ -817,6 +1071,8 @@ function useLogger(namespace = "v0:logger") {
|
|
|
817
1071
|
/**
|
|
818
1072
|
* @module useRegistry
|
|
819
1073
|
*
|
|
1074
|
+
* @see https://0.vuetifyjs.com/composables/registration/use-registry
|
|
1075
|
+
*
|
|
820
1076
|
* @remarks
|
|
821
1077
|
* A foundational composable for managing collections of items (tickets) with:
|
|
822
1078
|
* - Unique ID-based access
|
|
@@ -873,17 +1129,21 @@ function useRegistry(options) {
|
|
|
873
1129
|
}
|
|
874
1130
|
function on(event, cb) {
|
|
875
1131
|
if (!events) {
|
|
876
|
-
logger.warn(`
|
|
1132
|
+
logger.warn(`Events are disabled. Initialize with \`useRegistry({ events: true })\` to enable.`);
|
|
877
1133
|
return;
|
|
878
1134
|
}
|
|
879
1135
|
if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
|
|
880
1136
|
listeners.get(event).add(cb);
|
|
881
1137
|
}
|
|
882
1138
|
function off(event, cb) {
|
|
1139
|
+
if (!events) {
|
|
1140
|
+
logger.warn(`Events are disabled. Initialize with \`useRegistry({ events: true })\` to enable.`);
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
883
1143
|
listeners.get(event)?.delete(cb);
|
|
884
1144
|
}
|
|
885
1145
|
function dispose() {
|
|
886
|
-
|
|
1146
|
+
listeners.clear();
|
|
887
1147
|
clear();
|
|
888
1148
|
}
|
|
889
1149
|
function get(id) {
|
|
@@ -972,9 +1232,9 @@ function useRegistry(options) {
|
|
|
972
1232
|
return entries$1;
|
|
973
1233
|
}
|
|
974
1234
|
function clear() {
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1235
|
+
collection.clear();
|
|
1236
|
+
catalog.clear();
|
|
1237
|
+
directory.clear();
|
|
978
1238
|
invalidate();
|
|
979
1239
|
indexDependentCount = 0;
|
|
980
1240
|
needsReindex = false;
|
|
@@ -983,7 +1243,7 @@ function useRegistry(options) {
|
|
|
983
1243
|
}
|
|
984
1244
|
function invalidate() {
|
|
985
1245
|
if (batching) return;
|
|
986
|
-
|
|
1246
|
+
cache.clear();
|
|
987
1247
|
}
|
|
988
1248
|
function queueEmit(event, data) {
|
|
989
1249
|
if (batching) pendingEmits.push({
|
|
@@ -998,7 +1258,7 @@ function useRegistry(options) {
|
|
|
998
1258
|
pendingEmits = [];
|
|
999
1259
|
try {
|
|
1000
1260
|
const result = fn();
|
|
1001
|
-
|
|
1261
|
+
cache.clear();
|
|
1002
1262
|
for (const { event, data } of pendingEmits) emit(event, data);
|
|
1003
1263
|
return result;
|
|
1004
1264
|
} finally {
|
|
@@ -1009,8 +1269,8 @@ function useRegistry(options) {
|
|
|
1009
1269
|
function reindex() {
|
|
1010
1270
|
const startIndex = minDirtyIndex === Infinity ? 0 : minDirtyIndex;
|
|
1011
1271
|
if (startIndex === 0) {
|
|
1012
|
-
|
|
1013
|
-
|
|
1272
|
+
catalog.clear();
|
|
1273
|
+
directory.clear();
|
|
1014
1274
|
}
|
|
1015
1275
|
invalidate();
|
|
1016
1276
|
let index = 0;
|
|
@@ -1037,7 +1297,7 @@ function useRegistry(options) {
|
|
|
1037
1297
|
const size = collection.size;
|
|
1038
1298
|
const id = registration.id ?? /* @__PURE__ */ genId();
|
|
1039
1299
|
if (has(id)) {
|
|
1040
|
-
logger.warn(`Ticket
|
|
1300
|
+
logger.warn(`Ticket "${id}" already exists. Use \`upsert()\` to update or check \`has()\` before registering.`);
|
|
1041
1301
|
return get(id);
|
|
1042
1302
|
}
|
|
1043
1303
|
const valueIsUndefined = /* @__PURE__ */ isUndefined(registration.value);
|
|
@@ -1087,14 +1347,18 @@ function useRegistry(options) {
|
|
|
1087
1347
|
}
|
|
1088
1348
|
if (removed.length === 0) return;
|
|
1089
1349
|
invalidate();
|
|
1090
|
-
|
|
1350
|
+
for (const ticket of removed) queueEmit("unregister:ticket", ticket);
|
|
1091
1351
|
needsReindex = true;
|
|
1092
1352
|
}
|
|
1093
1353
|
function seek(direction = "first", from, predicate) {
|
|
1094
1354
|
if (collection.size === 0) return void 0;
|
|
1095
1355
|
if (needsReindex) reindex();
|
|
1356
|
+
if (!predicate && /* @__PURE__ */ isUndefined(from)) {
|
|
1357
|
+
const tickets$1 = values();
|
|
1358
|
+
return direction === "first" ? tickets$1[0] : tickets$1.at(-1);
|
|
1359
|
+
}
|
|
1096
1360
|
const tickets = values();
|
|
1097
|
-
const index = /* @__PURE__ */ isUndefined(from) ? void 0 :
|
|
1361
|
+
const index = /* @__PURE__ */ isUndefined(from) ? void 0 : /* @__PURE__ */ clamp(from, 0, tickets.length - 1);
|
|
1098
1362
|
if (direction === "last") {
|
|
1099
1363
|
const start = /* @__PURE__ */ isUndefined(index) ? tickets.length - 1 : index;
|
|
1100
1364
|
for (let i = start; i >= 0; i--) {
|
|
@@ -1563,7 +1827,7 @@ const Avatar = {
|
|
|
1563
1827
|
* ```
|
|
1564
1828
|
*/
|
|
1565
1829
|
function toArray(value) {
|
|
1566
|
-
return /* @__PURE__ */ isNullOrUndefined(value) ? [] :
|
|
1830
|
+
return /* @__PURE__ */ isNullOrUndefined(value) ? [] : /* @__PURE__ */ isArray(value) ? value : [value];
|
|
1567
1831
|
}
|
|
1568
1832
|
|
|
1569
1833
|
//#endregion
|
|
@@ -1655,7 +1919,8 @@ function useProxyModel(registry, model, options) {
|
|
|
1655
1919
|
flush: "sync",
|
|
1656
1920
|
deep: multiple
|
|
1657
1921
|
});
|
|
1658
|
-
function onRegister(
|
|
1922
|
+
function onRegister(data) {
|
|
1923
|
+
const ticket = data;
|
|
1659
1924
|
if (!pending.has(ticket.value) || ticket.disabled) return;
|
|
1660
1925
|
registryWatch.pause();
|
|
1661
1926
|
modelWatch.pause();
|
|
@@ -2385,1345 +2650,1350 @@ const Group = {
|
|
|
2385
2650
|
};
|
|
2386
2651
|
|
|
2387
2652
|
//#endregion
|
|
2388
|
-
//#region src/composables/
|
|
2653
|
+
//#region src/composables/useSingle/index.ts
|
|
2389
2654
|
/**
|
|
2390
|
-
* @module
|
|
2391
|
-
*
|
|
2392
|
-
* @see https://0.vuetifyjs.com/composables/plugins/use-hydration
|
|
2655
|
+
* @module useSingle
|
|
2393
2656
|
*
|
|
2394
2657
|
* @remarks
|
|
2395
|
-
*
|
|
2658
|
+
* Single-selection composable that extends useSelection to enforce only one selected item.
|
|
2396
2659
|
*
|
|
2397
2660
|
* Key features:
|
|
2398
|
-
* -
|
|
2399
|
-
* -
|
|
2400
|
-
* -
|
|
2401
|
-
* - Plugin installation support
|
|
2402
|
-
* - Perfect for hydration-safe rendering
|
|
2661
|
+
* - Auto-clears previous selection when selecting new item
|
|
2662
|
+
* - Singular computed properties (selectedId, selectedItem, selectedIndex, selectedValue)
|
|
2663
|
+
* - Perfect for tabs, radio buttons, theme selectors
|
|
2403
2664
|
*
|
|
2404
|
-
*
|
|
2665
|
+
* Inheritance chain: useRegistry → useSelection → useSingle
|
|
2405
2666
|
*/
|
|
2406
2667
|
/**
|
|
2407
|
-
* Creates a new
|
|
2668
|
+
* Creates a new single selection instance that enforces only one selected item at a time.
|
|
2408
2669
|
*
|
|
2409
|
-
*
|
|
2670
|
+
* Extends `createSelection` by automatically clearing previous selections when a new item is selected.
|
|
2671
|
+
* Adds computed singular properties: `selectedId`, `selectedItem`, `selectedIndex`, `selectedValue`.
|
|
2410
2672
|
*
|
|
2411
|
-
* @
|
|
2673
|
+
* @param options The options for the single selection instance.
|
|
2674
|
+
* @template Z The type of the single selection ticket.
|
|
2675
|
+
* @template E The type of the single selection context.
|
|
2676
|
+
* @returns A new single selection instance with single-selection enforcement.
|
|
2412
2677
|
*
|
|
2413
|
-
* @
|
|
2414
|
-
*
|
|
2415
|
-
*
|
|
2678
|
+
* @remarks
|
|
2679
|
+
* **Key Differences from `createSelection`:**
|
|
2680
|
+
* - Automatically clears `selectedIds` before selecting a new item (enforces single selection)
|
|
2681
|
+
* - Provides singular computed properties instead of plural sets
|
|
2682
|
+
* - Perfect for tabs, radio buttons, theme selectors, and other single-choice UI components
|
|
2416
2683
|
*
|
|
2417
|
-
*
|
|
2418
|
-
*
|
|
2419
|
-
*
|
|
2420
|
-
*
|
|
2421
|
-
*
|
|
2422
|
-
*/
|
|
2423
|
-
function createHydration() {
|
|
2424
|
-
const isHydrated = shallowRef(false);
|
|
2425
|
-
function hydrate() {
|
|
2426
|
-
isHydrated.value = true;
|
|
2427
|
-
}
|
|
2428
|
-
return {
|
|
2429
|
-
isHydrated: shallowReadonly(isHydrated),
|
|
2430
|
-
hydrate
|
|
2431
|
-
};
|
|
2432
|
-
}
|
|
2433
|
-
function createFallbackHydration() {
|
|
2434
|
-
return {
|
|
2435
|
-
isHydrated: shallowReadonly(shallowRef(true)),
|
|
2436
|
-
hydrate: () => {}
|
|
2437
|
-
};
|
|
2438
|
-
}
|
|
2439
|
-
/**
|
|
2440
|
-
* Creates a new hydration context trinity.
|
|
2684
|
+
* **Computed Properties:**
|
|
2685
|
+
* - `selectedId`: The ID of the selected item (undefined if none selected)
|
|
2686
|
+
* - `selectedItem`: The selected ticket object (undefined if none selected)
|
|
2687
|
+
* - `selectedIndex`: The index of the selected item (-1 if none selected)
|
|
2688
|
+
* - `selectedValue`: The value of the selected item (undefined if none selected)
|
|
2441
2689
|
*
|
|
2442
|
-
*
|
|
2443
|
-
*
|
|
2444
|
-
* @returns A new hydration context trinity.
|
|
2690
|
+
* **Inheritance Chain:**
|
|
2691
|
+
* `useRegistry` → `createSelection` → `createSingle` → `createStep`
|
|
2445
2692
|
*
|
|
2446
|
-
* @see https://0.vuetifyjs.com/composables/
|
|
2693
|
+
* @see https://0.vuetifyjs.com/composables/selection/use-single
|
|
2447
2694
|
*
|
|
2448
2695
|
* @example
|
|
2449
2696
|
* ```ts
|
|
2450
|
-
* import {
|
|
2697
|
+
* import { createSingle } from '@vuetify/v0'
|
|
2451
2698
|
*
|
|
2452
|
-
*
|
|
2453
|
-
* namespace: 'app:hydration',
|
|
2454
|
-
* })
|
|
2455
|
-
* ```
|
|
2456
|
-
*/
|
|
2457
|
-
function createHydrationContext(_options = {}) {
|
|
2458
|
-
const { namespace = "v0:hydration" } = _options;
|
|
2459
|
-
const [useHydrationContext, _provideHydrationContext] = createContext(namespace);
|
|
2460
|
-
const context = createHydration();
|
|
2461
|
-
function provideHydrationContext(_context = context, app) {
|
|
2462
|
-
return _provideHydrationContext(_context, app);
|
|
2463
|
-
}
|
|
2464
|
-
return createTrinity(useHydrationContext, provideHydrationContext, context);
|
|
2465
|
-
}
|
|
2466
|
-
/**
|
|
2467
|
-
* Creates a new hydration plugin.
|
|
2699
|
+
* const tabs = createSingle({ mandatory: true })
|
|
2468
2700
|
*
|
|
2469
|
-
*
|
|
2470
|
-
*
|
|
2471
|
-
*
|
|
2701
|
+
* tabs.onboard([
|
|
2702
|
+
* { id: 'home', value: 'Home' },
|
|
2703
|
+
* { id: 'about', value: 'About' },
|
|
2704
|
+
* { id: 'contact', value: 'Contact' },
|
|
2705
|
+
* ])
|
|
2472
2706
|
*
|
|
2473
|
-
*
|
|
2707
|
+
* tabs.first() // Select first tab
|
|
2474
2708
|
*
|
|
2475
|
-
*
|
|
2476
|
-
*
|
|
2477
|
-
* import { createApp } from 'vue'
|
|
2478
|
-
* import { createHydrationPlugin } from '@vuetify/v0'
|
|
2479
|
-
* import App from './App.vue'
|
|
2480
|
-
*
|
|
2481
|
-
* const app = createApp(App)
|
|
2482
|
-
*
|
|
2483
|
-
* app.use(createHydrationPlugin())
|
|
2709
|
+
* console.log(tabs.selectedId.value) // 'home'
|
|
2710
|
+
* console.log(tabs.selectedIndex.value) // 0
|
|
2484
2711
|
*
|
|
2485
|
-
*
|
|
2712
|
+
* tabs.select('about') // Switch to about tab
|
|
2713
|
+
* console.log(tabs.selectedId.value) // 'about'
|
|
2714
|
+
* console.log(tabs.selectedIds.size) // 1 (always enforces single selection)
|
|
2486
2715
|
* ```
|
|
2487
2716
|
*/
|
|
2488
|
-
function
|
|
2489
|
-
const {
|
|
2490
|
-
const
|
|
2717
|
+
function createSingle(_options = {}) {
|
|
2718
|
+
const { mandatory = false, multiple = false, ...options } = _options;
|
|
2719
|
+
const registry = createSelection({
|
|
2491
2720
|
...options,
|
|
2492
|
-
|
|
2721
|
+
mandatory,
|
|
2722
|
+
multiple
|
|
2493
2723
|
});
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2724
|
+
const selectedId = computed(() => registry.selectedIds.values().next().value);
|
|
2725
|
+
const selectedItem = computed(() => registry.selectedItems.value.values().next().value);
|
|
2726
|
+
const selectedIndex = computed(() => selectedItem.value?.index ?? -1);
|
|
2727
|
+
const selectedValue = computed(() => selectedItem.value?.value);
|
|
2728
|
+
function unselect(id) {
|
|
2729
|
+
if (mandatory && registry.selectedIds.size === 1) return;
|
|
2730
|
+
registry.selectedIds.delete(id);
|
|
2731
|
+
}
|
|
2732
|
+
function toggle(id) {
|
|
2733
|
+
if (registry.selectedIds.has(id)) unselect(id);
|
|
2734
|
+
else registry.select(id);
|
|
2735
|
+
}
|
|
2736
|
+
return {
|
|
2737
|
+
...registry,
|
|
2738
|
+
selectedId,
|
|
2739
|
+
selectedItem,
|
|
2740
|
+
selectedIndex,
|
|
2741
|
+
selectedValue,
|
|
2742
|
+
unselect,
|
|
2743
|
+
toggle,
|
|
2744
|
+
get size() {
|
|
2745
|
+
return registry.size;
|
|
2504
2746
|
}
|
|
2505
|
-
}
|
|
2747
|
+
};
|
|
2506
2748
|
}
|
|
2507
2749
|
/**
|
|
2508
|
-
*
|
|
2750
|
+
* Creates a new single selection context.
|
|
2509
2751
|
*
|
|
2510
|
-
* @param
|
|
2511
|
-
* @
|
|
2752
|
+
* @param options The options for the single selection context.
|
|
2753
|
+
* @template Z The type of the single selection ticket.
|
|
2754
|
+
* @template E The type of the single selection context.
|
|
2755
|
+
* @returns A new single selection context.
|
|
2512
2756
|
*
|
|
2513
|
-
* @see https://0.vuetifyjs.com/composables/
|
|
2757
|
+
* @see https://0.vuetifyjs.com/composables/selection/use-single
|
|
2758
|
+
*
|
|
2759
|
+
* @example
|
|
2760
|
+
* ```ts
|
|
2761
|
+
* import { createSingleContext } from '@vuetify/v0'
|
|
2762
|
+
*
|
|
2763
|
+
* // With default namespace 'v0:single'
|
|
2764
|
+
* export const [useSingle, provideSingle, context] = createSingleContext()
|
|
2765
|
+
*
|
|
2766
|
+
* // In a parent component:
|
|
2767
|
+
* provideSingle()
|
|
2768
|
+
*
|
|
2769
|
+
* // In a child component:
|
|
2770
|
+
* const single = useSingle()
|
|
2771
|
+
* single.select('tab-1')
|
|
2772
|
+
* ```
|
|
2773
|
+
*/
|
|
2774
|
+
function createSingleContext(_options = {}) {
|
|
2775
|
+
const { namespace = "v0:single", ...options } = _options;
|
|
2776
|
+
const [useSingleContext, _provideSingleContext] = createContext(namespace);
|
|
2777
|
+
const context = createSingle(options);
|
|
2778
|
+
function provideSingleContext(_context = context, app) {
|
|
2779
|
+
return _provideSingleContext(_context, app);
|
|
2780
|
+
}
|
|
2781
|
+
return createTrinity(useSingleContext, provideSingleContext, context);
|
|
2782
|
+
}
|
|
2783
|
+
/**
|
|
2784
|
+
* Returns the current single selection instance.
|
|
2785
|
+
*
|
|
2786
|
+
* @param namespace The namespace for the single selection context. Defaults to `'v0:single'`.
|
|
2787
|
+
* @returns The current single selection instance.
|
|
2788
|
+
*
|
|
2789
|
+
* @see https://0.vuetifyjs.com/composables/selection/use-single
|
|
2514
2790
|
*
|
|
2515
2791
|
* @example
|
|
2516
2792
|
* ```vue
|
|
2517
2793
|
* <script setup lang="ts">
|
|
2518
|
-
* import {
|
|
2794
|
+
* import { useSingle } from '@vuetify/v0'
|
|
2519
2795
|
*
|
|
2520
|
-
* const
|
|
2796
|
+
* const tabs = useSingle()
|
|
2521
2797
|
* <\/script>
|
|
2522
2798
|
*
|
|
2523
2799
|
* <template>
|
|
2524
2800
|
* <div>
|
|
2525
|
-
* <p>
|
|
2801
|
+
* <p>Selected: {{ tabs.selectedId }}</p>
|
|
2526
2802
|
* </div>
|
|
2527
2803
|
* </template>
|
|
2528
2804
|
* ```
|
|
2529
2805
|
*/
|
|
2530
|
-
function
|
|
2531
|
-
|
|
2532
|
-
if (!getCurrentInstance()) return fallback;
|
|
2533
|
-
try {
|
|
2534
|
-
return useContext(namespace, fallback);
|
|
2535
|
-
} catch {
|
|
2536
|
-
return fallback;
|
|
2537
|
-
}
|
|
2806
|
+
function useSingle(namespace = "v0:single") {
|
|
2807
|
+
return useContext(namespace);
|
|
2538
2808
|
}
|
|
2539
2809
|
|
|
2540
2810
|
//#endregion
|
|
2541
|
-
//#region src/composables/
|
|
2811
|
+
//#region src/composables/useTokens/index.ts
|
|
2542
2812
|
/**
|
|
2543
|
-
* @module
|
|
2813
|
+
* @module useTokens
|
|
2814
|
+
*
|
|
2815
|
+
* @see https://0.vuetifyjs.com/composables/registration/use-tokens
|
|
2544
2816
|
*
|
|
2545
2817
|
* @remarks
|
|
2546
|
-
*
|
|
2818
|
+
* Design token registry with alias resolution and W3C Design Tokens format support.
|
|
2547
2819
|
*
|
|
2548
2820
|
* Key features:
|
|
2549
|
-
* -
|
|
2550
|
-
* -
|
|
2551
|
-
* -
|
|
2552
|
-
* -
|
|
2553
|
-
* -
|
|
2554
|
-
* - Box model options (content-box/border-box)
|
|
2821
|
+
* - Alias resolution with circular reference detection
|
|
2822
|
+
* - Nested token flattening with dot notation
|
|
2823
|
+
* - W3C Design Tokens format ($value, $type, $description, $extensions)
|
|
2824
|
+
* - Path-based resolution (e.g., {colors}.blue.500)
|
|
2825
|
+
* - Resolution caching for performance (~28,590 ops/sec)
|
|
2555
2826
|
*
|
|
2556
|
-
*
|
|
2827
|
+
* Used by useTheme, useLocale, and useFeatures for token-based configuration.
|
|
2557
2828
|
*/
|
|
2558
2829
|
/**
|
|
2559
|
-
*
|
|
2560
|
-
* size changes.
|
|
2830
|
+
* Creates a new token instance.
|
|
2561
2831
|
*
|
|
2562
|
-
* @param
|
|
2563
|
-
* @param
|
|
2564
|
-
* @
|
|
2565
|
-
* @
|
|
2832
|
+
* @param tokens The tokens to use.
|
|
2833
|
+
* @param options The options for the token instance.
|
|
2834
|
+
* @template Z The type of the token ticket.
|
|
2835
|
+
* @template E The type of the token context.
|
|
2836
|
+
* @returns A new token instance.
|
|
2566
2837
|
*
|
|
2567
|
-
* @see https://
|
|
2568
|
-
* @see https://0.vuetifyjs.com/composables/
|
|
2838
|
+
* @see https://www.designtokens.org/tr/drafts/format/
|
|
2839
|
+
* @see https://0.vuetifyjs.com/composables/registration/use-tokens
|
|
2569
2840
|
*
|
|
2570
2841
|
* @example
|
|
2571
2842
|
* ```ts
|
|
2572
|
-
* import {
|
|
2573
|
-
* import { useResizeObserver } from '@vuetify/v0'
|
|
2574
|
-
*
|
|
2575
|
-
* const el = ref<HTMLElement>()
|
|
2576
|
-
* const width = ref(0)
|
|
2577
|
-
* const height = ref(0)
|
|
2843
|
+
* import { useTokens } from '@vuetify/v0'
|
|
2578
2844
|
*
|
|
2579
|
-
* const
|
|
2580
|
-
*
|
|
2581
|
-
*
|
|
2582
|
-
*
|
|
2583
|
-
* if (entry) {
|
|
2584
|
-
* width.value = entry.contentRect.width
|
|
2585
|
-
* height.value = entry.contentRect.height
|
|
2586
|
-
* console.log('Size changed:', width.value, 'x', height.value)
|
|
2587
|
-
* }
|
|
2845
|
+
* const tokens = useTokens({
|
|
2846
|
+
* colors: {
|
|
2847
|
+
* primary: '#3b82f6',
|
|
2848
|
+
* secondary: '{colors.primary}', // Alias reference
|
|
2588
2849
|
* },
|
|
2589
|
-
*
|
|
2590
|
-
* )
|
|
2591
|
-
*
|
|
2592
|
-
* // Pause observation
|
|
2593
|
-
* pause()
|
|
2850
|
+
* })
|
|
2594
2851
|
*
|
|
2595
|
-
* //
|
|
2596
|
-
*
|
|
2852
|
+
* console.log(tokens.resolve('{colors.primary}')) // '#3b82f6'
|
|
2853
|
+
* console.log(tokens.resolve('{colors.secondary}')) // '#3b82f6'
|
|
2597
2854
|
* ```
|
|
2598
2855
|
*/
|
|
2599
|
-
function
|
|
2600
|
-
const
|
|
2601
|
-
const
|
|
2602
|
-
const
|
|
2603
|
-
|
|
2604
|
-
function
|
|
2605
|
-
|
|
2606
|
-
observer.value = new ResizeObserver((entries) => {
|
|
2607
|
-
callback(entries.map((entry) => ({
|
|
2608
|
-
contentRect: {
|
|
2609
|
-
width: entry.contentRect.width,
|
|
2610
|
-
height: entry.contentRect.height,
|
|
2611
|
-
top: entry.contentRect.top,
|
|
2612
|
-
left: entry.contentRect.left
|
|
2613
|
-
},
|
|
2614
|
-
target: entry.target
|
|
2615
|
-
})));
|
|
2616
|
-
});
|
|
2617
|
-
observer.value.observe(target.value, { box: options.box || "content-box" });
|
|
2618
|
-
if (options.immediate) {
|
|
2619
|
-
const rect = target.value.getBoundingClientRect();
|
|
2620
|
-
callback([{
|
|
2621
|
-
contentRect: {
|
|
2622
|
-
width: rect.width,
|
|
2623
|
-
height: rect.height,
|
|
2624
|
-
top: rect.top,
|
|
2625
|
-
left: rect.left
|
|
2626
|
-
},
|
|
2627
|
-
target: target.value
|
|
2628
|
-
}]);
|
|
2629
|
-
}
|
|
2630
|
-
}
|
|
2631
|
-
watch([isHydrated, target], () => {
|
|
2632
|
-
cleanup();
|
|
2633
|
-
setup();
|
|
2634
|
-
}, { immediate: true });
|
|
2635
|
-
function cleanup() {
|
|
2636
|
-
if (observer.value) {
|
|
2637
|
-
observer.value.disconnect();
|
|
2638
|
-
observer.value = void 0;
|
|
2639
|
-
}
|
|
2640
|
-
}
|
|
2641
|
-
function pause() {
|
|
2642
|
-
isPaused.value = true;
|
|
2643
|
-
observer.value?.disconnect();
|
|
2644
|
-
}
|
|
2645
|
-
function resume() {
|
|
2646
|
-
isPaused.value = false;
|
|
2647
|
-
setup();
|
|
2856
|
+
function createTokens(tokens = {}, options = {}) {
|
|
2857
|
+
const logger = useLogger();
|
|
2858
|
+
const registry = useRegistry(options);
|
|
2859
|
+
const cache = /* @__PURE__ */ new Map();
|
|
2860
|
+
registry.onboard(flatten(tokens, options.prefix, !!options.flat));
|
|
2861
|
+
function isAlias(token) {
|
|
2862
|
+
return /* @__PURE__ */ isString(token) && token.length > 2 && token[0] === "{" && token.at(-1) === "}";
|
|
2648
2863
|
}
|
|
2649
|
-
function
|
|
2650
|
-
|
|
2864
|
+
function isTokenAlias(value) {
|
|
2865
|
+
return /* @__PURE__ */ isObject(value) && "$value" in value;
|
|
2651
2866
|
}
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
}
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2867
|
+
function resolve(token, visited = /* @__PURE__ */ new Set()) {
|
|
2868
|
+
const cacheKey = /* @__PURE__ */ isString(token) ? token : JSON.stringify(token);
|
|
2869
|
+
const cached = cache.get(cacheKey);
|
|
2870
|
+
if (!/* @__PURE__ */ isUndefined(cached)) return cached;
|
|
2871
|
+
const reference = isTokenAlias(token) ? token.$value : token;
|
|
2872
|
+
const isAliasReference = /* @__PURE__ */ isString(reference) && isAlias(reference);
|
|
2873
|
+
const clean = isAliasReference ? reference.slice(1, -1) : String(reference);
|
|
2874
|
+
if (visited.has(clean)) {
|
|
2875
|
+
logger.warn(`Circular alias detected for "${clean}"`);
|
|
2876
|
+
cache.set(cacheKey, void 0);
|
|
2877
|
+
return;
|
|
2878
|
+
}
|
|
2879
|
+
visited.add(clean);
|
|
2880
|
+
let found = registry.get(clean);
|
|
2881
|
+
let segments = [];
|
|
2882
|
+
if (!found && clean.includes(".")) {
|
|
2883
|
+
const parts = clean.split(".");
|
|
2884
|
+
for (let i = parts.length - 1; i > 0; i--) {
|
|
2885
|
+
const prefix = parts.slice(0, i).join(".");
|
|
2886
|
+
const suffix = parts.slice(i);
|
|
2887
|
+
const candidate = registry.get(prefix);
|
|
2888
|
+
if (!/* @__PURE__ */ isUndefined(candidate?.value)) {
|
|
2889
|
+
found = candidate;
|
|
2890
|
+
segments = suffix;
|
|
2891
|
+
break;
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
if (/* @__PURE__ */ isUndefined(found?.value)) {
|
|
2896
|
+
if (isAliasReference) logger.warn(`Alias not found for "${String(reference)}"`);
|
|
2897
|
+
cache.set(cacheKey, void 0);
|
|
2898
|
+
return;
|
|
2899
|
+
}
|
|
2900
|
+
let result;
|
|
2901
|
+
let current = found.value;
|
|
2902
|
+
if (segments.length > 0) {
|
|
2903
|
+
if (isTokenAlias(current)) current = current.$value;
|
|
2904
|
+
for (const segment of segments) {
|
|
2905
|
+
if (!/* @__PURE__ */ isObject(current) || !(segment in current)) {
|
|
2906
|
+
current = void 0;
|
|
2907
|
+
break;
|
|
2908
|
+
}
|
|
2909
|
+
current = current[segment];
|
|
2910
|
+
if (isTokenAlias(current)) current = current.$value;
|
|
2911
|
+
}
|
|
2912
|
+
if (/* @__PURE__ */ isUndefined(current)) {
|
|
2913
|
+
logger.warn(`Path not found inside "${clean}": ${segments.join(".")}`);
|
|
2914
|
+
cache.set(cacheKey, void 0);
|
|
2915
|
+
return;
|
|
2916
|
+
}
|
|
2917
|
+
result = current;
|
|
2918
|
+
} else if (isTokenAlias(current)) {
|
|
2919
|
+
const inner = current.$value;
|
|
2920
|
+
if (/* @__PURE__ */ isString(inner) && isAlias(inner)) return resolve(inner, visited);
|
|
2921
|
+
result = inner;
|
|
2922
|
+
} else if (/* @__PURE__ */ isString(current) && isAlias(current)) return resolve(current, visited);
|
|
2923
|
+
else result = current;
|
|
2924
|
+
cache.set(cacheKey, result);
|
|
2925
|
+
return result;
|
|
2926
|
+
}
|
|
2927
|
+
return {
|
|
2928
|
+
...registry,
|
|
2929
|
+
resolve,
|
|
2930
|
+
isAlias,
|
|
2931
|
+
get size() {
|
|
2932
|
+
return registry.size;
|
|
2933
|
+
}
|
|
2934
|
+
};
|
|
2935
|
+
}
|
|
2936
|
+
/**
|
|
2937
|
+
* Creates a new token context.
|
|
2674
2938
|
*
|
|
2675
|
-
*
|
|
2676
|
-
*
|
|
2939
|
+
* @param namespace The namespace for the token context.
|
|
2940
|
+
* @param tokens The tokens to use.
|
|
2941
|
+
* @template Z The type of the token ticket.
|
|
2942
|
+
* @template E The type of the token context.
|
|
2943
|
+
* @returns A new token context.
|
|
2677
2944
|
*
|
|
2678
|
-
*
|
|
2679
|
-
*
|
|
2680
|
-
*
|
|
2945
|
+
* @see https://0.vuetifyjs.com/composables/registration/use-tokens
|
|
2946
|
+
*
|
|
2947
|
+
* @example
|
|
2948
|
+
* ```ts
|
|
2949
|
+
* import { createTokensContext } from '@vuetify/v0'
|
|
2950
|
+
*
|
|
2951
|
+
* export const [useTokens, provideTokens, context] = createTokensContext({
|
|
2952
|
+
* namespace: 'v0:tokens',
|
|
2953
|
+
* tokens: {
|
|
2954
|
+
* colors: {
|
|
2955
|
+
* primary: '#3b82f6',
|
|
2956
|
+
* secondary: '{colors.primary}', // Alias reference
|
|
2957
|
+
* },
|
|
2958
|
+
* },
|
|
2681
2959
|
* })
|
|
2682
2960
|
* ```
|
|
2683
2961
|
*/
|
|
2684
|
-
function
|
|
2685
|
-
const
|
|
2686
|
-
const
|
|
2687
|
-
const
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2962
|
+
function createTokensContext(_options) {
|
|
2963
|
+
const { namespace = "v0:tokens", tokens = {}, ...options } = _options;
|
|
2964
|
+
const [useTokensContext, _provideTokensContext] = createContext(namespace);
|
|
2965
|
+
const context = createTokens(tokens, options);
|
|
2966
|
+
function provideTokensContext(_context = context, app) {
|
|
2967
|
+
return _provideTokensContext(_context, app);
|
|
2968
|
+
}
|
|
2969
|
+
return createTrinity(useTokensContext, provideTokensContext, context);
|
|
2970
|
+
}
|
|
2971
|
+
/**
|
|
2972
|
+
* Returns the current tokens instance.
|
|
2973
|
+
*
|
|
2974
|
+
* @param namespace The namespace for the tokens context. Defaults to `'v0:tokens'`.
|
|
2975
|
+
* @returns The current tokens instance.
|
|
2976
|
+
*
|
|
2977
|
+
* @see https://0.vuetifyjs.com/composables/registration/use-tokens
|
|
2978
|
+
*
|
|
2979
|
+
* @example
|
|
2980
|
+
* ```vue
|
|
2981
|
+
* <script setup lang="ts">
|
|
2982
|
+
* import { useTokens } from '@vuetify/v0'
|
|
2983
|
+
*
|
|
2984
|
+
* const tokens = useTokens()
|
|
2985
|
+
* <\/script>
|
|
2986
|
+
* ```
|
|
2987
|
+
*/
|
|
2988
|
+
function useTokens(namespace = "v0:tokens") {
|
|
2989
|
+
return useContext(namespace);
|
|
2990
|
+
}
|
|
2991
|
+
/**
|
|
2992
|
+
* Flattens a nested collection of tokens into a flat array of tokens.
|
|
2993
|
+
* Each token is represented by an object containing its ID & value.
|
|
2994
|
+
* @param tokens The collection of tokens to flatten.
|
|
2995
|
+
* @param prefix An optional prefix to prepend to each token ID.
|
|
2996
|
+
* @returns An array of flattened tokens, each with an ID and value.
|
|
2997
|
+
*/
|
|
2998
|
+
function flatten(tokens, prefix = "", flat = false) {
|
|
2999
|
+
const flattened = [];
|
|
3000
|
+
const stack = [{
|
|
3001
|
+
tokens,
|
|
3002
|
+
prefix,
|
|
3003
|
+
flat
|
|
3004
|
+
}];
|
|
3005
|
+
while (stack.length > 0) {
|
|
3006
|
+
const { tokens: currentTokens, prefix: currentPrefix, flat: flat$1 } = stack.pop();
|
|
3007
|
+
const meta = {};
|
|
3008
|
+
for (const k in currentTokens) if (k.startsWith("$")) meta[k] = currentTokens[k];
|
|
3009
|
+
if (Object.keys(meta).length > 0 && currentPrefix) flattened.push({
|
|
3010
|
+
id: currentPrefix,
|
|
3011
|
+
value: meta
|
|
3012
|
+
});
|
|
3013
|
+
for (const key in currentTokens) {
|
|
3014
|
+
if (key.startsWith("$")) continue;
|
|
3015
|
+
const value = currentTokens[key];
|
|
3016
|
+
const id = currentPrefix ? `${currentPrefix}.${key}` : key;
|
|
3017
|
+
if (!/* @__PURE__ */ isObject(value)) {
|
|
3018
|
+
flattened.push({
|
|
3019
|
+
id,
|
|
3020
|
+
value
|
|
3021
|
+
});
|
|
3022
|
+
continue;
|
|
3023
|
+
}
|
|
3024
|
+
if ("$value" in value) {
|
|
3025
|
+
flattened.push({
|
|
3026
|
+
id,
|
|
3027
|
+
value
|
|
3028
|
+
});
|
|
3029
|
+
const inner = value.$value;
|
|
3030
|
+
if (/* @__PURE__ */ isObject(inner) && !flat$1) for (const innerKey in inner) {
|
|
3031
|
+
if (innerKey.startsWith("$")) continue;
|
|
3032
|
+
const child = inner[innerKey];
|
|
3033
|
+
const childId = `${id}.${innerKey}`;
|
|
3034
|
+
if (!/* @__PURE__ */ isObject(child)) flattened.push({
|
|
3035
|
+
id: childId,
|
|
3036
|
+
value: child
|
|
3037
|
+
});
|
|
3038
|
+
else if ("$value" in child) flattened.push({
|
|
3039
|
+
id: childId,
|
|
3040
|
+
value: child
|
|
3041
|
+
});
|
|
3042
|
+
else stack.push({
|
|
3043
|
+
tokens: child,
|
|
3044
|
+
prefix: childId,
|
|
3045
|
+
flat: flat$1
|
|
3046
|
+
});
|
|
3047
|
+
}
|
|
3048
|
+
continue;
|
|
3049
|
+
}
|
|
3050
|
+
if (flat$1) {
|
|
3051
|
+
flattened.push({
|
|
3052
|
+
id,
|
|
3053
|
+
value
|
|
3054
|
+
});
|
|
3055
|
+
continue;
|
|
3056
|
+
}
|
|
3057
|
+
stack.push({
|
|
3058
|
+
tokens: value,
|
|
3059
|
+
prefix: id,
|
|
3060
|
+
flat: flat$1
|
|
3061
|
+
});
|
|
2692
3062
|
}
|
|
2693
|
-
}, { immediate: true });
|
|
2694
|
-
function pause() {
|
|
2695
|
-
width.value = 0;
|
|
2696
|
-
height.value = 0;
|
|
2697
|
-
_pause();
|
|
2698
3063
|
}
|
|
2699
|
-
return
|
|
2700
|
-
width,
|
|
2701
|
-
height,
|
|
2702
|
-
isActive,
|
|
2703
|
-
isPaused,
|
|
2704
|
-
pause,
|
|
2705
|
-
resume,
|
|
2706
|
-
stop
|
|
2707
|
-
};
|
|
3064
|
+
return flattened;
|
|
2708
3065
|
}
|
|
2709
3066
|
|
|
2710
3067
|
//#endregion
|
|
2711
|
-
//#region src/composables/
|
|
3068
|
+
//#region src/composables/useLocale/adapters/v0.ts
|
|
2712
3069
|
/**
|
|
2713
|
-
*
|
|
3070
|
+
* Vuetify0.x locale adapter implementation
|
|
3071
|
+
*
|
|
3072
|
+
* This adapter provides translation and number formatting
|
|
3073
|
+
* capabilities using the Intl API and supports both
|
|
3074
|
+
* numbered ({0}, {1}) and named ({name}) variables in translation strings.
|
|
3075
|
+
*/
|
|
3076
|
+
var Vuetify0LocaleAdapter = class {
|
|
3077
|
+
t(message, ...params) {
|
|
3078
|
+
let resolvedMessage = message;
|
|
3079
|
+
if (params.length > 0 && /* @__PURE__ */ isObject(params[0])) {
|
|
3080
|
+
const variables = params[0];
|
|
3081
|
+
resolvedMessage = resolvedMessage.replace(/{([a-zA-Z][a-zA-Z0-9_]*)}/g, (match, name) => {
|
|
3082
|
+
return /* @__PURE__ */ isUndefined(variables[name]) ? match : String(variables[name]);
|
|
3083
|
+
});
|
|
3084
|
+
params = params.slice(1);
|
|
3085
|
+
}
|
|
3086
|
+
resolvedMessage = resolvedMessage.replace(/\{(\d+)\}/g, (match, index) => {
|
|
3087
|
+
const idx = Number.parseInt(index, 10);
|
|
3088
|
+
if (!/* @__PURE__ */ isUndefined(params[idx])) return String(params[idx]);
|
|
3089
|
+
return match;
|
|
3090
|
+
});
|
|
3091
|
+
return resolvedMessage;
|
|
3092
|
+
}
|
|
3093
|
+
n(value, locale, ...params) {
|
|
3094
|
+
if (!IN_BROWSER || !locale) return value.toString();
|
|
3095
|
+
const options = params[0];
|
|
3096
|
+
return new Intl.NumberFormat(String(locale), options).format(value);
|
|
3097
|
+
}
|
|
3098
|
+
};
|
|
3099
|
+
|
|
3100
|
+
//#endregion
|
|
3101
|
+
//#region src/composables/useLocale/index.ts
|
|
3102
|
+
/**
|
|
3103
|
+
* @module useLocale
|
|
2714
3104
|
*
|
|
2715
3105
|
* @remarks
|
|
2716
|
-
*
|
|
2717
|
-
* Enables responsive truncation logic for Pagination, Breadcrumbs, and similar components.
|
|
3106
|
+
* Internationalization (i18n) composable with adapter pattern for message translation.
|
|
2718
3107
|
*
|
|
2719
3108
|
* Key features:
|
|
2720
|
-
* -
|
|
2721
|
-
* -
|
|
2722
|
-
* -
|
|
2723
|
-
* -
|
|
2724
|
-
* -
|
|
3109
|
+
* - Locale selection with createSingle
|
|
3110
|
+
* - Token-based message storage with useTokens
|
|
3111
|
+
* - Numbered and named placeholder support ({0}, {name})
|
|
3112
|
+
* - Number formatting with Intl.NumberFormat
|
|
3113
|
+
* - Adapter pattern for integration with i18n providers
|
|
2725
3114
|
*
|
|
2726
|
-
*
|
|
2727
|
-
* Use uniform mode (itemWidth option) for same-width items like Pagination buttons.
|
|
3115
|
+
* Integrates with createSingle for locale selection and useTokens for message resolution.
|
|
2728
3116
|
*/
|
|
2729
3117
|
/**
|
|
2730
|
-
* Creates a new
|
|
2731
|
-
*
|
|
2732
|
-
* @param options Configuration options
|
|
2733
|
-
* @returns Overflow context with container ref, capacity, and measurement functions
|
|
2734
|
-
*
|
|
2735
|
-
* @example Variable-width mode (Breadcrumbs)
|
|
2736
|
-
* ```vue
|
|
2737
|
-
* <script lang="ts" setup>
|
|
2738
|
-
* import { useTemplateRef } from 'vue'
|
|
2739
|
-
* import { createOverflow } from '@vuetify/v0'
|
|
2740
|
-
*
|
|
2741
|
-
* const containerRef = useTemplateRef('container')
|
|
2742
|
-
* const overflow = createOverflow({
|
|
2743
|
-
* container: containerRef,
|
|
2744
|
-
* gap: 8,
|
|
2745
|
-
* reserved: 40,
|
|
2746
|
-
* })
|
|
2747
|
-
* <\/script>
|
|
2748
|
-
*
|
|
2749
|
-
* <template>
|
|
2750
|
-
* <div ref="container">
|
|
2751
|
-
* <span
|
|
2752
|
-
* v-for="(item, i) in items.slice(0, overflow.capacity.value)"
|
|
2753
|
-
* :key="i"
|
|
2754
|
-
* :ref="el => overflow.measure(i, el)"
|
|
2755
|
-
* >
|
|
2756
|
-
* {{ item }}
|
|
2757
|
-
* </span>
|
|
2758
|
-
* <span v-if="overflow.isOverflowing.value">...</span>
|
|
2759
|
-
* </div>
|
|
2760
|
-
* </template>
|
|
2761
|
-
* ```
|
|
3118
|
+
* Creates a new locale instance.
|
|
2762
3119
|
*
|
|
2763
|
-
* @
|
|
2764
|
-
*
|
|
2765
|
-
*
|
|
2766
|
-
*
|
|
2767
|
-
*
|
|
2768
|
-
*
|
|
2769
|
-
* })
|
|
2770
|
-
* ```
|
|
3120
|
+
* @param options The options for the locale instance.
|
|
3121
|
+
* @template Z The type of the locale ticket.
|
|
3122
|
+
* @template E The type of the locale context.
|
|
3123
|
+
* @returns A new locale instance.
|
|
3124
|
+
*
|
|
3125
|
+
* @see https://0.vuetifyjs.com/composables/plugins/use-locale
|
|
2771
3126
|
*/
|
|
2772
|
-
function
|
|
2773
|
-
const {
|
|
2774
|
-
const
|
|
2775
|
-
const
|
|
2776
|
-
const
|
|
2777
|
-
|
|
2778
|
-
if (!
|
|
2779
|
-
if (widths.value.has(index)) {
|
|
2780
|
-
const next = new Map(widths.value);
|
|
2781
|
-
next.delete(index);
|
|
2782
|
-
widths.value = next;
|
|
2783
|
-
}
|
|
2784
|
-
return;
|
|
2785
|
-
}
|
|
2786
|
-
const style = getComputedStyle(el);
|
|
2787
|
-
const marginX = Number.parseFloat(style.marginLeft) + Number.parseFloat(style.marginRight);
|
|
2788
|
-
const w = el.offsetWidth + marginX;
|
|
2789
|
-
if (widths.value.get(index) !== w) widths.value = new Map(widths.value).set(index, w);
|
|
3127
|
+
function createLocale(_options = {}) {
|
|
3128
|
+
const { adapter = new Vuetify0LocaleAdapter(), messages = {}, ...options } = _options;
|
|
3129
|
+
const tokens = createTokens(messages);
|
|
3130
|
+
const registry = createSingle(options);
|
|
3131
|
+
for (const id in messages) {
|
|
3132
|
+
registry.register({ id });
|
|
3133
|
+
if (id === options.default && !registry.selectedId.value) registry.select(id);
|
|
2790
3134
|
}
|
|
2791
|
-
function
|
|
2792
|
-
|
|
3135
|
+
function t(key, params, fallback) {
|
|
3136
|
+
const locale = registry.selectedId.value;
|
|
3137
|
+
const args = toArray(params);
|
|
3138
|
+
if (!locale) return adapter.t(fallback ?? key, ...args);
|
|
3139
|
+
const path = `${locale}.${key}`;
|
|
3140
|
+
const message = tokens.get(path)?.value;
|
|
3141
|
+
const template = /* @__PURE__ */ isString(message) ? resolve(locale, message) : fallback ?? key;
|
|
3142
|
+
return adapter.t(template, ...args);
|
|
2793
3143
|
}
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
3144
|
+
function n(value, ...params) {
|
|
3145
|
+
return adapter.n(value, registry.selectedId.value, ...params);
|
|
3146
|
+
}
|
|
3147
|
+
function resolve(locale, str) {
|
|
3148
|
+
return str.replace(/{([a-zA-Z0-9.-_]+)}/g, (match, key) => {
|
|
3149
|
+
const [prefix, ...rest] = key.split(".");
|
|
3150
|
+
const target = registry.has(prefix) ? prefix : locale;
|
|
3151
|
+
const path = `${target}.${registry.has(prefix) ? rest.join(".") : key}`;
|
|
3152
|
+
const resolved = tokens.get(path)?.value;
|
|
3153
|
+
if (/* @__PURE__ */ isString(resolved)) return resolve(target, resolved);
|
|
3154
|
+
return match;
|
|
3155
|
+
});
|
|
3156
|
+
}
|
|
3157
|
+
return {
|
|
3158
|
+
...registry,
|
|
3159
|
+
t,
|
|
3160
|
+
n,
|
|
3161
|
+
get size() {
|
|
3162
|
+
return registry.size;
|
|
2801
3163
|
}
|
|
2802
|
-
|
|
2803
|
-
|
|
3164
|
+
};
|
|
3165
|
+
}
|
|
3166
|
+
function createLocaleFallback() {
|
|
2804
3167
|
return {
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
const available = width.value - toValue(reserved);
|
|
2809
|
-
if (width.value === 0) return Infinity;
|
|
2810
|
-
if (available <= 0) return 0;
|
|
2811
|
-
const g = toValue(gap);
|
|
2812
|
-
const uniformWidth = toValue(itemWidth);
|
|
2813
|
-
if (uniformWidth && uniformWidth > 0) {
|
|
2814
|
-
const first = uniformWidth;
|
|
2815
|
-
const subsequent = uniformWidth + g;
|
|
2816
|
-
if (available < first) return 0;
|
|
2817
|
-
return Math.max(1, Math.floor((available - first) / subsequent) + 1);
|
|
2818
|
-
}
|
|
2819
|
-
const entries = [...widths.value.entries()].toSorted((a, b) => a[0] - b[0]);
|
|
2820
|
-
if (toValue(reverse)) entries.reverse();
|
|
2821
|
-
let sum = 0;
|
|
2822
|
-
let count = 0;
|
|
2823
|
-
for (const [, w] of entries) {
|
|
2824
|
-
const next = sum + w + (count > 0 ? g : 0);
|
|
2825
|
-
if (next > available) break;
|
|
2826
|
-
sum = next;
|
|
2827
|
-
count++;
|
|
2828
|
-
}
|
|
2829
|
-
return count;
|
|
2830
|
-
}),
|
|
2831
|
-
total,
|
|
2832
|
-
isOverflowing: toRef(() => {
|
|
2833
|
-
return total.value > width.value - toValue(reserved);
|
|
2834
|
-
}),
|
|
2835
|
-
measure,
|
|
2836
|
-
reset
|
|
3168
|
+
size: 0,
|
|
3169
|
+
t: (key, _params, fallback) => fallback ?? key,
|
|
3170
|
+
n: String
|
|
2837
3171
|
};
|
|
2838
3172
|
}
|
|
2839
3173
|
/**
|
|
2840
|
-
* Creates
|
|
3174
|
+
* Creates a new locale context.
|
|
2841
3175
|
*
|
|
2842
|
-
* @param options
|
|
2843
|
-
* @
|
|
3176
|
+
* @param options The options for the locale context.
|
|
3177
|
+
* @template Z The type of the locale ticket.
|
|
3178
|
+
* @template E The type of the locale context.
|
|
3179
|
+
* @returns A new locale context.
|
|
3180
|
+
*
|
|
3181
|
+
* @see https://0.vuetifyjs.com/composables/plugins/use-locale
|
|
2844
3182
|
*
|
|
2845
3183
|
* @example
|
|
2846
3184
|
* ```ts
|
|
2847
|
-
*
|
|
2848
|
-
*
|
|
2849
|
-
*
|
|
2850
|
-
*
|
|
2851
|
-
*
|
|
3185
|
+
* import { createLocaleContext } from '@vuetify/v0'
|
|
3186
|
+
*
|
|
3187
|
+
* export const [useAppLocale, provideAppLocale, appLocale] = createLocaleContext({
|
|
3188
|
+
* namespace: 'app:locale',
|
|
3189
|
+
* messages: {
|
|
3190
|
+
* en: { hello: 'Hello' },
|
|
3191
|
+
* es: { hello: 'Hola' },
|
|
3192
|
+
* },
|
|
2852
3193
|
* })
|
|
2853
3194
|
*
|
|
2854
|
-
* // In parent component
|
|
2855
|
-
*
|
|
3195
|
+
* // In a parent component:
|
|
3196
|
+
* provideAppLocale()
|
|
2856
3197
|
*
|
|
2857
|
-
* // In child component
|
|
2858
|
-
* const
|
|
3198
|
+
* // In a child component:
|
|
3199
|
+
* const locale = useAppLocale()
|
|
3200
|
+
* locale.select('es')
|
|
2859
3201
|
* ```
|
|
2860
3202
|
*/
|
|
2861
|
-
function
|
|
2862
|
-
const { namespace = "v0:
|
|
2863
|
-
const [
|
|
2864
|
-
const context =
|
|
2865
|
-
function
|
|
2866
|
-
return
|
|
3203
|
+
function createLocaleContext(_options = {}) {
|
|
3204
|
+
const { namespace = "v0:locale", ...options } = _options;
|
|
3205
|
+
const [useLocaleContext, _provideLocaleContext] = createContext(namespace);
|
|
3206
|
+
const context = createLocale(options);
|
|
3207
|
+
function provideLocaleContext(_context = context, app) {
|
|
3208
|
+
return _provideLocaleContext(_context, app);
|
|
2867
3209
|
}
|
|
2868
|
-
return createTrinity(
|
|
3210
|
+
return createTrinity(useLocaleContext, provideLocaleContext, context);
|
|
2869
3211
|
}
|
|
2870
3212
|
/**
|
|
2871
|
-
*
|
|
3213
|
+
* Creates a new locale plugin.
|
|
2872
3214
|
*
|
|
2873
|
-
* @param
|
|
2874
|
-
* @
|
|
3215
|
+
* @param options The options for the locale plugin.
|
|
3216
|
+
* @template Z The type of the locale ticket.
|
|
3217
|
+
* @template E The type of the locale context.
|
|
3218
|
+
* @template R The type of the token ticket.
|
|
3219
|
+
* @template O The type of the token context.
|
|
3220
|
+
* @returns A new locale plugin.
|
|
2875
3221
|
*
|
|
2876
|
-
* @
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
3222
|
+
* @see https://0.vuetifyjs.com/composables/plugins/use-locale
|
|
3223
|
+
*/
|
|
3224
|
+
function createLocalePlugin(_options = {}) {
|
|
3225
|
+
const { namespace = "v0:locale", adapter = new Vuetify0LocaleAdapter(), messages = {}, ...options } = _options;
|
|
3226
|
+
const [, provideLocaleContext, context] = createLocaleContext({
|
|
3227
|
+
...options,
|
|
3228
|
+
namespace,
|
|
3229
|
+
adapter,
|
|
3230
|
+
messages
|
|
3231
|
+
});
|
|
3232
|
+
return createPlugin({
|
|
3233
|
+
namespace,
|
|
3234
|
+
provide: (app) => {
|
|
3235
|
+
provideLocaleContext(context, app);
|
|
3236
|
+
}
|
|
3237
|
+
});
|
|
3238
|
+
}
|
|
3239
|
+
/**
|
|
3240
|
+
* Returns the current locale instance.
|
|
2880
3241
|
*
|
|
2881
|
-
*
|
|
2882
|
-
* const overflow = useOverflow()
|
|
2883
|
-
* <\/script>
|
|
3242
|
+
* @returns The current locale instance.
|
|
2884
3243
|
*
|
|
2885
|
-
*
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
3244
|
+
* @see https://0.vuetifyjs.com/composables/plugins/use-locale
|
|
3245
|
+
*/
|
|
3246
|
+
function useLocale(namespace = "v0:locale") {
|
|
3247
|
+
const fallback = createLocaleFallback();
|
|
3248
|
+
if (!getCurrentInstance()) return fallback;
|
|
3249
|
+
try {
|
|
3250
|
+
return useContext(namespace, fallback);
|
|
3251
|
+
} catch {
|
|
3252
|
+
return fallback;
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
|
|
3256
|
+
//#endregion
|
|
3257
|
+
//#region src/composables/useHydration/index.ts
|
|
3258
|
+
/**
|
|
3259
|
+
* @module useHydration
|
|
3260
|
+
*
|
|
3261
|
+
* @see https://0.vuetifyjs.com/composables/plugins/use-hydration
|
|
3262
|
+
*
|
|
3263
|
+
* @remarks
|
|
3264
|
+
* SSR hydration state management composable.
|
|
3265
|
+
*
|
|
3266
|
+
* Key features:
|
|
3267
|
+
* - Hydration state detection (browser vs SSR)
|
|
3268
|
+
* - Root component detection
|
|
3269
|
+
* - Readonly hydration state refs
|
|
3270
|
+
* - Plugin installation support
|
|
3271
|
+
* - Perfect for hydration-safe rendering
|
|
3272
|
+
*
|
|
3273
|
+
* Essential for composables that need to behave differently during SSR vs client-side.
|
|
3274
|
+
*/
|
|
3275
|
+
/**
|
|
3276
|
+
* Creates a new hydration instance.
|
|
3277
|
+
*
|
|
3278
|
+
* @returns A new hydration instance.
|
|
3279
|
+
*
|
|
3280
|
+
* @see https://0.vuetifyjs.com/composables/plugins/use-hydration
|
|
3281
|
+
*
|
|
3282
|
+
* @example
|
|
3283
|
+
* ```ts
|
|
3284
|
+
* import { createHydration } from '@vuetify/v0'
|
|
3285
|
+
*
|
|
3286
|
+
* const hydration = createHydration()
|
|
3287
|
+
* console.log(hydration.isHydrated.value) // false
|
|
3288
|
+
* hydration.hydrate()
|
|
3289
|
+
* console.log(hydration.isHydrated.value) // true
|
|
2890
3290
|
* ```
|
|
2891
3291
|
*/
|
|
2892
|
-
function
|
|
2893
|
-
|
|
3292
|
+
function createHydration() {
|
|
3293
|
+
const isHydrated = shallowRef(false);
|
|
3294
|
+
function hydrate() {
|
|
3295
|
+
isHydrated.value = true;
|
|
3296
|
+
}
|
|
3297
|
+
return {
|
|
3298
|
+
isHydrated: shallowReadonly(isHydrated),
|
|
3299
|
+
hydrate
|
|
3300
|
+
};
|
|
3301
|
+
}
|
|
3302
|
+
function createFallbackHydration() {
|
|
3303
|
+
return {
|
|
3304
|
+
isHydrated: shallowReadonly(shallowRef(true)),
|
|
3305
|
+
hydrate: () => {}
|
|
3306
|
+
};
|
|
2894
3307
|
}
|
|
2895
|
-
|
|
2896
|
-
//#endregion
|
|
2897
|
-
//#region src/composables/usePagination/index.ts
|
|
2898
3308
|
/**
|
|
2899
|
-
*
|
|
2900
|
-
*
|
|
2901
|
-
* @remarks
|
|
2902
|
-
* Lightweight pagination composable for navigating through pages.
|
|
2903
|
-
*
|
|
2904
|
-
* Key features:
|
|
2905
|
-
* - No registry overhead - just a bounded integer
|
|
2906
|
-
* - Direct ref support for v-model compatibility
|
|
2907
|
-
* - Navigation methods: next, prev, first, last
|
|
2908
|
-
* - Computed visible items with ellipsis
|
|
2909
|
-
* - Trinity pattern for dependency injection
|
|
3309
|
+
* Creates a new hydration context trinity.
|
|
2910
3310
|
*
|
|
2911
|
-
*
|
|
2912
|
-
*
|
|
2913
|
-
|
|
2914
|
-
/**
|
|
2915
|
-
* Creates a pagination instance.
|
|
3311
|
+
* @param options Options for creating the hydration context.
|
|
3312
|
+
* @template E The type of the hydration context.
|
|
3313
|
+
* @returns A new hydration context trinity.
|
|
2916
3314
|
*
|
|
2917
|
-
* @
|
|
2918
|
-
* @returns A pagination context with navigation methods.
|
|
3315
|
+
* @see https://0.vuetifyjs.com/composables/plugins/use-hydration
|
|
2919
3316
|
*
|
|
2920
3317
|
* @example
|
|
2921
3318
|
* ```ts
|
|
2922
|
-
* import {
|
|
2923
|
-
*
|
|
2924
|
-
* // Basic usage
|
|
2925
|
-
* const pagination = createPagination({ size: 100 })
|
|
2926
|
-
* pagination.next()
|
|
2927
|
-
* pagination.items.value // [{ type: 'page', value: 1 }, { type: 'page', value: 2 }, ...]
|
|
3319
|
+
* import { createHydrationContext } from '@vuetify/v0'
|
|
2928
3320
|
*
|
|
2929
|
-
*
|
|
2930
|
-
*
|
|
2931
|
-
*
|
|
2932
|
-
* // Mutating pagination.page or the passed ref syncs both
|
|
3321
|
+
* export const [useHydrationContext, provideHydrationContext, context] = createHydrationContext({
|
|
3322
|
+
* namespace: 'app:hydration',
|
|
3323
|
+
* })
|
|
2933
3324
|
* ```
|
|
2934
3325
|
*/
|
|
2935
|
-
function
|
|
2936
|
-
const {
|
|
2937
|
-
const
|
|
2938
|
-
const
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
if (size <= 0 || /* @__PURE__ */ isNaN(size)) return 0;
|
|
2942
|
-
return Math.ceil(size / perPage);
|
|
2943
|
-
});
|
|
2944
|
-
function first() {
|
|
2945
|
-
page.value = 1;
|
|
2946
|
-
}
|
|
2947
|
-
function last() {
|
|
2948
|
-
page.value = Math.max(1, pages.value);
|
|
2949
|
-
}
|
|
2950
|
-
function next() {
|
|
2951
|
-
if (page.value < pages.value) page.value++;
|
|
2952
|
-
}
|
|
2953
|
-
function prev() {
|
|
2954
|
-
if (page.value > 1) page.value--;
|
|
2955
|
-
}
|
|
2956
|
-
function select(value) {
|
|
2957
|
-
if (value < 1) page.value = 1;
|
|
2958
|
-
else if (value > pages.value) page.value = Math.max(1, pages.value);
|
|
2959
|
-
else page.value = value;
|
|
2960
|
-
}
|
|
2961
|
-
const isFirst = computed(() => page.value <= 1);
|
|
2962
|
-
const isLast = computed(() => page.value >= pages.value);
|
|
2963
|
-
const pageStart = computed(() => (page.value - 1) * toValue(_itemsPerPage));
|
|
2964
|
-
const pageStop = computed(() => Math.min(pageStart.value + toValue(_itemsPerPage), toValue(_size)));
|
|
2965
|
-
function toPage(value) {
|
|
2966
|
-
return {
|
|
2967
|
-
type: "page",
|
|
2968
|
-
value
|
|
2969
|
-
};
|
|
2970
|
-
}
|
|
2971
|
-
function toEllipsis() {
|
|
2972
|
-
return ellipsis === false ? false : {
|
|
2973
|
-
type: "ellipsis",
|
|
2974
|
-
value: ellipsis
|
|
2975
|
-
};
|
|
2976
|
-
}
|
|
2977
|
-
function filter(array) {
|
|
2978
|
-
return array.filter((item) => item !== false);
|
|
3326
|
+
function createHydrationContext(_options = {}) {
|
|
3327
|
+
const { namespace = "v0:hydration" } = _options;
|
|
3328
|
+
const [useHydrationContext, _provideHydrationContext] = createContext(namespace);
|
|
3329
|
+
const context = createHydration();
|
|
3330
|
+
function provideHydrationContext(_context = context, app) {
|
|
3331
|
+
return _provideHydrationContext(_context, app);
|
|
2979
3332
|
}
|
|
2980
|
-
return
|
|
2981
|
-
page,
|
|
2982
|
-
ellipsis,
|
|
2983
|
-
items: computed(() => {
|
|
2984
|
-
const pageCount = pages.value;
|
|
2985
|
-
const visible = toValue(_visible);
|
|
2986
|
-
const current = page.value;
|
|
2987
|
-
if (pageCount <= 0 || /* @__PURE__ */ isNaN(pageCount) || pageCount > Number.MAX_SAFE_INTEGER) return [];
|
|
2988
|
-
if (visible <= 0) return [];
|
|
2989
|
-
if (visible <= 2) return [toPage(current)];
|
|
2990
|
-
if (pageCount <= visible) return (/* @__PURE__ */ range(pageCount, 1)).map(toPage);
|
|
2991
|
-
if (visible === 3) {
|
|
2992
|
-
const mid = current <= 1 ? 2 : current >= pageCount ? pageCount - 1 : current;
|
|
2993
|
-
return [
|
|
2994
|
-
toPage(1),
|
|
2995
|
-
toPage(mid),
|
|
2996
|
-
toPage(pageCount)
|
|
2997
|
-
];
|
|
2998
|
-
}
|
|
2999
|
-
const boundary = visible - 2;
|
|
3000
|
-
const middle = visible - 4;
|
|
3001
|
-
if (middle <= 0) {
|
|
3002
|
-
if (current <= boundary) return filter([
|
|
3003
|
-
...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
|
|
3004
|
-
toEllipsis(),
|
|
3005
|
-
toPage(pageCount)
|
|
3006
|
-
]);
|
|
3007
|
-
if (current > pageCount - boundary) return filter([
|
|
3008
|
-
toPage(1),
|
|
3009
|
-
toEllipsis(),
|
|
3010
|
-
...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
|
|
3011
|
-
]);
|
|
3012
|
-
return current <= Math.ceil(pageCount / 2) ? filter([
|
|
3013
|
-
toPage(1),
|
|
3014
|
-
toPage(current),
|
|
3015
|
-
toEllipsis(),
|
|
3016
|
-
toPage(pageCount)
|
|
3017
|
-
]) : filter([
|
|
3018
|
-
toPage(1),
|
|
3019
|
-
toEllipsis(),
|
|
3020
|
-
toPage(current),
|
|
3021
|
-
toPage(pageCount)
|
|
3022
|
-
]);
|
|
3023
|
-
}
|
|
3024
|
-
const leftThreshold = boundary - 1;
|
|
3025
|
-
const rightThreshold = pageCount - boundary + 2;
|
|
3026
|
-
if (current <= leftThreshold) return filter([
|
|
3027
|
-
...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
|
|
3028
|
-
toEllipsis(),
|
|
3029
|
-
toPage(pageCount)
|
|
3030
|
-
]);
|
|
3031
|
-
else if (current >= rightThreshold) return filter([
|
|
3032
|
-
toPage(1),
|
|
3033
|
-
toEllipsis(),
|
|
3034
|
-
...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
|
|
3035
|
-
]);
|
|
3036
|
-
else {
|
|
3037
|
-
const start = current - Math.floor(middle / 2);
|
|
3038
|
-
return filter([
|
|
3039
|
-
toPage(1),
|
|
3040
|
-
toEllipsis(),
|
|
3041
|
-
...(/* @__PURE__ */ range(middle, start)).map(toPage),
|
|
3042
|
-
toEllipsis(),
|
|
3043
|
-
toPage(pageCount)
|
|
3044
|
-
]);
|
|
3045
|
-
}
|
|
3046
|
-
}),
|
|
3047
|
-
pageStart,
|
|
3048
|
-
pageStop,
|
|
3049
|
-
isFirst,
|
|
3050
|
-
isLast,
|
|
3051
|
-
first,
|
|
3052
|
-
last,
|
|
3053
|
-
next,
|
|
3054
|
-
prev,
|
|
3055
|
-
select,
|
|
3056
|
-
get itemsPerPage() {
|
|
3057
|
-
return toValue(_itemsPerPage);
|
|
3058
|
-
},
|
|
3059
|
-
get size() {
|
|
3060
|
-
return toValue(_size);
|
|
3061
|
-
},
|
|
3062
|
-
get pages() {
|
|
3063
|
-
return pages.value;
|
|
3064
|
-
}
|
|
3065
|
-
};
|
|
3333
|
+
return createTrinity(useHydrationContext, provideHydrationContext, context);
|
|
3066
3334
|
}
|
|
3067
3335
|
/**
|
|
3068
|
-
* Creates a
|
|
3336
|
+
* Creates a new hydration plugin.
|
|
3069
3337
|
*
|
|
3070
|
-
* @param options The options
|
|
3071
|
-
* @
|
|
3338
|
+
* @param options The options for the hydration plugin.
|
|
3339
|
+
* @template E The type of the hydration context.
|
|
3340
|
+
* @returns A new hydration plugin.
|
|
3341
|
+
*
|
|
3342
|
+
* @see https://0.vuetifyjs.com/composables/plugins/use-hydration
|
|
3072
3343
|
*
|
|
3073
3344
|
* @example
|
|
3074
3345
|
* ```ts
|
|
3075
|
-
*
|
|
3076
|
-
*
|
|
3346
|
+
* import { createApp } from 'vue'
|
|
3347
|
+
* import { createHydrationPlugin } from '@vuetify/v0'
|
|
3348
|
+
* import App from './App.vue'
|
|
3077
3349
|
*
|
|
3078
|
-
*
|
|
3079
|
-
* const [usePagination, providePaginationContext] = createPaginationContext({
|
|
3080
|
-
* namespace: 'my-pagination',
|
|
3081
|
-
* size: 50,
|
|
3082
|
-
* })
|
|
3350
|
+
* const app = createApp(App)
|
|
3083
3351
|
*
|
|
3084
|
-
*
|
|
3085
|
-
* providePaginationContext()
|
|
3352
|
+
* app.use(createHydrationPlugin())
|
|
3086
3353
|
*
|
|
3087
|
-
*
|
|
3088
|
-
* const pagination = usePagination()
|
|
3089
|
-
* pagination.next()
|
|
3354
|
+
* app.mount('#app')
|
|
3090
3355
|
* ```
|
|
3091
3356
|
*/
|
|
3092
|
-
function
|
|
3093
|
-
const { namespace = "v0:
|
|
3094
|
-
const [
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3357
|
+
function createHydrationPlugin(_options = {}) {
|
|
3358
|
+
const { namespace = "v0:hydration", ...options } = _options;
|
|
3359
|
+
const [, provideHydrationContext, context] = createHydrationContext({
|
|
3360
|
+
...options,
|
|
3361
|
+
namespace
|
|
3362
|
+
});
|
|
3363
|
+
return createPlugin({
|
|
3364
|
+
namespace,
|
|
3365
|
+
provide: (app) => {
|
|
3366
|
+
provideHydrationContext(context, app);
|
|
3367
|
+
},
|
|
3368
|
+
setup: (app) => {
|
|
3369
|
+
app.mixin({ mounted() {
|
|
3370
|
+
if (!/* @__PURE__ */ isNull(this.$parent)) return;
|
|
3371
|
+
context.hydrate();
|
|
3372
|
+
} });
|
|
3373
|
+
}
|
|
3374
|
+
});
|
|
3100
3375
|
}
|
|
3101
3376
|
/**
|
|
3102
|
-
* Returns the current
|
|
3377
|
+
* Returns the current hydration instance.
|
|
3103
3378
|
*
|
|
3104
|
-
* @param namespace The namespace.
|
|
3105
|
-
* @returns The
|
|
3379
|
+
* @param namespace The namespace for the hydration context. Defaults to `v0:hydration`.
|
|
3380
|
+
* @returns The current hydration instance.
|
|
3381
|
+
*
|
|
3382
|
+
* @see https://0.vuetifyjs.com/composables/plugins/use-hydration
|
|
3106
3383
|
*
|
|
3107
3384
|
* @example
|
|
3108
3385
|
* ```vue
|
|
3109
|
-
* <script setup>
|
|
3110
|
-
*
|
|
3386
|
+
* <script setup lang="ts">
|
|
3387
|
+
* import { useHydration } from '@vuetify/v0'
|
|
3111
3388
|
*
|
|
3112
|
-
*
|
|
3389
|
+
* const hydration = useHydration()
|
|
3113
3390
|
* <\/script>
|
|
3114
3391
|
*
|
|
3115
3392
|
* <template>
|
|
3116
|
-
* <
|
|
3117
|
-
*
|
|
3393
|
+
* <div>
|
|
3394
|
+
* <p>Is hydrated: {{ hydration.isHydrated.value }}</p>
|
|
3395
|
+
* </div>
|
|
3118
3396
|
* </template>
|
|
3119
3397
|
* ```
|
|
3120
3398
|
*/
|
|
3121
|
-
function
|
|
3122
|
-
|
|
3399
|
+
function useHydration(namespace = "v0:hydration") {
|
|
3400
|
+
const fallback = createFallbackHydration();
|
|
3401
|
+
if (!getCurrentInstance()) return fallback;
|
|
3402
|
+
try {
|
|
3403
|
+
return useContext(namespace, fallback);
|
|
3404
|
+
} catch {
|
|
3405
|
+
return fallback;
|
|
3406
|
+
}
|
|
3123
3407
|
}
|
|
3124
3408
|
|
|
3125
3409
|
//#endregion
|
|
3126
|
-
//#region src/composables/
|
|
3410
|
+
//#region src/composables/useResizeObserver/index.ts
|
|
3127
3411
|
/**
|
|
3128
|
-
* @module
|
|
3412
|
+
* @module useResizeObserver
|
|
3129
3413
|
*
|
|
3130
3414
|
* @remarks
|
|
3131
|
-
*
|
|
3415
|
+
* ResizeObserver composable with lifecycle management.
|
|
3132
3416
|
*
|
|
3133
3417
|
* Key features:
|
|
3134
|
-
* -
|
|
3135
|
-
* -
|
|
3136
|
-
* -
|
|
3418
|
+
* - ResizeObserver API wrapper
|
|
3419
|
+
* - Pause/resume/stop functionality
|
|
3420
|
+
* - Automatic cleanup on unmount
|
|
3421
|
+
* - SSR-safe (checks SUPPORTS_OBSERVER)
|
|
3422
|
+
* - Hydration-aware
|
|
3423
|
+
* - Box model options (content-box/border-box)
|
|
3137
3424
|
*
|
|
3138
|
-
*
|
|
3425
|
+
* Perfect for responsive components and size-based rendering.
|
|
3139
3426
|
*/
|
|
3140
3427
|
/**
|
|
3141
|
-
*
|
|
3142
|
-
*
|
|
3143
|
-
* Extends `createSelection` by automatically clearing previous selections when a new item is selected.
|
|
3144
|
-
* Adds computed singular properties: `selectedId`, `selectedItem`, `selectedIndex`, `selectedValue`.
|
|
3145
|
-
*
|
|
3146
|
-
* @param options The options for the single selection instance.
|
|
3147
|
-
* @template Z The type of the single selection ticket.
|
|
3148
|
-
* @template E The type of the single selection context.
|
|
3149
|
-
* @returns A new single selection instance with single-selection enforcement.
|
|
3150
|
-
*
|
|
3151
|
-
* @remarks
|
|
3152
|
-
* **Key Differences from `createSelection`:**
|
|
3153
|
-
* - Automatically clears `selectedIds` before selecting a new item (enforces single selection)
|
|
3154
|
-
* - Provides singular computed properties instead of plural sets
|
|
3155
|
-
* - Perfect for tabs, radio buttons, theme selectors, and other single-choice UI components
|
|
3156
|
-
*
|
|
3157
|
-
* **Computed Properties:**
|
|
3158
|
-
* - `selectedId`: The ID of the selected item (undefined if none selected)
|
|
3159
|
-
* - `selectedItem`: The selected ticket object (undefined if none selected)
|
|
3160
|
-
* - `selectedIndex`: The index of the selected item (-1 if none selected)
|
|
3161
|
-
* - `selectedValue`: The value of the selected item (undefined if none selected)
|
|
3428
|
+
* A composable that uses the Resize Observer API to detect when an element's
|
|
3429
|
+
* size changes.
|
|
3162
3430
|
*
|
|
3163
|
-
*
|
|
3164
|
-
*
|
|
3431
|
+
* @param target The element to observe.
|
|
3432
|
+
* @param callback The callback to execute when the element's size changes.
|
|
3433
|
+
* @param options The options for the Resize Observer.
|
|
3434
|
+
* @returns An object with methods to control the observer.
|
|
3165
3435
|
*
|
|
3166
|
-
* @see https://
|
|
3436
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
|
|
3437
|
+
* @see https://0.vuetifyjs.com/composables/system/use-resize-observer
|
|
3167
3438
|
*
|
|
3168
3439
|
* @example
|
|
3169
3440
|
* ```ts
|
|
3170
|
-
* import {
|
|
3171
|
-
*
|
|
3172
|
-
* const tabs = createSingle({ mandatory: true })
|
|
3441
|
+
* import { ref } from 'vue'
|
|
3442
|
+
* import { useResizeObserver } from '@vuetify/v0'
|
|
3173
3443
|
*
|
|
3174
|
-
*
|
|
3175
|
-
*
|
|
3176
|
-
*
|
|
3177
|
-
* { id: 'contact', value: 'Contact' },
|
|
3178
|
-
* ])
|
|
3444
|
+
* const el = ref<HTMLElement>()
|
|
3445
|
+
* const width = ref(0)
|
|
3446
|
+
* const height = ref(0)
|
|
3179
3447
|
*
|
|
3180
|
-
*
|
|
3448
|
+
* const { pause, resume, isPaused } = useResizeObserver(
|
|
3449
|
+
* el,
|
|
3450
|
+
* (entries) => {
|
|
3451
|
+
* const entry = entries[0]
|
|
3452
|
+
* if (entry) {
|
|
3453
|
+
* width.value = entry.contentRect.width
|
|
3454
|
+
* height.value = entry.contentRect.height
|
|
3455
|
+
* console.log('Size changed:', width.value, 'x', height.value)
|
|
3456
|
+
* }
|
|
3457
|
+
* },
|
|
3458
|
+
* { immediate: true }
|
|
3459
|
+
* )
|
|
3181
3460
|
*
|
|
3182
|
-
*
|
|
3183
|
-
*
|
|
3461
|
+
* // Pause observation
|
|
3462
|
+
* pause()
|
|
3184
3463
|
*
|
|
3185
|
-
*
|
|
3186
|
-
*
|
|
3187
|
-
* console.log(tabs.selectedIds.size) // 1 (always enforces single selection)
|
|
3464
|
+
* // Resume observation
|
|
3465
|
+
* resume()
|
|
3188
3466
|
* ```
|
|
3189
3467
|
*/
|
|
3190
|
-
function
|
|
3191
|
-
const {
|
|
3192
|
-
const
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3468
|
+
function useResizeObserver(target, callback, options = {}) {
|
|
3469
|
+
const { isHydrated } = useHydration();
|
|
3470
|
+
const observer = shallowRef();
|
|
3471
|
+
const isPaused = shallowRef(false);
|
|
3472
|
+
const isActive = toRef(() => !!observer.value);
|
|
3473
|
+
function setup() {
|
|
3474
|
+
if (/* @__PURE__ */ isNull(observer.value)) return;
|
|
3475
|
+
if (!isHydrated.value || !SUPPORTS_OBSERVER || !target.value || isPaused.value) return;
|
|
3476
|
+
observer.value = new ResizeObserver((entries) => {
|
|
3477
|
+
callback(entries.map((entry) => ({
|
|
3478
|
+
contentRect: {
|
|
3479
|
+
width: entry.contentRect.width,
|
|
3480
|
+
height: entry.contentRect.height,
|
|
3481
|
+
top: entry.contentRect.top,
|
|
3482
|
+
left: entry.contentRect.left
|
|
3483
|
+
},
|
|
3484
|
+
target: entry.target
|
|
3485
|
+
})));
|
|
3486
|
+
if (options.once) stop();
|
|
3487
|
+
});
|
|
3488
|
+
observer.value.observe(target.value, { box: options.box || "content-box" });
|
|
3489
|
+
if (options.immediate) {
|
|
3490
|
+
const rect = target.value.getBoundingClientRect();
|
|
3491
|
+
callback([{
|
|
3492
|
+
contentRect: {
|
|
3493
|
+
width: rect.width,
|
|
3494
|
+
height: rect.height,
|
|
3495
|
+
top: rect.top,
|
|
3496
|
+
left: rect.left
|
|
3497
|
+
},
|
|
3498
|
+
target: target.value
|
|
3499
|
+
}]);
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3502
|
+
watchEffect(() => {
|
|
3503
|
+
const hydrated = isHydrated.value;
|
|
3504
|
+
const el = target.value;
|
|
3505
|
+
cleanup();
|
|
3506
|
+
if (hydrated && el) setup();
|
|
3196
3507
|
});
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
if (mandatory && registry.selectedIds.size === 1) return;
|
|
3203
|
-
registry.selectedIds.delete(id);
|
|
3508
|
+
function cleanup() {
|
|
3509
|
+
if (observer.value) {
|
|
3510
|
+
observer.value.disconnect();
|
|
3511
|
+
observer.value = void 0;
|
|
3512
|
+
}
|
|
3204
3513
|
}
|
|
3205
|
-
function
|
|
3206
|
-
|
|
3207
|
-
|
|
3514
|
+
function pause() {
|
|
3515
|
+
isPaused.value = true;
|
|
3516
|
+
observer.value?.disconnect();
|
|
3517
|
+
}
|
|
3518
|
+
function resume() {
|
|
3519
|
+
isPaused.value = false;
|
|
3520
|
+
setup();
|
|
3521
|
+
}
|
|
3522
|
+
function stop() {
|
|
3523
|
+
cleanup();
|
|
3524
|
+
observer.value = null;
|
|
3208
3525
|
}
|
|
3526
|
+
onScopeDispose(stop, true);
|
|
3209
3527
|
return {
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
unselect,
|
|
3216
|
-
toggle,
|
|
3217
|
-
get size() {
|
|
3218
|
-
return registry.size;
|
|
3219
|
-
}
|
|
3528
|
+
isActive: shallowReadonly(isActive),
|
|
3529
|
+
isPaused: shallowReadonly(isPaused),
|
|
3530
|
+
pause,
|
|
3531
|
+
resume,
|
|
3532
|
+
stop
|
|
3220
3533
|
};
|
|
3221
3534
|
}
|
|
3222
3535
|
/**
|
|
3223
|
-
*
|
|
3536
|
+
* A convenience composable that uses the Resize Observer API to track an
|
|
3537
|
+
* element's size.
|
|
3224
3538
|
*
|
|
3225
|
-
* @param
|
|
3226
|
-
* @
|
|
3227
|
-
* @template E The type of the single selection context.
|
|
3228
|
-
* @returns A new single selection context.
|
|
3539
|
+
* @param target The element to observe.
|
|
3540
|
+
* @returns An object with the element's width and height.
|
|
3229
3541
|
*
|
|
3230
|
-
* @see https://0.vuetifyjs.com/composables/
|
|
3542
|
+
* @see https://0.vuetifyjs.com/composables/system/use-resize-observer#use-element-size
|
|
3231
3543
|
*
|
|
3232
3544
|
* @example
|
|
3233
3545
|
* ```ts
|
|
3234
|
-
* import {
|
|
3235
|
-
*
|
|
3236
|
-
* // With default namespace 'v0:single'
|
|
3237
|
-
* export const [useSingle, provideSingle, context] = createSingleContext()
|
|
3546
|
+
* import { ref, watchEffect } from 'vue'
|
|
3547
|
+
* import { useElementSize } from '@vuetify/v0'
|
|
3238
3548
|
*
|
|
3239
|
-
*
|
|
3240
|
-
*
|
|
3549
|
+
* const box = ref<HTMLElement>()
|
|
3550
|
+
* const { width, height } = useElementSize(box)
|
|
3241
3551
|
*
|
|
3242
|
-
* //
|
|
3243
|
-
*
|
|
3244
|
-
*
|
|
3552
|
+
* // Width and height are reactive refs
|
|
3553
|
+
* watchEffect(() => {
|
|
3554
|
+
* console.log('Box size:', width.value, 'x', height.value)
|
|
3555
|
+
* })
|
|
3245
3556
|
* ```
|
|
3246
3557
|
*/
|
|
3247
|
-
function
|
|
3248
|
-
const
|
|
3249
|
-
const
|
|
3250
|
-
const
|
|
3251
|
-
|
|
3252
|
-
|
|
3558
|
+
function useElementSize(target) {
|
|
3559
|
+
const width = shallowRef(0);
|
|
3560
|
+
const height = shallowRef(0);
|
|
3561
|
+
const { pause: _pause, resume, stop, isActive, isPaused } = useResizeObserver(target, (entries) => {
|
|
3562
|
+
const entry = entries[0];
|
|
3563
|
+
if (entry) {
|
|
3564
|
+
width.value = entry.contentRect.width;
|
|
3565
|
+
height.value = entry.contentRect.height;
|
|
3566
|
+
}
|
|
3567
|
+
}, { immediate: true });
|
|
3568
|
+
function pause() {
|
|
3569
|
+
width.value = 0;
|
|
3570
|
+
height.value = 0;
|
|
3571
|
+
_pause();
|
|
3253
3572
|
}
|
|
3254
|
-
return
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
*
|
|
3264
|
-
* @example
|
|
3265
|
-
* ```vue
|
|
3266
|
-
* <script setup lang="ts">
|
|
3267
|
-
* import { useSingle } from '@vuetify/v0'
|
|
3268
|
-
*
|
|
3269
|
-
* const tabs = useSingle()
|
|
3270
|
-
* <\/script>
|
|
3271
|
-
*
|
|
3272
|
-
* <template>
|
|
3273
|
-
* <div>
|
|
3274
|
-
* <p>Selected: {{ tabs.selectedId }}</p>
|
|
3275
|
-
* </div>
|
|
3276
|
-
* </template>
|
|
3277
|
-
* ```
|
|
3278
|
-
*/
|
|
3279
|
-
function useSingle(namespace = "v0:single") {
|
|
3280
|
-
return useContext(namespace);
|
|
3573
|
+
return {
|
|
3574
|
+
width,
|
|
3575
|
+
height,
|
|
3576
|
+
isActive,
|
|
3577
|
+
isPaused,
|
|
3578
|
+
pause,
|
|
3579
|
+
resume,
|
|
3580
|
+
stop
|
|
3581
|
+
};
|
|
3281
3582
|
}
|
|
3282
3583
|
|
|
3283
3584
|
//#endregion
|
|
3284
|
-
//#region src/composables/
|
|
3285
|
-
/**
|
|
3286
|
-
* @module
|
|
3287
|
-
*
|
|
3288
|
-
* @see https://0.vuetifyjs.com/composables/registration/use-tokens
|
|
3585
|
+
//#region src/composables/useOverflow/index.ts
|
|
3586
|
+
/**
|
|
3587
|
+
* @module useOverflow
|
|
3289
3588
|
*
|
|
3290
3589
|
* @remarks
|
|
3291
|
-
*
|
|
3590
|
+
* Composable for computing how many items fit in a container based on available width.
|
|
3591
|
+
* Enables responsive truncation logic for Pagination, Breadcrumbs, and similar components.
|
|
3292
3592
|
*
|
|
3293
3593
|
* Key features:
|
|
3294
|
-
* -
|
|
3295
|
-
* -
|
|
3296
|
-
* -
|
|
3297
|
-
* -
|
|
3298
|
-
* -
|
|
3594
|
+
* - Container width tracking via ResizeObserver
|
|
3595
|
+
* - Two modes: variable-width (per-item) or uniform-width (sample-based)
|
|
3596
|
+
* - Computes capacity (how many items fit)
|
|
3597
|
+
* - SSR-safe with Infinity fallback
|
|
3598
|
+
* - Supports reserved space for nav buttons, ellipsis, etc.
|
|
3299
3599
|
*
|
|
3300
|
-
*
|
|
3600
|
+
* Use variable mode (default) for items with different widths like Breadcrumbs.
|
|
3601
|
+
* Use uniform mode (itemWidth option) for same-width items like Pagination buttons.
|
|
3301
3602
|
*/
|
|
3302
3603
|
/**
|
|
3303
|
-
* Creates a new
|
|
3604
|
+
* Creates a new overflow context for computing how many items fit in a container.
|
|
3304
3605
|
*
|
|
3305
|
-
* @param
|
|
3306
|
-
* @
|
|
3307
|
-
* @template Z The type of the token ticket.
|
|
3308
|
-
* @template E The type of the token context.
|
|
3309
|
-
* @returns A new token instance.
|
|
3606
|
+
* @param options Configuration options
|
|
3607
|
+
* @returns Overflow context with container ref, capacity, and measurement functions
|
|
3310
3608
|
*
|
|
3311
|
-
* @
|
|
3312
|
-
*
|
|
3609
|
+
* @example Variable-width mode (Breadcrumbs)
|
|
3610
|
+
* ```vue
|
|
3611
|
+
* <script lang="ts" setup>
|
|
3612
|
+
* import { useTemplateRef } from 'vue'
|
|
3613
|
+
* import { createOverflow } from '@vuetify/v0'
|
|
3313
3614
|
*
|
|
3314
|
-
*
|
|
3315
|
-
*
|
|
3316
|
-
*
|
|
3615
|
+
* const containerRef = useTemplateRef('container')
|
|
3616
|
+
* const overflow = createOverflow({
|
|
3617
|
+
* container: containerRef,
|
|
3618
|
+
* gap: 8,
|
|
3619
|
+
* reserved: 40,
|
|
3620
|
+
* })
|
|
3621
|
+
* <\/script>
|
|
3317
3622
|
*
|
|
3318
|
-
*
|
|
3319
|
-
*
|
|
3320
|
-
*
|
|
3321
|
-
*
|
|
3322
|
-
*
|
|
3323
|
-
*
|
|
3623
|
+
* <template>
|
|
3624
|
+
* <div ref="container">
|
|
3625
|
+
* <span
|
|
3626
|
+
* v-for="(item, i) in items.slice(0, overflow.capacity.value)"
|
|
3627
|
+
* :key="i"
|
|
3628
|
+
* :ref="el => overflow.measure(i, el)"
|
|
3629
|
+
* >
|
|
3630
|
+
* {{ item }}
|
|
3631
|
+
* </span>
|
|
3632
|
+
* <span v-if="overflow.isOverflowing.value">...</span>
|
|
3633
|
+
* </div>
|
|
3634
|
+
* </template>
|
|
3635
|
+
* ```
|
|
3324
3636
|
*
|
|
3325
|
-
*
|
|
3326
|
-
*
|
|
3637
|
+
* @example Uniform-width mode (Pagination)
|
|
3638
|
+
* ```ts
|
|
3639
|
+
* const overflow = createOverflow({
|
|
3640
|
+
* container: () => atom.value?.element,
|
|
3641
|
+
* itemWidth: buttonWidth,
|
|
3642
|
+
* reserved: () => buttonWidth.value * 4,
|
|
3643
|
+
* })
|
|
3327
3644
|
* ```
|
|
3328
3645
|
*/
|
|
3329
|
-
function
|
|
3330
|
-
const
|
|
3331
|
-
const
|
|
3332
|
-
const
|
|
3333
|
-
|
|
3334
|
-
function
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
function resolve(token, visited = /* @__PURE__ */ new Set()) {
|
|
3341
|
-
const cacheKey = /* @__PURE__ */ isString(token) ? token : JSON.stringify(token);
|
|
3342
|
-
const cached = cache.get(cacheKey);
|
|
3343
|
-
if (!/* @__PURE__ */ isUndefined(cached)) return cached;
|
|
3344
|
-
const reference = isTokenAlias(token) ? token.$value : token;
|
|
3345
|
-
const isAliasReference = /* @__PURE__ */ isString(reference) && isAlias(reference);
|
|
3346
|
-
const clean = isAliasReference ? reference.slice(1, -1) : String(reference);
|
|
3347
|
-
if (visited.has(clean)) {
|
|
3348
|
-
logger.warn(`Circular alias detected for "${clean}"`);
|
|
3349
|
-
cache.set(cacheKey, void 0);
|
|
3350
|
-
return;
|
|
3351
|
-
}
|
|
3352
|
-
visited.add(clean);
|
|
3353
|
-
let found = registry.get(clean);
|
|
3354
|
-
let segments = [];
|
|
3355
|
-
if (!found && clean.includes(".")) {
|
|
3356
|
-
const parts = clean.split(".");
|
|
3357
|
-
for (let i = parts.length - 1; i > 0; i--) {
|
|
3358
|
-
const prefix = parts.slice(0, i).join(".");
|
|
3359
|
-
const suffix = parts.slice(i);
|
|
3360
|
-
const candidate = registry.get(prefix);
|
|
3361
|
-
if (!/* @__PURE__ */ isUndefined(candidate?.value)) {
|
|
3362
|
-
found = candidate;
|
|
3363
|
-
segments = suffix;
|
|
3364
|
-
break;
|
|
3365
|
-
}
|
|
3646
|
+
function createOverflow(options = {}) {
|
|
3647
|
+
const { container: _container, gap = 0, reserved = 0, itemWidth, reverse } = options;
|
|
3648
|
+
const container = /* @__PURE__ */ isUndefined(_container) ? shallowRef() : toRef(_container);
|
|
3649
|
+
const widths = shallowRef(/* @__PURE__ */ new Map());
|
|
3650
|
+
const { width } = useElementSize(container);
|
|
3651
|
+
function measure(index, el) {
|
|
3652
|
+
if (!el) {
|
|
3653
|
+
if (widths.value.has(index)) {
|
|
3654
|
+
const next = new Map(widths.value);
|
|
3655
|
+
next.delete(index);
|
|
3656
|
+
widths.value = next;
|
|
3366
3657
|
}
|
|
3367
|
-
}
|
|
3368
|
-
if (/* @__PURE__ */ isUndefined(found?.value)) {
|
|
3369
|
-
if (isAliasReference) logger.warn(`Alias not found for "${String(reference)}"`);
|
|
3370
|
-
cache.set(cacheKey, void 0);
|
|
3371
3658
|
return;
|
|
3372
3659
|
}
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
for (const segment of segments) {
|
|
3378
|
-
if (!/* @__PURE__ */ isObject(current) || !(segment in current)) {
|
|
3379
|
-
current = void 0;
|
|
3380
|
-
break;
|
|
3381
|
-
}
|
|
3382
|
-
current = current[segment];
|
|
3383
|
-
if (isTokenAlias(current)) current = current.$value;
|
|
3384
|
-
}
|
|
3385
|
-
if (/* @__PURE__ */ isUndefined(current)) {
|
|
3386
|
-
logger.warn(`Path not found inside "${clean}": ${segments.join(".")}`);
|
|
3387
|
-
cache.set(cacheKey, void 0);
|
|
3388
|
-
return;
|
|
3389
|
-
}
|
|
3390
|
-
result = current;
|
|
3391
|
-
} else if (isTokenAlias(current)) {
|
|
3392
|
-
const inner = current.$value;
|
|
3393
|
-
if (/* @__PURE__ */ isString(inner) && isAlias(inner)) return resolve(inner, visited);
|
|
3394
|
-
result = inner;
|
|
3395
|
-
} else if (/* @__PURE__ */ isString(current) && isAlias(current)) return resolve(current, visited);
|
|
3396
|
-
else result = current;
|
|
3397
|
-
cache.set(cacheKey, result);
|
|
3398
|
-
return result;
|
|
3660
|
+
const style = getComputedStyle(el);
|
|
3661
|
+
const marginX = Number.parseFloat(style.marginLeft) + Number.parseFloat(style.marginRight);
|
|
3662
|
+
const w = el.offsetWidth + marginX;
|
|
3663
|
+
if (widths.value.get(index) !== w) widths.value = new Map(widths.value).set(index, w);
|
|
3399
3664
|
}
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3665
|
+
function reset() {
|
|
3666
|
+
widths.value = /* @__PURE__ */ new Map();
|
|
3667
|
+
}
|
|
3668
|
+
const total = computed(() => {
|
|
3669
|
+
const g = toValue(gap);
|
|
3670
|
+
let sum = 0;
|
|
3671
|
+
let count = 0;
|
|
3672
|
+
for (const w of widths.value.values()) {
|
|
3673
|
+
sum += w + (count > 0 ? g : 0);
|
|
3674
|
+
count++;
|
|
3406
3675
|
}
|
|
3676
|
+
return sum;
|
|
3677
|
+
});
|
|
3678
|
+
return {
|
|
3679
|
+
container,
|
|
3680
|
+
width,
|
|
3681
|
+
capacity: computed(() => {
|
|
3682
|
+
const available = width.value - toValue(reserved);
|
|
3683
|
+
if (width.value === 0) return Infinity;
|
|
3684
|
+
if (available <= 0) return 0;
|
|
3685
|
+
const g = toValue(gap);
|
|
3686
|
+
const uniformWidth = toValue(itemWidth);
|
|
3687
|
+
if (uniformWidth && uniformWidth > 0) {
|
|
3688
|
+
const first = uniformWidth;
|
|
3689
|
+
const subsequent = uniformWidth + g;
|
|
3690
|
+
if (available < first) return 0;
|
|
3691
|
+
return Math.max(1, Math.floor((available - first) / subsequent) + 1);
|
|
3692
|
+
}
|
|
3693
|
+
const entries = [...widths.value.entries()].toSorted((a, b) => a[0] - b[0]);
|
|
3694
|
+
if (toValue(reverse)) entries.reverse();
|
|
3695
|
+
let sum = 0;
|
|
3696
|
+
let count = 0;
|
|
3697
|
+
for (const [, w] of entries) {
|
|
3698
|
+
const next = sum + w + (count > 0 ? g : 0);
|
|
3699
|
+
if (next > available) break;
|
|
3700
|
+
sum = next;
|
|
3701
|
+
count++;
|
|
3702
|
+
}
|
|
3703
|
+
return count;
|
|
3704
|
+
}),
|
|
3705
|
+
total,
|
|
3706
|
+
isOverflowing: toRef(() => {
|
|
3707
|
+
return total.value > width.value - toValue(reserved);
|
|
3708
|
+
}),
|
|
3709
|
+
measure,
|
|
3710
|
+
reset
|
|
3407
3711
|
};
|
|
3408
3712
|
}
|
|
3409
3713
|
/**
|
|
3410
|
-
* Creates
|
|
3411
|
-
*
|
|
3412
|
-
* @param namespace The namespace for the token context.
|
|
3413
|
-
* @param tokens The tokens to use.
|
|
3414
|
-
* @template Z The type of the token ticket.
|
|
3415
|
-
* @template E The type of the token context.
|
|
3416
|
-
* @returns A new token context.
|
|
3714
|
+
* Creates an overflow context with dependency injection support.
|
|
3417
3715
|
*
|
|
3418
|
-
* @
|
|
3716
|
+
* @param options Configuration options including namespace
|
|
3717
|
+
* @returns Trinity tuple: [useContext, provideContext, defaultContext]
|
|
3419
3718
|
*
|
|
3420
3719
|
* @example
|
|
3421
3720
|
* ```ts
|
|
3422
|
-
*
|
|
3423
|
-
*
|
|
3424
|
-
*
|
|
3425
|
-
*
|
|
3426
|
-
*
|
|
3427
|
-
* colors: {
|
|
3428
|
-
* primary: '#3b82f6',
|
|
3429
|
-
* secondary: '{colors.primary}', // Alias reference
|
|
3430
|
-
* },
|
|
3431
|
-
* },
|
|
3721
|
+
* // Create injectable context
|
|
3722
|
+
* const [useOverflow, provideOverflow, overflow] = createOverflowContext({
|
|
3723
|
+
* namespace: 'my-overflow',
|
|
3724
|
+
* gap: 8,
|
|
3725
|
+
* reserved: 160,
|
|
3432
3726
|
* })
|
|
3727
|
+
*
|
|
3728
|
+
* // In parent component
|
|
3729
|
+
* provideOverflow()
|
|
3730
|
+
*
|
|
3731
|
+
* // In child component
|
|
3732
|
+
* const overflow = useOverflow()
|
|
3433
3733
|
* ```
|
|
3434
3734
|
*/
|
|
3435
|
-
function
|
|
3436
|
-
const { namespace = "v0:
|
|
3437
|
-
const [
|
|
3438
|
-
const context =
|
|
3439
|
-
function
|
|
3440
|
-
return
|
|
3735
|
+
function createOverflowContext(_options = {}) {
|
|
3736
|
+
const { namespace = "v0:overflow", ...options } = _options;
|
|
3737
|
+
const [useOverflowContext, _provideOverflowContext] = createContext(namespace);
|
|
3738
|
+
const context = createOverflow(options);
|
|
3739
|
+
function provideOverflowContext(_context = context, app) {
|
|
3740
|
+
return _provideOverflowContext(_context, app);
|
|
3441
3741
|
}
|
|
3442
|
-
return createTrinity(
|
|
3443
|
-
}
|
|
3444
|
-
/**
|
|
3445
|
-
* Returns the current
|
|
3446
|
-
*
|
|
3447
|
-
* @param namespace The namespace for the tokens context. Defaults to `'v0:tokens'`.
|
|
3448
|
-
* @returns The current tokens instance.
|
|
3742
|
+
return createTrinity(useOverflowContext, provideOverflowContext, context);
|
|
3743
|
+
}
|
|
3744
|
+
/**
|
|
3745
|
+
* Returns the current overflow context from dependency injection.
|
|
3449
3746
|
*
|
|
3450
|
-
* @
|
|
3747
|
+
* @param namespace The namespace for the overflow context. Defaults to `v0:overflow`.
|
|
3748
|
+
* @returns The current overflow context.
|
|
3451
3749
|
*
|
|
3452
3750
|
* @example
|
|
3453
3751
|
* ```vue
|
|
3454
|
-
* <script
|
|
3455
|
-
* import {
|
|
3752
|
+
* <script lang="ts" setup>
|
|
3753
|
+
* import { useOverflow } from '@vuetify/v0'
|
|
3456
3754
|
*
|
|
3457
|
-
*
|
|
3755
|
+
* // Inject overflow context provided by parent
|
|
3756
|
+
* const overflow = useOverflow()
|
|
3458
3757
|
* <\/script>
|
|
3758
|
+
*
|
|
3759
|
+
* <template>
|
|
3760
|
+
* <div>
|
|
3761
|
+
* <p>Capacity: {{ overflow.capacity.value }}</p>
|
|
3762
|
+
* </div>
|
|
3763
|
+
* </template>
|
|
3459
3764
|
* ```
|
|
3460
3765
|
*/
|
|
3461
|
-
function
|
|
3766
|
+
function useOverflow(namespace = "v0:overflow") {
|
|
3462
3767
|
return useContext(namespace);
|
|
3463
3768
|
}
|
|
3464
|
-
/**
|
|
3465
|
-
* Flattens a nested collection of tokens into a flat array of tokens.
|
|
3466
|
-
* Each token is represented by an object containing its ID & value.
|
|
3467
|
-
* @param tokens The collection of tokens to flatten.
|
|
3468
|
-
* @param prefix An optional prefix to prepend to each token ID.
|
|
3469
|
-
* @returns An array of flattened tokens, each with an ID and value.
|
|
3470
|
-
*/
|
|
3471
|
-
function flatten(tokens, prefix = "", flat = false) {
|
|
3472
|
-
const flattened = [];
|
|
3473
|
-
const stack = [{
|
|
3474
|
-
tokens,
|
|
3475
|
-
prefix,
|
|
3476
|
-
flat
|
|
3477
|
-
}];
|
|
3478
|
-
while (stack.length > 0) {
|
|
3479
|
-
const { tokens: currentTokens, prefix: currentPrefix, flat: flat$1 } = stack.pop();
|
|
3480
|
-
const meta = {};
|
|
3481
|
-
for (const k in currentTokens) if (k.startsWith("$")) meta[k] = currentTokens[k];
|
|
3482
|
-
if (Object.keys(meta).length > 0 && currentPrefix) flattened.push({
|
|
3483
|
-
id: currentPrefix,
|
|
3484
|
-
value: meta
|
|
3485
|
-
});
|
|
3486
|
-
for (const key in currentTokens) {
|
|
3487
|
-
if (key.startsWith("$")) continue;
|
|
3488
|
-
const value = currentTokens[key];
|
|
3489
|
-
const id = currentPrefix ? `${currentPrefix}.${key}` : key;
|
|
3490
|
-
if (!/* @__PURE__ */ isObject(value)) {
|
|
3491
|
-
flattened.push({
|
|
3492
|
-
id,
|
|
3493
|
-
value
|
|
3494
|
-
});
|
|
3495
|
-
continue;
|
|
3496
|
-
}
|
|
3497
|
-
if ("$value" in value) {
|
|
3498
|
-
flattened.push({
|
|
3499
|
-
id,
|
|
3500
|
-
value
|
|
3501
|
-
});
|
|
3502
|
-
const inner = value.$value;
|
|
3503
|
-
if (/* @__PURE__ */ isObject(inner) && !flat$1) for (const innerKey in inner) {
|
|
3504
|
-
if (innerKey.startsWith("$")) continue;
|
|
3505
|
-
const child = inner[innerKey];
|
|
3506
|
-
const childId = `${id}.${innerKey}`;
|
|
3507
|
-
if (!/* @__PURE__ */ isObject(child)) flattened.push({
|
|
3508
|
-
id: childId,
|
|
3509
|
-
value: child
|
|
3510
|
-
});
|
|
3511
|
-
else if ("$value" in child) flattened.push({
|
|
3512
|
-
id: childId,
|
|
3513
|
-
value: child
|
|
3514
|
-
});
|
|
3515
|
-
else stack.push({
|
|
3516
|
-
tokens: child,
|
|
3517
|
-
prefix: childId,
|
|
3518
|
-
flat: flat$1
|
|
3519
|
-
});
|
|
3520
|
-
}
|
|
3521
|
-
continue;
|
|
3522
|
-
}
|
|
3523
|
-
if (flat$1) {
|
|
3524
|
-
flattened.push({
|
|
3525
|
-
id,
|
|
3526
|
-
value
|
|
3527
|
-
});
|
|
3528
|
-
continue;
|
|
3529
|
-
}
|
|
3530
|
-
stack.push({
|
|
3531
|
-
tokens: value,
|
|
3532
|
-
prefix: id,
|
|
3533
|
-
flat: flat$1
|
|
3534
|
-
});
|
|
3535
|
-
}
|
|
3536
|
-
}
|
|
3537
|
-
return flattened;
|
|
3538
|
-
}
|
|
3539
|
-
|
|
3540
|
-
//#endregion
|
|
3541
|
-
//#region src/composables/useLocale/adapters/v0.ts
|
|
3542
|
-
/**
|
|
3543
|
-
* Vuetify0.x locale adapter implementation
|
|
3544
|
-
*
|
|
3545
|
-
* This adapter provides translation and number formatting
|
|
3546
|
-
* capabilities using the Intl API and supports both
|
|
3547
|
-
* numbered ({0}, {1}) and named ({name}) variables in translation strings.
|
|
3548
|
-
*/
|
|
3549
|
-
var Vuetify0LocaleAdapter = class {
|
|
3550
|
-
t(message, ...params) {
|
|
3551
|
-
let resolvedMessage = message;
|
|
3552
|
-
if (params.length > 0 && /* @__PURE__ */ isObject(params[0])) {
|
|
3553
|
-
const variables = params[0];
|
|
3554
|
-
resolvedMessage = resolvedMessage.replace(/{([a-zA-Z][a-zA-Z0-9_]*)}/g, (match, name) => {
|
|
3555
|
-
return /* @__PURE__ */ isUndefined(variables[name]) ? match : String(variables[name]);
|
|
3556
|
-
});
|
|
3557
|
-
params = params.slice(1);
|
|
3558
|
-
}
|
|
3559
|
-
resolvedMessage = resolvedMessage.replace(/\{(\d+)\}/g, (match, index) => {
|
|
3560
|
-
const idx = Number.parseInt(index, 10);
|
|
3561
|
-
if (!/* @__PURE__ */ isUndefined(params[idx])) return String(params[idx]);
|
|
3562
|
-
return match;
|
|
3563
|
-
});
|
|
3564
|
-
return resolvedMessage;
|
|
3565
|
-
}
|
|
3566
|
-
n(value, locale, ...params) {
|
|
3567
|
-
if (!IN_BROWSER || !locale) return value.toString();
|
|
3568
|
-
const options = params[0];
|
|
3569
|
-
return new Intl.NumberFormat(String(locale), options).format(value);
|
|
3570
|
-
}
|
|
3571
|
-
};
|
|
3572
3769
|
|
|
3573
3770
|
//#endregion
|
|
3574
|
-
//#region src/composables/
|
|
3771
|
+
//#region src/composables/usePagination/index.ts
|
|
3575
3772
|
/**
|
|
3576
|
-
* @module
|
|
3773
|
+
* @module usePagination
|
|
3577
3774
|
*
|
|
3578
3775
|
* @remarks
|
|
3579
|
-
*
|
|
3776
|
+
* Lightweight pagination composable for navigating through pages.
|
|
3580
3777
|
*
|
|
3581
3778
|
* Key features:
|
|
3582
|
-
* -
|
|
3583
|
-
* -
|
|
3584
|
-
* -
|
|
3585
|
-
* -
|
|
3586
|
-
* -
|
|
3779
|
+
* - No registry overhead - just a bounded integer
|
|
3780
|
+
* - Direct ref support for v-model compatibility
|
|
3781
|
+
* - Navigation methods: next, prev, first, last
|
|
3782
|
+
* - Computed visible items with ellipsis
|
|
3783
|
+
* - Trinity pattern for dependency injection
|
|
3587
3784
|
*
|
|
3588
|
-
*
|
|
3785
|
+
* Unlike registry-based composables, pagination tracks a single number
|
|
3786
|
+
* within a range, making it efficient for large page counts.
|
|
3589
3787
|
*/
|
|
3590
3788
|
/**
|
|
3591
|
-
* Creates a
|
|
3789
|
+
* Creates a pagination instance.
|
|
3592
3790
|
*
|
|
3593
|
-
* @param options The options for the
|
|
3594
|
-
* @
|
|
3595
|
-
* @template E The type of the locale context.
|
|
3596
|
-
* @returns A new locale instance.
|
|
3791
|
+
* @param options The options for the pagination instance.
|
|
3792
|
+
* @returns A pagination context with navigation methods.
|
|
3597
3793
|
*
|
|
3598
|
-
* @
|
|
3794
|
+
* @example
|
|
3795
|
+
* ```ts
|
|
3796
|
+
* import { createPagination } from '@vuetify/v0'
|
|
3797
|
+
*
|
|
3798
|
+
* // Basic usage
|
|
3799
|
+
* const pagination = createPagination({ size: 100 })
|
|
3800
|
+
* pagination.next()
|
|
3801
|
+
* pagination.items.value // [{ type: 'page', value: 1 }, { type: 'page', value: 2 }, ...]
|
|
3802
|
+
*
|
|
3803
|
+
* // With v-model (pass a ref)
|
|
3804
|
+
* const page = ref(1)
|
|
3805
|
+
* const pagination = createPagination({ page, size: 100 })
|
|
3806
|
+
* // Mutating pagination.page or the passed ref syncs both
|
|
3807
|
+
* ```
|
|
3599
3808
|
*/
|
|
3600
|
-
function
|
|
3601
|
-
const {
|
|
3602
|
-
const
|
|
3603
|
-
const
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
if (
|
|
3809
|
+
function createPagination(_options = {}) {
|
|
3810
|
+
const { page: _page = 1, itemsPerPage: _itemsPerPage = 10, size: _size = 0, visible: _visible = 7, ellipsis = "..." } = _options;
|
|
3811
|
+
const page = isRef(_page) ? _page : shallowRef(_page);
|
|
3812
|
+
const pages = computed(() => {
|
|
3813
|
+
const size = toValue(_size);
|
|
3814
|
+
const perPage = toValue(_itemsPerPage);
|
|
3815
|
+
if (size <= 0 || /* @__PURE__ */ isNaN(size)) return 0;
|
|
3816
|
+
return Math.ceil(size / perPage);
|
|
3817
|
+
});
|
|
3818
|
+
function first() {
|
|
3819
|
+
page.value = 1;
|
|
3607
3820
|
}
|
|
3608
|
-
function
|
|
3609
|
-
|
|
3610
|
-
const args = toArray(params);
|
|
3611
|
-
if (!locale) return adapter.t(fallback ?? key, ...args);
|
|
3612
|
-
const path = `${locale}.${key}`;
|
|
3613
|
-
const message = tokens.get(path)?.value;
|
|
3614
|
-
const template = /* @__PURE__ */ isString(message) ? resolve(locale, message) : fallback ?? key;
|
|
3615
|
-
return adapter.t(template, ...args);
|
|
3821
|
+
function last() {
|
|
3822
|
+
page.value = Math.max(1, pages.value);
|
|
3616
3823
|
}
|
|
3617
|
-
function
|
|
3618
|
-
|
|
3824
|
+
function next() {
|
|
3825
|
+
if (page.value < pages.value) page.value++;
|
|
3619
3826
|
}
|
|
3620
|
-
function
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3827
|
+
function prev() {
|
|
3828
|
+
if (page.value > 1) page.value--;
|
|
3829
|
+
}
|
|
3830
|
+
function select(value) {
|
|
3831
|
+
if (value < 1) page.value = 1;
|
|
3832
|
+
else if (value > pages.value) page.value = Math.max(1, pages.value);
|
|
3833
|
+
else page.value = value;
|
|
3834
|
+
}
|
|
3835
|
+
const isFirst = computed(() => page.value <= 1);
|
|
3836
|
+
const isLast = computed(() => page.value >= pages.value);
|
|
3837
|
+
const pageStart = computed(() => (page.value - 1) * toValue(_itemsPerPage));
|
|
3838
|
+
const pageStop = computed(() => Math.min(pageStart.value + toValue(_itemsPerPage), toValue(_size)));
|
|
3839
|
+
function toPage(value) {
|
|
3840
|
+
return {
|
|
3841
|
+
type: "page",
|
|
3842
|
+
value
|
|
3843
|
+
};
|
|
3844
|
+
}
|
|
3845
|
+
function toEllipsis() {
|
|
3846
|
+
return ellipsis === false ? false : {
|
|
3847
|
+
type: "ellipsis",
|
|
3848
|
+
value: ellipsis
|
|
3849
|
+
};
|
|
3850
|
+
}
|
|
3851
|
+
function filter(array) {
|
|
3852
|
+
return array.filter((item) => item !== false);
|
|
3629
3853
|
}
|
|
3630
3854
|
return {
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3855
|
+
page,
|
|
3856
|
+
ellipsis,
|
|
3857
|
+
items: computed(() => {
|
|
3858
|
+
const pageCount = pages.value;
|
|
3859
|
+
const visible = toValue(_visible);
|
|
3860
|
+
const current = page.value;
|
|
3861
|
+
if (pageCount <= 0 || /* @__PURE__ */ isNaN(pageCount) || pageCount > Number.MAX_SAFE_INTEGER) return [];
|
|
3862
|
+
if (visible <= 0) return [];
|
|
3863
|
+
if (visible <= 2) return [toPage(current)];
|
|
3864
|
+
if (pageCount <= visible) return (/* @__PURE__ */ range(pageCount, 1)).map(toPage);
|
|
3865
|
+
if (visible === 3) {
|
|
3866
|
+
const mid = current <= 1 ? 2 : current >= pageCount ? pageCount - 1 : current;
|
|
3867
|
+
return [
|
|
3868
|
+
toPage(1),
|
|
3869
|
+
toPage(mid),
|
|
3870
|
+
toPage(pageCount)
|
|
3871
|
+
];
|
|
3872
|
+
}
|
|
3873
|
+
const boundary = visible - 2;
|
|
3874
|
+
const middle = visible - 4;
|
|
3875
|
+
if (middle <= 0) {
|
|
3876
|
+
if (current <= boundary) return filter([
|
|
3877
|
+
...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
|
|
3878
|
+
toEllipsis(),
|
|
3879
|
+
toPage(pageCount)
|
|
3880
|
+
]);
|
|
3881
|
+
if (current > pageCount - boundary) return filter([
|
|
3882
|
+
toPage(1),
|
|
3883
|
+
toEllipsis(),
|
|
3884
|
+
...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
|
|
3885
|
+
]);
|
|
3886
|
+
return current <= Math.ceil(pageCount / 2) ? filter([
|
|
3887
|
+
toPage(1),
|
|
3888
|
+
toPage(current),
|
|
3889
|
+
toEllipsis(),
|
|
3890
|
+
toPage(pageCount)
|
|
3891
|
+
]) : filter([
|
|
3892
|
+
toPage(1),
|
|
3893
|
+
toEllipsis(),
|
|
3894
|
+
toPage(current),
|
|
3895
|
+
toPage(pageCount)
|
|
3896
|
+
]);
|
|
3897
|
+
}
|
|
3898
|
+
const leftThreshold = boundary - 1;
|
|
3899
|
+
const rightThreshold = pageCount - boundary + 2;
|
|
3900
|
+
if (current <= leftThreshold) return filter([
|
|
3901
|
+
...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
|
|
3902
|
+
toEllipsis(),
|
|
3903
|
+
toPage(pageCount)
|
|
3904
|
+
]);
|
|
3905
|
+
else if (current >= rightThreshold) return filter([
|
|
3906
|
+
toPage(1),
|
|
3907
|
+
toEllipsis(),
|
|
3908
|
+
...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
|
|
3909
|
+
]);
|
|
3910
|
+
else {
|
|
3911
|
+
const start = current - Math.floor(middle / 2);
|
|
3912
|
+
return filter([
|
|
3913
|
+
toPage(1),
|
|
3914
|
+
toEllipsis(),
|
|
3915
|
+
...(/* @__PURE__ */ range(middle, start)).map(toPage),
|
|
3916
|
+
toEllipsis(),
|
|
3917
|
+
toPage(pageCount)
|
|
3918
|
+
]);
|
|
3919
|
+
}
|
|
3920
|
+
}),
|
|
3921
|
+
pageStart,
|
|
3922
|
+
pageStop,
|
|
3923
|
+
isFirst,
|
|
3924
|
+
isLast,
|
|
3925
|
+
first,
|
|
3926
|
+
last,
|
|
3927
|
+
next,
|
|
3928
|
+
prev,
|
|
3929
|
+
select,
|
|
3930
|
+
get itemsPerPage() {
|
|
3931
|
+
return toValue(_itemsPerPage);
|
|
3932
|
+
},
|
|
3634
3933
|
get size() {
|
|
3635
|
-
return
|
|
3934
|
+
return toValue(_size);
|
|
3935
|
+
},
|
|
3936
|
+
get pages() {
|
|
3937
|
+
return pages.value;
|
|
3636
3938
|
}
|
|
3637
3939
|
};
|
|
3638
3940
|
}
|
|
3639
|
-
function createLocaleFallback() {
|
|
3640
|
-
return {
|
|
3641
|
-
size: 0,
|
|
3642
|
-
t: (key, _params, fallback) => fallback ?? key,
|
|
3643
|
-
n: String
|
|
3644
|
-
};
|
|
3645
|
-
}
|
|
3646
3941
|
/**
|
|
3647
|
-
* Creates a
|
|
3648
|
-
*
|
|
3649
|
-
* @param options The options for the locale context.
|
|
3650
|
-
* @template Z The type of the locale ticket.
|
|
3651
|
-
* @template E The type of the locale context.
|
|
3652
|
-
* @returns A new locale context.
|
|
3942
|
+
* Creates a pagination context for dependency injection.
|
|
3653
3943
|
*
|
|
3654
|
-
* @
|
|
3944
|
+
* @param options The options including namespace.
|
|
3945
|
+
* @returns A trinity: [usePagination, providePagination, defaultContext]
|
|
3655
3946
|
*
|
|
3656
3947
|
* @example
|
|
3657
3948
|
* ```ts
|
|
3658
|
-
*
|
|
3949
|
+
* // With default namespace 'v0:pagination'
|
|
3950
|
+
* const [usePagination, providePaginationContext] = createPaginationContext({ size: 50 })
|
|
3659
3951
|
*
|
|
3660
|
-
*
|
|
3661
|
-
*
|
|
3662
|
-
*
|
|
3663
|
-
*
|
|
3664
|
-
* es: { hello: 'Hola' },
|
|
3665
|
-
* },
|
|
3952
|
+
* // Or with custom namespace
|
|
3953
|
+
* const [usePagination, providePaginationContext] = createPaginationContext({
|
|
3954
|
+
* namespace: 'my-pagination',
|
|
3955
|
+
* size: 50,
|
|
3666
3956
|
* })
|
|
3667
3957
|
*
|
|
3668
|
-
* //
|
|
3669
|
-
*
|
|
3958
|
+
* // Parent component
|
|
3959
|
+
* providePaginationContext()
|
|
3670
3960
|
*
|
|
3671
|
-
* //
|
|
3672
|
-
* const
|
|
3673
|
-
*
|
|
3961
|
+
* // Child component
|
|
3962
|
+
* const pagination = usePagination()
|
|
3963
|
+
* pagination.next()
|
|
3674
3964
|
* ```
|
|
3675
3965
|
*/
|
|
3676
|
-
function
|
|
3677
|
-
const { namespace = "v0:
|
|
3678
|
-
const [
|
|
3679
|
-
const context =
|
|
3680
|
-
function
|
|
3681
|
-
return
|
|
3966
|
+
function createPaginationContext(_options = {}) {
|
|
3967
|
+
const { namespace = "v0:pagination", ...options } = _options;
|
|
3968
|
+
const [usePaginationContext, _providePaginationContext] = createContext(namespace);
|
|
3969
|
+
const context = createPagination(options);
|
|
3970
|
+
function providePaginationContext(_context = context, app) {
|
|
3971
|
+
return _providePaginationContext(_context, app);
|
|
3682
3972
|
}
|
|
3683
|
-
return createTrinity(
|
|
3973
|
+
return createTrinity(usePaginationContext, providePaginationContext, context);
|
|
3684
3974
|
}
|
|
3685
3975
|
/**
|
|
3686
|
-
*
|
|
3976
|
+
* Returns the current pagination instance from context.
|
|
3687
3977
|
*
|
|
3688
|
-
* @param
|
|
3689
|
-
* @
|
|
3690
|
-
* @template E The type of the locale context.
|
|
3691
|
-
* @template R The type of the token ticket.
|
|
3692
|
-
* @template O The type of the token context.
|
|
3693
|
-
* @returns A new locale plugin.
|
|
3978
|
+
* @param namespace The namespace. @default 'v0:pagination'
|
|
3979
|
+
* @returns The pagination context.
|
|
3694
3980
|
*
|
|
3695
|
-
* @
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
const [, provideLocaleContext, context] = createLocaleContext({
|
|
3700
|
-
...options,
|
|
3701
|
-
namespace,
|
|
3702
|
-
adapter,
|
|
3703
|
-
messages
|
|
3704
|
-
});
|
|
3705
|
-
return createPlugin({
|
|
3706
|
-
namespace,
|
|
3707
|
-
provide: (app) => {
|
|
3708
|
-
provideLocaleContext(context, app);
|
|
3709
|
-
}
|
|
3710
|
-
});
|
|
3711
|
-
}
|
|
3712
|
-
/**
|
|
3713
|
-
* Returns the current locale instance.
|
|
3981
|
+
* @example
|
|
3982
|
+
* ```vue
|
|
3983
|
+
* <script setup lang="ts">
|
|
3984
|
+
* import { usePagination } from '@vuetify/v0'
|
|
3714
3985
|
*
|
|
3715
|
-
*
|
|
3986
|
+
* const pagination = usePagination()
|
|
3987
|
+
* <\/script>
|
|
3716
3988
|
*
|
|
3717
|
-
*
|
|
3989
|
+
* <template>
|
|
3990
|
+
* <button @click="pagination.prev()" :disabled="pagination.isFirst.value">Prev</button>
|
|
3991
|
+
* <button @click="pagination.next()" :disabled="pagination.isLast.value">Next</button>
|
|
3992
|
+
* </template>
|
|
3993
|
+
* ```
|
|
3718
3994
|
*/
|
|
3719
|
-
function
|
|
3720
|
-
|
|
3721
|
-
if (!getCurrentInstance()) return fallback;
|
|
3722
|
-
try {
|
|
3723
|
-
return useContext(namespace, fallback);
|
|
3724
|
-
} catch {
|
|
3725
|
-
return fallback;
|
|
3726
|
-
}
|
|
3995
|
+
function usePagination(namespace = "v0:pagination") {
|
|
3996
|
+
return useContext(namespace);
|
|
3727
3997
|
}
|
|
3728
3998
|
|
|
3729
3999
|
//#endregion
|
|
@@ -5134,7 +5404,7 @@ function useEventListener(target, event, listener, options) {
|
|
|
5134
5404
|
* @see https://0.vuetifyjs.com/composables/system/use-event-listener
|
|
5135
5405
|
*/
|
|
5136
5406
|
function useWindowEventListener(event, listener, options) {
|
|
5137
|
-
return useEventListener(window, event, listener, options);
|
|
5407
|
+
return IN_BROWSER ? useEventListener(window, event, listener, options) : () => {};
|
|
5138
5408
|
}
|
|
5139
5409
|
/**
|
|
5140
5410
|
* Attaches an event listener to the document.
|
|
@@ -5148,7 +5418,7 @@ function useWindowEventListener(event, listener, options) {
|
|
|
5148
5418
|
* @see https://0.vuetifyjs.com/composables/system/use-event-listener
|
|
5149
5419
|
*/
|
|
5150
5420
|
function useDocumentEventListener(event, listener, options) {
|
|
5151
|
-
return useEventListener(document, event, listener, options);
|
|
5421
|
+
return IN_BROWSER ? useEventListener(document, event, listener, options) : () => {};
|
|
5152
5422
|
}
|
|
5153
5423
|
|
|
5154
5424
|
//#endregion
|
|
@@ -5371,7 +5641,7 @@ function createBreakpointsPlugin(_options = {}) {
|
|
|
5371
5641
|
},
|
|
5372
5642
|
setup: (app) => {
|
|
5373
5643
|
app.mixin({ mounted() {
|
|
5374
|
-
if (this.$parent
|
|
5644
|
+
if (!/* @__PURE__ */ isNull(this.$parent)) return;
|
|
5375
5645
|
const hydration = useHydration();
|
|
5376
5646
|
function listener() {
|
|
5377
5647
|
context.update();
|
|
@@ -5416,6 +5686,236 @@ function useBreakpoints(namespace = "v0:breakpoints") {
|
|
|
5416
5686
|
return useContext(namespace);
|
|
5417
5687
|
}
|
|
5418
5688
|
|
|
5689
|
+
//#endregion
|
|
5690
|
+
//#region src/composables/useClickOutside/index.ts
|
|
5691
|
+
/**
|
|
5692
|
+
* @module useClickOutside
|
|
5693
|
+
*
|
|
5694
|
+
* @remarks
|
|
5695
|
+
* Detects clicks outside of specified element(s) with automatic cleanup.
|
|
5696
|
+
*
|
|
5697
|
+
* Key features:
|
|
5698
|
+
* - Two-phase detection (pointerdown → pointerup) prevents drag-out false positives
|
|
5699
|
+
* - Touch scroll threshold ignores swipes/scrolls on mobile
|
|
5700
|
+
* - Capture phase listeners work with stopPropagation
|
|
5701
|
+
* - Pause/resume/stop functionality
|
|
5702
|
+
* - Optional iframe focus detection
|
|
5703
|
+
* - SSR-safe (no-op when not in browser)
|
|
5704
|
+
*
|
|
5705
|
+
* Common use cases: closing popovers, dropdowns, modals, and menus.
|
|
5706
|
+
*
|
|
5707
|
+
* Accessibility: This composable handles pointer interactions only. For accessible
|
|
5708
|
+
* components (dialogs, popovers, menus), pair with `useKeydown` for Escape key
|
|
5709
|
+
* dismissal per WCAG/APG requirements.
|
|
5710
|
+
*/
|
|
5711
|
+
/**
|
|
5712
|
+
* Detects clicks outside of the specified element(s).
|
|
5713
|
+
*
|
|
5714
|
+
* Uses two-phase detection (pointerdown → pointerup) to prevent false positives
|
|
5715
|
+
* when users drag from inside to outside an element.
|
|
5716
|
+
*
|
|
5717
|
+
* @param target Element ref(s) to detect clicks outside of. Accepts a single ref/getter or array of refs/getters.
|
|
5718
|
+
* @param handler Callback invoked when a click outside is detected.
|
|
5719
|
+
* @param options Configuration options.
|
|
5720
|
+
* @returns An object with methods to control the listener.
|
|
5721
|
+
*
|
|
5722
|
+
* @see https://0.vuetifyjs.com/composables/system/use-click-outside
|
|
5723
|
+
*
|
|
5724
|
+
* @example Native element ref
|
|
5725
|
+
* ```ts
|
|
5726
|
+
* const menuRef = useTemplateRef<HTMLElement>('menu')
|
|
5727
|
+
*
|
|
5728
|
+
* useClickOutside(menuRef, () => { isOpen.value = false })
|
|
5729
|
+
* ```
|
|
5730
|
+
*
|
|
5731
|
+
* @example Component ref (e.g., Atom)
|
|
5732
|
+
* ```ts
|
|
5733
|
+
* const atomRef = useTemplateRef<AtomExpose>('atom')
|
|
5734
|
+
*
|
|
5735
|
+
* // Pass the exposed element TemplateRef via getter
|
|
5736
|
+
* useClickOutside(
|
|
5737
|
+
* () => atomRef.value?.element,
|
|
5738
|
+
* () => { isOpen.value = false }
|
|
5739
|
+
* )
|
|
5740
|
+
* ```
|
|
5741
|
+
*
|
|
5742
|
+
* @example Multiple targets
|
|
5743
|
+
* ```ts
|
|
5744
|
+
* const popoverRef = useTemplateRef<AtomExpose>('popover')
|
|
5745
|
+
* const anchorRef = useTemplateRef<HTMLElement>('anchor')
|
|
5746
|
+
*
|
|
5747
|
+
* useClickOutside(
|
|
5748
|
+
* [() => popoverRef.value?.element, anchorRef],
|
|
5749
|
+
* () => { isOpen.value = false }
|
|
5750
|
+
* )
|
|
5751
|
+
* ```
|
|
5752
|
+
*
|
|
5753
|
+
* @example Ignoring elements (CSS selectors or refs)
|
|
5754
|
+
* ```ts
|
|
5755
|
+
* useClickOutside(
|
|
5756
|
+
* () => navRef.value?.element,
|
|
5757
|
+
* () => { isOpen.value = false },
|
|
5758
|
+
* { ignore: ['[data-app-bar]'] }
|
|
5759
|
+
* )
|
|
5760
|
+
* ```
|
|
5761
|
+
*/
|
|
5762
|
+
function useClickOutside(target, handler, options = {}) {
|
|
5763
|
+
const { capture = true, touchScrollThreshold = 30, detectIframe = false, ignore = [] } = options;
|
|
5764
|
+
const isPaused = shallowRef(false);
|
|
5765
|
+
const isActive = toRef(() => !isPaused.value);
|
|
5766
|
+
let initialTarget = null;
|
|
5767
|
+
let startPosition = {
|
|
5768
|
+
x: 0,
|
|
5769
|
+
y: 0
|
|
5770
|
+
};
|
|
5771
|
+
let cleanupPointerDown;
|
|
5772
|
+
let cleanupPointerUp;
|
|
5773
|
+
let cleanupBlur;
|
|
5774
|
+
/**
|
|
5775
|
+
* Resolve target(s) to an array of HTMLElements.
|
|
5776
|
+
*/
|
|
5777
|
+
function getTargets() {
|
|
5778
|
+
return toArray(target).map((source) => toValue(source)).filter((el) => !/* @__PURE__ */ isNullOrUndefined(el));
|
|
5779
|
+
}
|
|
5780
|
+
/**
|
|
5781
|
+
* Resolve ignore targets to a tuple of [selectors, elements].
|
|
5782
|
+
* Called once per event to avoid repeated toValue() calls in hot path.
|
|
5783
|
+
*/
|
|
5784
|
+
function resolveIgnoreTargets() {
|
|
5785
|
+
const ignoreTargets = toValue(ignore);
|
|
5786
|
+
if (ignoreTargets.length === 0) return [[], []];
|
|
5787
|
+
const selectors = [];
|
|
5788
|
+
const elements = [];
|
|
5789
|
+
for (const ignoreTarget of ignoreTargets) if (/* @__PURE__ */ isString(ignoreTarget)) selectors.push(ignoreTarget);
|
|
5790
|
+
else {
|
|
5791
|
+
const ignoreEl = toValue(ignoreTarget);
|
|
5792
|
+
if (ignoreEl) elements.push(ignoreEl);
|
|
5793
|
+
}
|
|
5794
|
+
return [selectors, elements];
|
|
5795
|
+
}
|
|
5796
|
+
/**
|
|
5797
|
+
* Check if an element matches resolved ignore targets.
|
|
5798
|
+
*/
|
|
5799
|
+
function isIgnored(el, selectors, elements) {
|
|
5800
|
+
if (!el) return false;
|
|
5801
|
+
for (const selector of selectors) try {
|
|
5802
|
+
if (el.matches(selector) || !/* @__PURE__ */ isNull(el.closest(selector))) return true;
|
|
5803
|
+
} catch {}
|
|
5804
|
+
for (const ignoreEl of elements) if (ignoreEl === el || ignoreEl.contains(el)) return true;
|
|
5805
|
+
return false;
|
|
5806
|
+
}
|
|
5807
|
+
/**
|
|
5808
|
+
* Check if any element in the event path should be ignored.
|
|
5809
|
+
*/
|
|
5810
|
+
function shouldIgnore(path) {
|
|
5811
|
+
const [selectors, elements] = resolveIgnoreTargets();
|
|
5812
|
+
if (selectors.length === 0 && elements.length === 0) return false;
|
|
5813
|
+
return path.some((node) => node instanceof Element && isIgnored(node, selectors, elements));
|
|
5814
|
+
}
|
|
5815
|
+
/**
|
|
5816
|
+
* Check if the event target is outside all target elements.
|
|
5817
|
+
*/
|
|
5818
|
+
function isOutside(eventTarget) {
|
|
5819
|
+
if (!eventTarget) return false;
|
|
5820
|
+
if (!(eventTarget instanceof Node)) return false;
|
|
5821
|
+
const targets = getTargets();
|
|
5822
|
+
if (targets.length === 0) return false;
|
|
5823
|
+
return targets.every((el) => {
|
|
5824
|
+
return el !== eventTarget && !el.contains(eventTarget);
|
|
5825
|
+
});
|
|
5826
|
+
}
|
|
5827
|
+
/**
|
|
5828
|
+
* Validate that the target is still in the DOM.
|
|
5829
|
+
*/
|
|
5830
|
+
function isValidTarget(eventTarget) {
|
|
5831
|
+
if (!(eventTarget instanceof Element)) return false;
|
|
5832
|
+
if (!eventTarget.isConnected) return false;
|
|
5833
|
+
return true;
|
|
5834
|
+
}
|
|
5835
|
+
/**
|
|
5836
|
+
* Handle pointerdown - store initial target and position.
|
|
5837
|
+
*/
|
|
5838
|
+
function onPointerDown(event) {
|
|
5839
|
+
if (isPaused.value) return;
|
|
5840
|
+
if (event.defaultPrevented) return;
|
|
5841
|
+
initialTarget = event.composedPath()[0] ?? event.target;
|
|
5842
|
+
startPosition = {
|
|
5843
|
+
x: event.clientX,
|
|
5844
|
+
y: event.clientY
|
|
5845
|
+
};
|
|
5846
|
+
}
|
|
5847
|
+
/**
|
|
5848
|
+
* Handle pointerup - check if it's an outside click.
|
|
5849
|
+
*/
|
|
5850
|
+
function onPointerUp(event) {
|
|
5851
|
+
if (isPaused.value) return;
|
|
5852
|
+
if (event.defaultPrevented) return;
|
|
5853
|
+
if (!initialTarget) return;
|
|
5854
|
+
const pointerdownTarget = initialTarget;
|
|
5855
|
+
initialTarget = null;
|
|
5856
|
+
if (!isValidTarget(pointerdownTarget)) return;
|
|
5857
|
+
const path = event.composedPath();
|
|
5858
|
+
const pointerupTarget = path[0] ?? event.target;
|
|
5859
|
+
if (event.pointerType === "touch") {
|
|
5860
|
+
const dx = Math.abs(event.clientX - startPosition.x);
|
|
5861
|
+
const dy = Math.abs(event.clientY - startPosition.y);
|
|
5862
|
+
if (dx >= touchScrollThreshold || dy >= touchScrollThreshold) return;
|
|
5863
|
+
}
|
|
5864
|
+
if (isOutside(pointerdownTarget) && isOutside(pointerupTarget) && !shouldIgnore(path)) handler(event);
|
|
5865
|
+
}
|
|
5866
|
+
/**
|
|
5867
|
+
* Handle window blur - detect focus moving to iframe.
|
|
5868
|
+
*/
|
|
5869
|
+
function onBlur(event) {
|
|
5870
|
+
if (isPaused.value) return;
|
|
5871
|
+
if (event.defaultPrevented) return;
|
|
5872
|
+
if (document.activeElement instanceof HTMLIFrameElement) {
|
|
5873
|
+
const iframeIsOutside = getTargets().every((el) => !el.contains(document.activeElement));
|
|
5874
|
+
const [selectors, elements] = resolveIgnoreTargets();
|
|
5875
|
+
if (iframeIsOutside && !isIgnored(document.activeElement, selectors, elements)) handler(event);
|
|
5876
|
+
}
|
|
5877
|
+
}
|
|
5878
|
+
function setup() {
|
|
5879
|
+
cleanupPointerDown = useDocumentEventListener("pointerdown", onPointerDown, capture);
|
|
5880
|
+
cleanupPointerUp = useDocumentEventListener("pointerup", onPointerUp, capture);
|
|
5881
|
+
if (!detectIframe) return;
|
|
5882
|
+
cleanupBlur = useWindowEventListener("blur", onBlur, capture);
|
|
5883
|
+
}
|
|
5884
|
+
function cleanup() {
|
|
5885
|
+
cleanupPointerDown?.();
|
|
5886
|
+
cleanupPointerUp?.();
|
|
5887
|
+
cleanupBlur?.();
|
|
5888
|
+
cleanupPointerDown = void 0;
|
|
5889
|
+
cleanupPointerUp = void 0;
|
|
5890
|
+
cleanupBlur = void 0;
|
|
5891
|
+
}
|
|
5892
|
+
function pause() {
|
|
5893
|
+
if (isPaused.value) return;
|
|
5894
|
+
isPaused.value = true;
|
|
5895
|
+
initialTarget = null;
|
|
5896
|
+
cleanup();
|
|
5897
|
+
}
|
|
5898
|
+
function resume() {
|
|
5899
|
+
if (!isPaused.value) return;
|
|
5900
|
+
isPaused.value = false;
|
|
5901
|
+
setup();
|
|
5902
|
+
}
|
|
5903
|
+
function stop() {
|
|
5904
|
+
isPaused.value = true;
|
|
5905
|
+
initialTarget = null;
|
|
5906
|
+
cleanup();
|
|
5907
|
+
}
|
|
5908
|
+
setup();
|
|
5909
|
+
onScopeDispose(stop, true);
|
|
5910
|
+
return {
|
|
5911
|
+
isActive: shallowReadonly(isActive),
|
|
5912
|
+
isPaused: shallowReadonly(isPaused),
|
|
5913
|
+
pause,
|
|
5914
|
+
resume,
|
|
5915
|
+
stop
|
|
5916
|
+
};
|
|
5917
|
+
}
|
|
5918
|
+
|
|
5419
5919
|
//#endregion
|
|
5420
5920
|
//#region src/composables/useFeatures/index.ts
|
|
5421
5921
|
/**
|
|
@@ -5613,7 +6113,7 @@ function useFeatures(namespace = "v0:features") {
|
|
|
5613
6113
|
* Filters arrays based on query strings with configurable matching strategies.
|
|
5614
6114
|
*/
|
|
5615
6115
|
function defaultFilter(query, item, keys, mode = "some") {
|
|
5616
|
-
const queries =
|
|
6116
|
+
const queries = toArray(query).map((q) => String(q).toLowerCase());
|
|
5617
6117
|
function match(value, q) {
|
|
5618
6118
|
return String(value).toLowerCase().includes(q);
|
|
5619
6119
|
}
|
|
@@ -5656,7 +6156,7 @@ function createFilter(options = {}) {
|
|
|
5656
6156
|
return { items: computed(() => {
|
|
5657
6157
|
const q = toValue(queryRef);
|
|
5658
6158
|
query.value = q;
|
|
5659
|
-
const queries = (
|
|
6159
|
+
const queries = toArray(q).filter((q$1) => String(q$1).trim());
|
|
5660
6160
|
if (queries.length === 0) return itemsRef.value;
|
|
5661
6161
|
const queryParam = queries.length === 1 ? queries[0] : queries;
|
|
5662
6162
|
return itemsRef.value.filter((item) => filterFunction(queryParam, item));
|
|
@@ -5819,7 +6319,7 @@ function createForm(options) {
|
|
|
5819
6319
|
return parse(validateOn).includes(event);
|
|
5820
6320
|
}
|
|
5821
6321
|
const isValidating = computed(() => {
|
|
5822
|
-
for (const ticket of registry.
|
|
6322
|
+
for (const ticket of registry.values()) if (ticket.isValidating.value) return true;
|
|
5823
6323
|
return false;
|
|
5824
6324
|
});
|
|
5825
6325
|
const isValid = computed(() => {
|
|
@@ -5827,7 +6327,7 @@ function createForm(options) {
|
|
|
5827
6327
|
for (const ticket of registry.values()) {
|
|
5828
6328
|
hasFields = true;
|
|
5829
6329
|
if (ticket.isValid.value === false) return false;
|
|
5830
|
-
if (ticket.isValid.value
|
|
6330
|
+
if (/* @__PURE__ */ isNull(ticket.isValid.value)) return null;
|
|
5831
6331
|
}
|
|
5832
6332
|
return hasFields ? true : null;
|
|
5833
6333
|
});
|
|
@@ -5843,7 +6343,7 @@ function createForm(options) {
|
|
|
5843
6343
|
return validating.map((id$1) => registry.get(id$1)).filter(Boolean).every((ticket) => ticket.isValid.value === true);
|
|
5844
6344
|
}
|
|
5845
6345
|
function register(registration) {
|
|
5846
|
-
const model = shallowRef(registration.value
|
|
6346
|
+
const model = shallowRef(/* @__PURE__ */ isNullOrUndefined(registration.value) ? "" : toValue(registration.value));
|
|
5847
6347
|
const rules = registration.rules || [];
|
|
5848
6348
|
const errors = shallowRef([]);
|
|
5849
6349
|
const isValidating$1 = shallowRef(false);
|
|
@@ -6042,12 +6542,14 @@ function useForm(namespace = "v0:form") {
|
|
|
6042
6542
|
*/
|
|
6043
6543
|
function useIntersectionObserver(target, callback, options = {}) {
|
|
6044
6544
|
const { isHydrated } = useHydration();
|
|
6545
|
+
const targetRef = isRef(target) ? target : shallowRef(target);
|
|
6045
6546
|
const observer = shallowRef();
|
|
6046
6547
|
const isPaused = shallowRef(false);
|
|
6047
6548
|
const isIntersecting = shallowRef(false);
|
|
6048
6549
|
const isActive = toRef(() => !!observer.value);
|
|
6049
6550
|
function setup() {
|
|
6050
|
-
if (
|
|
6551
|
+
if (/* @__PURE__ */ isNull(observer.value)) return;
|
|
6552
|
+
if (!isHydrated.value || !SUPPORTS_INTERSECTION_OBSERVER || !targetRef.value || isPaused.value) return;
|
|
6051
6553
|
observer.value = new IntersectionObserver((entries) => {
|
|
6052
6554
|
const transformedEntries = entries.map((entry) => ({
|
|
6053
6555
|
boundingClientRect: entry.boundingClientRect,
|
|
@@ -6061,26 +6563,29 @@ function useIntersectionObserver(target, callback, options = {}) {
|
|
|
6061
6563
|
const latestEntry = transformedEntries.at(-1);
|
|
6062
6564
|
if (latestEntry) isIntersecting.value = latestEntry.isIntersecting;
|
|
6063
6565
|
callback(transformedEntries);
|
|
6566
|
+
if (options.once && latestEntry?.isIntersecting) stop();
|
|
6064
6567
|
}, {
|
|
6065
6568
|
root: options.root || null,
|
|
6066
6569
|
rootMargin: options.rootMargin || "0px",
|
|
6067
6570
|
threshold: options.threshold || 0
|
|
6068
6571
|
});
|
|
6069
|
-
observer.value.observe(
|
|
6572
|
+
observer.value.observe(targetRef.value);
|
|
6070
6573
|
if (options.immediate) callback([{
|
|
6071
|
-
boundingClientRect:
|
|
6574
|
+
boundingClientRect: targetRef.value.getBoundingClientRect(),
|
|
6072
6575
|
intersectionRatio: 0,
|
|
6073
6576
|
intersectionRect: new DOMRect(0, 0, 0, 0),
|
|
6074
6577
|
isIntersecting: false,
|
|
6075
6578
|
rootBounds: null,
|
|
6076
|
-
target:
|
|
6579
|
+
target: targetRef.value,
|
|
6077
6580
|
time: performance.now()
|
|
6078
6581
|
}]);
|
|
6079
6582
|
}
|
|
6080
|
-
|
|
6583
|
+
watchEffect(() => {
|
|
6584
|
+
const hydrated = isHydrated.value;
|
|
6585
|
+
const target$1 = targetRef.value;
|
|
6081
6586
|
cleanup();
|
|
6082
|
-
setup();
|
|
6083
|
-
}
|
|
6587
|
+
if (hydrated && target$1) setup();
|
|
6588
|
+
});
|
|
6084
6589
|
function cleanup() {
|
|
6085
6590
|
if (observer.value) {
|
|
6086
6591
|
observer.value.disconnect();
|
|
@@ -6098,6 +6603,7 @@ function useIntersectionObserver(target, callback, options = {}) {
|
|
|
6098
6603
|
}
|
|
6099
6604
|
function stop() {
|
|
6100
6605
|
cleanup();
|
|
6606
|
+
observer.value = null;
|
|
6101
6607
|
}
|
|
6102
6608
|
onScopeDispose(stop, true);
|
|
6103
6609
|
return {
|
|
@@ -6194,6 +6700,10 @@ function useElementIntersection(target, options = {}) {
|
|
|
6194
6700
|
* ```ts
|
|
6195
6701
|
* import { useKeydown } from '@vuetify/v0'
|
|
6196
6702
|
*
|
|
6703
|
+
* // Single handler
|
|
6704
|
+
* useKeydown({ key: 'Escape', handler: () => console.log('Escape pressed') })
|
|
6705
|
+
*
|
|
6706
|
+
* // Multiple handlers
|
|
6197
6707
|
* const { isActive, start, stop } = useKeydown([
|
|
6198
6708
|
* { key: 'Enter', handler: () => console.log('Enter pressed') },
|
|
6199
6709
|
* { key: 'Escape', handler: () => console.log('Escape pressed'), preventDefault: true },
|
|
@@ -6209,8 +6719,7 @@ function useKeydown(handlers) {
|
|
|
6209
6719
|
let cleanup = null;
|
|
6210
6720
|
const isActive = toRef(() => !!cleanup);
|
|
6211
6721
|
function onKeydown(event) {
|
|
6212
|
-
const
|
|
6213
|
-
const handler = (Array.isArray(handlerList) ? handlerList : [handlerList]).find((h) => h.key === event.key);
|
|
6722
|
+
const handler = toArray(toValue(handlers)).find((h) => h.key === event.key);
|
|
6214
6723
|
if (handler) {
|
|
6215
6724
|
if (handler.preventDefault) event.preventDefault();
|
|
6216
6725
|
if (handler.stopPropagation) event.stopPropagation();
|
|
@@ -6300,7 +6809,7 @@ function useMutationObserver(target, callback, options = {}) {
|
|
|
6300
6809
|
const { isHydrated } = useHydration();
|
|
6301
6810
|
const observer = shallowRef();
|
|
6302
6811
|
const isPaused = shallowRef(false);
|
|
6303
|
-
const isActive =
|
|
6812
|
+
const isActive = toRef(() => !!observer.value);
|
|
6304
6813
|
const observerOptions = {
|
|
6305
6814
|
childList: options.childList ?? true,
|
|
6306
6815
|
attributes: options.attributes ?? false,
|
|
@@ -6311,6 +6820,7 @@ function useMutationObserver(target, callback, options = {}) {
|
|
|
6311
6820
|
attributeFilter: options.attributeFilter
|
|
6312
6821
|
};
|
|
6313
6822
|
function setup() {
|
|
6823
|
+
if (/* @__PURE__ */ isNull(observer.value)) return;
|
|
6314
6824
|
if (!isHydrated.value || !SUPPORTS_MUTATION_OBSERVER || !target.value || isPaused.value) return;
|
|
6315
6825
|
observer.value = new MutationObserver((mutations) => {
|
|
6316
6826
|
callback(mutations.map((mutation) => ({
|
|
@@ -6324,6 +6834,7 @@ function useMutationObserver(target, callback, options = {}) {
|
|
|
6324
6834
|
attributeNamespace: mutation.attributeNamespace,
|
|
6325
6835
|
oldValue: mutation.oldValue
|
|
6326
6836
|
})));
|
|
6837
|
+
if (options.once) stop();
|
|
6327
6838
|
});
|
|
6328
6839
|
observer.value.observe(target.value, observerOptions);
|
|
6329
6840
|
if (options.immediate) {
|
|
@@ -6344,12 +6855,15 @@ function useMutationObserver(target, callback, options = {}) {
|
|
|
6344
6855
|
attributeNamespace: null,
|
|
6345
6856
|
oldValue: null
|
|
6346
6857
|
}]);
|
|
6858
|
+
if (options.once) stop();
|
|
6347
6859
|
}
|
|
6348
6860
|
}
|
|
6349
|
-
|
|
6861
|
+
watchEffect(() => {
|
|
6862
|
+
const hydrated = isHydrated.value;
|
|
6863
|
+
const el = target.value;
|
|
6350
6864
|
cleanup();
|
|
6351
|
-
setup();
|
|
6352
|
-
}
|
|
6865
|
+
if (hydrated && el) setup();
|
|
6866
|
+
});
|
|
6353
6867
|
function cleanup() {
|
|
6354
6868
|
if (observer.value) {
|
|
6355
6869
|
observer.value.disconnect();
|
|
@@ -6366,6 +6880,7 @@ function useMutationObserver(target, callback, options = {}) {
|
|
|
6366
6880
|
}
|
|
6367
6881
|
function stop() {
|
|
6368
6882
|
cleanup();
|
|
6883
|
+
observer.value = null;
|
|
6369
6884
|
}
|
|
6370
6885
|
onScopeDispose(stop, true);
|
|
6371
6886
|
return {
|
|
@@ -7014,10 +7529,12 @@ var Vuetify0ThemeAdapter = class extends ThemeAdapter {
|
|
|
7014
7529
|
}
|
|
7015
7530
|
upsert(styles) {
|
|
7016
7531
|
if (!IN_BROWSER) return;
|
|
7017
|
-
|
|
7532
|
+
const selector = this.stylesheetId.startsWith("#") ? this.stylesheetId : `#${this.stylesheetId}`;
|
|
7533
|
+
const id = this.stylesheetId.startsWith("#") ? this.stylesheetId.slice(1) : this.stylesheetId;
|
|
7534
|
+
let styleEl = document.querySelector(selector);
|
|
7018
7535
|
if (!styleEl) {
|
|
7019
7536
|
styleEl = document.createElement("style");
|
|
7020
|
-
styleEl.id =
|
|
7537
|
+
styleEl.id = id;
|
|
7021
7538
|
if (this.cspNonce) styleEl.setAttribute("nonce", this.cspNonce);
|
|
7022
7539
|
document.head.append(styleEl);
|
|
7023
7540
|
}
|
|
@@ -7772,7 +8289,7 @@ function useVirtual(items, _options = {}) {
|
|
|
7772
8289
|
cancelAnimationFrame(raf);
|
|
7773
8290
|
cancelAnimationFrame(rebuildRaf);
|
|
7774
8291
|
cancelAnimationFrame(edgeRaf);
|
|
7775
|
-
});
|
|
8292
|
+
}, true);
|
|
7776
8293
|
return {
|
|
7777
8294
|
element,
|
|
7778
8295
|
items: computedItems,
|
|
@@ -7788,4 +8305,4 @@ function useVirtual(items, _options = {}) {
|
|
|
7788
8305
|
}
|
|
7789
8306
|
|
|
7790
8307
|
//#endregion
|
|
7791
|
-
export { Atom_default as Atom, Avatar, AvatarFallback_default as AvatarFallback, AvatarImage_default as AvatarImage, AvatarRoot_default as AvatarRoot, COMMON_ELEMENTS, ConsolaLoggerAdapter, ExpansionPanel, ExpansionPanelActivator_default as ExpansionPanelActivator, ExpansionPanelContent_default as ExpansionPanelContent, ExpansionPanelHeader_default as ExpansionPanelHeader, ExpansionPanelItem_default as ExpansionPanelItem, ExpansionPanelRoot_default as ExpansionPanelRoot, Group, GroupItem_default as GroupItem, GroupRoot_default as GroupRoot, IN_BROWSER, MemoryAdapter, Pagination, PaginationEllipsis_default as PaginationEllipsis, PaginationFirst_default as PaginationFirst, PaginationItem_default as PaginationItem, PaginationLast_default as PaginationLast, PaginationNext_default as PaginationNext, PaginationPrev_default as PaginationPrev, PaginationRoot_default as PaginationRoot, PaginationStatus_default as PaginationStatus, PermissionAdapter, PinoLoggerAdapter, Popover, PopoverAnchor_default as PopoverAnchor, PopoverContent_default as PopoverContent, PopoverRoot_default as PopoverRoot, SELF_CLOSING_TAGS, SUPPORTS_INTERSECTION_OBSERVER, SUPPORTS_MATCH_MEDIA, SUPPORTS_MUTATION_OBSERVER, SUPPORTS_OBSERVER, SUPPORTS_TOUCH, Selection, SelectionItem_default as SelectionItem, SelectionRoot_default as SelectionRoot, Single, SingleItem_default as SingleItem, SingleRoot_default as SingleRoot, Step, StepItem_default as StepItem, StepRoot_default as StepRoot, Vuetify0LocaleAdapter, Vuetify0LoggerAdapter, Vuetify0ThemeAdapter, __LOGGER_ENABLED__, clamp, createBreakpoints, createBreakpointsContext, createBreakpointsPlugin, createContext, createFallbackHydration, createFeatures, createFeaturesContext, createFeaturesPlugin, createFilter, createFilterContext, createForm, createFormContext, createGroup, createGroupContext, createHydration, createHydrationContext, createHydrationPlugin, createLocale, createLocaleContext, createLocaleFallback, createLocalePlugin, createLogger, createLoggerContext, createLoggerPlugin, createOverflow, createOverflowContext, createPagination, createPaginationContext, createPermissions, createPermissionsContext, createPermissionsPlugin, createPlugin, createQueue, createQueueContext, createRegistryContext, createSelection, createSelectionContext, createSingle, createSingleContext, createStep, createStepContext, createStorage, createStorageContext, createStoragePlugin, createTheme, createThemeContext, createThemePlugin, createTimeline, createTimelineContext, createTokens, createTokensContext, createTrinity, debounce, genId, isArray, isBoolean, isFunction, isNaN, isNull, isNullOrUndefined, isNumber, isObject, isPrimitive, isSelfClosingTag, isString, isSymbol, isUndefined, mergeDeep, provideAvatarContext, provideContext, provideExpansionPanelItem, provideExpansionPanelSelection, provideGroupRoot, providePaginationControls, providePaginationItems, providePaginationRoot, providePopoverContext, provideSelectionRoot, provideSingleRoot, provideStepRoot, range, toArray, toReactive, useAvatarRoot, useBreakpoints, useContext, useDocumentEventListener, useElementIntersection, useElementSize, useEventListener, useExpansionPanelItem, useExpansionPanelRoot, useFeatures, useFilter, useFilterContext, useForm, useGroup, useGroupRoot, useHydration, useIntersectionObserver, useKeydown, useLocale, useLogger, useMutationObserver, useOverflow, usePagination, usePaginationControls, usePaginationItems, usePaginationRoot, usePermissions, usePopoverContext, useProxyModel, useProxyRegistry, useQueue, useRegistry, useResizeObserver, useSelection, useSelectionRoot, useSingle, useSingleRoot, useStep, useStepRoot, useStorage, useTheme, useTimeline, useToggleScope, useTokens, useVirtual, useWindowEventListener, version };
|
|
8308
|
+
export { Atom_default as Atom, Avatar, AvatarFallback_default as AvatarFallback, AvatarImage_default as AvatarImage, AvatarRoot_default as AvatarRoot, COMMON_ELEMENTS, ConsolaLoggerAdapter, ExpansionPanel, ExpansionPanelActivator_default as ExpansionPanelActivator, ExpansionPanelContent_default as ExpansionPanelContent, ExpansionPanelHeader_default as ExpansionPanelHeader, ExpansionPanelItem_default as ExpansionPanelItem, ExpansionPanelRoot_default as ExpansionPanelRoot, Group, GroupItem_default as GroupItem, GroupRoot_default as GroupRoot, IN_BROWSER, MemoryAdapter, Pagination, PaginationEllipsis_default as PaginationEllipsis, PaginationFirst_default as PaginationFirst, PaginationItem_default as PaginationItem, PaginationLast_default as PaginationLast, PaginationNext_default as PaginationNext, PaginationPrev_default as PaginationPrev, PaginationRoot_default as PaginationRoot, PaginationStatus_default as PaginationStatus, PermissionAdapter, PinoLoggerAdapter, Popover, PopoverAnchor_default as PopoverAnchor, PopoverContent_default as PopoverContent, PopoverRoot_default as PopoverRoot, SELF_CLOSING_TAGS, SUPPORTS_INTERSECTION_OBSERVER, SUPPORTS_MATCH_MEDIA, SUPPORTS_MUTATION_OBSERVER, SUPPORTS_OBSERVER, SUPPORTS_TOUCH, Selection, SelectionItem_default as SelectionItem, SelectionRoot_default as SelectionRoot, Single, SingleItem_default as SingleItem, SingleRoot_default as SingleRoot, Step, StepItem_default as StepItem, StepRoot_default as StepRoot, Vuetify0LocaleAdapter, Vuetify0LoggerAdapter, Vuetify0ThemeAdapter, __LOGGER_ENABLED__, clamp, createBreakpoints, createBreakpointsContext, createBreakpointsPlugin, createContext, createFallbackHydration, createFeatures, createFeaturesContext, createFeaturesPlugin, createFilter, createFilterContext, createForm, createFormContext, createGroup, createGroupContext, createHydration, createHydrationContext, createHydrationPlugin, createLocale, createLocaleContext, createLocaleFallback, createLocalePlugin, createLogger, createLoggerContext, createLoggerPlugin, createOverflow, createOverflowContext, createPagination, createPaginationContext, createPermissions, createPermissionsContext, createPermissionsPlugin, createPlugin, createQueue, createQueueContext, createRegistryContext, createSelection, createSelectionContext, createSingle, createSingleContext, createStep, createStepContext, createStorage, createStorageContext, createStoragePlugin, createTheme, createThemeContext, createThemePlugin, createTimeline, createTimelineContext, createTokens, createTokensContext, createTrinity, debounce, genId, isArray, isBoolean, isFunction, isNaN, isNull, isNullOrUndefined, isNumber, isObject, isPrimitive, isSelfClosingTag, isString, isSymbol, isUndefined, mergeDeep, provideAvatarContext, provideContext, provideExpansionPanelItem, provideExpansionPanelSelection, provideGroupRoot, providePaginationControls, providePaginationItems, providePaginationRoot, providePopoverContext, provideSelectionRoot, provideSingleRoot, provideStepRoot, range, toArray, toReactive, useAvatarRoot, useBreakpoints, useClickOutside, useContext, useDocumentEventListener, useElementIntersection, useElementSize, useEventListener, useExpansionPanelItem, useExpansionPanelRoot, useFeatures, useFilter, useFilterContext, useForm, useGroup, useGroupRoot, useHydration, useIntersectionObserver, useKeydown, useLocale, useLogger, useMutationObserver, useOverflow, usePagination, usePaginationControls, usePaginationItems, usePaginationRoot, usePermissions, usePopoverContext, useProxyModel, useProxyRegistry, useQueue, useRegistry, useResizeObserver, useSelection, useSelectionRoot, useSingle, useSingleRoot, useStep, useStepRoot, useStorage, useTheme, useTimeline, useToggleScope, useTokens, useVirtual, useWindowEventListener, version };
|