@vuetify/v0 0.0.2-beta.3 → 0.0.2
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 +252 -0
- package/dist/{index.mjs → browser/index.js} +616 -81
- package/dist/components/index.d.ts +7 -0
- package/dist/components/index.js +9 -0
- package/dist/components-Cc48kKWf.js +551 -0
- package/dist/composables/index.d.ts +7 -0
- package/dist/composables/index.js +8 -0
- package/dist/composables-7Cbs2sdO.js +1003 -0
- package/dist/constants/index.d.ts +2 -0
- package/dist/constants/index.js +5 -0
- package/dist/constants-DiTCgvMU.js +1 -0
- package/dist/factories/index.d.ts +2 -0
- package/dist/factories/index.js +3 -0
- package/dist/factories-CPq2yMlr.js +79 -0
- package/dist/globals--2b7sF4-.js +12 -0
- package/dist/htmlElements-SjqYu0am.js +78 -0
- package/dist/{index.d.mts → index-BnsMFYhs.d.ts} +861 -832
- package/dist/index-BozA2pze.d.ts +23 -0
- package/dist/index-BqrLvboW.d.ts +61 -0
- package/dist/index-LBy5OPMu.d.ts +11 -0
- package/dist/index-tUyghL98.d.ts +19 -0
- package/dist/index-z_zwVNP8.d.ts +79 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +11 -0
- package/dist/transformers/index.d.ts +2 -0
- package/dist/transformers/index.js +4 -0
- package/dist/transformers-BAeg3QJF.js +122 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +1 -0
- package/dist/useTheme-DSYiiz0R.js +1244 -0
- package/dist/utilities/index.d.ts +3 -0
- package/dist/utilities/index.js +3 -0
- package/dist/utilities-D9rWEgQK.js +86 -0
- package/package.json +62 -14
|
@@ -0,0 +1,1244 @@
|
|
|
1
|
+
import { createContext, createPlugin, createTrinity, useContext } from "./factories-CPq2yMlr.js";
|
|
2
|
+
import { genId, isArray, isObject, isString, mergeDeep } from "./utilities-D9rWEgQK.js";
|
|
3
|
+
import { IN_BROWSER, __LOGGER_ENABLED__ } from "./globals--2b7sF4-.js";
|
|
4
|
+
import { toArray } from "./transformers-BAeg3QJF.js";
|
|
5
|
+
import { computed, getCurrentInstance, onMounted, onScopeDispose, shallowReactive, shallowReadonly, shallowRef, toRef, watch } from "vue";
|
|
6
|
+
|
|
7
|
+
//#region src/composables/useHydration/index.ts
|
|
8
|
+
const [useHydrationContext, provideHydrationContext] = createContext("v0:hydration");
|
|
9
|
+
/**
|
|
10
|
+
* Creates a hydration context for tracking client-side hydration state in SSR applications.
|
|
11
|
+
* This function provides a way to determine when the application has been fully hydrated
|
|
12
|
+
* on the client side, which is essential for SSR/SSG compatibility.
|
|
13
|
+
*
|
|
14
|
+
* @returns A hydration context object with reactive state and hydration control.
|
|
15
|
+
*/
|
|
16
|
+
function createHydration() {
|
|
17
|
+
const isHydrated = shallowRef(false);
|
|
18
|
+
function hydrate() {
|
|
19
|
+
isHydrated.value = true;
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
isHydrated: shallowReadonly(isHydrated),
|
|
23
|
+
hydrate
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Simple hook to access the hydration context.
|
|
28
|
+
*
|
|
29
|
+
* @returns The hydration context containing hydration state and controls.
|
|
30
|
+
*/
|
|
31
|
+
function useHydration() {
|
|
32
|
+
return useHydrationContext();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Creates a Vue plugin for hydration state management in SSR/SSG applications.
|
|
36
|
+
* This plugin automatically detects when the root component is mounted and
|
|
37
|
+
* triggers the hydration process, ensuring proper client-side hydration timing.
|
|
38
|
+
*
|
|
39
|
+
* @returns A Vue plugin object with install method.
|
|
40
|
+
*/
|
|
41
|
+
function createHydrationPlugin() {
|
|
42
|
+
const context = createHydration();
|
|
43
|
+
return createPlugin({
|
|
44
|
+
namespace: "v0:hydration",
|
|
45
|
+
provide: (app) => {
|
|
46
|
+
provideHydrationContext(context, app);
|
|
47
|
+
},
|
|
48
|
+
setup: (app) => {
|
|
49
|
+
app.mixin({ mounted() {
|
|
50
|
+
if (this.$parent !== null) return;
|
|
51
|
+
context.hydrate();
|
|
52
|
+
} });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/composables/useBreakpoints/index.ts
|
|
59
|
+
const [useBreakpointsContext, provideBreakpointsContext] = createContext("v0:breakpoints");
|
|
60
|
+
/**
|
|
61
|
+
* Simple hook to access the breakpoints context.
|
|
62
|
+
*
|
|
63
|
+
* @returns The breakpoints context containing current breakpoint information.
|
|
64
|
+
*/
|
|
65
|
+
function useBreakpoints() {
|
|
66
|
+
return useBreakpointsContext();
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Creates default breakpoint configuration.
|
|
70
|
+
*
|
|
71
|
+
* @returns The default breakpoint configuration object.
|
|
72
|
+
*/
|
|
73
|
+
function createDefaultBreakpoints() {
|
|
74
|
+
return {
|
|
75
|
+
mobileBreakpoint: "md",
|
|
76
|
+
breakpoints: {
|
|
77
|
+
xs: 0,
|
|
78
|
+
sm: 600,
|
|
79
|
+
md: 960,
|
|
80
|
+
lg: 1280,
|
|
81
|
+
xl: 1920,
|
|
82
|
+
xxl: 2560
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Creates a reactive breakpoints system for responsive behavior management.
|
|
88
|
+
* This function provides access to viewport dimensions, breakpoint detection, and helper flags
|
|
89
|
+
* for determining current screen size and implementing responsive logic.
|
|
90
|
+
*
|
|
91
|
+
* @param options Optional configuration for breakpoint thresholds and mobile breakpoint.
|
|
92
|
+
* @returns A breakpoints context object with reactive state and utility methods.
|
|
93
|
+
*/
|
|
94
|
+
function createBreakpoints(options = {}) {
|
|
95
|
+
const defaults = createDefaultBreakpoints();
|
|
96
|
+
const { mobileBreakpoint, breakpoints } = mergeDeep(defaults, options);
|
|
97
|
+
const sorted = Object.entries(breakpoints).sort((a, b) => a[1] - b[1]);
|
|
98
|
+
const names = sorted.map(([n]) => n);
|
|
99
|
+
const mb = typeof mobileBreakpoint === "number" ? mobileBreakpoint : breakpoints[mobileBreakpoint] ?? breakpoints.md;
|
|
100
|
+
const state = shallowReactive({
|
|
101
|
+
breakpoints,
|
|
102
|
+
name: "xs",
|
|
103
|
+
width: 0,
|
|
104
|
+
height: 0,
|
|
105
|
+
isMobile: true,
|
|
106
|
+
xs: true,
|
|
107
|
+
sm: false,
|
|
108
|
+
md: false,
|
|
109
|
+
lg: false,
|
|
110
|
+
xl: false,
|
|
111
|
+
xxl: false,
|
|
112
|
+
smAndUp: false,
|
|
113
|
+
mdAndUp: false,
|
|
114
|
+
lgAndUp: false,
|
|
115
|
+
xlAndUp: false,
|
|
116
|
+
xxlAndUp: false,
|
|
117
|
+
smAndDown: true,
|
|
118
|
+
mdAndDown: true,
|
|
119
|
+
lgAndDown: true,
|
|
120
|
+
xlAndDown: true,
|
|
121
|
+
xxlAndDown: true,
|
|
122
|
+
update
|
|
123
|
+
});
|
|
124
|
+
function update() {
|
|
125
|
+
if (!IN_BROWSER) return;
|
|
126
|
+
state.width = window.innerWidth;
|
|
127
|
+
state.height = window.innerHeight;
|
|
128
|
+
let current = "xs";
|
|
129
|
+
for (let i = sorted.length - 1; i >= 0; i--) if (state.width >= sorted[i][1]) {
|
|
130
|
+
current = sorted[i][0];
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
state.name = current;
|
|
134
|
+
const index = names.indexOf(current);
|
|
135
|
+
state.isMobile = state.width < mb;
|
|
136
|
+
state.xs = index === 0;
|
|
137
|
+
state.sm = index === 1;
|
|
138
|
+
state.md = index === 2;
|
|
139
|
+
state.lg = index === 3;
|
|
140
|
+
state.xl = index === 4;
|
|
141
|
+
state.xxl = index === 5;
|
|
142
|
+
state.smAndUp = index >= 1;
|
|
143
|
+
state.mdAndUp = index >= 2;
|
|
144
|
+
state.lgAndUp = index >= 3;
|
|
145
|
+
state.xlAndUp = index >= 4;
|
|
146
|
+
state.xxlAndUp = index >= 5;
|
|
147
|
+
state.smAndDown = index <= 1;
|
|
148
|
+
state.mdAndDown = index <= 2;
|
|
149
|
+
state.lgAndDown = index <= 3;
|
|
150
|
+
state.xlAndDown = index <= 4;
|
|
151
|
+
state.xxlAndDown = index <= 5;
|
|
152
|
+
}
|
|
153
|
+
if (getCurrentInstance()) onMounted(() => {
|
|
154
|
+
const { isHydrated } = useHydration();
|
|
155
|
+
if (isHydrated.value) update();
|
|
156
|
+
watch(isHydrated, (hydrated) => {
|
|
157
|
+
if (hydrated) update();
|
|
158
|
+
}, { immediate: true });
|
|
159
|
+
});
|
|
160
|
+
if (IN_BROWSER) {
|
|
161
|
+
function listener() {
|
|
162
|
+
update();
|
|
163
|
+
}
|
|
164
|
+
window.addEventListener("resize", listener, { passive: true });
|
|
165
|
+
if (getCurrentInstance()) onScopeDispose(() => window.removeEventListener("resize", listener));
|
|
166
|
+
}
|
|
167
|
+
return state;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Creates a Vue plugin for managing responsive breakpoints with automatic updates.
|
|
171
|
+
* This plugin sets up breakpoint tracking and updates the context when the window
|
|
172
|
+
* is resized, providing reactive breakpoint state throughout the application.
|
|
173
|
+
*
|
|
174
|
+
* @param options Optional configuration for breakpoint thresholds and mobile breakpoint.
|
|
175
|
+
* @returns A Vue plugin object with install method.
|
|
176
|
+
*/
|
|
177
|
+
function createBreakpointsPlugin(options = {}) {
|
|
178
|
+
const context = createBreakpoints(options);
|
|
179
|
+
return createPlugin({
|
|
180
|
+
namespace: "v0:breakpoints",
|
|
181
|
+
provide: (app) => {
|
|
182
|
+
provideBreakpointsContext(context, app);
|
|
183
|
+
},
|
|
184
|
+
setup: (app) => {
|
|
185
|
+
app.mixin({ mounted() {
|
|
186
|
+
context.update();
|
|
187
|
+
} });
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
//#endregion
|
|
193
|
+
//#region src/composables/useLogger/adapters/consola.ts
|
|
194
|
+
var ConsolaLoggerAdapter = class {
|
|
195
|
+
consola;
|
|
196
|
+
constructor(consolaInstance) {
|
|
197
|
+
if (!consolaInstance) throw new Error("Consola instance is required for ConsolaLoggerAdapter");
|
|
198
|
+
this.consola = consolaInstance;
|
|
199
|
+
}
|
|
200
|
+
debug(message, ...args) {
|
|
201
|
+
this.consola.debug(message, ...args);
|
|
202
|
+
}
|
|
203
|
+
info(message, ...args) {
|
|
204
|
+
this.consola.info(message, ...args);
|
|
205
|
+
}
|
|
206
|
+
warn(message, ...args) {
|
|
207
|
+
this.consola.warn(message, ...args);
|
|
208
|
+
}
|
|
209
|
+
error(message, ...args) {
|
|
210
|
+
this.consola.error(message, ...args);
|
|
211
|
+
}
|
|
212
|
+
trace(message, ...args) {
|
|
213
|
+
if (this.consola.trace) this.consola.trace(message, ...args);
|
|
214
|
+
else this.consola.debug(message, ...args);
|
|
215
|
+
}
|
|
216
|
+
fatal(message, ...args) {
|
|
217
|
+
if (this.consola.fatal) this.consola.fatal(message, ...args);
|
|
218
|
+
else this.consola.error("[FATAL]", message, ...args);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
//#endregion
|
|
223
|
+
//#region src/composables/useLogger/adapters/pino.ts
|
|
224
|
+
/**
|
|
225
|
+
* Pino logger adapter implementation
|
|
226
|
+
*
|
|
227
|
+
* This adapter integrates with the Pino logging library,
|
|
228
|
+
* providing high-performance structured logging optimized
|
|
229
|
+
* for Node.js applications with minimal overhead.
|
|
230
|
+
*/
|
|
231
|
+
var PinoLoggerAdapter = class {
|
|
232
|
+
pino;
|
|
233
|
+
constructor(pinoInstance) {
|
|
234
|
+
if (!pinoInstance) throw new Error("Pino instance is required for PinoLoggerAdapter");
|
|
235
|
+
this.pino = pinoInstance;
|
|
236
|
+
}
|
|
237
|
+
debug(message, ...args) {
|
|
238
|
+
this.pino.debug(this.format(message, ...args));
|
|
239
|
+
}
|
|
240
|
+
info(message, ...args) {
|
|
241
|
+
this.pino.info(this.format(message, ...args));
|
|
242
|
+
}
|
|
243
|
+
warn(message, ...args) {
|
|
244
|
+
this.pino.warn(this.format(message, ...args));
|
|
245
|
+
}
|
|
246
|
+
error(message, ...args) {
|
|
247
|
+
this.pino.error(this.format(message, ...args));
|
|
248
|
+
}
|
|
249
|
+
trace(message, ...args) {
|
|
250
|
+
this.pino.trace(this.format(message, ...args));
|
|
251
|
+
}
|
|
252
|
+
fatal(message, ...args) {
|
|
253
|
+
this.pino.fatal(this.format(message, ...args));
|
|
254
|
+
}
|
|
255
|
+
format(message, ...args) {
|
|
256
|
+
if (args.length === 0) return { msg: message };
|
|
257
|
+
if (args.length === 1 && typeof args[0] === "object" && args[0] !== null) return {
|
|
258
|
+
...args[0],
|
|
259
|
+
msg: message
|
|
260
|
+
};
|
|
261
|
+
return {
|
|
262
|
+
msg: message,
|
|
263
|
+
args
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
//#endregion
|
|
269
|
+
//#region src/composables/useLogger/adapters/v0.ts
|
|
270
|
+
/**
|
|
271
|
+
* Vuetify0.x logger adapter implementation
|
|
272
|
+
*
|
|
273
|
+
* This adapter provides console-based logging with proper formatting,
|
|
274
|
+
* color coding, timestamps, and log level filtering for development
|
|
275
|
+
* and production environments.
|
|
276
|
+
*/
|
|
277
|
+
var Vuetify0LoggerAdapter = class {
|
|
278
|
+
prefix;
|
|
279
|
+
colors;
|
|
280
|
+
timestamps;
|
|
281
|
+
constructor(options = {}) {
|
|
282
|
+
this.prefix = options.prefix || "v0";
|
|
283
|
+
this.colors = options.colors !== false;
|
|
284
|
+
this.timestamps = options.timestamps !== false;
|
|
285
|
+
}
|
|
286
|
+
debug(message, ...args) {
|
|
287
|
+
this.log("debug", "debug", message, ...args);
|
|
288
|
+
}
|
|
289
|
+
info(message, ...args) {
|
|
290
|
+
this.log("info", "info", message, ...args);
|
|
291
|
+
}
|
|
292
|
+
warn(message, ...args) {
|
|
293
|
+
this.log("warn", "warn", message, ...args);
|
|
294
|
+
}
|
|
295
|
+
error(message, ...args) {
|
|
296
|
+
this.log("error", "error", message, ...args);
|
|
297
|
+
}
|
|
298
|
+
trace(message, ...args) {
|
|
299
|
+
this.log("trace", "trace", message, ...args);
|
|
300
|
+
}
|
|
301
|
+
fatal(message, ...args) {
|
|
302
|
+
this.log("fatal", "error", message, ...args);
|
|
303
|
+
}
|
|
304
|
+
format(level, message, ...args) {
|
|
305
|
+
const timestamp = this.timestamps ? this.timestamp() : "";
|
|
306
|
+
const prefixTag = `[${this.prefix} ${level.toLowerCase()}]`;
|
|
307
|
+
const formattedMessage = [
|
|
308
|
+
timestamp,
|
|
309
|
+
prefixTag,
|
|
310
|
+
message
|
|
311
|
+
].filter(Boolean).join(" ");
|
|
312
|
+
return [formattedMessage, ...args];
|
|
313
|
+
}
|
|
314
|
+
timestamp() {
|
|
315
|
+
if (!IN_BROWSER) return (/* @__PURE__ */ new Date()).toISOString();
|
|
316
|
+
const now = /* @__PURE__ */ new Date();
|
|
317
|
+
return now.toTimeString().split(" ")[0];
|
|
318
|
+
}
|
|
319
|
+
style(level) {
|
|
320
|
+
if (!this.colors || !IN_BROWSER) return "";
|
|
321
|
+
const styles = {
|
|
322
|
+
trace: "color: #64748b",
|
|
323
|
+
debug: "color: #3b82f6",
|
|
324
|
+
info: "color: #10b981",
|
|
325
|
+
warn: "color: #f59e0b",
|
|
326
|
+
error: "color: #ef4444",
|
|
327
|
+
fatal: "color: #dc2626; font-weight: bold",
|
|
328
|
+
silent: ""
|
|
329
|
+
};
|
|
330
|
+
return styles[level] || "";
|
|
331
|
+
}
|
|
332
|
+
log(level, method, message, ...args) {
|
|
333
|
+
const [formattedMessage, ...restArgs] = this.format(level, message, ...args);
|
|
334
|
+
const style = this.style(level);
|
|
335
|
+
if (IN_BROWSER && style && typeof console[method] === "function") console[method](`%c${formattedMessage}`, style, ...restArgs);
|
|
336
|
+
else if (typeof console[method] === "function") console[method](formattedMessage, ...restArgs);
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
//#endregion
|
|
341
|
+
//#region src/composables/useLogger/index.ts
|
|
342
|
+
const [useLoggerContext, provideLoggerContext] = createContext("v0:logger");
|
|
343
|
+
/**
|
|
344
|
+
* Creates a logger context for application logging with configurable adapters and levels.
|
|
345
|
+
* This function provides a consistent logging interface that can be configured with
|
|
346
|
+
* different adapters and optimized for production builds with conditional compilation.
|
|
347
|
+
*
|
|
348
|
+
* @param options Configuration for the logger adapter, level, and behavior.
|
|
349
|
+
* @returns A logger context object with logging methods and controls.
|
|
350
|
+
*/
|
|
351
|
+
function createLogger(options = {}) {
|
|
352
|
+
const { adapter = new Vuetify0LoggerAdapter({ prefix: options.prefix }), level: initialLevel = "info", enabled: initialEnabled = __LOGGER_ENABLED__ } = options;
|
|
353
|
+
const currentLevel = shallowRef(initialLevel);
|
|
354
|
+
const isEnabled = shallowRef(initialEnabled);
|
|
355
|
+
function value(level$1) {
|
|
356
|
+
const levels = {
|
|
357
|
+
trace: 0,
|
|
358
|
+
debug: 1,
|
|
359
|
+
info: 2,
|
|
360
|
+
warn: 3,
|
|
361
|
+
error: 4,
|
|
362
|
+
fatal: 5,
|
|
363
|
+
silent: 6
|
|
364
|
+
};
|
|
365
|
+
return levels[level$1] ?? 2;
|
|
366
|
+
}
|
|
367
|
+
function can(level$1) {
|
|
368
|
+
if (!isEnabled.value) return false;
|
|
369
|
+
return value(level$1) >= value(currentLevel.value);
|
|
370
|
+
}
|
|
371
|
+
function format(message) {
|
|
372
|
+
return message;
|
|
373
|
+
}
|
|
374
|
+
function debug(message, ...args) {
|
|
375
|
+
if (can("debug")) adapter.debug(format(message), ...args);
|
|
376
|
+
}
|
|
377
|
+
function info(message, ...args) {
|
|
378
|
+
if (can("info")) adapter.info(format(message), ...args);
|
|
379
|
+
}
|
|
380
|
+
function warn(message, ...args) {
|
|
381
|
+
if (can("warn")) adapter.warn(format(message), ...args);
|
|
382
|
+
}
|
|
383
|
+
function error(message, ...args) {
|
|
384
|
+
if (can("error")) adapter.error(format(message), ...args);
|
|
385
|
+
}
|
|
386
|
+
function trace(message, ...args) {
|
|
387
|
+
if (can("trace")) adapter.trace?.(format(message), ...args);
|
|
388
|
+
}
|
|
389
|
+
function fatal(message, ...args) {
|
|
390
|
+
if (can("fatal")) adapter.fatal?.(format(message), ...args);
|
|
391
|
+
}
|
|
392
|
+
function level(newLevel) {
|
|
393
|
+
currentLevel.value = newLevel;
|
|
394
|
+
}
|
|
395
|
+
function current() {
|
|
396
|
+
return currentLevel.value;
|
|
397
|
+
}
|
|
398
|
+
function enabled() {
|
|
399
|
+
return isEnabled.value;
|
|
400
|
+
}
|
|
401
|
+
function enable() {
|
|
402
|
+
isEnabled.value = true;
|
|
403
|
+
}
|
|
404
|
+
function disable() {
|
|
405
|
+
isEnabled.value = false;
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
debug,
|
|
409
|
+
info,
|
|
410
|
+
warn,
|
|
411
|
+
error,
|
|
412
|
+
trace,
|
|
413
|
+
fatal,
|
|
414
|
+
level,
|
|
415
|
+
current,
|
|
416
|
+
enabled,
|
|
417
|
+
enable,
|
|
418
|
+
disable
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
function createFallbackLogger(namespace = "v0:logger") {
|
|
422
|
+
function format(message, type) {
|
|
423
|
+
return `[${namespace} ${type}] ${message}`;
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
debug: (message, ...args) => console.log(format(message, "debug"), ...args),
|
|
427
|
+
info: (message, ...args) => console.log(format(message, "info"), ...args),
|
|
428
|
+
warn: (message, ...args) => console.log(format(message, "warn"), ...args),
|
|
429
|
+
error: (message, ...args) => console.log(format(message, "error"), ...args),
|
|
430
|
+
trace: (message, ...args) => console.log(format(message, "trace"), ...args),
|
|
431
|
+
fatal: (message, ...args) => console.log(format(message, "fatal"), ...args),
|
|
432
|
+
level: () => {},
|
|
433
|
+
current: () => "info",
|
|
434
|
+
enabled: () => true,
|
|
435
|
+
enable: () => {},
|
|
436
|
+
disable: () => {}
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
function useLogger(namespace) {
|
|
440
|
+
if (getCurrentInstance()) try {
|
|
441
|
+
return useLoggerContext(namespace);
|
|
442
|
+
} catch (error) {
|
|
443
|
+
if (process.env.NODE_ENV !== "production" && IN_BROWSER && namespace) console.warn(error);
|
|
444
|
+
}
|
|
445
|
+
return createFallbackLogger(namespace);
|
|
446
|
+
}
|
|
447
|
+
function createLoggerPlugin(options = {}) {
|
|
448
|
+
const context = createLogger(options);
|
|
449
|
+
return createPlugin({
|
|
450
|
+
namespace: "v0:logger",
|
|
451
|
+
provide: (app) => {
|
|
452
|
+
provideLoggerContext(context, app);
|
|
453
|
+
},
|
|
454
|
+
setup: (_app) => {
|
|
455
|
+
if (process.env.NODE_ENV !== "production" && IN_BROWSER) window.__v0Logger__ = context;
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
//#endregion
|
|
461
|
+
//#region src/composables/useRegistry/index.ts
|
|
462
|
+
/**
|
|
463
|
+
* Creates a registry for managing collections of items with registration and lookup capabilities.
|
|
464
|
+
* This function provides the foundation for item management systems with ID-based, value-based,
|
|
465
|
+
* and index-based access patterns.
|
|
466
|
+
*
|
|
467
|
+
* @param options Optional configuration for enabling events.
|
|
468
|
+
* @template Z The type of items managed by the registry.
|
|
469
|
+
* @template E The type of the registry context.
|
|
470
|
+
* @returns The registry context object.
|
|
471
|
+
*/
|
|
472
|
+
function useRegistry(options) {
|
|
473
|
+
const logger = useLogger();
|
|
474
|
+
const collection = /* @__PURE__ */ new Map();
|
|
475
|
+
const catalog = /* @__PURE__ */ new Map();
|
|
476
|
+
const directory = /* @__PURE__ */ new Map();
|
|
477
|
+
const cache = /* @__PURE__ */ new Map();
|
|
478
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
479
|
+
function emit(event, data) {
|
|
480
|
+
if (!options?.events) return;
|
|
481
|
+
const cbs = listeners.get(event);
|
|
482
|
+
if (!cbs) return;
|
|
483
|
+
for (const cb of cbs) cb(data);
|
|
484
|
+
}
|
|
485
|
+
function on(event, cb) {
|
|
486
|
+
if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
|
|
487
|
+
listeners.get(event).add(cb);
|
|
488
|
+
}
|
|
489
|
+
function off(event, cb) {
|
|
490
|
+
listeners.get(event)?.delete(cb);
|
|
491
|
+
}
|
|
492
|
+
function dispose() {
|
|
493
|
+
if (listeners.size > 0) listeners.clear();
|
|
494
|
+
clear();
|
|
495
|
+
}
|
|
496
|
+
function get(id) {
|
|
497
|
+
return collection.get(id);
|
|
498
|
+
}
|
|
499
|
+
function upsert(id, patch = {}) {
|
|
500
|
+
const existing = get(id);
|
|
501
|
+
if (!existing) return register({
|
|
502
|
+
...patch,
|
|
503
|
+
id
|
|
504
|
+
});
|
|
505
|
+
const hasValue = Object.prototype.hasOwnProperty.call(patch, "value");
|
|
506
|
+
let value = existing.value;
|
|
507
|
+
let valueIsIndex = existing.valueIsIndex;
|
|
508
|
+
if (hasValue) {
|
|
509
|
+
if (patch.value === void 0) {
|
|
510
|
+
value = existing.index;
|
|
511
|
+
valueIsIndex = true;
|
|
512
|
+
} else {
|
|
513
|
+
value = patch.value;
|
|
514
|
+
valueIsIndex = false;
|
|
515
|
+
}
|
|
516
|
+
if (!Object.is(value, existing.value)) {
|
|
517
|
+
unassign(existing.value, id);
|
|
518
|
+
assign(value, id);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
const updated = {
|
|
522
|
+
...existing,
|
|
523
|
+
...patch,
|
|
524
|
+
id,
|
|
525
|
+
index: existing.index,
|
|
526
|
+
value,
|
|
527
|
+
valueIsIndex
|
|
528
|
+
};
|
|
529
|
+
collection.set(id, updated);
|
|
530
|
+
invalidate();
|
|
531
|
+
return updated;
|
|
532
|
+
}
|
|
533
|
+
function browse(value) {
|
|
534
|
+
return catalog.get(value);
|
|
535
|
+
}
|
|
536
|
+
function lookup(index) {
|
|
537
|
+
return directory.get(index);
|
|
538
|
+
}
|
|
539
|
+
function has(id) {
|
|
540
|
+
return collection.has(id);
|
|
541
|
+
}
|
|
542
|
+
function assign(value, id) {
|
|
543
|
+
const bucket = catalog.get(value);
|
|
544
|
+
if (bucket) {
|
|
545
|
+
if (isArray(bucket)) {
|
|
546
|
+
if (!bucket.includes(id)) bucket.push(id);
|
|
547
|
+
} else if (bucket !== id) catalog.set(value, [bucket, id]);
|
|
548
|
+
} else catalog.set(value, id);
|
|
549
|
+
}
|
|
550
|
+
function unassign(value, id) {
|
|
551
|
+
const bucket = catalog.get(value);
|
|
552
|
+
if (!bucket) return;
|
|
553
|
+
if (isArray(bucket)) {
|
|
554
|
+
const next = bucket.filter((v) => v !== id);
|
|
555
|
+
if (next.length === 0) catalog.delete(value);
|
|
556
|
+
else if (next.length === 1) catalog.set(value, next[0]);
|
|
557
|
+
else catalog.set(value, next);
|
|
558
|
+
} else if (bucket === id) catalog.delete(value);
|
|
559
|
+
}
|
|
560
|
+
function keys() {
|
|
561
|
+
const cached = cache.get("keys");
|
|
562
|
+
if (cached != void 0) return cached;
|
|
563
|
+
const keys$1 = Array.from(collection.keys());
|
|
564
|
+
cache.set("keys", keys$1);
|
|
565
|
+
return keys$1;
|
|
566
|
+
}
|
|
567
|
+
function values() {
|
|
568
|
+
const cached = cache.get("values");
|
|
569
|
+
if (cached != void 0) return cached;
|
|
570
|
+
const values$1 = Array.from(collection.values());
|
|
571
|
+
cache.set("values", values$1);
|
|
572
|
+
return values$1;
|
|
573
|
+
}
|
|
574
|
+
function entries() {
|
|
575
|
+
const cached = cache.get("entries");
|
|
576
|
+
if (cached != void 0) return cached;
|
|
577
|
+
const entries$1 = Array.from(collection.entries());
|
|
578
|
+
cache.set("entries", entries$1);
|
|
579
|
+
return entries$1;
|
|
580
|
+
}
|
|
581
|
+
function clear() {
|
|
582
|
+
if (collection.size > 0) collection.clear();
|
|
583
|
+
if (catalog.size > 0) catalog.clear();
|
|
584
|
+
if (directory.size > 0) directory.clear();
|
|
585
|
+
invalidate();
|
|
586
|
+
}
|
|
587
|
+
function invalidate() {
|
|
588
|
+
if (cache.size > 0) cache.clear();
|
|
589
|
+
}
|
|
590
|
+
function reindex() {
|
|
591
|
+
if (catalog.size > 0) catalog.clear();
|
|
592
|
+
if (directory.size > 0) directory.clear();
|
|
593
|
+
let index = 0;
|
|
594
|
+
for (const item of values()) {
|
|
595
|
+
if (item.index !== index) {
|
|
596
|
+
item.index = index;
|
|
597
|
+
if (item.valueIsIndex) item.value = index;
|
|
598
|
+
}
|
|
599
|
+
directory.set(index, item.id);
|
|
600
|
+
assign(item.value, item.id);
|
|
601
|
+
index++;
|
|
602
|
+
}
|
|
603
|
+
invalidate();
|
|
604
|
+
}
|
|
605
|
+
function register(registration = {}) {
|
|
606
|
+
const size = collection.size;
|
|
607
|
+
const id = registration.id ?? genId();
|
|
608
|
+
if (has(id)) {
|
|
609
|
+
logger.warn(`Item with id "${id}" already exists in the registry. Skipping registration.`);
|
|
610
|
+
return get(id);
|
|
611
|
+
}
|
|
612
|
+
const index = registration.index ?? size;
|
|
613
|
+
const value = registration.value === void 0 ? index : registration.value;
|
|
614
|
+
const valueIsIndex = registration.value === void 0;
|
|
615
|
+
const item = {
|
|
616
|
+
...registration,
|
|
617
|
+
id,
|
|
618
|
+
index,
|
|
619
|
+
value,
|
|
620
|
+
valueIsIndex
|
|
621
|
+
};
|
|
622
|
+
collection.set(item.id, item);
|
|
623
|
+
directory.set(item.index, item.id);
|
|
624
|
+
assign(item.value, item.id);
|
|
625
|
+
invalidate();
|
|
626
|
+
emit("register", item);
|
|
627
|
+
return item;
|
|
628
|
+
}
|
|
629
|
+
function unregister(id) {
|
|
630
|
+
const item = collection.get(id);
|
|
631
|
+
if (!item) return;
|
|
632
|
+
collection.delete(item.id);
|
|
633
|
+
directory.delete(item.index);
|
|
634
|
+
unassign(item.value, item.id);
|
|
635
|
+
emit("unregister", item);
|
|
636
|
+
reindex();
|
|
637
|
+
}
|
|
638
|
+
return {
|
|
639
|
+
collection,
|
|
640
|
+
emit,
|
|
641
|
+
on,
|
|
642
|
+
off,
|
|
643
|
+
dispose,
|
|
644
|
+
has,
|
|
645
|
+
keys,
|
|
646
|
+
clear,
|
|
647
|
+
browse,
|
|
648
|
+
entries,
|
|
649
|
+
values,
|
|
650
|
+
lookup,
|
|
651
|
+
get,
|
|
652
|
+
upsert,
|
|
653
|
+
register,
|
|
654
|
+
unregister,
|
|
655
|
+
reindex,
|
|
656
|
+
onboard(registrations) {
|
|
657
|
+
return registrations.map((registration) => this.register(registration));
|
|
658
|
+
},
|
|
659
|
+
get size() {
|
|
660
|
+
return collection.size;
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Creates a registry context with full injection/provision control.
|
|
666
|
+
* Returns the complete trinity for advanced usage scenarios.
|
|
667
|
+
*
|
|
668
|
+
* @param namespace The namespace for the registry context.
|
|
669
|
+
* @param options Optional configuration for reactivity behavior.
|
|
670
|
+
* @template Z The type of tickets managed by the registry.
|
|
671
|
+
* @template E The type of the registry context.
|
|
672
|
+
* @returns A tuple containing the inject function, provide function, and the registry context.
|
|
673
|
+
*/
|
|
674
|
+
function createRegistryContext(namespace, options) {
|
|
675
|
+
const [useRegistryContext, _provideRegistryContext] = createContext(namespace);
|
|
676
|
+
const context = useRegistry(options);
|
|
677
|
+
function provideRegistryContext(_context = context, app) {
|
|
678
|
+
return _provideRegistryContext(_context, app);
|
|
679
|
+
}
|
|
680
|
+
return createTrinity(useRegistryContext, provideRegistryContext, context);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
//#endregion
|
|
684
|
+
//#region src/composables/useSelection/index.ts
|
|
685
|
+
/**
|
|
686
|
+
* Creates a selection context for managing collections of selectable items.
|
|
687
|
+
* This function extends the registry functionality with selection state management.
|
|
688
|
+
*
|
|
689
|
+
* @param options Optional configuration for selection behavior.
|
|
690
|
+
* @template Z The type of items managed by the selection.
|
|
691
|
+
* @template E The type of the selection context.
|
|
692
|
+
* @returns The selection context object.
|
|
693
|
+
*/
|
|
694
|
+
function useSelection(options) {
|
|
695
|
+
const registry = useRegistry(options);
|
|
696
|
+
const selectedIds = shallowReactive(/* @__PURE__ */ new Set());
|
|
697
|
+
const enroll = options?.enroll ?? false;
|
|
698
|
+
const mandatory = options?.mandatory ?? false;
|
|
699
|
+
const selectedItems = computed(() => {
|
|
700
|
+
return new Set(Array.from(selectedIds).map((id) => registry.get(id)));
|
|
701
|
+
});
|
|
702
|
+
const selectedValues = computed(() => {
|
|
703
|
+
return new Set(Array.from(selectedItems.value).map((item) => item?.value));
|
|
704
|
+
});
|
|
705
|
+
function mandate() {
|
|
706
|
+
if (!mandatory || registry.selectedIds.size > 0 || registry.size === 0) return;
|
|
707
|
+
select(registry.lookup(0));
|
|
708
|
+
}
|
|
709
|
+
function select(id) {
|
|
710
|
+
const item = registry.get(id);
|
|
711
|
+
if (!item || item.disabled) return;
|
|
712
|
+
selectedIds.add(id);
|
|
713
|
+
}
|
|
714
|
+
function unselect(id) {
|
|
715
|
+
if (mandatory && selectedIds.size === 1) return;
|
|
716
|
+
selectedIds.delete(id);
|
|
717
|
+
}
|
|
718
|
+
function toggle(id) {
|
|
719
|
+
if (selectedIds.has(id)) unselect(id);
|
|
720
|
+
else select(id);
|
|
721
|
+
}
|
|
722
|
+
function register(registration = {}) {
|
|
723
|
+
const id = registration.id ?? genId();
|
|
724
|
+
const item = {
|
|
725
|
+
disabled: false,
|
|
726
|
+
...registration,
|
|
727
|
+
id,
|
|
728
|
+
isActive: toRef(() => selectedIds.has(id)),
|
|
729
|
+
select: () => select(id),
|
|
730
|
+
unselect: () => unselect(id),
|
|
731
|
+
toggle: () => toggle(id)
|
|
732
|
+
};
|
|
733
|
+
const ticket = registry.register(item);
|
|
734
|
+
if (enroll && !item.disabled) selectedIds.add(ticket.id);
|
|
735
|
+
if (mandatory === "force") mandate();
|
|
736
|
+
return ticket;
|
|
737
|
+
}
|
|
738
|
+
function unregister(id) {
|
|
739
|
+
selectedIds.delete(id);
|
|
740
|
+
registry.unregister(id);
|
|
741
|
+
}
|
|
742
|
+
function reset() {
|
|
743
|
+
registry.clear();
|
|
744
|
+
registry.reindex();
|
|
745
|
+
registry.mandate();
|
|
746
|
+
}
|
|
747
|
+
return {
|
|
748
|
+
...registry,
|
|
749
|
+
selectedIds,
|
|
750
|
+
selectedItems,
|
|
751
|
+
selectedValues,
|
|
752
|
+
register,
|
|
753
|
+
unregister,
|
|
754
|
+
reset,
|
|
755
|
+
mandate,
|
|
756
|
+
select,
|
|
757
|
+
unselect,
|
|
758
|
+
toggle
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
//#endregion
|
|
763
|
+
//#region src/composables/useGroup/index.ts
|
|
764
|
+
/**
|
|
765
|
+
* Creates a group selection context for managing collections of items where multiple selections can be made.
|
|
766
|
+
* This function extends the selection functionality with group selection capabilities.
|
|
767
|
+
*
|
|
768
|
+
* @param options Optional configuration for group selection behavior.
|
|
769
|
+
* @template Z The type of items managed by the group selection.
|
|
770
|
+
* @template E The type of the group selection context.
|
|
771
|
+
* @returns The group selection context object.
|
|
772
|
+
*/
|
|
773
|
+
function useGroup(options) {
|
|
774
|
+
const registry = useSelection(options);
|
|
775
|
+
const selectedIndexes = computed(() => {
|
|
776
|
+
return new Set(Array.from(registry.selectedItems.value).map((item) => item?.index));
|
|
777
|
+
});
|
|
778
|
+
function select(ids) {
|
|
779
|
+
for (const id of toArray(ids)) registry.select(id);
|
|
780
|
+
}
|
|
781
|
+
function unselect(ids) {
|
|
782
|
+
for (const id of toArray(ids)) registry.unselect(id);
|
|
783
|
+
}
|
|
784
|
+
function toggle(ids) {
|
|
785
|
+
for (const id of toArray(ids)) registry.toggle(id);
|
|
786
|
+
}
|
|
787
|
+
return {
|
|
788
|
+
...registry,
|
|
789
|
+
select,
|
|
790
|
+
unselect,
|
|
791
|
+
toggle,
|
|
792
|
+
selectedIndexes
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
//#endregion
|
|
797
|
+
//#region src/composables/useSingle/index.ts
|
|
798
|
+
/**
|
|
799
|
+
* Creates a single selection context for managing collections where only one item can be selected.
|
|
800
|
+
* This function extends the selection functionality with single-selection constraints.
|
|
801
|
+
*
|
|
802
|
+
* @param options Optional configuration for single selection behavior.
|
|
803
|
+
* @template Z The type of items managed by the single selection.
|
|
804
|
+
* @template E The type of the single selection context.
|
|
805
|
+
* @returns The single selection context object.
|
|
806
|
+
*/
|
|
807
|
+
function useSingle(options) {
|
|
808
|
+
const registry = useSelection(options);
|
|
809
|
+
const mandatory = options?.mandatory ?? true;
|
|
810
|
+
const selectedId = computed(() => registry.selectedIds.values().next().value);
|
|
811
|
+
const selectedItem = computed(() => registry.selectedItems.value.values().next().value);
|
|
812
|
+
const selectedIndex = computed(() => selectedItem.value?.index ?? -1);
|
|
813
|
+
const selectedValue = computed(() => selectedItem.value?.value);
|
|
814
|
+
function select(id) {
|
|
815
|
+
const item = registry.get(id);
|
|
816
|
+
if (!item || item.disabled) return;
|
|
817
|
+
registry.selectedIds.clear();
|
|
818
|
+
registry.selectedIds.add(id);
|
|
819
|
+
}
|
|
820
|
+
function unselect(id) {
|
|
821
|
+
if (mandatory && registry.selectedIds.size === 1) return;
|
|
822
|
+
registry.selectedIds.delete(id);
|
|
823
|
+
}
|
|
824
|
+
function toggle(id) {
|
|
825
|
+
if (registry.selectedIds.has(id)) unselect(id);
|
|
826
|
+
else select(id);
|
|
827
|
+
}
|
|
828
|
+
return {
|
|
829
|
+
...registry,
|
|
830
|
+
selectedId,
|
|
831
|
+
selectedItem,
|
|
832
|
+
selectedIndex,
|
|
833
|
+
selectedValue,
|
|
834
|
+
select,
|
|
835
|
+
unselect,
|
|
836
|
+
toggle
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
//#endregion
|
|
841
|
+
//#region src/composables/useStep/index.ts
|
|
842
|
+
/**
|
|
843
|
+
* Creates a step selection context for managing collections where users can navigate through items sequentially.
|
|
844
|
+
* This function extends the single selection functionality with stepping navigation.
|
|
845
|
+
*
|
|
846
|
+
* @param options Optional configuration for step behavior.
|
|
847
|
+
* @template Z The type of items managed by the step selection.
|
|
848
|
+
* @template E The type of the step selection context.
|
|
849
|
+
* @returns The step selection context object.
|
|
850
|
+
*/
|
|
851
|
+
function useStep(options) {
|
|
852
|
+
const registry = useSingle(options);
|
|
853
|
+
function first() {
|
|
854
|
+
if (registry.size === 0) return;
|
|
855
|
+
registry.selectedIds.clear();
|
|
856
|
+
registry.select(registry.lookup(0));
|
|
857
|
+
}
|
|
858
|
+
function last() {
|
|
859
|
+
const size = registry.size;
|
|
860
|
+
if (size === 0) return;
|
|
861
|
+
registry.selectedIds.clear();
|
|
862
|
+
registry.select(registry.lookup(size - 1));
|
|
863
|
+
}
|
|
864
|
+
function next() {
|
|
865
|
+
step(1);
|
|
866
|
+
}
|
|
867
|
+
function prev() {
|
|
868
|
+
step(-1);
|
|
869
|
+
}
|
|
870
|
+
function wrapped(length, index) {
|
|
871
|
+
return (index + length) % length;
|
|
872
|
+
}
|
|
873
|
+
function step(count = 1) {
|
|
874
|
+
const length = registry.size;
|
|
875
|
+
if (!length) return;
|
|
876
|
+
const direction = Math.sign(count || 1);
|
|
877
|
+
let hops = 0;
|
|
878
|
+
let index = wrapped(length, registry.selectedIndex.value + count);
|
|
879
|
+
let id = registry.lookup(index);
|
|
880
|
+
while (id !== void 0 && registry.get(id)?.disabled && hops < length) {
|
|
881
|
+
index = wrapped(length, index + direction);
|
|
882
|
+
id = registry.lookup(index);
|
|
883
|
+
hops++;
|
|
884
|
+
}
|
|
885
|
+
if (id === void 0 || hops === length) return;
|
|
886
|
+
registry.selectedIds.clear();
|
|
887
|
+
registry.select(id);
|
|
888
|
+
}
|
|
889
|
+
return {
|
|
890
|
+
...registry,
|
|
891
|
+
first,
|
|
892
|
+
last,
|
|
893
|
+
next,
|
|
894
|
+
prev,
|
|
895
|
+
step
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
//#endregion
|
|
900
|
+
//#region src/composables/useTokens/index.ts
|
|
901
|
+
/**
|
|
902
|
+
* Creates a token registry for managing design token collections with alias resolution.
|
|
903
|
+
* Returns the token context directly for simple usage.
|
|
904
|
+
*
|
|
905
|
+
* @param tokens A collection of tokens to initialize with
|
|
906
|
+
* @template Z The structure of the registry token items.
|
|
907
|
+
* @template E The available methods for the token's context.
|
|
908
|
+
* @returns The token context object.
|
|
909
|
+
* @see https://www.designtokens.org/tr/drafts/format/
|
|
910
|
+
*/
|
|
911
|
+
function useTokens(tokens = {}) {
|
|
912
|
+
const logger = useLogger();
|
|
913
|
+
const registry = useRegistry();
|
|
914
|
+
const cache = /* @__PURE__ */ new Map();
|
|
915
|
+
registry.onboard(flatten(tokens));
|
|
916
|
+
function isAlias(token) {
|
|
917
|
+
return isString(token) && token.length > 2 && token[0] === "{" && token.at(-1) === "}";
|
|
918
|
+
}
|
|
919
|
+
function isTokenAlias(value) {
|
|
920
|
+
return isObject(value) && "$value" in value;
|
|
921
|
+
}
|
|
922
|
+
function resolve(token) {
|
|
923
|
+
const cacheKey = isString(token) ? token : JSON.stringify(token);
|
|
924
|
+
const cached = cache.get(cacheKey);
|
|
925
|
+
if (cached !== void 0) return cached;
|
|
926
|
+
const reference = isTokenAlias(token) ? token.$value : token;
|
|
927
|
+
const clean = isString(reference) && isAlias(reference) ? reference.slice(1, -1) : String(reference);
|
|
928
|
+
let found = registry.get(clean);
|
|
929
|
+
let segments = [];
|
|
930
|
+
if (!found && clean.includes(".")) {
|
|
931
|
+
const parts = clean.split(".");
|
|
932
|
+
for (let i = parts.length - 1; i > 0; i--) {
|
|
933
|
+
const prefix = parts.slice(0, i).join(".");
|
|
934
|
+
const suffix = parts.slice(i);
|
|
935
|
+
const candidate = registry.get(prefix);
|
|
936
|
+
if (candidate?.value !== void 0) {
|
|
937
|
+
found = candidate;
|
|
938
|
+
segments = suffix;
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
if (found?.value === void 0) {
|
|
944
|
+
logger.warn(`Alias not found for "${String(reference)}"`);
|
|
945
|
+
cache.set(cacheKey, void 0);
|
|
946
|
+
return void 0;
|
|
947
|
+
}
|
|
948
|
+
let result;
|
|
949
|
+
let current = found.value;
|
|
950
|
+
if (segments.length > 0) {
|
|
951
|
+
if (isTokenAlias(current)) current = current.$value;
|
|
952
|
+
for (const segment of segments) {
|
|
953
|
+
if (!isObject(current) || !(segment in current)) {
|
|
954
|
+
current = void 0;
|
|
955
|
+
break;
|
|
956
|
+
}
|
|
957
|
+
current = current[segment];
|
|
958
|
+
if (isTokenAlias(current)) current = current.$value;
|
|
959
|
+
}
|
|
960
|
+
if (current === void 0) {
|
|
961
|
+
logger.warn(`Path not found inside "${clean}": ${segments.join(".")}`);
|
|
962
|
+
cache.set(cacheKey, void 0);
|
|
963
|
+
return void 0;
|
|
964
|
+
}
|
|
965
|
+
result = current;
|
|
966
|
+
} else if (isTokenAlias(current)) {
|
|
967
|
+
const inner = current.$value;
|
|
968
|
+
if (isString(inner) && isAlias(inner)) return resolve(inner);
|
|
969
|
+
result = inner;
|
|
970
|
+
} else if (isString(current) && isAlias(current)) return resolve(current);
|
|
971
|
+
else result = current;
|
|
972
|
+
cache.set(cacheKey, result);
|
|
973
|
+
return result;
|
|
974
|
+
}
|
|
975
|
+
return {
|
|
976
|
+
...registry,
|
|
977
|
+
resolve
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Creates a token registry context with full injection/provision control.
|
|
982
|
+
* Returns the complete trinity for advanced usage scenarios.
|
|
983
|
+
*
|
|
984
|
+
* @param namespace The namespace for the token registry context
|
|
985
|
+
* @param tokens An optional collection of tokens to initialize
|
|
986
|
+
* @template Z The structure of the registry token items.
|
|
987
|
+
* @template E The available methods for the token's context.
|
|
988
|
+
* @returns A tuple containing the inject function, provide function, and the token context.
|
|
989
|
+
*/
|
|
990
|
+
function createTokensContext(namespace, tokens = {}) {
|
|
991
|
+
const [useTokensContext, _provideTokensContext] = createContext(namespace);
|
|
992
|
+
const context = useTokens(tokens);
|
|
993
|
+
function provideTokensContext(_context = context, app) {
|
|
994
|
+
return _provideTokensContext(_context, app);
|
|
995
|
+
}
|
|
996
|
+
return createTrinity(useTokensContext, provideTokensContext, context);
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Flattens a nested collection of tokens into a flat array of tokens.
|
|
1000
|
+
* Each token is represented by an object containing its ID & value.
|
|
1001
|
+
* @param tokens The collection of tokens to flatten.
|
|
1002
|
+
* @param prefix An optional prefix to prepend to each token ID.
|
|
1003
|
+
* @returns An array of flattened tokens, each with an ID and value.
|
|
1004
|
+
*/
|
|
1005
|
+
function flatten(tokens, prefix = "") {
|
|
1006
|
+
const flattened = [];
|
|
1007
|
+
const stack = [{
|
|
1008
|
+
tokens,
|
|
1009
|
+
prefix
|
|
1010
|
+
}];
|
|
1011
|
+
while (stack.length > 0) {
|
|
1012
|
+
const { tokens: currentTokens, prefix: currentPrefix } = stack.pop();
|
|
1013
|
+
const meta = {};
|
|
1014
|
+
for (const k in currentTokens) if (k.startsWith("$")) meta[k] = currentTokens[k];
|
|
1015
|
+
if (Object.keys(meta).length > 0 && currentPrefix) flattened.push({
|
|
1016
|
+
id: currentPrefix,
|
|
1017
|
+
value: meta
|
|
1018
|
+
});
|
|
1019
|
+
for (const key in currentTokens) {
|
|
1020
|
+
if (key.startsWith("$")) continue;
|
|
1021
|
+
const value = currentTokens[key];
|
|
1022
|
+
const id = currentPrefix ? `${currentPrefix}.${key}` : key;
|
|
1023
|
+
if (!isObject(value)) {
|
|
1024
|
+
flattened.push({
|
|
1025
|
+
id,
|
|
1026
|
+
value
|
|
1027
|
+
});
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
if ("$value" in value) {
|
|
1031
|
+
flattened.push({
|
|
1032
|
+
id,
|
|
1033
|
+
value
|
|
1034
|
+
});
|
|
1035
|
+
const inner = value.$value;
|
|
1036
|
+
if (isObject(inner)) for (const innerKey in inner) {
|
|
1037
|
+
if (innerKey.startsWith("$")) continue;
|
|
1038
|
+
const child = inner[innerKey];
|
|
1039
|
+
const childId = `${id}.${innerKey}`;
|
|
1040
|
+
if (!isObject(child)) flattened.push({
|
|
1041
|
+
id: childId,
|
|
1042
|
+
value: child
|
|
1043
|
+
});
|
|
1044
|
+
else if ("$value" in child) flattened.push({
|
|
1045
|
+
id: childId,
|
|
1046
|
+
value: child
|
|
1047
|
+
});
|
|
1048
|
+
else stack.push({
|
|
1049
|
+
tokens: child,
|
|
1050
|
+
prefix: childId
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
continue;
|
|
1054
|
+
}
|
|
1055
|
+
stack.push({
|
|
1056
|
+
tokens: value,
|
|
1057
|
+
prefix: id
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
return flattened;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
//#endregion
|
|
1065
|
+
//#region src/composables/useTheme/adapters/adapter.ts
|
|
1066
|
+
var ThemeAdapter = class {
|
|
1067
|
+
prefix;
|
|
1068
|
+
constructor(prefix) {
|
|
1069
|
+
this.prefix = prefix;
|
|
1070
|
+
}
|
|
1071
|
+
generate(colors) {
|
|
1072
|
+
let css = "";
|
|
1073
|
+
for (const theme in colors) {
|
|
1074
|
+
const themeColors = colors[theme];
|
|
1075
|
+
if (!themeColors) continue;
|
|
1076
|
+
const vars = Object.entries(themeColors).map(([key, val]) => ` --${this.prefix}-${key}: ${val};`).join("\n");
|
|
1077
|
+
css += `.${this.prefix}-theme--${theme} {\n${vars}\n}\n`;
|
|
1078
|
+
}
|
|
1079
|
+
return css;
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
|
|
1083
|
+
//#endregion
|
|
1084
|
+
//#region src/composables/useTheme/adapters/v0.ts
|
|
1085
|
+
/**
|
|
1086
|
+
* Theme adapter implementation for Vuetify v0 design system.
|
|
1087
|
+
* This adapter generates CSS custom properties and injects them into the DOM
|
|
1088
|
+
* as a stylesheet, allowing themes to be applied globally.
|
|
1089
|
+
*/
|
|
1090
|
+
var Vuetify0ThemeAdapter = class extends ThemeAdapter {
|
|
1091
|
+
cspNonce;
|
|
1092
|
+
stylesheetId = "v0-theme-stylesheet";
|
|
1093
|
+
constructor(options = {}) {
|
|
1094
|
+
super(options.prefix ?? "v0");
|
|
1095
|
+
this.cspNonce = options.cspNonce;
|
|
1096
|
+
this.stylesheetId = options.stylesheetId ?? this.stylesheetId;
|
|
1097
|
+
}
|
|
1098
|
+
update(colors) {
|
|
1099
|
+
if (!IN_BROWSER) return;
|
|
1100
|
+
this.upsert(this.generate(colors));
|
|
1101
|
+
}
|
|
1102
|
+
upsert(styles) {
|
|
1103
|
+
if (!IN_BROWSER) return;
|
|
1104
|
+
let styleEl = document.querySelector(`#${this.stylesheetId}`);
|
|
1105
|
+
if (!styleEl) {
|
|
1106
|
+
styleEl = document.createElement("style");
|
|
1107
|
+
styleEl.id = this.stylesheetId.startsWith("#") ? this.stylesheetId.slice(1) : this.stylesheetId;
|
|
1108
|
+
if (this.cspNonce) styleEl.setAttribute("nonce", this.cspNonce);
|
|
1109
|
+
document.head.append(styleEl);
|
|
1110
|
+
}
|
|
1111
|
+
styleEl.textContent = styles;
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1114
|
+
|
|
1115
|
+
//#endregion
|
|
1116
|
+
//#region src/composables/useTheme/index.ts
|
|
1117
|
+
/**
|
|
1118
|
+
* Creates a theme registry for managing theme selections with dynamic color resolution.
|
|
1119
|
+
* Supports token-based color systems and lazy theme loading for optimal performance.
|
|
1120
|
+
*
|
|
1121
|
+
* @param namespace The namespace for the theme context.
|
|
1122
|
+
* @param options Configuration including adapter and themes.
|
|
1123
|
+
* @template Z The type of theme context.
|
|
1124
|
+
* @template E The type of theme items managed by the registry.
|
|
1125
|
+
* @returns A tuple containing inject, provide functions and the theme context.
|
|
1126
|
+
*/
|
|
1127
|
+
function createTheme(namespace = "v0:theme", options = {}) {
|
|
1128
|
+
const { themes = {}, palette = {} } = options;
|
|
1129
|
+
const [useThemeContext, _provideThemeContext] = createContext(namespace);
|
|
1130
|
+
const registry = useSingle();
|
|
1131
|
+
for (const id in themes) {
|
|
1132
|
+
register({
|
|
1133
|
+
id,
|
|
1134
|
+
value: themes[id].colors,
|
|
1135
|
+
dark: themes[id].dark ?? false,
|
|
1136
|
+
lazy: themes[id].lazy ?? false
|
|
1137
|
+
});
|
|
1138
|
+
if (id === options.default && !registry.selectedId.value) registry.select(id);
|
|
1139
|
+
}
|
|
1140
|
+
const names = computed(() => registry.keys());
|
|
1141
|
+
const colors = computed(() => {
|
|
1142
|
+
const resolved = {};
|
|
1143
|
+
for (const theme of registry.values()) {
|
|
1144
|
+
if (theme.lazy && theme.id !== registry.selectedId.value) continue;
|
|
1145
|
+
resolved[String(theme.id)] = resolve(theme.id, theme.value);
|
|
1146
|
+
}
|
|
1147
|
+
return resolved;
|
|
1148
|
+
});
|
|
1149
|
+
function cycle(themes$1 = names.value) {
|
|
1150
|
+
const current = themes$1.indexOf(registry.selectedId.value ?? "");
|
|
1151
|
+
const next = current === -1 ? 0 : (current + 1) % themes$1.length;
|
|
1152
|
+
registry.select(themes$1[next]);
|
|
1153
|
+
}
|
|
1154
|
+
function resolve(themeId, colors$1) {
|
|
1155
|
+
const resolved = {};
|
|
1156
|
+
for (const [key, value] of Object.entries(colors$1)) resolved[key] = resolveTokenReference(themeId, value);
|
|
1157
|
+
return resolved;
|
|
1158
|
+
}
|
|
1159
|
+
function resolveTokenReference(themeId, value) {
|
|
1160
|
+
return value.replace(/{([a-zA-Z0-9.-_]+)}/g, (match, tokenKey) => {
|
|
1161
|
+
if (palette[tokenKey]) return isString(palette[tokenKey]) ? palette[tokenKey] : match;
|
|
1162
|
+
const [targetTheme, ...colorPath] = tokenKey.split(".");
|
|
1163
|
+
const colorKey = colorPath.join(".");
|
|
1164
|
+
if (targetTheme && colorKey) {
|
|
1165
|
+
const targetColors = themes[targetTheme];
|
|
1166
|
+
const resolved$1 = targetColors?.[colorKey];
|
|
1167
|
+
return isString(resolved$1) ? resolveTokenReference(targetTheme, resolved$1) : match;
|
|
1168
|
+
}
|
|
1169
|
+
const currentColors = themes[themeId];
|
|
1170
|
+
const resolved = currentColors?.[tokenKey];
|
|
1171
|
+
return isString(resolved) ? resolveTokenReference(themeId, resolved) : match;
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
function register(registration = {}) {
|
|
1175
|
+
const item = {
|
|
1176
|
+
lazy: false,
|
|
1177
|
+
dark: false,
|
|
1178
|
+
...registration
|
|
1179
|
+
};
|
|
1180
|
+
return registry.register(item);
|
|
1181
|
+
}
|
|
1182
|
+
const context = {
|
|
1183
|
+
...registry,
|
|
1184
|
+
colors,
|
|
1185
|
+
register,
|
|
1186
|
+
cycle
|
|
1187
|
+
};
|
|
1188
|
+
function provideThemeContext(_context = context, app) {
|
|
1189
|
+
return _provideThemeContext(_context, app);
|
|
1190
|
+
}
|
|
1191
|
+
return createTrinity(useThemeContext, provideThemeContext, context);
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Simple hook to access the theme context.
|
|
1195
|
+
*
|
|
1196
|
+
* @returns The theme context containing current theme state and utilities.
|
|
1197
|
+
*/
|
|
1198
|
+
function useTheme() {
|
|
1199
|
+
return useContext("v0:theme")();
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Creates a Vue plugin for theme management with automatic color system updates.
|
|
1203
|
+
* Integrates with token system for dynamic color resolution and provides reactive
|
|
1204
|
+
* theme switching capabilities throughout the application.
|
|
1205
|
+
*
|
|
1206
|
+
* @param options Configuration for themes, palette, and adapter.
|
|
1207
|
+
* @template Z The type of theme context.
|
|
1208
|
+
* @template E The type of theme items.
|
|
1209
|
+
* @template R The type of token context.
|
|
1210
|
+
* @template O The type of token items.
|
|
1211
|
+
* @returns A Vue plugin object with install method.
|
|
1212
|
+
*/
|
|
1213
|
+
function createThemePlugin(_options = {}) {
|
|
1214
|
+
const { adapter = new Vuetify0ThemeAdapter(), palette = {}, themes = {},...options } = _options;
|
|
1215
|
+
const [, provideThemeTokenContext, tokensContext] = createTokensContext("v0:theme:tokens", {
|
|
1216
|
+
palette,
|
|
1217
|
+
...themes
|
|
1218
|
+
});
|
|
1219
|
+
const [, provideThemeContext, themeContext] = createTheme("v0:theme", {
|
|
1220
|
+
...options,
|
|
1221
|
+
themes,
|
|
1222
|
+
palette
|
|
1223
|
+
});
|
|
1224
|
+
function update(colors) {
|
|
1225
|
+
adapter.update(colors);
|
|
1226
|
+
}
|
|
1227
|
+
return createPlugin({
|
|
1228
|
+
namespace: "v0:theme",
|
|
1229
|
+
provide: (app) => {
|
|
1230
|
+
provideThemeContext(themeContext, app);
|
|
1231
|
+
provideThemeTokenContext(tokensContext, app);
|
|
1232
|
+
},
|
|
1233
|
+
setup: () => {
|
|
1234
|
+
if (IN_BROWSER) watch(themeContext.colors, update, {
|
|
1235
|
+
immediate: true,
|
|
1236
|
+
deep: true
|
|
1237
|
+
});
|
|
1238
|
+
else update(themeContext.colors.value);
|
|
1239
|
+
}
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
//#endregion
|
|
1244
|
+
export { ConsolaLoggerAdapter, PinoLoggerAdapter, Vuetify0LoggerAdapter, createBreakpoints, createBreakpointsPlugin, createHydration, createHydrationPlugin, createLogger, createLoggerPlugin, createRegistryContext, createTheme, createThemePlugin, createTokensContext, provideBreakpointsContext, provideHydrationContext, useBreakpoints, useBreakpointsContext, useGroup, useHydration, useHydrationContext, useLogger, useRegistry, useSelection, useSingle, useStep, useTheme, useTokens };
|