@vuetify/v0 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/SKILL.md +95 -0
  2. package/dist/adapter-CWp5eQ6R.d.mts +17 -0
  3. package/dist/{adapter-DIx4Rntl.d.mts → adapter-hDuQ9aNN.d.mts} +4 -4
  4. package/dist/browser/index.js +3168 -929
  5. package/dist/components/index.d.mts +2 -2
  6. package/dist/components/index.mjs +23 -23
  7. package/dist/{components-CACff-Lt.mjs → components-mSy9az8x.mjs} +1358 -242
  8. package/dist/composables/index.d.mts +16 -15
  9. package/dist/composables/index.mjs +22 -22
  10. package/dist/{consola-BVzYzeLH.d.mts → consola-C1OjyZVf.d.mts} +1 -1
  11. package/dist/constants/index.d.mts +1 -1
  12. package/dist/constants/index.mjs +1 -1
  13. package/dist/{createDataTable-CLI4TALE.mjs → createDataTable-DIl09siC.mjs} +5 -5
  14. package/dist/{createGroup-Cf6lK9BR.mjs → createGroup-PvzsLu9E.mjs} +1 -1
  15. package/dist/{createPagination-Bu8vE0JW.mjs → createPagination-DZSJ8aO5.mjs} +5 -5
  16. package/dist/{createSelection-DvOR2tKk.mjs → createSelection-DbWP6c7H.mjs} +1 -1
  17. package/dist/{createSingle-Bbcwcq4R.mjs → createSingle-CeAJpbXs.mjs} +6 -6
  18. package/dist/{createTokens-CMRBDlzN.mjs → createTokens-C1gIuKCK.mjs} +27 -5
  19. package/dist/data-table/adapters/client/index.d.mts +1 -1
  20. package/dist/data-table/adapters/client/index.mjs +3 -3
  21. package/dist/data-table/adapters/index.d.mts +4 -4
  22. package/dist/data-table/adapters/index.mjs +5 -5
  23. package/dist/data-table/adapters/server/index.d.mts +1 -1
  24. package/dist/data-table/adapters/server/index.mjs +3 -3
  25. package/dist/data-table/adapters/virtual/index.d.mts +1 -1
  26. package/dist/data-table/adapters/virtual/index.mjs +3 -3
  27. package/dist/data-table/index.d.mts +5 -5
  28. package/dist/data-table/index.mjs +14 -14
  29. package/dist/date/index.mjs +1 -1
  30. package/dist/features/adapters/posthog/index.mjs +2 -2
  31. package/dist/features/index.d.mts +1 -1
  32. package/dist/features/index.mjs +8 -8
  33. package/dist/{globals-BqRH_jdr.mjs → globals-3i9umzyz.mjs} +1 -1
  34. package/dist/{index-8sflFIN8.d.mts → index-BoK8Ln2f.d.mts} +3 -1
  35. package/dist/{index-CKbxWC4t.d.mts → index-C5XZRWlO.d.mts} +2691 -1303
  36. package/dist/{index-CmCMrlt3.d.mts → index-CLDBngUs.d.mts} +1 -1
  37. package/dist/{index-BE848tZ2.d.mts → index-D0qnkRbl.d.mts} +505 -197
  38. package/dist/index.d.mts +19 -18
  39. package/dist/index.mjs +23 -23
  40. package/dist/json/attributes.json +312 -0
  41. package/dist/json/importMap.json +84 -0
  42. package/dist/json/tags.json +168 -0
  43. package/dist/json/web-types.json +1147 -132
  44. package/dist/locale/adapters/index.d.mts +3 -2
  45. package/dist/locale/adapters/index.mjs +2 -2
  46. package/dist/locale/adapters/v0/index.d.mts +1 -1
  47. package/dist/locale/adapters/v0/index.mjs +2 -2
  48. package/dist/locale/adapters/vue-i18n/index.d.mts +22 -0
  49. package/dist/locale/adapters/vue-i18n/index.mjs +26 -0
  50. package/dist/locale/index.d.mts +4 -3
  51. package/dist/locale/index.mjs +8 -9
  52. package/dist/logger/adapters/consola/index.d.mts +1 -1
  53. package/dist/logger/adapters/index.d.mts +4 -4
  54. package/dist/logger/adapters/index.mjs +2 -2
  55. package/dist/logger/adapters/pino/index.d.mts +1 -1
  56. package/dist/logger/adapters/pino/index.mjs +1 -1
  57. package/dist/logger/adapters/v0/index.d.mts +1 -1
  58. package/dist/logger/adapters/v0/index.mjs +2 -2
  59. package/dist/logger/index.d.mts +5 -5
  60. package/dist/logger/index.mjs +3 -3
  61. package/dist/notifications/index.d.mts +78 -0
  62. package/dist/notifications/index.mjs +215 -0
  63. package/dist/palettes/ant/index.d.mts +1 -1
  64. package/dist/palettes/index.d.mts +2 -2
  65. package/dist/palettes/material/index.d.mts +1 -1
  66. package/dist/palettes/md1/index.d.mts +1 -1
  67. package/dist/palettes/md2/index.d.mts +1 -1
  68. package/dist/palettes/radix/index.d.mts +1 -1
  69. package/dist/palettes/tailwind/index.d.mts +1 -1
  70. package/dist/permissions/adapters/index.d.mts +2 -2
  71. package/dist/permissions/adapters/index.mjs +1 -1
  72. package/dist/permissions/adapters/v0/index.d.mts +1 -1
  73. package/dist/permissions/adapters/v0/index.mjs +1 -1
  74. package/dist/permissions/index.d.mts +1 -1
  75. package/dist/permissions/index.mjs +5 -5
  76. package/dist/{pino-Bzighnkd.d.mts → pino-CM1iLXmi.d.mts} +1 -1
  77. package/dist/{posthog-DbPqmaAp.mjs → posthog-CCQFQ5XD.mjs} +1 -1
  78. package/dist/rules/index.d.mts +1 -1
  79. package/dist/rules/index.mjs +9 -10
  80. package/dist/{server-Ch9J86tb.d.mts → server-Ct-h4-OA.d.mts} +1 -1
  81. package/dist/{server-CZ6ahbEH.mjs → server-DHET3KNW.mjs} +1 -1
  82. package/dist/storage/adapters/index.d.mts +2 -2
  83. package/dist/storage/adapters/memory/index.d.mts +1 -1
  84. package/dist/storage/index.d.mts +3 -3
  85. package/dist/storage/index.mjs +21 -21
  86. package/dist/theme/adapters/index.d.mts +1 -1
  87. package/dist/theme/adapters/index.mjs +2 -2
  88. package/dist/theme/adapters/v0/index.d.mts +1 -1
  89. package/dist/theme/adapters/v0/index.mjs +2 -2
  90. package/dist/theme/index.d.mts +1 -1
  91. package/dist/theme/index.mjs +8 -8
  92. package/dist/{useClickOutside-YOGwKo7v.mjs → useClickOutside-hOhNk4eS.mjs} +1251 -165
  93. package/dist/{useFeatures-BGDgUrdr.mjs → useFeatures-CXRQvucQ.mjs} +3 -3
  94. package/dist/{useLocale-C8I_fNAS.mjs → useLocale-BaufqE5Z.mjs} +16 -30
  95. package/dist/{useLogger-B70T4SgK.mjs → useLogger-cGK00Ywd.mjs} +2 -2
  96. package/dist/{usePermissions-afVt2qR7.mjs → usePermissions-CR0HkjsF.mjs} +2 -2
  97. package/dist/{useRules-7C92Xzyt.mjs → useRules-LD-_-Hxt.mjs} +3 -3
  98. package/dist/{useTheme-jp2wL2FJ.mjs → useTheme-Cs79AFNR.mjs} +5 -5
  99. package/dist/utilities/index.d.mts +1 -1
  100. package/dist/utilities/index.mjs +2 -1
  101. package/dist/{v0-V-cQsuZD.mjs → v0-BJYwZgtp.mjs} +1 -1
  102. package/dist/v0-BdD1F6JK.d.mts +19 -0
  103. package/dist/{v0-YAqbXudL.mjs → v0-BoWQ8RK0.mjs} +1 -1
  104. package/dist/{v0-CdUL2eOn.d.mts → v0-CMeh39LQ.d.mts} +1 -1
  105. package/dist/v0-CehUknHv.mjs +62 -0
  106. package/dist/{v0-1bfmyZ5_.d.mts → v0-DFAgNbvC.d.mts} +1 -1
  107. package/dist/{v0-1xxC39wf.d.mts → v0-JPf99NKg.d.mts} +1 -1
  108. package/dist/{v0-CGlkn5u7.mjs → v0-Zu7neOYg.mjs} +1 -1
  109. package/dist/{virtual-DwBiFA25.d.mts → virtual-B1lRbO7Q.d.mts} +1 -1
  110. package/dist/{virtual-eDNVH1fz.mjs → virtual-D0_D5gkh.mjs} +1 -1
  111. package/package.json +12 -4
  112. package/dist/v0-CAKa6tzG.d.mts +0 -22
  113. package/dist/v0-CHvU3jQE.mjs +0 -35
  114. /package/dist/{adapter-JTFG3Jn8.d.mts → adapter-CJQav2JB.d.mts} +0 -0
  115. /package/dist/{index-MHkcqY_y.d.mts → index-BL4uTXWQ.d.mts} +0 -0
  116. /package/dist/{index-B5ZDDkXv.d.mts → index-BvTD7kU8.d.mts} +0 -0
  117. /package/dist/{index-NsE5nARK.d.mts → index-DSao7i57.d.mts} +0 -0
  118. /package/dist/{memory-BTtRdnNn.d.mts → memory-DyBabFV1.d.mts} +0 -0
@@ -1,16 +1,16 @@
1
- import { a as SUPPORTS_OBSERVER, i as SUPPORTS_MUTATION_OBSERVER, n as SUPPORTS_INTERSECTION_OBSERVER, r as SUPPORTS_MATCH_MEDIA, t as IN_BROWSER } from "./globals-BqRH_jdr.mjs";
1
+ import { a as SUPPORTS_OBSERVER, i as SUPPORTS_MUTATION_OBSERVER, n as SUPPORTS_INTERSECTION_OBSERVER, r as SUPPORTS_MATCH_MEDIA, t as IN_BROWSER } from "./globals-3i9umzyz.mjs";
2
2
  import { clamp, instanceExists, isElement, isFunction, isNull, isNullOrUndefined, isNumber, isObject, isString, isUndefined, mergeDeep, useId as useId$1 } from "./utilities/index.mjs";
3
3
  import { i as useContext, n as createContext, t as createTrinity } from "./createTrinity-C_c8eedz.mjs";
4
- import { a as createPlugin, i as useLogger, o as createPluginContext } from "./useLogger-B70T4SgK.mjs";
5
- import { i as createRegistry } from "./createTokens-CMRBDlzN.mjs";
6
- import { i as createModel, t as createSelection } from "./createSelection-DvOR2tKk.mjs";
7
- import { t as createSingle } from "./createSingle-Bbcwcq4R.mjs";
8
- import { t as createGroup } from "./createGroup-Cf6lK9BR.mjs";
4
+ import { a as createPlugin, i as useLogger, o as createPluginContext } from "./useLogger-cGK00Ywd.mjs";
5
+ import { i as createRegistry } from "./createTokens-C1gIuKCK.mjs";
6
+ import { i as createModel, t as createSelection } from "./createSelection-DbWP6c7H.mjs";
7
+ import { t as createSingle } from "./createSingle-CeAJpbXs.mjs";
8
+ import { t as createGroup } from "./createGroup-PvzsLu9E.mjs";
9
9
  import { t as toArray } from "./toArray-VdROPpy-.mjs";
10
- import { a as useLocale } from "./useLocale-C8I_fNAS.mjs";
11
- import { a as useRules, o as isStandardSchema, s as toRule } from "./useRules-7C92Xzyt.mjs";
10
+ import { a as useLocale } from "./useLocale-BaufqE5Z.mjs";
11
+ import { a as useRules, o as isStandardSchema } from "./useRules-LD-_-Hxt.mjs";
12
12
  import { t as MemoryAdapter } from "./memory-XCMtkld1.mjs";
13
- import { computed, effectScope, isRef, nextTick, onScopeDispose, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRef, toValue, unref, watch, watchEffect } from "vue";
13
+ import { computed, effectScope, isRef, nextTick, onMounted, onScopeDispose, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRef, toValue, unref, watch, watchEffect } from "vue";
14
14
  //#region src/composables/createBreadcrumbs/index.ts
15
15
  /**
16
16
  * @module createBreadcrumbs
@@ -747,7 +747,8 @@ function useProxyModel(context, model, options) {
747
747
  const modelAsArray = transformIn(model);
748
748
  const pending = new Set(modelAsArray);
749
749
  context.apply(modelAsArray, applyOptions);
750
- for (const value of modelAsArray) if (Array.from(context.selectedValues.value).includes(value)) pending.delete(value);
750
+ const selected = new Set(context.selectedValues.value);
751
+ for (const value of modelAsArray) if (selected.has(value)) pending.delete(value);
751
752
  const contextWatch = watch(context.selectedValues, (val) => {
752
753
  modelWatch.pause();
753
754
  model.value = transformOut(Array.from(toValue(val)));
@@ -1010,24 +1011,22 @@ function createBreakpoints(_options = {}) {
1010
1011
  const mdAndUp = shallowRef(initialIndex >= 2);
1011
1012
  const lgAndUp = shallowRef(initialIndex >= 3);
1012
1013
  const xlAndUp = shallowRef(initialIndex >= 4);
1013
- const xxlAndUp = shallowRef(initialIndex >= 5);
1014
1014
  const smAndDown = shallowRef(initialIndex <= 1);
1015
1015
  const mdAndDown = shallowRef(initialIndex <= 2);
1016
1016
  const lgAndDown = shallowRef(initialIndex <= 3);
1017
1017
  const xlAndDown = shallowRef(initialIndex <= 4);
1018
- const xxlAndDown = shallowRef(initialIndex <= 5);
1019
1018
  function update() {
1020
1019
  if (!IN_BROWSER) return;
1021
1020
  width.value = window.innerWidth;
1022
1021
  height.value = window.innerHeight;
1023
1022
  let current = "xs";
1024
- for (let i = sorted.length - 1; i >= 0; i--) if (width.value >= sorted[i][1]) {
1023
+ for (let i = sorted.length - 1; i >= 0; i--) if (window.matchMedia(`(min-width: ${sorted[i][1]}px)`).matches) {
1025
1024
  current = sorted[i][0];
1026
1025
  break;
1027
1026
  }
1028
1027
  name.value = current;
1029
1028
  const index = names.indexOf(current);
1030
- isMobile.value = width.value < mb;
1029
+ isMobile.value = /* @__PURE__ */ isNumber(mb) ? !window.matchMedia(`(min-width: ${mb}px)`).matches : index < names.indexOf(mobileBreakpoint);
1031
1030
  xs.value = index === 0;
1032
1031
  sm.value = index === 1;
1033
1032
  md.value = index === 2;
@@ -1038,15 +1037,14 @@ function createBreakpoints(_options = {}) {
1038
1037
  mdAndUp.value = index >= 2;
1039
1038
  lgAndUp.value = index >= 3;
1040
1039
  xlAndUp.value = index >= 4;
1041
- xxlAndUp.value = index >= 5;
1042
1040
  smAndDown.value = index <= 1;
1043
1041
  mdAndDown.value = index <= 2;
1044
1042
  lgAndDown.value = index <= 3;
1045
1043
  xlAndDown.value = index <= 4;
1046
- xxlAndDown.value = index <= 5;
1047
1044
  }
1048
1045
  return {
1049
1046
  breakpoints,
1047
+ mobileBreakpoint,
1050
1048
  name: readonly(name),
1051
1049
  width: readonly(width),
1052
1050
  height: readonly(height),
@@ -1061,25 +1059,25 @@ function createBreakpoints(_options = {}) {
1061
1059
  mdAndUp: readonly(mdAndUp),
1062
1060
  lgAndUp: readonly(lgAndUp),
1063
1061
  xlAndUp: readonly(xlAndUp),
1064
- xxlAndUp: readonly(xxlAndUp),
1065
1062
  smAndDown: readonly(smAndDown),
1066
1063
  mdAndDown: readonly(mdAndDown),
1067
1064
  lgAndDown: readonly(lgAndDown),
1068
1065
  xlAndDown: readonly(xlAndDown),
1069
- xxlAndDown: readonly(xxlAndDown),
1070
1066
  ssr: isSSR,
1071
1067
  update
1072
1068
  };
1073
1069
  }
1074
1070
  function createBreakpointsFallback(options = {}) {
1075
1071
  if (options.ssr) return createBreakpoints(options);
1072
+ const defaults = createDefaultBreakpoints();
1076
1073
  return {
1077
- breakpoints: createDefaultBreakpoints().breakpoints,
1074
+ breakpoints: defaults.breakpoints,
1075
+ mobileBreakpoint: defaults.mobileBreakpoint,
1078
1076
  name: readonly(shallowRef("xs")),
1079
1077
  width: readonly(shallowRef(0)),
1080
1078
  height: readonly(shallowRef(0)),
1081
- isMobile: readonly(shallowRef(false)),
1082
- xs: readonly(shallowRef(false)),
1079
+ isMobile: readonly(shallowRef(true)),
1080
+ xs: readonly(shallowRef(true)),
1083
1081
  sm: readonly(shallowRef(false)),
1084
1082
  md: readonly(shallowRef(false)),
1085
1083
  lg: readonly(shallowRef(false)),
@@ -1089,12 +1087,10 @@ function createBreakpointsFallback(options = {}) {
1089
1087
  mdAndUp: readonly(shallowRef(false)),
1090
1088
  lgAndUp: readonly(shallowRef(false)),
1091
1089
  xlAndUp: readonly(shallowRef(false)),
1092
- xxlAndUp: readonly(shallowRef(false)),
1093
- smAndDown: readonly(shallowRef(false)),
1094
- mdAndDown: readonly(shallowRef(false)),
1095
- lgAndDown: readonly(shallowRef(false)),
1096
- xlAndDown: readonly(shallowRef(false)),
1097
- xxlAndDown: readonly(shallowRef(false)),
1090
+ smAndDown: readonly(shallowRef(true)),
1091
+ mdAndDown: readonly(shallowRef(true)),
1092
+ lgAndDown: readonly(shallowRef(true)),
1093
+ xlAndDown: readonly(shallowRef(true)),
1098
1094
  ssr: false,
1099
1095
  update: () => {}
1100
1096
  };
@@ -1362,14 +1358,14 @@ function useDate(namespace = "v0:date") {
1362
1358
  * @example
1363
1359
  * ```ts
1364
1360
  * import { createForm, createValidation } from '@vuetify/v0'
1361
+ * import { shallowRef } from 'vue'
1365
1362
  *
1366
1363
  * const form = createForm()
1367
1364
  *
1368
- * // Validations register themselves — form is just the coordinator
1369
- * const validation = createValidation()
1370
- * const email = validation.register({
1371
- * id: 'email',
1372
- * value: '',
1365
+ * // Each validation is one input — form is just the coordinator
1366
+ * const email = shallowRef('')
1367
+ * const validation = createValidation({
1368
+ * value: email,
1373
1369
  * rules: ['required', 'email'],
1374
1370
  * })
1375
1371
  *
@@ -1379,12 +1375,12 @@ function useDate(namespace = "v0:date") {
1379
1375
  * form.reset()
1380
1376
  * ```
1381
1377
  */
1382
- function createForm(options) {
1383
- const registry = createRegistry(options);
1384
- const disabled = shallowRef(false);
1385
- const readonly = shallowRef(false);
1386
- if (!/* @__PURE__ */ isNullOrUndefined(options?.disabled)) disabled.value = toValue(options.disabled) ?? false;
1387
- if (!/* @__PURE__ */ isNullOrUndefined(options?.readonly)) readonly.value = toValue(options.readonly) ?? false;
1378
+ function createForm(options = {}) {
1379
+ const { disabled = false, readonly = false, ..._options } = options;
1380
+ const registry = createRegistry({
1381
+ ..._options,
1382
+ reactive: true
1383
+ });
1388
1384
  const isValidating = computed(() => {
1389
1385
  for (const ticket of registry.values()) if (ticket.value.isValidating.value) return true;
1390
1386
  return false;
@@ -1404,14 +1400,14 @@ function createForm(options) {
1404
1400
  return registry.register(registration);
1405
1401
  }
1406
1402
  function reset() {
1407
- for (const ticket of registry.values()) for (const field of ticket.value.values()) field.reset();
1403
+ for (const ticket of registry.values()) ticket.value.reset();
1408
1404
  }
1409
1405
  async function submit(id) {
1410
1406
  const ids = id ? toArray(id) : [...registry.keys()];
1411
- return (await Promise.all(ids.map(async (id) => {
1412
- const ticket = registry.get(id);
1407
+ return (await Promise.all(ids.map(async (key) => {
1408
+ const ticket = registry.get(key);
1413
1409
  if (!ticket) return true;
1414
- return (await Promise.all([...ticket.value.values()].map((field) => field.validate()))).every(Boolean);
1410
+ return ticket.value.validate();
1415
1411
  }))).every(Boolean);
1416
1412
  }
1417
1413
  return {
@@ -2012,6 +2008,132 @@ function useElementIntersection(target, options = {}) {
2012
2008
  };
2013
2009
  }
2014
2010
  //#endregion
2011
+ //#region src/composables/useTimer/index.ts
2012
+ /**
2013
+ * @module useTimer
2014
+ *
2015
+ * @remarks
2016
+ * A reactive timer composable with pause/resume support.
2017
+ *
2018
+ * Key features:
2019
+ * - Start/stop/pause/resume controls
2020
+ * - Reactive remaining time tracking
2021
+ * - One-shot (default) or repeating mode
2022
+ * - Automatic cleanup on scope disposal
2023
+ * - SSR-safe
2024
+ */
2025
+ /**
2026
+ * Creates a reactive timer with pause/resume support.
2027
+ *
2028
+ * @param handler Callback invoked when the timer fires.
2029
+ * @param options Timer configuration.
2030
+ * @returns Timer controls and reactive state.
2031
+ *
2032
+ * @see https://0.vuetifyjs.com/composables/system/use-timer
2033
+ *
2034
+ * @example
2035
+ * ```ts
2036
+ * const timer = useTimer(() => console.log('fired'), { duration: 5000 })
2037
+ *
2038
+ * timer.start()
2039
+ * timer.pause()
2040
+ * timer.resume()
2041
+ * timer.stop()
2042
+ * ```
2043
+ */
2044
+ function useTimer(handler, options = {}) {
2045
+ const { duration = 1e3, repeat = false } = options;
2046
+ const remaining = shallowRef(duration);
2047
+ const isActive = shallowRef(false);
2048
+ const isPaused = shallowRef(false);
2049
+ let timeoutId;
2050
+ let trackingId;
2051
+ let startedAt = 0;
2052
+ let budget = duration;
2053
+ function clearTimer() {
2054
+ if (!/* @__PURE__ */ isUndefined(timeoutId)) {
2055
+ globalThis.clearTimeout(timeoutId);
2056
+ timeoutId = void 0;
2057
+ }
2058
+ }
2059
+ function startTracking() {
2060
+ stopTracking();
2061
+ if (!IN_BROWSER) return;
2062
+ trackingId = globalThis.setInterval(() => {
2063
+ if (!isActive.value || isPaused.value) return;
2064
+ const elapsed = Date.now() - startedAt;
2065
+ remaining.value = Math.max(0, budget - elapsed);
2066
+ }, 100);
2067
+ }
2068
+ function stopTracking() {
2069
+ if (!/* @__PURE__ */ isUndefined(trackingId)) {
2070
+ globalThis.clearInterval(trackingId);
2071
+ trackingId = void 0;
2072
+ }
2073
+ }
2074
+ function fire() {
2075
+ clearTimer();
2076
+ stopTracking();
2077
+ handler();
2078
+ if (repeat) begin(duration);
2079
+ else {
2080
+ isActive.value = false;
2081
+ remaining.value = 0;
2082
+ }
2083
+ }
2084
+ function begin(ms) {
2085
+ clearTimer();
2086
+ stopTracking();
2087
+ budget = ms;
2088
+ startedAt = Date.now();
2089
+ remaining.value = ms;
2090
+ isActive.value = true;
2091
+ isPaused.value = false;
2092
+ timeoutId = globalThis.setTimeout(fire, ms);
2093
+ startTracking();
2094
+ }
2095
+ function start() {
2096
+ if (duration < 0) return;
2097
+ begin(duration);
2098
+ }
2099
+ function stop() {
2100
+ clearTimer();
2101
+ stopTracking();
2102
+ isActive.value = false;
2103
+ isPaused.value = false;
2104
+ remaining.value = duration;
2105
+ }
2106
+ function pause() {
2107
+ if (!isActive.value || isPaused.value) return;
2108
+ const elapsed = Date.now() - startedAt;
2109
+ budget = Math.max(0, budget - elapsed);
2110
+ remaining.value = budget;
2111
+ clearTimer();
2112
+ stopTracking();
2113
+ isPaused.value = true;
2114
+ }
2115
+ function resume() {
2116
+ if (!isPaused.value) return;
2117
+ isPaused.value = false;
2118
+ startedAt = Date.now();
2119
+ timeoutId = globalThis.setTimeout(fire, budget);
2120
+ startTracking();
2121
+ }
2122
+ onScopeDispose(() => {
2123
+ clearTimer();
2124
+ stopTracking();
2125
+ }, true);
2126
+ return {
2127
+ start,
2128
+ stop,
2129
+ pause,
2130
+ resume,
2131
+ remaining,
2132
+ isActive,
2133
+ isPaused
2134
+ };
2135
+ }
2136
+ //#endregion
2015
2137
  //#region src/composables/useLazy/index.ts
2016
2138
  /**
2017
2139
  * @module useLazy
@@ -2075,11 +2197,16 @@ function useElementIntersection(target, options = {}) {
2075
2197
  * ```
2076
2198
  */
2077
2199
  function useLazy(active, options = {}) {
2078
- const { eager = false } = options;
2200
+ const { eager = false, delay = 0 } = options;
2079
2201
  const isBooted = shallowRef(false);
2080
2202
  const hasContent = toRef(() => isBooted.value || toValue(eager) || toValue(active));
2203
+ const timer = delay > 0 ? useTimer(() => {
2204
+ isBooted.value = true;
2205
+ }, { duration: delay }) : void 0;
2081
2206
  const stop = watch(() => toValue(active), (value) => {
2082
- if (value) isBooted.value = true;
2207
+ if (value) if (timer) timer.start();
2208
+ else isBooted.value = true;
2209
+ else timer?.stop();
2083
2210
  }, { immediate: true });
2084
2211
  function reset() {
2085
2212
  isBooted.value = false;
@@ -2140,7 +2267,7 @@ function useLazy(active, options = {}) {
2140
2267
  function useMediaQuery(query) {
2141
2268
  const matches = shallowRef(false);
2142
2269
  const mediaQueryList = shallowRef(null);
2143
- const resolvedQuery = computed(() => toValue(query));
2270
+ const resolvedQuery = toRef(() => toValue(query));
2144
2271
  const hydration = useHydration();
2145
2272
  let cleanup = null;
2146
2273
  function update() {
@@ -2985,21 +3112,22 @@ function createQueue(_options = {}) {
2985
3112
  ...options,
2986
3113
  events: true
2987
3114
  });
2988
- const timeouts = /* @__PURE__ */ new Map();
2989
- function startTimeout(ticket) {
3115
+ const timers = /* @__PURE__ */ new Map();
3116
+ function createTicketTimer(ticket) {
2990
3117
  if (/* @__PURE__ */ isUndefined(ticket.timeout) || ticket.timeout < 0 || ticket.isPaused) return;
2991
- const timeout = setTimeout(() => {
2992
- timeouts.delete(ticket.id);
3118
+ const timer = useTimer(() => {
3119
+ timers.delete(ticket.id);
2993
3120
  registry.unregister(ticket.id);
2994
3121
  resume();
2995
- }, ticket.timeout);
2996
- timeouts.set(ticket.id, timeout);
2997
- }
2998
- function clearTimeout(id) {
2999
- const timeout = timeouts.get(id);
3000
- if (timeout) {
3001
- globalThis.clearTimeout(timeout);
3002
- timeouts.delete(id);
3122
+ }, { duration: ticket.timeout });
3123
+ timers.set(ticket.id, timer);
3124
+ timer.start();
3125
+ }
3126
+ function stopTimer(id) {
3127
+ const timer = timers.get(id);
3128
+ if (timer) {
3129
+ timer.stop();
3130
+ timers.delete(id);
3003
3131
  }
3004
3132
  }
3005
3133
  function register(registration = {}) {
@@ -3013,14 +3141,14 @@ function createQueue(_options = {}) {
3013
3141
  dismiss: () => unregister(id)
3014
3142
  };
3015
3143
  const registered = registry.register(ticket);
3016
- startTimeout(registered);
3144
+ createTicketTimer(registered);
3017
3145
  return registered;
3018
3146
  }
3019
3147
  function unregister(id) {
3020
3148
  const ticket = /* @__PURE__ */ isUndefined(id) ? registry.seek("first") : registry.get(id);
3021
3149
  if (!ticket) return void 0;
3022
3150
  const wasFirst = ticket.index === 0;
3023
- clearTimeout(ticket.id);
3151
+ stopTimer(ticket.id);
3024
3152
  registry.unregister(ticket.id);
3025
3153
  if (wasFirst) resume();
3026
3154
  return ticket;
@@ -3031,7 +3159,7 @@ function createQueue(_options = {}) {
3031
3159
  const ticket = registry.get(id);
3032
3160
  if (!ticket) continue;
3033
3161
  if (ticket.index === 0) hadFirst = true;
3034
- clearTimeout(ticket.id);
3162
+ stopTimer(ticket.id);
3035
3163
  }
3036
3164
  registry.offboard(ids);
3037
3165
  if (hadFirst) resume();
@@ -3039,18 +3167,21 @@ function createQueue(_options = {}) {
3039
3167
  function pause() {
3040
3168
  const ticket = registry.seek("first");
3041
3169
  if (!ticket || ticket.isPaused) return void 0;
3042
- clearTimeout(ticket.id);
3170
+ timers.get(ticket.id)?.pause();
3043
3171
  return registry.upsert(ticket.id, { isPaused: true });
3044
3172
  }
3045
3173
  function resume() {
3046
3174
  const ticket = registry.seek("first");
3047
3175
  if (!ticket || ticket.index !== 0 || !ticket.isPaused) return void 0;
3048
3176
  const updated = registry.upsert(ticket.id, { isPaused: false });
3049
- startTimeout(updated);
3177
+ const timer = timers.get(updated.id);
3178
+ if (timer) timer.start();
3179
+ else createTicketTimer(updated);
3050
3180
  return updated;
3051
3181
  }
3052
3182
  function clear() {
3053
- for (const id of timeouts.keys()) clearTimeout(id);
3183
+ for (const timer of timers.values()) timer.stop();
3184
+ timers.clear();
3054
3185
  registry.clear();
3055
3186
  }
3056
3187
  function dispose() {
@@ -3125,6 +3256,909 @@ function useQueue(namespace = "v0:queue") {
3125
3256
  return useContext(namespace);
3126
3257
  }
3127
3258
  //#endregion
3259
+ //#region src/composables/useNotifications/index.ts
3260
+ /**
3261
+ * @module useNotifications
3262
+ * @see https://0.vuetifyjs.com/composables/plugins/use-notifications
3263
+ *
3264
+ * @remarks
3265
+ * Notification management composable built on createRegistry and createQueue.
3266
+ * Registry stores the full notification lifecycle (permanent record).
3267
+ * Queue manages the display surface (toasts) with FIFO ordering and auto-dismiss.
3268
+ *
3269
+ * Supports:
3270
+ * - Push notifications with severity levels and configurable timeouts
3271
+ * - State mutations: read, seen, archived, snoozed
3272
+ * - Bulk operations: readAll, archiveAll, clear
3273
+ * - Toast queue with pause/resume and auto-dismiss
3274
+ * - Adapter integration via event system
3275
+ * - Plugin installation via createNotificationsPlugin
3276
+ */
3277
+ function createNotifications(options = {}) {
3278
+ const { timeout = 3e3, ...registryOptions } = options;
3279
+ const registry = createRegistry({
3280
+ ...registryOptions,
3281
+ events: true,
3282
+ reactive: true
3283
+ });
3284
+ const queue = createQueue({
3285
+ timeout,
3286
+ events: true,
3287
+ reactive: true
3288
+ });
3289
+ registry.on("unregister:ticket", (ticket) => {
3290
+ if (queue.has(ticket.id)) queue.unregister(ticket.id);
3291
+ });
3292
+ /**
3293
+ * Build a full {@link NotificationTicket} from raw input
3294
+ *
3295
+ * @param input The notification input to hydrate.
3296
+ * @returns A partial ticket with timestamps, lifecycle methods, and an ID.
3297
+ * @internal Used by {@link send}, {@link register}, and {@link onboard}.
3298
+ */
3299
+ function hydrate(input) {
3300
+ const id = input.id ?? /* @__PURE__ */ useId$1();
3301
+ const now = /* @__PURE__ */ new Date();
3302
+ return {
3303
+ ...input,
3304
+ id,
3305
+ createdAt: now,
3306
+ readAt: null,
3307
+ seenAt: null,
3308
+ archivedAt: null,
3309
+ snoozedUntil: null,
3310
+ read: () => read(id),
3311
+ unread: () => unread(id),
3312
+ seen: () => seen(id),
3313
+ archive: () => archive(id),
3314
+ unarchive: () => unarchive(id),
3315
+ snooze: (until) => snooze(id, until),
3316
+ wake: () => wake(id),
3317
+ dismiss: () => queue.unregister(id)
3318
+ };
3319
+ }
3320
+ /**
3321
+ * Create a notification and enqueue it for toast display
3322
+ *
3323
+ * @param input The notification input to create.
3324
+ * @returns The hydrated notification ticket.
3325
+ * @remarks Hydrates the input with timestamps and lifecycle methods, registers
3326
+ * it in the registry, adds it to the display queue, and emits
3327
+ * `notification:received`. The ticket auto-dismisses after the configured
3328
+ * timeout unless paused.
3329
+ *
3330
+ * @see https://0.vuetifyjs.com/composables/plugins/use-notifications#send
3331
+ *
3332
+ * @example
3333
+ * ```ts
3334
+ * import { useNotifications } from '@vuetify/v0'
3335
+ *
3336
+ * const notifications = useNotifications()
3337
+ *
3338
+ * // Basic toast
3339
+ * notifications.send({ subject: 'Changes saved', severity: 'success' })
3340
+ *
3341
+ * // With custom timeout
3342
+ * const ticket = notifications.send({ subject: 'Connection lost', severity: 'error', timeout: 10000 })
3343
+ *
3344
+ * console.log(ticket.id) // 'generated-id'
3345
+ * console.log(ticket.createdAt) // Date
3346
+ * console.log(ticket.readAt) // null
3347
+ *
3348
+ * // Ticket convenience methods
3349
+ * ticket.read() // marks as read
3350
+ * ticket.dismiss() // removes from toast queue, keeps in registry
3351
+ * ```
3352
+ */
3353
+ function send(input) {
3354
+ const ticket = registry.register(hydrate(input));
3355
+ queue.register(/* @__PURE__ */ isUndefined(input.timeout) ? { id: ticket.id } : {
3356
+ id: ticket.id,
3357
+ timeout: input.timeout
3358
+ });
3359
+ registry.emit("notification:received", ticket);
3360
+ return ticket;
3361
+ }
3362
+ /**
3363
+ * Register a notification in the registry without adding it to the toast queue
3364
+ *
3365
+ * @param input The notification input to register.
3366
+ * @returns The hydrated notification ticket.
3367
+ * @remarks Overrides the base registry `register` to hydrate the input with
3368
+ * timestamps and lifecycle methods. Use for historical or pre-existing items
3369
+ * that should appear in an inbox but not pop up as toasts.
3370
+ *
3371
+ * @see https://0.vuetifyjs.com/composables/plugins/use-notifications#register
3372
+ *
3373
+ * @example
3374
+ * ```ts
3375
+ * import { useNotifications } from '@vuetify/v0'
3376
+ *
3377
+ * const notifications = useNotifications()
3378
+ *
3379
+ * // Register a historical item (appears in inbox, not as toast)
3380
+ * const ticket = notifications.register({ id: 'old-1', subject: 'Deployed v2.1' })
3381
+ *
3382
+ * console.log(ticket.createdAt) // Date
3383
+ * console.log(ticket.readAt) // null
3384
+ *
3385
+ * // Lifecycle methods work the same as send()
3386
+ * ticket.read()
3387
+ * ticket.archive()
3388
+ * ```
3389
+ */
3390
+ function register(input) {
3391
+ return registry.register(hydrate(input));
3392
+ }
3393
+ /**
3394
+ * Bulk version of {@link register}
3395
+ *
3396
+ * @param inputs Array of notification inputs to register.
3397
+ * @returns Array of hydrated notification tickets.
3398
+ * @remarks Registers multiple notifications into the registry without toast
3399
+ * display. Wraps all registrations in a single `registry.batch()` call so
3400
+ * reactive updates are deferred until the entire batch completes.
3401
+ *
3402
+ * @see https://0.vuetifyjs.com/composables/plugins/use-notifications#onboard
3403
+ *
3404
+ * @example
3405
+ * ```ts
3406
+ * import { useNotifications } from '@vuetify/v0'
3407
+ *
3408
+ * const notifications = useNotifications()
3409
+ *
3410
+ * // Load historical items on mount
3411
+ * const tickets = notifications.onboard([
3412
+ * { id: 'n1', subject: 'Welcome to the team' },
3413
+ * { id: 'n2', subject: 'Setup complete', severity: 'success' },
3414
+ * ])
3415
+ *
3416
+ * console.log(tickets.length) // 2
3417
+ * console.log(notifications.queue.values()) // [] (no toasts)
3418
+ * console.log(notifications.values().length) // 2 (in registry)
3419
+ * ```
3420
+ */
3421
+ function onboard(inputs) {
3422
+ return registry.batch(() => inputs.map((input) => register(input)));
3423
+ }
3424
+ /**
3425
+ * Mark a notification as read
3426
+ *
3427
+ * @param id The ID of the notification to mark as read.
3428
+ * @remarks Sets `readAt` to the current time and emits `notification:read`.
3429
+ * Adapters listen to this event for outbound sync.
3430
+ *
3431
+ * @see https://0.vuetifyjs.com/composables/plugins/use-notifications#read
3432
+ *
3433
+ * @example
3434
+ * ```ts
3435
+ * import { useNotifications } from '@vuetify/v0'
3436
+ *
3437
+ * const notifications = useNotifications()
3438
+ *
3439
+ * const ticket = notifications.send({ subject: 'New message' })
3440
+ *
3441
+ * notifications.read(ticket.id)
3442
+ * console.log(ticket.readAt) // Date
3443
+ *
3444
+ * // Or via ticket convenience method
3445
+ * ticket.read()
3446
+ * ```
3447
+ */
3448
+ function read(id) {
3449
+ registry.upsert(id, { readAt: /* @__PURE__ */ new Date() }, "notification:read");
3450
+ }
3451
+ /**
3452
+ * Mark a notification as unread
3453
+ *
3454
+ * @param id The ID of the notification to mark as unread.
3455
+ * @remarks Clears `readAt` and emits `notification:unread`.
3456
+ *
3457
+ * @see https://0.vuetifyjs.com/composables/plugins/use-notifications#unread
3458
+ *
3459
+ * @example
3460
+ * ```ts
3461
+ * import { useNotifications } from '@vuetify/v0'
3462
+ *
3463
+ * const notifications = useNotifications()
3464
+ *
3465
+ * notifications.unread('notif-1')
3466
+ * ```
3467
+ */
3468
+ function unread(id) {
3469
+ registry.upsert(id, { readAt: null }, "notification:unread");
3470
+ }
3471
+ /**
3472
+ * Mark a notification as seen
3473
+ *
3474
+ * @param id The ID of the notification to mark as seen.
3475
+ * @remarks Sets `seenAt` to the current time and emits `notification:seen`.
3476
+ * Distinct from read — seen tracks whether the notification appeared in the
3477
+ * user's viewport, while read indicates the user actively engaged with it.
3478
+ *
3479
+ * @see https://0.vuetifyjs.com/composables/plugins/use-notifications#seen
3480
+ *
3481
+ * @example
3482
+ * ```ts
3483
+ * import { useNotifications } from '@vuetify/v0'
3484
+ *
3485
+ * const notifications = useNotifications()
3486
+ *
3487
+ * // Mark as seen when visible in inbox viewport
3488
+ * notifications.seen('notif-1')
3489
+ * ```
3490
+ */
3491
+ function seen(id) {
3492
+ registry.upsert(id, { seenAt: /* @__PURE__ */ new Date() }, "notification:seen");
3493
+ }
3494
+ /**
3495
+ * Archive a notification
3496
+ *
3497
+ * @param id The ID of the notification to archive.
3498
+ * @remarks Sets `archivedAt` to the current time and emits `notification:archived`.
3499
+ *
3500
+ * @see https://0.vuetifyjs.com/composables/plugins/use-notifications#archive
3501
+ *
3502
+ * @example
3503
+ * ```ts
3504
+ * import { useNotifications } from '@vuetify/v0'
3505
+ *
3506
+ * const notifications = useNotifications()
3507
+ *
3508
+ * notifications.archive('notif-1')
3509
+ * ```
3510
+ */
3511
+ function archive(id) {
3512
+ registry.upsert(id, { archivedAt: /* @__PURE__ */ new Date() }, "notification:archived");
3513
+ }
3514
+ /**
3515
+ * Restore an archived notification
3516
+ *
3517
+ * @param id The ID of the notification to unarchive.
3518
+ * @remarks Clears `archivedAt` and emits `notification:unarchived`.
3519
+ *
3520
+ * @see https://0.vuetifyjs.com/composables/plugins/use-notifications#unarchive
3521
+ *
3522
+ * @example
3523
+ * ```ts
3524
+ * import { useNotifications } from '@vuetify/v0'
3525
+ *
3526
+ * const notifications = useNotifications()
3527
+ *
3528
+ * notifications.unarchive('notif-1')
3529
+ * ```
3530
+ */
3531
+ function unarchive(id) {
3532
+ registry.upsert(id, { archivedAt: null }, "notification:unarchived");
3533
+ }
3534
+ /**
3535
+ * Snooze a notification until a given time
3536
+ *
3537
+ * @param id The ID of the notification to snooze.
3538
+ * @param until The time at which the snooze expires.
3539
+ * @remarks Sets `snoozedUntil` and emits `notification:snoozed`. The composable
3540
+ * does not auto-wake — consumers are responsible for calling {@link wake} when
3541
+ * the snooze expires (e.g., via a timer or server push).
3542
+ *
3543
+ * @see https://0.vuetifyjs.com/composables/plugins/use-notifications#snooze
3544
+ *
3545
+ * @example
3546
+ * ```ts
3547
+ * import { useNotifications } from '@vuetify/v0'
3548
+ *
3549
+ * const notifications = useNotifications()
3550
+ *
3551
+ * const tomorrow = new Date(Date.now() + 86_400_000)
3552
+ * notifications.snooze('notif-1', tomorrow)
3553
+ * ```
3554
+ */
3555
+ function snooze(id, until) {
3556
+ registry.upsert(id, { snoozedUntil: until }, "notification:snoozed");
3557
+ }
3558
+ /**
3559
+ * Cancel a snooze
3560
+ *
3561
+ * @param id The ID of the notification to wake.
3562
+ * @remarks Clears `snoozedUntil` and emits `notification:unsnoozed`.
3563
+ *
3564
+ * @see https://0.vuetifyjs.com/composables/plugins/use-notifications#wake
3565
+ *
3566
+ * @example
3567
+ * ```ts
3568
+ * import { useNotifications } from '@vuetify/v0'
3569
+ *
3570
+ * const notifications = useNotifications()
3571
+ *
3572
+ * notifications.wake('notif-1')
3573
+ * ```
3574
+ */
3575
+ function wake(id) {
3576
+ registry.upsert(id, { snoozedUntil: null }, "notification:unsnoozed");
3577
+ }
3578
+ /**
3579
+ * Mark all unread notifications as read
3580
+ *
3581
+ * @remarks Iterates all notifications and marks unread ones as read in a single
3582
+ * batch. Skips already-read notifications to preserve their original `readAt`
3583
+ * timestamp. Emits `notification:read` for each affected notification.
3584
+ *
3585
+ * @see https://0.vuetifyjs.com/composables/plugins/use-notifications#readall
3586
+ *
3587
+ * @example
3588
+ * ```ts
3589
+ * import { useNotifications } from '@vuetify/v0'
3590
+ *
3591
+ * const notifications = useNotifications()
3592
+ *
3593
+ * notifications.send({ subject: 'Message 1' })
3594
+ * notifications.send({ subject: 'Message 2' })
3595
+ *
3596
+ * notifications.readAll()
3597
+ *
3598
+ * console.log(notifications.values().every(t => t.readAt !== null)) // true
3599
+ * ```
3600
+ */
3601
+ function readAll() {
3602
+ const now = /* @__PURE__ */ new Date();
3603
+ registry.batch(() => {
3604
+ for (const ticket of registry.values()) if (/* @__PURE__ */ isNull(ticket.readAt)) registry.upsert(ticket.id, { readAt: now }, "notification:read");
3605
+ });
3606
+ }
3607
+ /**
3608
+ * Archive all unarchived notifications
3609
+ *
3610
+ * @remarks Iterates all notifications and archives unarchived ones in a single
3611
+ * batch. Skips already-archived notifications to preserve their original
3612
+ * `archivedAt` timestamp. Emits `notification:archived` for each affected notification.
3613
+ *
3614
+ * @see https://0.vuetifyjs.com/composables/plugins/use-notifications#archiveall
3615
+ *
3616
+ * @example
3617
+ * ```ts
3618
+ * import { useNotifications } from '@vuetify/v0'
3619
+ *
3620
+ * const notifications = useNotifications()
3621
+ *
3622
+ * notifications.send({ subject: 'Alert 1' })
3623
+ * notifications.send({ subject: 'Alert 2' })
3624
+ *
3625
+ * notifications.archiveAll()
3626
+ *
3627
+ * console.log(notifications.values().every(t => t.archivedAt !== null)) // true
3628
+ * ```
3629
+ */
3630
+ function archiveAll() {
3631
+ const now = /* @__PURE__ */ new Date();
3632
+ registry.batch(() => {
3633
+ for (const ticket of registry.values()) if (/* @__PURE__ */ isNull(ticket.archivedAt)) registry.upsert(ticket.id, { archivedAt: now }, "notification:archived");
3634
+ });
3635
+ }
3636
+ return {
3637
+ ...registry,
3638
+ send,
3639
+ register,
3640
+ onboard,
3641
+ queue,
3642
+ read,
3643
+ unread,
3644
+ seen,
3645
+ archive,
3646
+ unarchive,
3647
+ snooze,
3648
+ wake,
3649
+ readAll,
3650
+ archiveAll,
3651
+ get size() {
3652
+ return registry.size;
3653
+ }
3654
+ };
3655
+ }
3656
+ function noop() {}
3657
+ const EMPTY = [];
3658
+ function createNotificationsFallback() {
3659
+ const stub = {
3660
+ id: "",
3661
+ index: 0,
3662
+ value: 0,
3663
+ valueIsIndex: true,
3664
+ createdAt: /* @__PURE__ */ new Date(0),
3665
+ readAt: null,
3666
+ seenAt: null,
3667
+ archivedAt: null,
3668
+ snoozedUntil: null,
3669
+ read: noop,
3670
+ unread: noop,
3671
+ seen: noop,
3672
+ archive: noop,
3673
+ unarchive: noop,
3674
+ snooze: noop,
3675
+ wake: noop,
3676
+ dismiss: noop,
3677
+ unregister: noop
3678
+ };
3679
+ return {
3680
+ collection: /* @__PURE__ */ new Map(),
3681
+ size: 0,
3682
+ send: () => stub,
3683
+ queue: {
3684
+ collection: /* @__PURE__ */ new Map(),
3685
+ size: 0,
3686
+ register: () => ({}),
3687
+ unregister: () => void 0,
3688
+ has: () => false,
3689
+ get: () => void 0,
3690
+ keys: () => EMPTY,
3691
+ values: () => EMPTY,
3692
+ entries: () => EMPTY,
3693
+ browse: () => void 0,
3694
+ lookup: () => void 0,
3695
+ upsert: () => ({}),
3696
+ seek: () => void 0,
3697
+ move: () => void 0,
3698
+ on: noop,
3699
+ off: noop,
3700
+ emit: noop,
3701
+ dispose: noop,
3702
+ clear: noop,
3703
+ reindex: noop,
3704
+ batch: (fn) => fn(),
3705
+ onboard: () => EMPTY,
3706
+ offboard: noop,
3707
+ pause: () => void 0,
3708
+ resume: () => void 0
3709
+ },
3710
+ read: noop,
3711
+ unread: noop,
3712
+ seen: noop,
3713
+ archive: noop,
3714
+ unarchive: noop,
3715
+ snooze: noop,
3716
+ wake: noop,
3717
+ readAll: noop,
3718
+ archiveAll: noop,
3719
+ clear: noop,
3720
+ has: () => false,
3721
+ get: () => void 0,
3722
+ keys: () => EMPTY,
3723
+ values: () => EMPTY,
3724
+ entries: () => EMPTY,
3725
+ browse: () => void 0,
3726
+ lookup: () => void 0,
3727
+ upsert: () => stub,
3728
+ register: () => stub,
3729
+ unregister: noop,
3730
+ reindex: noop,
3731
+ move: () => void 0,
3732
+ seek: () => void 0,
3733
+ on: noop,
3734
+ off: noop,
3735
+ emit: noop,
3736
+ dispose: noop,
3737
+ batch: (fn) => fn(),
3738
+ onboard: () => EMPTY,
3739
+ offboard: noop
3740
+ };
3741
+ }
3742
+ /**
3743
+ * Notification management with registry, event system, and optional adapters.
3744
+ *
3745
+ * @see https://0.vuetifyjs.com/composables/plugins/use-notifications
3746
+ *
3747
+ * @example
3748
+ * ```ts
3749
+ * import { createNotificationsPlugin, useNotifications } from '@vuetify/v0'
3750
+ *
3751
+ * // Install as plugin
3752
+ * app.use(createNotificationsPlugin())
3753
+ *
3754
+ * // Use in components
3755
+ * const notifications = useNotifications()
3756
+ * notifications.send({ subject: 'Saved', severity: 'success' })
3757
+ * ```
3758
+ */
3759
+ const [createNotificationsContext, createNotificationsPlugin, useNotifications] = createPluginContext("v0:notifications", (options) => createNotifications(options), {
3760
+ fallback: () => createNotificationsFallback(),
3761
+ setup: (context, app, options) => {
3762
+ app.onUnmount(() => {
3763
+ context.dispose();
3764
+ context.queue.dispose();
3765
+ });
3766
+ const { adapter } = options;
3767
+ if (!adapter) return;
3768
+ adapter.setup({
3769
+ send: context.send,
3770
+ register: context.register,
3771
+ on: context.on,
3772
+ off: context.off
3773
+ });
3774
+ if (/* @__PURE__ */ isFunction(adapter.dispose)) app.onUnmount(() => adapter.dispose());
3775
+ }
3776
+ });
3777
+ //#endregion
3778
+ //#region src/composables/usePopover/index.ts
3779
+ /**
3780
+ * @module usePopover
3781
+ *
3782
+ * @remarks
3783
+ * Composable for native popover API behavior with CSS anchor positioning.
3784
+ * Manages open/close state, anchor styles, content attributes, and
3785
+ * bidirectional sync between reactive state and native popover events.
3786
+ *
3787
+ * Key features:
3788
+ * - Native popover API (showPopover/hidePopover)
3789
+ * - CSS anchor positioning (position-area, position-try-fallbacks)
3790
+ * - Toggle event sync for native state changes
3791
+ * - SSR-safe (no DOM ops outside browser)
3792
+ * - Optional external isOpen ref for v-model integration
3793
+ *
3794
+ * Perfect for building select, combobox, tooltip, and menu components
3795
+ * without wrapping the Popover compound component.
3796
+ */
3797
+ function usePopover(options = {}) {
3798
+ const { id: _id, positionArea = "bottom", positionTry = "most-width bottom", showDelay = 0, hideDelay = 0 } = options;
3799
+ const id = _id ?? /* @__PURE__ */ useId$1();
3800
+ const isOpen = options.isOpen ?? shallowRef(false);
3801
+ const showTimer = showDelay > 0 ? useTimer(() => {
3802
+ isOpen.value = true;
3803
+ }, { duration: showDelay }) : void 0;
3804
+ const hideTimer = hideDelay > 0 ? useTimer(() => {
3805
+ isOpen.value = false;
3806
+ }, { duration: hideDelay }) : void 0;
3807
+ function open() {
3808
+ hideTimer?.stop();
3809
+ if (showTimer) showTimer.start();
3810
+ else isOpen.value = true;
3811
+ }
3812
+ function close() {
3813
+ showTimer?.stop();
3814
+ if (hideTimer) hideTimer.start();
3815
+ else isOpen.value = false;
3816
+ }
3817
+ function toggle() {
3818
+ if (isOpen.value) close();
3819
+ else open();
3820
+ }
3821
+ const anchorStyles = toRef(() => ({ anchorName: `--${id}` }));
3822
+ const contentAttrs = toRef(() => ({
3823
+ id,
3824
+ popover: ""
3825
+ }));
3826
+ const contentStyles = toRef(() => ({
3827
+ "position": "fixed",
3828
+ "margin": "unset",
3829
+ "inset-area": positionArea,
3830
+ "position-area": positionArea,
3831
+ "position-anchor": `--${id}`,
3832
+ "position-try-fallbacks": positionTry
3833
+ }));
3834
+ /* v8 ignore start -- browser popover API */
3835
+ function attach(el) {
3836
+ onMounted(() => {
3837
+ const element = toValue(el);
3838
+ if (isOpen.value) element?.showPopover();
3839
+ });
3840
+ watch(isOpen, (value) => {
3841
+ const element = toValue(el);
3842
+ if (!element?.isConnected) return;
3843
+ if (value === element.matches?.(":popover-open")) return;
3844
+ if (value) element.showPopover?.();
3845
+ else element.hidePopover?.();
3846
+ });
3847
+ useEventListener(el, "toggle", (e) => {
3848
+ if (!toValue(el)?.isConnected) return;
3849
+ isOpen.value = e.newState === "open";
3850
+ });
3851
+ }
3852
+ /* v8 ignore stop */
3853
+ return {
3854
+ isOpen,
3855
+ id,
3856
+ open,
3857
+ close,
3858
+ toggle,
3859
+ anchorStyles,
3860
+ contentAttrs,
3861
+ contentStyles,
3862
+ attach
3863
+ };
3864
+ }
3865
+ //#endregion
3866
+ //#region src/composables/useRaf/index.ts
3867
+ /**
3868
+ * @module useRaf
3869
+ *
3870
+ * @see https://0.vuetifyjs.com/composables/system/use-raf
3871
+ *
3872
+ * @remarks
3873
+ * Scope-disposed safe requestAnimationFrame composable.
3874
+ *
3875
+ * Key features:
3876
+ * - Cancel-then-request pattern (deduplicates rapid calls)
3877
+ * - Automatic cleanup on scope disposal
3878
+ * - SSR-safe (no-op in non-browser environments)
3879
+ * - Callable function with cancel method
3880
+ *
3881
+ * Perfect for throttling updates to the next animation frame.
3882
+ */
3883
+ /**
3884
+ * Scope-disposed safe requestAnimationFrame.
3885
+ *
3886
+ * @param callback Function to call on animation frame.
3887
+ * @returns Callable that requests frame (canceling pending), with cancel method.
3888
+ *
3889
+ * @see https://0.vuetifyjs.com/composables/system/use-raf
3890
+ *
3891
+ * @example
3892
+ * ```ts
3893
+ * // Basic usage
3894
+ * const update = useRaf((timestamp) => {
3895
+ * // Runs on next animation frame
3896
+ * })
3897
+ *
3898
+ * update() // Request frame
3899
+ * update() // Cancels previous, requests new (deduplicated)
3900
+ *
3901
+ * // Manual cancel
3902
+ * update.cancel()
3903
+ *
3904
+ * // Check if pending
3905
+ * if (update.isActive.value) { ... }
3906
+ * ```
3907
+ */
3908
+ function useRaf(callback) {
3909
+ const id = shallowRef();
3910
+ const isActive = toRef(() => !/* @__PURE__ */ isUndefined(id.value));
3911
+ function cancel() {
3912
+ if (!/* @__PURE__ */ isUndefined(id.value)) {
3913
+ cancelAnimationFrame(id.value);
3914
+ id.value = void 0;
3915
+ }
3916
+ }
3917
+ function request() {
3918
+ if (!IN_BROWSER) return;
3919
+ cancel();
3920
+ id.value = requestAnimationFrame((timestamp) => {
3921
+ id.value = void 0;
3922
+ callback(timestamp);
3923
+ });
3924
+ }
3925
+ onScopeDispose(cancel, true);
3926
+ return Object.assign(request, {
3927
+ cancel,
3928
+ isActive
3929
+ });
3930
+ }
3931
+ //#endregion
3932
+ //#region src/composables/createFocusTraversal/index.ts
3933
+ /**
3934
+ * @module createFocusTraversal
3935
+ *
3936
+ * @remarks
3937
+ * Internal factory for keyboard-driven traversal of ordered item lists.
3938
+ * Encapsulates stride-based navigation, disabled-item skipping, circular/bounded
3939
+ * movement, grid row scoping, and key-to-action mapping shared by
3940
+ * useRovingFocus and useVirtualFocus.
3941
+ *
3942
+ * Not exported from the package barrel — consumed only by sibling composables.
3943
+ */
3944
+ function mod(length, index) {
3945
+ return (index % length + length) % length;
3946
+ }
3947
+ function isRtl(e) {
3948
+ if (!IN_BROWSER) return false;
3949
+ const el = e.currentTarget;
3950
+ return el ? getComputedStyle(el).direction === "rtl" : false;
3951
+ }
3952
+ function createFocusTraversal(items, activate, options = {}) {
3953
+ const { orientation = "vertical", circular = false, columns: _columns } = options;
3954
+ const activeId = shallowRef();
3955
+ function enabled() {
3956
+ return items().filter((item) => !toValue(item.disabled));
3957
+ }
3958
+ function indexOf(id) {
3959
+ return items().findIndex((item) => item.id === id);
3960
+ }
3961
+ function go(id) {
3962
+ activeId.value = id;
3963
+ activate(id);
3964
+ }
3965
+ function step(stride) {
3966
+ const all = items();
3967
+ const length = all.length;
3968
+ if (!length) return;
3969
+ const current = /* @__PURE__ */ isUndefined(activeId.value) ? -1 : indexOf(activeId.value);
3970
+ const direction = stride > 0 ? 1 : -1;
3971
+ const absStride = Math.abs(stride);
3972
+ let index = current + stride;
3973
+ const maxHops = Math.ceil(length / absStride);
3974
+ let hops = 0;
3975
+ if (circular) index = mod(length, index);
3976
+ else if (index < 0 || index >= length) return;
3977
+ while (hops < maxHops) {
3978
+ const item = all[index];
3979
+ if (item && !toValue(item.disabled)) {
3980
+ go(item.id);
3981
+ return;
3982
+ }
3983
+ hops++;
3984
+ index = circular ? mod(length, index + stride) : index + direction * absStride;
3985
+ if (!circular && (index < 0 || index >= length)) return;
3986
+ }
3987
+ }
3988
+ function next() {
3989
+ step(1);
3990
+ }
3991
+ function prev() {
3992
+ step(-1);
3993
+ }
3994
+ function first() {
3995
+ const list = enabled();
3996
+ if (list.length > 0) go(list[0].id);
3997
+ }
3998
+ function last() {
3999
+ const list = enabled();
4000
+ if (list.length > 0) go(list.at(-1).id);
4001
+ }
4002
+ /** Focus first non-disabled item in the current row (grid mode) */
4003
+ function rowFirst() {
4004
+ const cols = toValue(_columns) ?? 0;
4005
+ if (!cols) return first();
4006
+ const all = items();
4007
+ const current = /* @__PURE__ */ isUndefined(activeId.value) ? 0 : indexOf(activeId.value);
4008
+ const rowStart = current - current % cols;
4009
+ const rowEnd = Math.min(rowStart + cols, all.length);
4010
+ for (let i = rowStart; i < rowEnd; i++) {
4011
+ const item = all[i];
4012
+ if (item && !toValue(item.disabled)) {
4013
+ go(item.id);
4014
+ return;
4015
+ }
4016
+ }
4017
+ }
4018
+ /** Focus last non-disabled item in the current row (grid mode) */
4019
+ function rowLast() {
4020
+ const cols = toValue(_columns) ?? 0;
4021
+ if (!cols) return last();
4022
+ const all = items();
4023
+ const current = /* @__PURE__ */ isUndefined(activeId.value) ? 0 : indexOf(activeId.value);
4024
+ const rowStart = current - current % cols;
4025
+ const rowEnd = Math.min(rowStart + cols, all.length);
4026
+ for (let i = rowEnd - 1; i >= rowStart; i--) {
4027
+ const item = all[i];
4028
+ if (item && !toValue(item.disabled)) {
4029
+ go(item.id);
4030
+ return;
4031
+ }
4032
+ }
4033
+ }
4034
+ function onKeydown(e) {
4035
+ const cols = toValue(_columns);
4036
+ const rtl = isRtl(e);
4037
+ if (cols) {
4038
+ switch (e.key) {
4039
+ case "ArrowRight":
4040
+ e.preventDefault();
4041
+ step(rtl ? -1 : 1);
4042
+ break;
4043
+ case "ArrowLeft":
4044
+ e.preventDefault();
4045
+ step(rtl ? 1 : -1);
4046
+ break;
4047
+ case "ArrowDown":
4048
+ e.preventDefault();
4049
+ step(cols);
4050
+ break;
4051
+ case "ArrowUp":
4052
+ e.preventDefault();
4053
+ step(-cols);
4054
+ break;
4055
+ case "Home":
4056
+ e.preventDefault();
4057
+ if (e.ctrlKey) first();
4058
+ else rowFirst();
4059
+ break;
4060
+ case "End":
4061
+ e.preventDefault();
4062
+ if (e.ctrlKey) last();
4063
+ else rowLast();
4064
+ break;
4065
+ }
4066
+ return;
4067
+ }
4068
+ const prevKeys = [];
4069
+ const nextKeys = [];
4070
+ if (orientation === "vertical" || orientation === "both") {
4071
+ prevKeys.push("ArrowUp");
4072
+ nextKeys.push("ArrowDown");
4073
+ }
4074
+ if (orientation === "horizontal" || orientation === "both") {
4075
+ prevKeys.push(rtl ? "ArrowRight" : "ArrowLeft");
4076
+ nextKeys.push(rtl ? "ArrowLeft" : "ArrowRight");
4077
+ }
4078
+ if (prevKeys.includes(e.key)) {
4079
+ e.preventDefault();
4080
+ prev();
4081
+ } else if (nextKeys.includes(e.key)) {
4082
+ e.preventDefault();
4083
+ next();
4084
+ } else if (e.key === "Home") {
4085
+ e.preventDefault();
4086
+ first();
4087
+ } else if (e.key === "End") {
4088
+ e.preventDefault();
4089
+ last();
4090
+ }
4091
+ }
4092
+ return {
4093
+ activeId,
4094
+ step,
4095
+ next,
4096
+ prev,
4097
+ first,
4098
+ last,
4099
+ onKeydown
4100
+ };
4101
+ }
4102
+ //#endregion
4103
+ //#region src/composables/useRovingFocus/index.ts
4104
+ /**
4105
+ * @module useRovingFocus
4106
+ *
4107
+ * @remarks
4108
+ * Roving tabindex composable for keyboard navigation within composite widgets.
4109
+ *
4110
+ * Key features:
4111
+ * - Arrow key navigation with orientation support
4112
+ * - Grid mode for 2D navigation (data tables, calendars)
4113
+ * - Automatic disabled item skipping
4114
+ * - Optional circular navigation
4115
+ * - Home/End key support (row-scoped in grid mode)
4116
+ * - Auto-attaches keydown listener when target is provided
4117
+ *
4118
+ * Perfect for toolbars, menus, tree items, grids, and other composite widgets.
4119
+ */
4120
+ function useRovingFocus(items, options = {}) {
4121
+ const { orientation, circular, columns, onFocus, target } = options;
4122
+ function applyFocus(id) {
4123
+ const item = items().find((i) => i.id === id);
4124
+ if (!item?.el) return false;
4125
+ onFocus?.(id);
4126
+ nextTick(() => {
4127
+ toValue(item.el)?.focus();
4128
+ });
4129
+ return true;
4130
+ }
4131
+ const traversal = createFocusTraversal(items, (id) => applyFocus(id), {
4132
+ orientation,
4133
+ circular,
4134
+ columns
4135
+ });
4136
+ function focus(id) {
4137
+ const prev = traversal.activeId.value;
4138
+ traversal.activeId.value = id;
4139
+ if (!applyFocus(id)) traversal.activeId.value = prev;
4140
+ }
4141
+ function enabled() {
4142
+ return items().filter((item) => !toValue(item.disabled));
4143
+ }
4144
+ function isTabbable(id) {
4145
+ if (!/* @__PURE__ */ isUndefined(traversal.activeId.value)) return traversal.activeId.value === id;
4146
+ const list = enabled();
4147
+ return list.length > 0 && list[0]?.id === id;
4148
+ }
4149
+ if (target) useEventListener(target, "keydown", traversal.onKeydown);
4150
+ return {
4151
+ focusedId: traversal.activeId,
4152
+ isTabbable,
4153
+ focus,
4154
+ next: traversal.next,
4155
+ prev: traversal.prev,
4156
+ first: traversal.first,
4157
+ last: traversal.last,
4158
+ onKeydown: traversal.onKeydown
4159
+ };
4160
+ }
4161
+ //#endregion
3128
4162
  //#region src/composables/useRtl/adapters/v0.ts
3129
4163
  var Vuetify0RtlAdapter = class {
3130
4164
  setup(app, context, target) {
@@ -3343,6 +4377,9 @@ function createSlider(options = {}) {
3343
4377
  function ceil(index) {
3344
4378
  set(index, max);
3345
4379
  }
4380
+ function onboard(registrations) {
4381
+ return model.batch(() => registrations.map((r) => register(r)));
4382
+ }
3346
4383
  return {
3347
4384
  ...model,
3348
4385
  values,
@@ -3359,6 +4396,7 @@ function createSlider(options = {}) {
3359
4396
  orientation,
3360
4397
  inverted,
3361
4398
  register,
4399
+ onboard,
3362
4400
  snap,
3363
4401
  fromValue,
3364
4402
  fromPercent,
@@ -3438,12 +4476,12 @@ function createStack(_options = {}) {
3438
4476
  const id = input.id ?? /* @__PURE__ */ useId$1();
3439
4477
  const blocking = input.blocking ?? false;
3440
4478
  const onDismiss = input.onDismiss;
3441
- const zIndex = computed(() => {
4479
+ const zIndex = toRef(() => {
3442
4480
  const position = ids().indexOf(id);
3443
4481
  if (position === -1) return baseZIndex;
3444
4482
  return baseZIndex + position * increment;
3445
4483
  });
3446
- const globalTop = computed(() => {
4484
+ const globalTop = toRef(() => {
3447
4485
  const list = ids();
3448
4486
  if (list.length === 0) return false;
3449
4487
  return list.at(-1) === id;
@@ -4016,6 +5054,12 @@ function createTimeline(_options = {}) {
4016
5054
  function redo() {
4017
5055
  if (stack.length === 0) return void 0;
4018
5056
  const item = stack.pop();
5057
+ if (registry.size >= size) {
5058
+ const oldest = registry.seek("first");
5059
+ if (overflow.length === size) overflow.shift();
5060
+ overflow.push(oldest);
5061
+ registry.unregister(oldest.id);
5062
+ }
4019
5063
  const ticket = registry.register(item);
4020
5064
  registry.reindex();
4021
5065
  return ticket;
@@ -4093,31 +5137,27 @@ function useTimeline(namespace = "v0:timeline") {
4093
5137
  * @module createValidation
4094
5138
  *
4095
5139
  * @remarks
4096
- * Per-field validation lifecycle composable.
5140
+ * Per-input validation composable built on createGroup.
4097
5141
  *
4098
5142
  * Key features:
5143
+ * - Each ticket is a rule — enable/disable via select/unselect
4099
5144
  * - Auto-register with parent form via useForm() injection
4100
5145
  * - Async rule support with generation-based race safety
4101
5146
  * - Silent validation mode
4102
5147
  * - Tri-state isValid (null/true/false)
4103
- * - isPristine tracking
4104
5148
  *
4105
5149
  * Uses useRules() to resolve alias strings via shared context.
4106
5150
  */
5151
+ const UNSET = Symbol("unset");
4107
5152
  /**
4108
- * Resolves raw rule inputs to FormValidationRule[].
4109
- * Uses the provided RulesContext if available, otherwise handles
4110
- * functions and Standard Schema objects directly.
4111
- */
4112
- function resolveRules(raw, context) {
4113
- if (context) return context.resolve(raw);
4114
- return raw.filter((r) => /* @__PURE__ */ isFunction(r) || isStandardSchema(r)).map((r) => isStandardSchema(r) ? toRule(r) : r);
4115
- }
4116
- /**
4117
- * Creates a new validation instance.
5153
+ * Creates a per-input validation instance.
5154
+ *
5155
+ * Built on `createGroup` each registered rule becomes a ticket that can be
5156
+ * enabled/disabled via selection methods (`select`, `unselect`, `toggle`).
5157
+ * Only selected (active) rules run during validation.
4118
5158
  *
4119
5159
  * @param options The options for the validation instance.
4120
- * @returns A new validation context with register(), aggregate state, etc.
5160
+ * @returns A new validation context.
4121
5161
  *
4122
5162
  * @see https://0.vuetifyjs.com/composables/forms/create-validation
4123
5163
  *
@@ -4125,111 +5165,83 @@ function resolveRules(raw, context) {
4125
5165
  * ```ts
4126
5166
  * import { createValidation } from '@vuetify/v0'
4127
5167
  *
4128
- * const validation = createValidation()
4129
- *
4130
- * const field = validation.register({
4131
- * id: 'email',
4132
- * value: '',
4133
- * rules: [(v) => !!v || 'Required'],
5168
+ * const validation = createValidation({
5169
+ * rules: [
5170
+ * v => !!v || 'Required',
5171
+ * v => /^.+@\S+\.\S+$/.test(String(v)) || 'Invalid email',
5172
+ * ],
4134
5173
  * })
4135
5174
  *
4136
- * await field.validate()
5175
+ * await validation.validate('')
4137
5176
  *
4138
- * console.log(field.errors.value) // ['Required']
5177
+ * console.log(validation.errors.value) // ['Required', 'Invalid email']
4139
5178
  *
4140
- * field.reset()
5179
+ * validation.reset()
4141
5180
  * ```
4142
5181
  */
4143
- function createValidation(options = {}) {
4144
- const registry = createRegistry(options);
4145
- const rules = useRules();
4146
- const isValidating = computed(() => {
4147
- for (const ticket of registry.values()) if (ticket.isValidating.value) return true;
4148
- return false;
5182
+ function createValidation(_options = {}) {
5183
+ const { rules: initialRules = [], value: valueSource, enroll = true, ...options } = _options;
5184
+ const group = createGroup({
5185
+ ...options,
5186
+ enroll,
5187
+ multiple: true
4149
5188
  });
4150
- const isValid = computed(() => {
4151
- let hasNull = false;
4152
- let hasFields = false;
4153
- for (const ticket of registry.values()) {
4154
- hasFields = true;
4155
- if (ticket.isValid.value === false) return false;
4156
- if (/* @__PURE__ */ isNull(ticket.isValid.value)) hasNull = true;
5189
+ const rulesContext = useRules();
5190
+ const errors = shallowRef([]);
5191
+ const isValid = shallowRef(null);
5192
+ const isValidating = shallowRef(false);
5193
+ let generation = 0;
5194
+ function register(input) {
5195
+ if (/* @__PURE__ */ isFunction(input) || /* @__PURE__ */ isString(input) || isStandardSchema(input)) {
5196
+ const resolved = rulesContext.resolve([input]);
5197
+ return group.register({ value: resolved[0] ?? (() => true) });
4157
5198
  }
4158
- if (!hasFields) return null;
4159
- return hasNull ? null : true;
4160
- });
4161
- function register(registration = {}) {
4162
- const model = shallowRef(/* @__PURE__ */ isNullOrUndefined(registration.value) ? "" : toValue(registration.value));
4163
- const resolved = resolveRules(registration.rules || [], rules);
4164
- const errors = shallowRef([]);
4165
- const isFieldValidating = shallowRef(false);
4166
- const initialValue = model.value;
4167
- const isPristine = shallowRef(true);
4168
- const isFieldValid = shallowRef(null);
4169
- function reset() {
4170
- model.value = initialValue;
4171
- errors.value = [];
4172
- isPristine.value = true;
4173
- isFieldValid.value = null;
4174
- isFieldValidating.value = false;
4175
- validationGeneration++;
5199
+ return group.register(input);
5200
+ }
5201
+ function onboard(rules) {
5202
+ return group.batch(() => rules.map((r) => register(r)));
5203
+ }
5204
+ async function validate(_value = UNSET, silent = false) {
5205
+ const val = _value === UNSET ? toValue(valueSource) : _value;
5206
+ const activeRules = [];
5207
+ for (const ticket of group.selectedItems.value) activeRules.push(ticket.value);
5208
+ if (activeRules.length === 0) {
5209
+ if (!silent) isValid.value = true;
5210
+ return true;
4176
5211
  }
4177
- let validationGeneration = 0;
4178
- async function validate(silent = false) {
4179
- if (resolved.length === 0) return isFieldValid.value = true;
4180
- const generation = ++validationGeneration;
4181
- isFieldValidating.value = true;
4182
- try {
4183
- const results = await Promise.all(resolved.map((rule) => rule(model.value)));
4184
- if (generation !== validationGeneration) return isFieldValid.value ?? false;
4185
- const errorMessages = results.filter((result) => /* @__PURE__ */ isString(result) || result === false).map((result) => result === false ? "Validation failed" : result);
4186
- if (!silent) {
4187
- errors.value = errorMessages;
4188
- isFieldValid.value = errorMessages.length === 0;
4189
- isPristine.value = model.value === initialValue;
4190
- }
4191
- return errorMessages.length === 0;
4192
- } finally {
4193
- if (generation === validationGeneration) isFieldValidating.value = false;
5212
+ const gen = ++generation;
5213
+ if (!silent) isValidating.value = true;
5214
+ try {
5215
+ const results = await Promise.all(activeRules.map((rule) => rule(val)));
5216
+ if (gen !== generation) return isValid.value ?? false;
5217
+ const errorMessages = results.filter((result) => /* @__PURE__ */ isString(result) || result === false).map((result) => result === false ? "Validation failed" : result);
5218
+ if (!silent) {
5219
+ errors.value = errorMessages;
5220
+ isValid.value = errorMessages.length === 0;
4194
5221
  }
5222
+ return errorMessages.length === 0;
5223
+ } finally {
5224
+ if (gen === generation && !silent) isValidating.value = false;
4195
5225
  }
4196
- const item = {
4197
- ...registration,
4198
- rules: resolved,
4199
- errors,
4200
- disabled: registration.disabled || false,
4201
- isValidating: isFieldValidating,
4202
- isPristine,
4203
- isValid: isFieldValid,
4204
- reset,
4205
- validate
4206
- };
4207
- const ticket = registry.register(item);
4208
- Object.defineProperty(ticket, "value", {
4209
- get() {
4210
- return model.value;
4211
- },
4212
- set(val) {
4213
- model.value = val;
4214
- isPristine.value = val === initialValue;
4215
- isFieldValid.value = null;
4216
- },
4217
- enumerable: true,
4218
- configurable: true
4219
- });
4220
- return ticket;
4221
5226
  }
4222
- function onboard(registrations) {
4223
- return registry.batch(() => registrations.map((r) => register(r)));
5227
+ function reset() {
5228
+ errors.value = [];
5229
+ isValid.value = null;
5230
+ isValidating.value = false;
5231
+ generation++;
4224
5232
  }
5233
+ if (initialRules.length > 0) onboard(initialRules);
4225
5234
  const context = {
4226
- ...registry,
5235
+ ...group,
4227
5236
  register,
4228
5237
  onboard,
5238
+ validate,
5239
+ reset,
5240
+ errors,
4229
5241
  isValid,
4230
5242
  isValidating,
4231
5243
  get size() {
4232
- return registry.size;
5244
+ return group.size;
4233
5245
  }
4234
5246
  };
4235
5247
  const form = useForm();
@@ -4684,6 +5696,80 @@ function useVirtual(namespace = "v0:virtual") {
4684
5696
  return useContext(namespace);
4685
5697
  }
4686
5698
  //#endregion
5699
+ //#region src/composables/useVirtualFocus/index.ts
5700
+ /**
5701
+ * @module useVirtualFocus
5702
+ *
5703
+ * @remarks
5704
+ * Virtual focus composable for aria-activedescendant keyboard navigation.
5705
+ * DOM focus stays on a control element while a virtual cursor highlights
5706
+ * items in a list.
5707
+ *
5708
+ * Key features:
5709
+ * - Arrow key navigation with orientation support
5710
+ * - Grid mode for 2D navigation
5711
+ * - Automatic disabled item skipping
5712
+ * - Optional circular navigation
5713
+ * - Home/End key support (row-scoped in grid mode)
5714
+ * - Auto-attaches keydown listener when target is provided
5715
+ * - Sets aria-activedescendant and data-highlighted attributes
5716
+ *
5717
+ * Perfect for comboboxes, autocompletes, listboxes, and other widgets
5718
+ * where DOM focus must remain on the control element.
5719
+ */
5720
+ function useVirtualFocus(items, options) {
5721
+ const { control, target, orientation, circular, columns, onHighlight } = options;
5722
+ let previousEl = null;
5723
+ function applyHighlight(id) {
5724
+ const item = items().find((i) => i.id === id);
5725
+ if (!item?.el) return false;
5726
+ const el = toValue(item.el);
5727
+ if (!el) return false;
5728
+ const controlEl = toValue(control);
5729
+ if (controlEl) {
5730
+ const itemId = el.getAttribute("id");
5731
+ if (itemId) controlEl.setAttribute("aria-activedescendant", itemId);
5732
+ }
5733
+ if (previousEl) delete previousEl.dataset.highlighted;
5734
+ el.dataset.highlighted = "";
5735
+ previousEl = el;
5736
+ el.scrollIntoView({ block: "nearest" });
5737
+ onHighlight?.(id);
5738
+ return true;
5739
+ }
5740
+ const traversal = createFocusTraversal(items, (id) => applyHighlight(id), {
5741
+ orientation,
5742
+ circular,
5743
+ columns
5744
+ });
5745
+ function highlight(id) {
5746
+ const prev = traversal.activeId.value;
5747
+ traversal.activeId.value = id;
5748
+ if (!applyHighlight(id)) traversal.activeId.value = prev;
5749
+ }
5750
+ function clear() {
5751
+ traversal.activeId.value = void 0;
5752
+ const controlEl = toValue(control);
5753
+ if (controlEl) controlEl.removeAttribute("aria-activedescendant");
5754
+ if (previousEl) {
5755
+ delete previousEl.dataset.highlighted;
5756
+ previousEl = null;
5757
+ }
5758
+ }
5759
+ const listener = target ?? control;
5760
+ if (listener) useEventListener(listener, "keydown", traversal.onKeydown);
5761
+ return {
5762
+ highlightedId: traversal.activeId,
5763
+ highlight,
5764
+ clear,
5765
+ next: traversal.next,
5766
+ prev: traversal.prev,
5767
+ first: traversal.first,
5768
+ last: traversal.last,
5769
+ onKeydown: traversal.onKeydown
5770
+ };
5771
+ }
5772
+ //#endregion
4687
5773
  //#region src/composables/useEventListener/index.ts
4688
5774
  /**
4689
5775
  * @module useEventListener
@@ -5029,4 +6115,4 @@ function useClickOutside(target, handler, options = {}) {
5029
6115
  };
5030
6116
  }
5031
6117
  //#endregion
5032
- export { createDatePlugin as $, Vuetify0RtlAdapter as A, useMediaQuery as B, useStack as C, createRtlFallback as D, createRtlContext as E, createNestedContext as F, useElementIntersection as G, usePrefersDark as H, useNested as I, createForm as J, useIntersectionObserver as K, multipleOpenStrategy as L, createQueueContext as M, useQueue as N, createRtlPlugin as O, createNested as P, createDateContext as Q, singleOpenStrategy as R, createStackPlugin as S, createRtl as T, usePrefersReducedMotion as U, usePrefersContrast as V, useLazy as W, useForm as X, createFormContext as Y, createDate as Z, createStep as _, useHydration as _t, createVirtual as a, toReactive as at, createStack as b, useBreadcrumbs as bt, useToggleScope as c, createOverflowContext as ct, createTimelineContext as d, useResizeObserver as dt, useDate as et, useTimeline as f, toElement as ft, useStorage as g, createHydrationPlugin as gt, createStoragePlugin as h, createHydrationContext as ht, useWindowEventListener as i, useBreakpoints as it, createQueue as j, useRtl as k, createValidation as l, useOverflow as lt, createStorageContext as m, createHydration as mt, useDocumentEventListener as n, createBreakpointsContext as nt, createVirtualContext as o, useProxyModel as ot, createStorage as p, createFallbackHydration as pt, useHotkey as q, useEventListener as r, createBreakpointsPlugin as rt, useVirtual as s, createOverflow as st, useClickOutside as t, createBreakpoints as tt, createTimeline as u, useElementSize as ut, createStepContext as v, createBreadcrumbs as vt, createSlider as w, createStackContext as x, useStep as y, createBreadcrumbsContext as yt, useMutationObserver as z };
6118
+ export { useTimer as $, useRtl as A, createQueueContext as B, createStackPlugin as C, createHydration as Ct, createRtlContext as D, createBreadcrumbs as Dt, createRtl as E, useHydration as Et, createNotifications as F, multipleOpenStrategy as G, createNested as H, createNotificationsContext as I, useMediaQuery as J, singleOpenStrategy as K, createNotificationsPlugin as L, useRovingFocus as M, useRaf as N, createRtlFallback as O, createBreadcrumbsContext as Ot, usePopover as P, useLazy as Q, useNotifications as R, createStackContext as S, createFallbackHydration as St, createSlider as T, createHydrationPlugin as Tt, createNestedContext as U, useQueue as V, useNested as W, usePrefersDark as X, usePrefersContrast as Y, usePrefersReducedMotion as Z, useStorage as _, createOverflowContext as _t, useVirtualFocus as a, useForm as at, useStep as b, useResizeObserver as bt, useVirtual as c, createDatePlugin as ct, createTimeline as d, createBreakpointsContext as dt, useElementIntersection as et, createTimelineContext as f, createBreakpointsPlugin as ft, createStoragePlugin as g, createOverflow as gt, createStorageContext as h, useProxyModel as ht, useWindowEventListener as i, createFormContext as it, Vuetify0RtlAdapter as j, createRtlPlugin as k, useBreadcrumbs as kt, useToggleScope as l, useDate as lt, createStorage as m, toReactive as mt, useDocumentEventListener as n, useHotkey as nt, createVirtual as o, createDate as ot, useTimeline as p, useBreakpoints as pt, useMutationObserver as q, useEventListener as r, createForm as rt, createVirtualContext as s, createDateContext as st, useClickOutside as t, useIntersectionObserver as tt, createValidation as u, createBreakpoints as ut, createStep as v, useOverflow as vt, useStack as w, createHydrationContext as wt, createStack as x, toElement as xt, createStepContext as y, useElementSize as yt, createQueue as z };