@vuetify/v0 1.1.0 → 1.2.0

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 (162) hide show
  1. package/README.md +8 -0
  2. package/dist/{adapter-Butx_2UU.d.mts → adapter-BXunW_mw.d.mts} +1 -1
  3. package/dist/{createPagination-B5dfqY-a.mjs → adapter-BiQV5j8B.mjs} +98 -3
  4. package/dist/adapter-ClB-53_F.d.mts +62 -0
  5. package/dist/{adapter-hq9YlPOF.d.mts → adapter-CqS8xexO.d.mts} +2 -2
  6. package/dist/adapter-CrRJuxRk.mjs +21 -0
  7. package/dist/{adapter-_srNCBEi.d.mts → adapter-PDTe5nFT.d.mts} +6 -7
  8. package/dist/browser/index.d.ts +15101 -12955
  9. package/dist/browser/index.js +16553 -14035
  10. package/dist/components/index.d.mts +2 -2
  11. package/dist/components/index.mjs +2 -2
  12. package/dist/{components-B2BZ1B3K.mjs → components-DBhVg-t1.mjs} +8210 -6301
  13. package/dist/composables/index.d.mts +22 -20
  14. package/dist/composables/index.mjs +37 -34
  15. package/dist/{composables-DONBl_c9.mjs → composables-ZGhVN2EJ.mjs} +32 -1061
  16. package/dist/{consola-BPGkJp_q.d.mts → consola-CdMU53rU.d.mts} +1 -1
  17. package/dist/constants/index.d.mts +1 -1
  18. package/dist/constants/index.mjs +1 -1
  19. package/dist/{createDataTable-DgyHBfAr.mjs → createDataTable-VmQEDn-7.mjs} +23 -7
  20. package/dist/{createGroup-BXKKcb9-.mjs → createGroup-B_LL7xRr.mjs} +2 -2
  21. package/dist/createPlugin-Oo6t-vBw.mjs +157 -0
  22. package/dist/{createRegistry-B9cOWny2.mjs → createRegistry-DVdvep0v.mjs} +1 -1
  23. package/dist/{createSelection-CWE554_g.mjs → createSelection-BNdDz8oY.mjs} +1 -1
  24. package/dist/{createSingle-Dz-1Iv7u.mjs → createSingle-DHyIQ9Cj.mjs} +1 -1
  25. package/dist/{createTokens-C4qcW2Kv.mjs → createTokens-DoISbwd7.mjs} +2 -2
  26. package/dist/data-table/adapters/client/index.d.mts +1 -1
  27. package/dist/data-table/adapters/client/index.mjs +1 -1
  28. package/dist/data-table/adapters/index.d.mts +4 -4
  29. package/dist/data-table/adapters/index.mjs +4 -4
  30. package/dist/data-table/adapters/server/index.d.mts +1 -1
  31. package/dist/data-table/adapters/server/index.mjs +1 -1
  32. package/dist/data-table/adapters/virtual/index.d.mts +1 -1
  33. package/dist/data-table/adapters/virtual/index.mjs +1 -1
  34. package/dist/data-table/index.d.mts +5 -5
  35. package/dist/data-table/index.mjs +5 -5
  36. package/dist/date/index.d.mts +1 -1
  37. package/dist/date/index.mjs +2 -2
  38. package/dist/features/adapters/flagsmith/index.d.mts +1 -1
  39. package/dist/features/adapters/flagsmith/index.mjs +1 -1
  40. package/dist/features/adapters/launchdarkly/index.d.mts +1 -1
  41. package/dist/features/adapters/launchdarkly/index.mjs +1 -1
  42. package/dist/features/adapters/posthog/index.d.mts +1 -1
  43. package/dist/features/adapters/posthog/index.mjs +2 -2
  44. package/dist/features/index.d.mts +2 -2
  45. package/dist/features/index.mjs +2 -2
  46. package/dist/{flagsmith-DA2GCyGv.d.mts → flagsmith-DcIDrI7_.d.mts} +1 -1
  47. package/dist/{globals-CtaXlEOI.mjs → globals-DaoKtIGF.mjs} +1 -1
  48. package/dist/{index-CTl0cGH6.d.mts → index-Cm6CbqI6.d.mts} +7258 -5609
  49. package/dist/{index-Dc7lKT6q.d.mts → index-Cx0cWGlx.d.mts} +461 -130
  50. package/dist/{index-QnpcDsV7.d.mts → index-D0ub61GV.d.mts} +2 -2
  51. package/dist/{index-RGiSdjYt.d.mts → index-DSPkZ0ql.d.mts} +2 -2
  52. package/dist/{index-DCtHbY0a.d.mts → index-Di9e8sOm.d.mts} +24 -19
  53. package/dist/{index-DF0tMI2_.d.mts → index-DjRE4lLl.d.mts} +108 -2
  54. package/dist/index.d.mts +25 -24
  55. package/dist/index.mjs +40 -37
  56. package/dist/json/attributes.json +488 -0
  57. package/dist/json/importMap.json +92 -0
  58. package/dist/json/tags.json +234 -0
  59. package/dist/json/web-types.json +1540 -113
  60. package/dist/{launchdarkly-Bp8p9PMD.d.mts → launchdarkly-CUzunYol.d.mts} +1 -1
  61. package/dist/locale/adapters/index.d.mts +2 -2
  62. package/dist/locale/adapters/index.mjs +2 -2
  63. package/dist/locale/adapters/v0/index.d.mts +1 -1
  64. package/dist/locale/adapters/v0/index.mjs +1 -1
  65. package/dist/locale/adapters/vue-i18n/index.d.mts +1 -1
  66. package/dist/locale/adapters/vue-i18n/index.mjs +1 -1
  67. package/dist/locale/index.d.mts +3 -3
  68. package/dist/locale/index.mjs +3 -3
  69. package/dist/locale/messages/en/index.d.mts +3 -0
  70. package/dist/locale/messages/en/index.mjs +1 -0
  71. package/dist/logger/adapters/consola/index.d.mts +1 -1
  72. package/dist/logger/adapters/index.d.mts +4 -4
  73. package/dist/logger/adapters/index.mjs +1 -1
  74. package/dist/logger/adapters/pino/index.d.mts +1 -1
  75. package/dist/logger/adapters/v0/index.d.mts +1 -1
  76. package/dist/logger/adapters/v0/index.mjs +1 -1
  77. package/dist/logger/index.d.mts +5 -5
  78. package/dist/logger/index.mjs +2 -2
  79. package/dist/notifications/index.d.mts +1 -1
  80. package/dist/notifications/index.mjs +2 -2
  81. package/dist/palettes/ant/index.d.mts +1 -1
  82. package/dist/palettes/index.d.mts +3 -3
  83. package/dist/palettes/material/index.d.mts +1 -1
  84. package/dist/palettes/md1/index.d.mts +1 -1
  85. package/dist/palettes/md2/index.d.mts +1 -1
  86. package/dist/palettes/radix/index.d.mts +1 -1
  87. package/dist/palettes/tailwind/index.d.mts +1 -1
  88. package/dist/permissions/adapters/index.d.mts +1 -1
  89. package/dist/permissions/adapters/index.mjs +1 -1
  90. package/dist/permissions/adapters/v0/index.d.mts +1 -1
  91. package/dist/permissions/adapters/v0/index.mjs +1 -1
  92. package/dist/permissions/index.d.mts +1 -1
  93. package/dist/permissions/index.mjs +2 -2
  94. package/dist/{pino-B6BI1AOK.d.mts → pino-DjeWBFnj.d.mts} +1 -1
  95. package/dist/popover/adapters/floating-ui/index.d.mts +25 -0
  96. package/dist/popover/adapters/floating-ui/index.mjs +98 -0
  97. package/dist/popover/index.d.mts +3 -0
  98. package/dist/popover/index.mjs +3 -0
  99. package/dist/{posthog-sFqk0bGy.d.mts → posthog-CJAYssdr.d.mts} +1 -1
  100. package/dist/rules/index.d.mts +1 -1
  101. package/dist/rules/index.mjs +1 -1
  102. package/dist/{server-DbtTGb67.d.mts → server-5vbJ7GGs.d.mts} +2 -2
  103. package/dist/{server-D5BJBjB0.mjs → server-m7iD1vOr.mjs} +1 -2
  104. package/dist/storage/adapters/index.d.mts +2 -2
  105. package/dist/storage/adapters/index.mjs +1 -1
  106. package/dist/storage/adapters/memory/index.d.mts +1 -1
  107. package/dist/storage/adapters/memory/index.mjs +1 -1
  108. package/dist/storage/index.d.mts +3 -3
  109. package/dist/storage/index.mjs +2 -2
  110. package/dist/theme/adapters/index.d.mts +1 -1
  111. package/dist/theme/adapters/index.mjs +3 -3
  112. package/dist/theme/adapters/unhead/index.d.mts +1 -1
  113. package/dist/theme/adapters/unhead/index.mjs +1 -1
  114. package/dist/theme/adapters/v0/index.d.mts +1 -1
  115. package/dist/theme/adapters/v0/index.mjs +1 -1
  116. package/dist/theme/index.d.mts +2 -2
  117. package/dist/theme/index.mjs +4 -4
  118. package/dist/types/index.d.mts +1 -1
  119. package/dist/{unhead-BAWCwNOF.mjs → unhead-C8zbWr45.mjs} +2 -2
  120. package/dist/{useEventListener-DSOeDtC-.mjs → useEventListener-2eAMwUdw.mjs} +2 -2
  121. package/dist/useFeatures-DLafCOWt.mjs +221 -0
  122. package/dist/{useLocale-D-V0DENN.mjs → useLocale-K_45ulPt.mjs} +4 -4
  123. package/dist/{useLogger-CbvzXCWb.mjs → useLogger-YyWuYvrH.mjs} +5 -159
  124. package/dist/{useNotifications-Cqg3uDz0.mjs → useNotifications-Kh_KvM7j.mjs} +57 -154
  125. package/dist/{usePermissions-BTdUVyQe.mjs → usePermissions-BCGwsOES.mjs} +4 -4
  126. package/dist/usePopover-BTo0AGIQ.mjs +420 -0
  127. package/dist/{useRovingFocus-g6ifibLg.mjs → useRovingFocus-CkRnVmJ1.mjs} +1122 -531
  128. package/dist/{useRules-DEiHd_eY.mjs → useRules-BuE5ffOJ.mjs} +3 -2
  129. package/dist/{useStorage-BSfQfDbg.mjs → useStorage-BT8-mjVs.mjs} +5 -4
  130. package/dist/useTheme-DkK6tPqY.mjs +445 -0
  131. package/dist/useTimer-CGVXk397.mjs +151 -0
  132. package/dist/utilities/index.d.mts +2 -2
  133. package/dist/utilities/index.mjs +181 -2
  134. package/dist/{v0-CveVV6On.mjs → v0-B_53w7Fu.mjs} +2 -2
  135. package/dist/{v0-BnUwMaOv.d.mts → v0-CDW1Ktk-.d.mts} +1 -1
  136. package/dist/{v0-CZBkpQjr.mjs → v0-D0Exc2bO.mjs} +2 -2
  137. package/dist/{v0-rSxMW8FJ.d.mts → v0-DfFK-p4X.d.mts} +1 -1
  138. package/dist/{v0-DotXXGXU.d.mts → v0-GI9U-rPA.d.mts} +2 -2
  139. package/dist/{v0-CCWHeKJM.mjs → v0-SuxTihEe.mjs} +1 -1
  140. package/dist/{v0-A4GLojAr.mjs → v0-yZ2ZUtVF.mjs} +1 -2
  141. package/dist/{virtual-DFvOoeG6.mjs → virtual-Bbu26O_h.mjs} +1 -2
  142. package/dist/{virtual-C-NsLue0.d.mts → virtual-JIdFLpTQ.d.mts} +2 -2
  143. package/package.json +8 -1
  144. package/dist/adapter-CVoWg0sw.mjs +0 -99
  145. package/dist/useFeatures-YJmONiU7.mjs +0 -124
  146. package/dist/useTheme-C0_i__N6.mjs +0 -162
  147. /package/dist/{adapter-CTsckKAR.mjs → adapter-8UADsA3-.mjs} +0 -0
  148. /package/dist/{adapter-BCtceV3b.mjs → adapter-BuSfpxur.mjs} +0 -0
  149. /package/dist/{adapter-Bc0yPSfo.d.mts → adapter-D5nT3bLw.d.mts} +0 -0
  150. /package/dist/{adapter-bwZo7b5p.mjs → adapter-D8zizQVP.mjs} +0 -0
  151. /package/dist/{adapter-q1sRACUc.mjs → adapter-TiGrumDi.mjs} +0 -0
  152. /package/dist/{adapter-BYzU4Ey5.d.mts → adapter-ryk5H6YZ.d.mts} +0 -0
  153. /package/dist/{index-CROz1VSV.d.mts → index-CX869wwk.d.mts} +0 -0
  154. /package/dist/{index-FtE8MXJ_.d.mts → index-CsuikFeJ.d.mts} +0 -0
  155. /package/dist/{index-DidZKN6S.d.mts → index-Djhiy3nS.d.mts} +0 -0
  156. /package/dist/{index-CyNk9FpR.d.mts → index-DtiaWx5U.d.mts} +0 -0
  157. /package/dist/{index-DXMlYoiQ.d.mts → index-H_hl6-4b.d.mts} +0 -0
  158. /package/dist/{index-BgCkepP_.d.mts → index-u_qSc2xS.d.mts} +0 -0
  159. /package/dist/{memory-DbyZeNjg.d.mts → memory-B7z1RR9r.d.mts} +0 -0
  160. /package/dist/{memory-BJjkQzxs.mjs → memory-CR6zvuaP.mjs} +0 -0
  161. /package/dist/{toArray-C89ZaEuF.mjs → toArray-MY9Kb1Qy.mjs} +0 -0
  162. /package/dist/{v0-UNyykD27.mjs → v0-Ch0yBKEj.mjs} +0 -0
@@ -1,16 +1,20 @@
1
- import { a as SUPPORTS_OBSERVER, n as SUPPORTS_INTERSECTION_OBSERVER, t as IN_BROWSER } from "./globals-CtaXlEOI.mjs";
2
- import { clamp, instanceExists, isArray, isElement, isFunction, isNaN, isNull, isNullOrUndefined, isObject, isString, isUndefined, range, resolveIds, resolveIndexes, useId as useId$1 } from "./utilities/index.mjs";
3
- import { i as useContext, t as createTrinity } from "./createTrinity-z-3q9QWR.mjs";
4
- import { a as createPlugin, i as useLogger, o as createPluginContext } from "./useLogger-CbvzXCWb.mjs";
5
- import { t as createRegistry } from "./createRegistry-B9cOWny2.mjs";
6
- import { i as createModel, t as createSelection } from "./createSelection-CWE554_g.mjs";
7
- import { t as createSingle } from "./createSingle-Dz-1Iv7u.mjs";
8
- import { t as toArray } from "./toArray-C89ZaEuF.mjs";
9
- import { n as useEventListener, r as useWindowEventListener, t as useDocumentEventListener } from "./useEventListener-DSOeDtC-.mjs";
10
- import { t as createGroup } from "./createGroup-BXKKcb9-.mjs";
11
- import { c as useTimer } from "./useNotifications-Cqg3uDz0.mjs";
12
- import { i as createFilter } from "./createPagination-B5dfqY-a.mjs";
13
- import { a as useRules, o as isStandardSchema } from "./useRules-DEiHd_eY.mjs";
1
+ import { a as SUPPORTS_OBSERVER, n as SUPPORTS_INTERSECTION_OBSERVER, t as IN_BROWSER } from "./globals-DaoKtIGF.mjs";
2
+ import { clamp, getActiveElement, instanceExists, isArray, isElement, isFunction, isNaN, isNull, isNullOrUndefined, isObject, isString, isUndefined, pxToNumber, range, resolveIds, resolveIndexes, useId as useId$1 } from "./utilities/index.mjs";
3
+ import { i as useContext, n as createContext, t as createTrinity } from "./createTrinity-z-3q9QWR.mjs";
4
+ import { n as createPluginContext, t as createPlugin } from "./createPlugin-Oo6t-vBw.mjs";
5
+ import { i as useLogger } from "./useLogger-YyWuYvrH.mjs";
6
+ import { t as createRegistry } from "./createRegistry-DVdvep0v.mjs";
7
+ import { i as createModel, t as createSelection } from "./createSelection-BNdDz8oY.mjs";
8
+ import { t as createSingle } from "./createSingle-DHyIQ9Cj.mjs";
9
+ import { t as toArray } from "./toArray-MY9Kb1Qy.mjs";
10
+ import { n as useEventListener, r as useWindowEventListener, t as useDocumentEventListener } from "./useEventListener-2eAMwUdw.mjs";
11
+ import { p as useHydration } from "./useTheme-DkK6tPqY.mjs";
12
+ import { i as useProxyRegistry, t as createGroup } from "./createGroup-B_LL7xRr.mjs";
13
+ import { t as useTimer } from "./useTimer-CGVXk397.mjs";
14
+ import { i as usePopover } from "./usePopover-BTo0AGIQ.mjs";
15
+ import { a as createFilter } from "./adapter-BiQV5j8B.mjs";
16
+ import { a as extractLeaves, t as createDataTable } from "./createDataTable-VmQEDn-7.mjs";
17
+ import { a as useRules, o as isStandardSchema } from "./useRules-BuE5ffOJ.mjs";
14
18
  import { computed, effectScope, hasInjectionContext, isReadonly, isRef, nextTick, onScopeDispose, ref, shallowReactive, shallowReadonly, shallowRef, toRef, toValue, watch } from "vue";
15
19
  //#region src/composables/useClickOutside/index.ts
16
20
  /**
@@ -254,7 +258,7 @@ function useClickOutside(target, handler, options = {}) {
254
258
  if (isPaused.value) return;
255
259
  if (event.defaultPrevented) return;
256
260
  if (document.activeElement instanceof HTMLIFrameElement) {
257
- const iframeIsOutside = getTargets().every((el) => !el.contains(document.activeElement));
261
+ const iframeIsOutside = getTargets().every((el) => !el.contains(/* @__PURE__ */ getActiveElement()));
258
262
  const [selectors, elements] = resolveIgnoreTargets();
259
263
  if (iframeIsOutside && !isIgnored(document.activeElement, selectors, elements)) handler(event);
260
264
  }
@@ -409,7 +413,7 @@ function toElement(target) {
409
413
  * ```
410
414
  */
411
415
  function createStack(_options = {}) {
412
- const { baseZIndex = 2e3, increment = 10, ...options } = _options;
416
+ const { baseZIndex = 2e3, increment = 10, default: defaultTeleport, ...options } = _options;
413
417
  const selection = createSelection({
414
418
  ...options,
415
419
  multiple: true
@@ -470,6 +474,7 @@ function createStack(_options = {}) {
470
474
  }, true);
471
475
  return ticket;
472
476
  }
477
+ const defaultRef = toRef(() => defaultTeleport);
473
478
  return {
474
479
  ...selection,
475
480
  register,
@@ -478,6 +483,7 @@ function createStack(_options = {}) {
478
483
  scrimZIndex,
479
484
  isBlocking,
480
485
  topElement,
486
+ default: defaultRef,
481
487
  get size() {
482
488
  return selection.size;
483
489
  }
@@ -708,109 +714,6 @@ function useToggleScope(source, fn) {
708
714
  return controls;
709
715
  }
710
716
  //#endregion
711
- //#region src/composables/useHydration/index.ts
712
- /**
713
- * @module useHydration
714
- *
715
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
716
- *
717
- * @remarks
718
- * SSR hydration state management composable.
719
- *
720
- * Key features:
721
- * - Hydration state detection (browser vs SSR)
722
- * - Root component detection
723
- * - Readonly hydration state refs
724
- * - Plugin installation support
725
- * - Perfect for hydration-safe rendering
726
- *
727
- * Essential for composables that need to behave differently during SSR vs client-side.
728
- *
729
- * @example
730
- * ```ts
731
- * import { useHydration } from '@vuetify/v0'
732
- *
733
- * const { isHydrated } = useHydration()
734
- * if (isHydrated.value) {
735
- * // safe for client-only logic
736
- * }
737
- * ```
738
- */
739
- /**
740
- * Creates a new hydration instance.
741
- *
742
- * @returns A new hydration instance.
743
- *
744
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
745
- *
746
- * @example
747
- * ```ts
748
- * import { createHydration } from '@vuetify/v0'
749
- *
750
- * const hydration = createHydration()
751
- * console.log(hydration.isHydrated.value) // false
752
- * hydration.hydrate()
753
- * console.log(hydration.isHydrated.value) // true
754
- * ```
755
- */
756
- function createHydration() {
757
- const isHydrated = shallowRef(false);
758
- const isSettled = shallowRef(false);
759
- function hydrate() {
760
- isHydrated.value = true;
761
- }
762
- function settle() {
763
- isSettled.value = true;
764
- }
765
- return {
766
- isHydrated: shallowReadonly(isHydrated),
767
- isSettled: shallowReadonly(isSettled),
768
- hydrate,
769
- settle
770
- };
771
- }
772
- function createFallbackHydration() {
773
- return {
774
- isHydrated: shallowReadonly(shallowRef(true)),
775
- isSettled: shallowReadonly(shallowRef(true)),
776
- hydrate: () => {},
777
- settle: () => {}
778
- };
779
- }
780
- /**
781
- * Creates a new hydration context trinity.
782
- *
783
- * @param options Options for creating the hydration context.
784
- * @template E The type of the hydration context.
785
- * @returns A new hydration context trinity.
786
- *
787
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
788
- *
789
- * @example
790
- * ```ts
791
- * import { createHydrationContext } from '@vuetify/v0'
792
- *
793
- * export const [useHydrationContext, provideHydrationContext, context] = createHydrationContext({
794
- * namespace: 'app:hydration',
795
- * })
796
- * ```
797
- */
798
- const [createHydrationContext, createHydrationPlugin, useHydration] = createPluginContext("v0:hydration", () => createHydration(), {
799
- fallback: () => createFallbackHydration(),
800
- setup: (context, app) => {
801
- const { mount } = app;
802
- app.mount = (...args) => {
803
- const vm = mount(...args);
804
- nextTick().then(() => {
805
- context.hydrate();
806
- nextTick().then(() => context.settle());
807
- });
808
- app.mount = mount;
809
- return vm;
810
- };
811
- }
812
- });
813
- //#endregion
814
717
  //#region src/composables/createObserver/index.ts
815
718
  /**
816
719
  * @module createObserver
@@ -942,12 +845,6 @@ function createObserver(target, userCallback, config) {
942
845
  * })
943
846
  * ```
944
847
  */
945
- /* @__NO_SIDE_EFFECTS__ */
946
- function pxToNumber(value, fallback = 0) {
947
- if (!/* @__PURE__ */ isString(value)) return fallback;
948
- const parsed = Number.parseFloat(value);
949
- return /* @__PURE__ */ isNaN(parsed) ? fallback : parsed;
950
- }
951
848
  /**
952
849
  * Synthesize the entry the observer would report for `el`, for the `immediate`
953
850
  * callback that fires before the observer's own first delivery.
@@ -1235,7 +1132,7 @@ function createOverflow(options = {}) {
1235
1132
  /* v8 ignore start -- browser-only measurement code */
1236
1133
  if (!IN_BROWSER) return;
1237
1134
  const style = getComputedStyle(el);
1238
- const marginX = (Number.parseFloat(style.marginLeft) || 0) + (Number.parseFloat(style.marginRight) || 0);
1135
+ const marginX = /* @__PURE__ */ pxToNumber(style.marginLeft) + /* @__PURE__ */ pxToNumber(style.marginRight);
1239
1136
  const w = (el.offsetWidth || 0) + marginX;
1240
1137
  if (widths.value.get(index) !== w) widths.value = new Map(widths.value).set(index, w);
1241
1138
  /* v8 ignore stop */
@@ -1944,238 +1841,6 @@ function useStep(namespace = "v0:step") {
1944
1841
  return useContext(namespace);
1945
1842
  }
1946
1843
  //#endregion
1947
- //#region src/composables/useDelay/index.ts
1948
- /**
1949
- * @module useDelay
1950
- *
1951
- * @see https://0.vuetifyjs.com/composables/system/use-delay
1952
- *
1953
- * @remarks
1954
- * Schedule open and close transitions with configurable delays.
1955
- *
1956
- * Composes `useTimer` for the underlying lifecycle (start / stop / pause / resume,
1957
- * plus reactive `isActive`, `isPaused`, `remaining`) and adds direction tracking
1958
- * (`isOpening`) and promise-based resolution.
1959
- *
1960
- * Key features:
1961
- * - Reactive `openDelay` and `closeDelay` via `MaybeRefOrGetter`
1962
- * - `start(isOpening)` returns a `Promise<boolean>` that resolves once the delay elapses
1963
- * - Per-call `minDelay` floor enforces a minimum delay
1964
- * - Automatic cleanup on scope disposal (timer cleared, pending promise settled with current direction)
1965
- * - SSR-safe
1966
- *
1967
- * @example
1968
- * ```ts
1969
- * import { useDelay } from '@vuetify/v0'
1970
- *
1971
- * const delay = useDelay(isOpening => isVisible.value = isOpening, {
1972
- * openDelay: 300,
1973
- * closeDelay: 200,
1974
- * })
1975
- *
1976
- * delay.start(true)
1977
- * delay.start(false, { minDelay: 500 })
1978
- * ```
1979
- */
1980
- /**
1981
- * Schedule open and close transitions with configurable delays.
1982
- *
1983
- * Composes `useTimer` for the lifecycle (`start` / `stop` / `pause` / `resume` plus
1984
- * reactive `isActive`, `isPaused`, `remaining`) and adds direction tracking
1985
- * (`isOpening`) and promise-based resolution.
1986
- *
1987
- * @param callback Optional callback invoked once a started delay elapses; receives the resolved direction.
1988
- * @param options Reactive delay settings.
1989
- * @returns Lifecycle controls and reactive state.
1990
- *
1991
- * @see https://0.vuetifyjs.com/composables/system/use-delay
1992
- *
1993
- * @example
1994
- * ```ts
1995
- * const delay = useDelay(isOpening => isVisible.value = isOpening, {
1996
- * openDelay: 300,
1997
- * closeDelay: 200,
1998
- * })
1999
- *
2000
- * delay.start(true)
2001
- * delay.start(false, { minDelay: 500 })
2002
- * delay.pause()
2003
- * delay.resume()
2004
- * delay.stop()
2005
- * ```
2006
- */
2007
- function useDelay(callback, options = {}) {
2008
- const { openDelay, closeDelay } = options;
2009
- const isOpening = shallowRef(false);
2010
- let minDelay = 0;
2011
- let resolver;
2012
- function fire() {
2013
- const direction = isOpening.value;
2014
- const resolve = resolver;
2015
- resolver = void 0;
2016
- resolve?.(direction);
2017
- callback?.(direction);
2018
- }
2019
- function duration() {
2020
- const raw = isOpening.value ? toValue(openDelay) : toValue(closeDelay);
2021
- return Math.max(minDelay, raw ?? 0);
2022
- }
2023
- const timer = useTimer(fire, { duration });
2024
- function start(direction, options = {}) {
2025
- const previous = resolver;
2026
- resolver = void 0;
2027
- isOpening.value = direction;
2028
- minDelay = options.minDelay ?? 0;
2029
- previous?.(direction);
2030
- const ms = duration();
2031
- return new Promise((resolve) => {
2032
- resolver = resolve;
2033
- if (ms <= 0) {
2034
- timer.stop();
2035
- fire();
2036
- return;
2037
- }
2038
- timer.start();
2039
- });
2040
- }
2041
- function stop() {
2042
- const resolve = resolver;
2043
- resolver = void 0;
2044
- timer.stop();
2045
- resolve?.(isOpening.value);
2046
- }
2047
- onScopeDispose(() => {
2048
- const resolve = resolver;
2049
- resolver = void 0;
2050
- resolve?.(isOpening.value);
2051
- }, true);
2052
- return {
2053
- start,
2054
- stop,
2055
- pause: timer.pause,
2056
- resume: timer.resume,
2057
- isActive: shallowReadonly(timer.isActive),
2058
- isPaused: shallowReadonly(timer.isPaused),
2059
- remaining: shallowReadonly(timer.remaining),
2060
- isOpening: shallowReadonly(isOpening)
2061
- };
2062
- }
2063
- //#endregion
2064
- //#region src/composables/usePopover/index.ts
2065
- /**
2066
- * @module usePopover
2067
- *
2068
- * @see https://0.vuetifyjs.com/composables/system/use-popover
2069
- *
2070
- * @remarks
2071
- * Composable for native popover API behavior with CSS anchor positioning.
2072
- * Manages open/close state, anchor styles, content attributes, bidirectional
2073
- * sync between reactive state and native popover events, and configurable
2074
- * open / close delays via `useDelay`.
2075
- *
2076
- * Key features:
2077
- * - Native popover API (showPopover/hidePopover)
2078
- * - CSS anchor positioning (position-area, position-try-fallbacks)
2079
- * - Reactive open/close delays via `useDelay`
2080
- * - Toggle event sync for native state changes
2081
- * - SSR-safe (no DOM ops outside browser)
2082
- * - Optional external isOpen ref for v-model integration
2083
- *
2084
- * @example
2085
- * ```ts
2086
- * import { usePopover } from '@vuetify/v0'
2087
- *
2088
- * const popover = usePopover({ openDelay: 200, closeDelay: 100 })
2089
- * popover.open()
2090
- * popover.toggle()
2091
- * ```
2092
- */
2093
- function usePopover(options = {}) {
2094
- const { id: _id, positionArea = "bottom", positionTry = "most-width bottom", openDelay, closeDelay } = options;
2095
- const id = _id ?? /* @__PURE__ */ useId$1();
2096
- const anchor = `--${String(id).replace(/[^a-zA-Z0-9_-]/g, "")}`;
2097
- const isOpen = options.isOpen ?? shallowRef(false);
2098
- const delay = useDelay((direction) => {
2099
- isOpen.value = direction;
2100
- }, {
2101
- openDelay,
2102
- closeDelay
2103
- });
2104
- function open() {
2105
- delay.start(true);
2106
- }
2107
- function close() {
2108
- delay.start(false);
2109
- }
2110
- function toggle() {
2111
- if (isOpen.value) close();
2112
- else open();
2113
- }
2114
- function cancel() {
2115
- delay.stop();
2116
- }
2117
- const anchorStyles = toRef(() => ({ anchorName: anchor }));
2118
- const contentAttrs = toRef(() => ({
2119
- id,
2120
- popover: ""
2121
- }));
2122
- const contentStyles = toRef(() => ({
2123
- "position": "fixed",
2124
- "margin": "unset",
2125
- "inset-area": positionArea,
2126
- "position-area": positionArea,
2127
- "position-anchor": anchor,
2128
- "position-try-fallbacks": positionTry
2129
- }));
2130
- function attach(el) {
2131
- let current;
2132
- function hide(element) {
2133
- if (!IN_BROWSER || /* @__PURE__ */ isNullOrUndefined(element)) return;
2134
- try {
2135
- if (element.matches?.(":popover-open") === false) return;
2136
- element.hidePopover?.();
2137
- } catch {}
2138
- }
2139
- function sync() {
2140
- if (!IN_BROWSER) return;
2141
- const element = toValue(el);
2142
- if (!/* @__PURE__ */ isNullOrUndefined(current) && current !== element) hide(current);
2143
- current = /* @__PURE__ */ isNullOrUndefined(element) ? void 0 : element;
2144
- if (/* @__PURE__ */ isNullOrUndefined(element)) return;
2145
- if (!isOpen.value) {
2146
- hide(element);
2147
- return;
2148
- }
2149
- if (!element.isConnected) return;
2150
- if (element.matches?.(":popover-open")) return;
2151
- element.showPopover?.();
2152
- }
2153
- watch([isOpen, () => toValue(el)], sync, {
2154
- immediate: true,
2155
- flush: "post"
2156
- });
2157
- onScopeDispose(() => {
2158
- hide(toValue(el) ?? current);
2159
- }, true);
2160
- useEventListener(el, "toggle", (e) => {
2161
- if (!toValue(el)?.isConnected) return;
2162
- isOpen.value = e.newState === "open";
2163
- });
2164
- }
2165
- return {
2166
- isOpen,
2167
- id,
2168
- open,
2169
- close,
2170
- toggle,
2171
- cancel,
2172
- anchorStyles,
2173
- contentAttrs,
2174
- contentStyles,
2175
- attach
2176
- };
2177
- }
2178
- //#endregion
2179
1844
  //#region src/composables/createFocusTraversal/index.ts
2180
1845
  /**
2181
1846
  * @module createFocusTraversal
@@ -2541,7 +2206,7 @@ var ClientComboboxAdapter = class extends ComboboxAdapter {
2541
2206
  * ```
2542
2207
  */
2543
2208
  function createCombobox(options = {}) {
2544
- const { multiple = false, mandatory = false, disabled = false, strict = false, error = false, errorMessages, adapter, displayValue = (v) => String(v ?? ""), id: _id, name, form } = options;
2209
+ const { multiple = false, mandatory = false, disabled = false, strict = false, error = false, errorMessages, adapter, positionAdapter, displayValue = (v) => String(v ?? ""), id: _id, name, form } = options;
2545
2210
  const id = _id ?? /* @__PURE__ */ useId$1();
2546
2211
  const inputId = `${id}-input`;
2547
2212
  const listboxId = `${id}-listbox`;
@@ -2582,7 +2247,11 @@ function createCombobox(options = {}) {
2582
2247
  query,
2583
2248
  items
2584
2249
  });
2585
- const popover = usePopover({ id });
2250
+ const popover = usePopover({
2251
+ id,
2252
+ adapter: positionAdapter
2253
+ });
2254
+ popover.attachAnchor(inputEl);
2586
2255
  const isOpen = popover.isOpen;
2587
2256
  const cursor = useVirtualFocus(() => selection.values().filter((ticket) => filtered.value.has(ticket.id) && !toValue(ticket.disabled)).map((ticket) => ({
2588
2257
  id: ticket.id,
@@ -2811,126 +2480,1119 @@ function useLazy(active, options = {}) {
2811
2480
  };
2812
2481
  }
2813
2482
  //#endregion
2814
- //#region src/composables/createForm/index.ts
2483
+ //#region src/composables/createSortable/index.ts
2815
2484
  /**
2816
- * @module createForm
2485
+ * @module createSortable
2817
2486
  *
2818
- * @see https://0.vuetifyjs.com/composables/forms/create-form
2487
+ * @see https://0.vuetifyjs.com/composables/data/create-sortable
2819
2488
  *
2820
2489
  * @remarks
2821
- * Form composable that coordinates validation across multiple fields.
2822
- *
2823
- * Key features:
2824
- * - Pure registry of ValidationContext instances
2825
- * - Validations auto-register via useForm() injection
2826
- * - Form-level submit, reset, and aggregate state
2827
- * - Exposes disabled/readonly refs for component consumption
2828
- * - Trinity pattern for DI
2490
+ * Headless ordered-list primitive. Extends `createModel` with mutation primitives
2491
+ * (`swap`, `reorder`) and an enriched `move:ticket` event on the registry event
2492
+ * bus. Honors `disabled` as a real mutation gate at both root and per-ticket scope.
2829
2493
  *
2830
- * Per-field validation logic lives in createValidation.
2831
- * createForm is the mothership it coordinates, not creates.
2494
+ * Pure logical composable no DnD, no DOM, no keyboard. Drag-and-drop integration
2495
+ * composes with `useDragDrop`; keyboard reorder composes with `useVirtualFocus`.
2832
2496
  *
2833
2497
  * @example
2834
2498
  * ```ts
2835
- * import { createForm } from '@vuetify/v0'
2499
+ * import { createSortable } from '@vuetify/v0'
2500
+ * import type { SortableTicketInput } from '@vuetify/v0'
2836
2501
  *
2837
- * const form = createForm()
2838
- * const isValid = await form.validate()
2839
- * if (isValid) form.submit()
2502
+ * interface TaskTicket extends SortableTicketInput {
2503
+ * value: { id: number; label: string }
2504
+ * }
2505
+ *
2506
+ * const sortable = createSortable<TaskTicket>()
2507
+ * const a = sortable.register({ value: { id: 1, label: 'A' } })
2508
+ * const b = sortable.register({ value: { id: 2, label: 'B' } })
2509
+ *
2510
+ * sortable.move(a.id, 1)
2511
+ * sortable.on('move:ticket', ({ ticket, from, to }) => {
2512
+ * console.log(`${ticket.value.label} moved ${from} → ${to}`)
2513
+ * })
2840
2514
  * ```
2841
2515
  */
2842
2516
  /**
2843
- * Creates a new form instance.
2844
- *
2845
- * A form is a pure registry of validation contexts. Validations register
2846
- * themselves via `useForm()` injection. The form coordinates submit, reset,
2847
- * and aggregate state across all registered validations.
2848
- *
2849
- * @param options The options for the form instance.
2850
- * @returns A new form context.
2517
+ * Create an ordered-list state primitive.
2851
2518
  *
2852
- * @see https://0.vuetifyjs.com/composables/forms/create-form
2519
+ * @param options Sortable options.
2520
+ * @returns A {@link SortableContext} that extends {@link ModelContext}.
2853
2521
  *
2854
2522
  * @example
2855
2523
  * ```ts
2856
- * import { createForm, createValidation } from '@vuetify/v0'
2857
- * import { shallowRef } from 'vue'
2858
- *
2859
- * const form = createForm()
2860
- *
2861
- * // Each validation is one input — form is just the coordinator
2862
- * const email = shallowRef('')
2863
- * const validation = createValidation({
2864
- * value: email,
2865
- * rules: ['required', 'email'],
2866
- * })
2867
- *
2868
- * form.register({ value: validation })
2869
- *
2870
- * await form.submit()
2871
- * form.reset()
2524
+ * const sortable = createSortable<Todo>()
2525
+ * sortable.onboard(todos.value.map(value => ({ value })))
2872
2526
  * ```
2873
2527
  */
2874
- function createForm(_options = {}) {
2875
- const { disabled = false, readonly = false, ...options } = _options;
2876
- const registry = createRegistry({
2528
+ function createSortable(_options = {}) {
2529
+ const { disabled, ...options } = _options;
2530
+ const model = createModel({
2877
2531
  ...options,
2878
- reactive: true
2879
- });
2880
- const isValidating = computed(() => {
2881
- for (const ticket of registry.values()) if (ticket.value.isValidating.value) return true;
2882
- return false;
2883
- });
2884
- const isValid = computed(() => {
2885
- let hasNull = false;
2886
- let hasFields = false;
2887
- for (const ticket of registry.values()) {
2888
- hasFields = true;
2889
- if (ticket.value.isValid.value === false) return false;
2890
- if (/* @__PURE__ */ isNull(ticket.value.isValid.value)) hasNull = true;
2891
- }
2892
- if (!hasFields) return null;
2893
- return hasNull ? null : true;
2532
+ events: true,
2533
+ disabled
2894
2534
  });
2895
- function register(registration) {
2896
- return registry.register(registration);
2535
+ const logger = useLogger();
2536
+ function move(id, toIndex) {
2537
+ if (toValue(disabled)) return void 0;
2538
+ const ticket = model.get(id);
2539
+ if (!ticket) return void 0;
2540
+ if (toValue(ticket.disabled)) return void 0;
2541
+ const from = ticket.index;
2542
+ const result = model.move(id, toIndex);
2543
+ if (result && result.index !== from) model.emit("move:ticket", {
2544
+ ticket: result,
2545
+ from,
2546
+ to: result.index
2547
+ });
2548
+ return result;
2897
2549
  }
2898
- function reset() {
2899
- for (const ticket of registry.values()) ticket.value.reset();
2550
+ function swap(a, b) {
2551
+ if (toValue(disabled)) return;
2552
+ if (a === b) return;
2553
+ const ta = model.get(a);
2554
+ const tb = model.get(b);
2555
+ if (!ta || !tb) return;
2556
+ if (toValue(ta.disabled) || toValue(tb.disabled)) return;
2557
+ const indexA = ta.index;
2558
+ const indexB = tb.index;
2559
+ model.batch(() => {
2560
+ const ra = model.move(a, indexB);
2561
+ if (ra && ra.index !== indexA) model.emit("move:ticket", {
2562
+ ticket: ra,
2563
+ from: indexA,
2564
+ to: ra.index
2565
+ });
2566
+ const rb = model.move(b, indexA);
2567
+ if (rb && rb.index !== indexB) model.emit("move:ticket", {
2568
+ ticket: rb,
2569
+ from: indexB,
2570
+ to: rb.index
2571
+ });
2572
+ });
2900
2573
  }
2901
- async function submit(id) {
2902
- const ids = /* @__PURE__ */ isUndefined(id) ? [...registry.keys()] : /* @__PURE__ */ toArray(id);
2903
- return (await Promise.all(ids.map(async (key) => {
2904
- const ticket = registry.get(key);
2905
- if (!ticket) return true;
2906
- return ticket.value.validate();
2907
- }))).every(Boolean);
2574
+ function reorder(ids) {
2575
+ if (toValue(disabled)) return;
2576
+ const currentSize = model.size;
2577
+ if (ids.length !== currentSize) {
2578
+ logger.warn(`Reorder expected ${currentSize} ids, got ${ids.length}.`);
2579
+ return;
2580
+ }
2581
+ const known = new Set(model.keys());
2582
+ const seen = /* @__PURE__ */ new Set();
2583
+ for (const id of ids) {
2584
+ if (!known.has(id)) {
2585
+ logger.warn(`Reorder contains unknown id "${String(id)}".`);
2586
+ return;
2587
+ }
2588
+ if (seen.has(id)) {
2589
+ logger.warn(`Reorder contains duplicate id "${String(id)}".`);
2590
+ return;
2591
+ }
2592
+ seen.add(id);
2593
+ }
2594
+ const froms = /* @__PURE__ */ new Map();
2595
+ for (const id of ids) {
2596
+ const ticket = model.get(id);
2597
+ if (ticket) froms.set(id, ticket.index);
2598
+ }
2599
+ model.batch(() => {
2600
+ model.reorder(ids);
2601
+ for (const [index, id] of ids.entries()) {
2602
+ const from = froms.get(id);
2603
+ if (from === index) continue;
2604
+ const ticket = model.get(id);
2605
+ if (ticket) model.emit("move:ticket", {
2606
+ ticket,
2607
+ from,
2608
+ to: index
2609
+ });
2610
+ }
2611
+ });
2908
2612
  }
2909
2613
  return {
2910
- ...registry,
2911
- register,
2912
- submit,
2913
- reset,
2914
- disabled,
2915
- readonly,
2916
- isValid,
2917
- isValidating,
2614
+ ...model,
2615
+ move,
2616
+ swap,
2617
+ reorder,
2918
2618
  get size() {
2919
- return registry.size;
2619
+ return model.size;
2920
2620
  }
2921
2621
  };
2922
2622
  }
2623
+ //#endregion
2624
+ //#region src/composables/createDataGrid/editing.ts
2923
2625
  /**
2924
- * Creates a new form context using the Trinity pattern.
2626
+ * @module createDataGrid/editing
2925
2627
  *
2926
- * @param options The options for the form context.
2927
- * @returns A Trinity tuple: [useFormContext, provideFormContext, formContext]
2628
+ * @remarks
2629
+ * Cell editing state management. Tracks active cell, validation errors,
2630
+ * and dirty (staged but uncommitted) edits. Does not mutate source data —
2631
+ * commit fires a callback for the consumer to handle.
2632
+ *
2633
+ * When a `registry` is provided, editing state for rows that get
2634
+ * unregistered (or wiped via `clear`) is pruned automatically so consumers
2635
+ * can never observe `active` pointing at a phantom row or stale entries
2636
+ * lingering in `dirty`.
2637
+ *
2638
+ * @internal Consumed only by `createDataGrid`. This is a fully decoupled 2D
2639
+ * cell-edit primitive — it imports nothing from `createDataGrid` /
2640
+ * `createDataTable` (only `#v0/utilities`, `#v0/types`, and Vue), so it is a
2641
+ * latent composable. It is parameterized over a row type `T` (defaulting to
2642
+ * `unknown`), but a type parameter is not an import — the primitive stays
2643
+ * fully decoupled from the grid's concrete ticket types and deliberately
2644
+ * row-agnostic: `T` is intentionally unconstrained, so do not narrow it to
2645
+ * the grid's `Record<string, unknown>` when promoting. Promote it to a
2646
+ * standalone `createCellEditing/`
2647
+ * composable the moment a second consumer appears (e.g. an editable
2648
+ * `createDataTable`). See `.claude/rules/composables.md` §"Sub-modules:
2649
+ * inline, private sibling, or promote".
2650
+ */
2651
+ /**
2652
+ * Creates cell editing state for a data grid.
2928
2653
  *
2929
- * @see https://0.vuetifyjs.com/composables/forms/create-form
2654
+ * @param options Cell editing configuration including columns and commit callback
2655
+ * @returns Cell editing state and controls
2930
2656
  *
2931
2657
  * @example
2932
2658
  * ```ts
2933
- * import { createFormContext } from '@vuetify/v0'
2659
+ * const editing = createCellEditing({
2660
+ * columns: [{ id: 'name', editable: true }],
2661
+ * registry: table,
2662
+ * onEdit (row, column, value) { ... },
2663
+ * })
2664
+ *
2665
+ * editing.edit(1, 'name')
2666
+ * editing.commit('Alice')
2667
+ * ```
2668
+ */
2669
+ function createCellEditing(options) {
2670
+ const { columns, onEdit, lookup, registry } = options;
2671
+ function resolveColumn(column) {
2672
+ for (const col of toValue(columns)) if (col.id === column) return col;
2673
+ }
2674
+ const active = shallowRef(null);
2675
+ const error = shallowRef(null);
2676
+ const dirty = shallowReactive(/* @__PURE__ */ new Map());
2677
+ function edit(row, column) {
2678
+ const col = resolveColumn(column);
2679
+ if (!col) return;
2680
+ if (/* @__PURE__ */ isFunction(col.editable)) {
2681
+ const item = lookup?.(row);
2682
+ if (/* @__PURE__ */ isUndefined(item) || !col.editable(item)) return;
2683
+ } else if (col.editable !== true) return;
2684
+ error.value = null;
2685
+ active.value = {
2686
+ row,
2687
+ column
2688
+ };
2689
+ }
2690
+ function commit(value) {
2691
+ const cell = active.value;
2692
+ if (!cell) return;
2693
+ const col = resolveColumn(cell.column);
2694
+ if (col?.validate) {
2695
+ const result = col.validate(value, lookup?.(cell.row));
2696
+ if (result !== true && /* @__PURE__ */ isString(result) && result.length > 0) {
2697
+ error.value = result;
2698
+ return;
2699
+ }
2700
+ }
2701
+ const entry = dirty.get(cell.row);
2702
+ if (entry) {
2703
+ entry.delete(cell.column);
2704
+ if (entry.size === 0) dirty.delete(cell.row);
2705
+ }
2706
+ error.value = null;
2707
+ active.value = null;
2708
+ onEdit?.(cell.row, cell.column, value);
2709
+ }
2710
+ function cancel() {
2711
+ const cell = active.value;
2712
+ if (cell) {
2713
+ const entry = dirty.get(cell.row);
2714
+ if (entry) {
2715
+ entry.delete(cell.column);
2716
+ if (entry.size === 0) dirty.delete(cell.row);
2717
+ }
2718
+ }
2719
+ error.value = null;
2720
+ active.value = null;
2721
+ }
2722
+ function onUnregister(data) {
2723
+ const ticket = data;
2724
+ if (active.value?.row === ticket.id) {
2725
+ active.value = null;
2726
+ error.value = null;
2727
+ }
2728
+ dirty.delete(ticket.id);
2729
+ }
2730
+ function onClear() {
2731
+ active.value = null;
2732
+ error.value = null;
2733
+ dirty.clear();
2734
+ }
2735
+ if (registry) {
2736
+ registry.on("unregister:ticket", onUnregister);
2737
+ registry.on("clear:registry", onClear);
2738
+ onScopeDispose(() => {
2739
+ registry.off("unregister:ticket", onUnregister);
2740
+ registry.off("clear:registry", onClear);
2741
+ }, true);
2742
+ }
2743
+ return {
2744
+ active,
2745
+ edit,
2746
+ commit,
2747
+ cancel,
2748
+ error,
2749
+ dirty
2750
+ };
2751
+ }
2752
+ //#endregion
2753
+ //#region src/composables/createDataGrid/layout.ts
2754
+ /**
2755
+ * @module createDataGrid/layout
2756
+ *
2757
+ * @remarks
2758
+ * Manages column layout state for data grids: sizing (percentages 0-100),
2759
+ * pinning (left/right/scrollable regions), delta-based resizing compatible
2760
+ * with the Splitter two-panel model, column reordering, and visibility.
2761
+ *
2762
+ * Column existence, display order, AND grid layout config (size, pin,
2763
+ * min/max, resizable, reorderable) all live on the table's columns registry
2764
+ * (passed in as the only argument). `createRegistry` preserves the extra
2765
+ * grid fields on each ticket, so layout reads its config straight off the
2766
+ * registered ticket — there is no separate defs array. Columns onboarded
2767
+ * before OR after the layout is built are picked up identically: an initial
2768
+ * loop seeds the already-registered set and a `register:ticket` handler seeds
2769
+ * the rest. Removed columns have their pin/size/visibility state pruned via
2770
+ * unregister events.
2771
+ *
2772
+ * Sizing uses percentages so it is compatible with the Splitter component.
2773
+ * Offsets are computed per-region (left, scrollable, right) independently
2774
+ * and over visible columns only — hidden columns are excluded from the
2775
+ * render set without auto-redistribution (headless: the consumer rebalances
2776
+ * via `distribute()` or CSS).
2777
+ *
2778
+ * @internal Private sibling of `createDataGrid`, and intentionally NOT a
2779
+ * promotion candidate: it is bound to the table's `DataTableColumnTicket`
2780
+ * types, so it is parent-specific glue rather than a reusable primitive. See
2781
+ * `.claude/rules/composables.md` §"Sub-modules: inline, private sibling, or
2782
+ * promote".
2783
+ */
2784
+ const DEFAULT_EXTRAS = {
2785
+ minSize: 2,
2786
+ maxSize: 100,
2787
+ resizable: true,
2788
+ reorderable: true
2789
+ };
2790
+ function offsets(cols, reverse = false) {
2791
+ let offset = 0;
2792
+ const start = reverse ? cols.length - 1 : 0;
2793
+ const step = reverse ? -1 : 1;
2794
+ const end = reverse ? -1 : cols.length;
2795
+ for (let i = start; i !== end; i += step) {
2796
+ cols[i].offset = offset;
2797
+ offset += cols[i].size;
2798
+ }
2799
+ }
2800
+ /**
2801
+ * Creates a column layout manager for a data grid.
2802
+ *
2803
+ * Reads column existence, display order, AND grid layout config (size, pin,
2804
+ * min/max, resizable, reorderable) from the table's columns registry. The
2805
+ * grid fields ride on each ticket — `createRegistry` preserves them — so
2806
+ * there is no separate defs array. Visibility is layout-local view state
2807
+ * (not a ticket field) kept here and reconciled via register / unregister /
2808
+ * clear events.
2809
+ *
2810
+ * Generic over the registry's input/output ticket types so the caller's
2811
+ * concrete grid column ticket (`DataGridColumnTicket`) flows through. Layout
2812
+ * only ever reads from the registry (`values`, `keys`, `on`, `reorder`,
2813
+ * `move`, `lookup`) — it never registers — so the output ticket need only be
2814
+ * readable as a {@link GridColumnDef}.
2815
+ *
2816
+ * @param cols The table's columns registry — source of truth for which
2817
+ * columns exist, in what order, and their layout config
2818
+ * @returns Column layout state and mutation methods
2819
+ */
2820
+ function createColumnLayout(cols) {
2821
+ const logger = useLogger();
2822
+ const configs = /* @__PURE__ */ new Map();
2823
+ const group = createGroup({ multiple: true });
2824
+ const sizes = shallowReactive(/* @__PURE__ */ new Map());
2825
+ const hidden = shallowReactive(/* @__PURE__ */ new Set());
2826
+ const order = [];
2827
+ function record(id) {
2828
+ if (!order.includes(id)) order.push(id);
2829
+ }
2830
+ /** Even share over the currently-registered leaves: explicit sizes are
2831
+ * honored, unsized columns split the remaining percentage. */
2832
+ function share(id) {
2833
+ const leaves = /* @__PURE__ */ extractLeaves(cols.values());
2834
+ let used = 0;
2835
+ let unsized = 0;
2836
+ for (const leaf of leaves) {
2837
+ const size = configs.get(String(leaf.id))?.size;
2838
+ if (/* @__PURE__ */ isUndefined(size)) unsized++;
2839
+ else used += size;
2840
+ }
2841
+ const own = configs.get(id)?.size;
2842
+ if (!/* @__PURE__ */ isUndefined(own)) return own;
2843
+ const remainder = Math.max(0, 100 - used);
2844
+ return unsized > 0 ? remainder / unsized : 0;
2845
+ }
2846
+ function seedLeaf(leaf) {
2847
+ const id = String(leaf.id);
2848
+ if (!configs.has(id)) configs.set(id, {
2849
+ size: leaf.size,
2850
+ minSize: leaf.minSize ?? DEFAULT_EXTRAS.minSize,
2851
+ maxSize: leaf.maxSize ?? DEFAULT_EXTRAS.maxSize,
2852
+ resizable: leaf.resizable ?? DEFAULT_EXTRAS.resizable,
2853
+ reorderable: leaf.reorderable ?? DEFAULT_EXTRAS.reorderable,
2854
+ pinned: leaf.pinned ?? false
2855
+ });
2856
+ if (!group.has(id)) group.register({
2857
+ id,
2858
+ value: id
2859
+ });
2860
+ if (!sizes.has(id)) sizes.set(id, share(id));
2861
+ for (const other of /* @__PURE__ */ extractLeaves(cols.values())) {
2862
+ const otherId = String(other.id);
2863
+ if (configs.has(otherId) && /* @__PURE__ */ isUndefined(configs.get(otherId)?.size)) sizes.set(otherId, share(otherId));
2864
+ }
2865
+ const config = configs.get(id);
2866
+ if (config.pinned === "left") group.select(id);
2867
+ else if (config.pinned === "right") group.mix(id);
2868
+ }
2869
+ function dropLeaf(id) {
2870
+ group.unregister(id);
2871
+ configs.delete(id);
2872
+ sizes.delete(id);
2873
+ hidden.delete(id);
2874
+ }
2875
+ function leavesOf(ticket) {
2876
+ return /* @__PURE__ */ extractLeaves([ticket]);
2877
+ }
2878
+ for (const ticket of cols.values()) {
2879
+ record(String(ticket.id));
2880
+ for (const leaf of leavesOf(ticket)) seedLeaf(leaf);
2881
+ }
2882
+ cols.on("register:ticket", (ticket) => {
2883
+ record(String(ticket.id));
2884
+ for (const leaf of leavesOf(ticket)) seedLeaf(leaf);
2885
+ });
2886
+ cols.on("unregister:ticket", (ticket) => {
2887
+ const top = order.indexOf(String(ticket.id));
2888
+ if (top !== -1) order.splice(top, 1);
2889
+ for (const leaf of leavesOf(ticket)) dropLeaf(String(leaf.id));
2890
+ });
2891
+ cols.on("clear:registry", () => {
2892
+ group.clear();
2893
+ configs.clear();
2894
+ sizes.clear();
2895
+ hidden.clear();
2896
+ order.length = 0;
2897
+ });
2898
+ function position(id) {
2899
+ if (group.selectedIds.has(id)) return "left";
2900
+ if (group.mixed(id)) return "right";
2901
+ return false;
2902
+ }
2903
+ function resolve(includeHidden) {
2904
+ const resolved = [];
2905
+ let index = 0;
2906
+ for (const ticket of /* @__PURE__ */ extractLeaves(cols.values())) {
2907
+ const id = String(ticket.id);
2908
+ const visible = !hidden.has(id);
2909
+ if (!includeHidden && !visible) continue;
2910
+ const meta = configs.get(id) ?? DEFAULT_EXTRAS;
2911
+ resolved.push({
2912
+ id,
2913
+ index: index++,
2914
+ size: sizes.get(id) ?? 0,
2915
+ offset: 0,
2916
+ pinned: position(id),
2917
+ resizable: meta.resizable,
2918
+ reorderable: meta.reorderable,
2919
+ minSize: meta.minSize,
2920
+ maxSize: meta.maxSize,
2921
+ visible
2922
+ });
2923
+ }
2924
+ return resolved;
2925
+ }
2926
+ function split(resolved) {
2927
+ const left = [];
2928
+ const scrollable = [];
2929
+ const right = [];
2930
+ for (const col of resolved) if (col.pinned === "left") left.push(col);
2931
+ else if (col.pinned === "right") right.push(col);
2932
+ else scrollable.push(col);
2933
+ offsets(left);
2934
+ offsets(scrollable);
2935
+ offsets(right, true);
2936
+ return {
2937
+ left,
2938
+ scrollable,
2939
+ right
2940
+ };
2941
+ }
2942
+ const pinned = computed(() => split(resolve(false)));
2943
+ const columns = computed(() => {
2944
+ const { left, scrollable, right } = pinned.value;
2945
+ return [
2946
+ ...left,
2947
+ ...scrollable,
2948
+ ...right
2949
+ ];
2950
+ });
2951
+ const allColumns = computed(() => resolve(true));
2952
+ function pin(id, pos) {
2953
+ if (!group.has(id)) return;
2954
+ group.unselect(id);
2955
+ group.unmix(id);
2956
+ if (pos === "left") group.select(id);
2957
+ else if (pos === "right") group.mix(id);
2958
+ }
2959
+ function resize(id, delta) {
2960
+ const { left, scrollable, right } = pinned.value;
2961
+ const all = [
2962
+ ...left,
2963
+ ...scrollable,
2964
+ ...right
2965
+ ];
2966
+ const target = all.find((c) => c.id === id);
2967
+ if (!target || !target.resizable) return;
2968
+ const allIndex = all.findIndex((c) => c.id === id);
2969
+ if (allIndex === -1 || allIndex === all.length - 1) return;
2970
+ const neighbor = all[allIndex + 1];
2971
+ const total = target.size + neighbor.size;
2972
+ const lower = Math.max(target.minSize, total - neighbor.maxSize);
2973
+ const upper = Math.min(target.maxSize, total - neighbor.minSize);
2974
+ const clamped = /* @__PURE__ */ clamp(target.size + delta, lower, upper);
2975
+ sizes.set(id, clamped);
2976
+ sizes.set(neighbor.id, total - clamped);
2977
+ }
2978
+ function reorder(from, to) {
2979
+ if (from === to) return;
2980
+ const tops = [...cols.values()];
2981
+ const ids = [];
2982
+ const leafToTop = [];
2983
+ for (const [top, ticket] of tops.entries()) for (const leaf of /* @__PURE__ */ extractLeaves([ticket])) {
2984
+ ids.push(String(leaf.id));
2985
+ leafToTop.push(top);
2986
+ }
2987
+ const visible = ids.filter((id) => !hidden.has(id));
2988
+ const fromId = visible[from];
2989
+ const toId = visible[to];
2990
+ if (/* @__PURE__ */ isUndefined(fromId) || /* @__PURE__ */ isUndefined(toId)) return;
2991
+ const fromTop = leafToTop[ids.indexOf(fromId)];
2992
+ const toTop = leafToTop[ids.indexOf(toId)];
2993
+ if (/* @__PURE__ */ isUndefined(fromTop) || /* @__PURE__ */ isUndefined(toTop) || fromTop === toTop) return;
2994
+ if (tops[fromTop]?.reorderable === false) return;
2995
+ const id = cols.lookup(fromTop);
2996
+ if (/* @__PURE__ */ isUndefined(id)) return;
2997
+ cols.move(id, toTop);
2998
+ }
2999
+ function distribute(incoming) {
3000
+ const visible = (/* @__PURE__ */ extractLeaves(cols.values())).filter((leaf) => !hidden.has(String(leaf.id)));
3001
+ if (incoming.length !== visible.length) {
3002
+ logger.warn(`createDataGrid: distribute() expected ${visible.length} sizes but received ${incoming.length}; ignoring.`);
3003
+ return;
3004
+ }
3005
+ const sum = incoming.reduce((acc, value) => acc + value, 0);
3006
+ if (sum <= 0) {
3007
+ logger.warn(`createDataGrid: distribute() received sizes summing to ${sum}; expected a positive total.`);
3008
+ return;
3009
+ }
3010
+ const metas = visible.map((col) => configs.get(String(col.id)) ?? DEFAULT_EXTRAS);
3011
+ let current = incoming.map((value, i) => /* @__PURE__ */ clamp(value / sum * 100, metas[i].minSize, metas[i].maxSize));
3012
+ let residual = 100 - current.reduce((acc, value) => acc + value, 0);
3013
+ const max = visible.length * 2;
3014
+ for (let pass = 0; pass < max && Math.abs(residual) >= 1e-6; pass++) {
3015
+ const room = current.map((value, i) => residual > 0 ? metas[i].maxSize - value : value - metas[i].minSize);
3016
+ const available = room.reduce((acc, value) => acc + Math.max(0, value), 0);
3017
+ if (available <= 0) break;
3018
+ current = current.map((value, i) => room[i] <= 0 ? value : value + residual * (room[i] / available));
3019
+ residual = 100 - current.reduce((acc, value) => acc + value, 0);
3020
+ }
3021
+ if (Math.abs(residual) >= 1e-6) logger.warn(`createDataGrid: distribute() could not reach 100 within min/max constraints (off by ${residual.toFixed(2)})`);
3022
+ for (const [i, col] of visible.entries()) sizes.set(String(col.id), current[i]);
3023
+ }
3024
+ function show(id) {
3025
+ hidden.delete(id);
3026
+ }
3027
+ function hide(id) {
3028
+ hidden.add(id);
3029
+ }
3030
+ function toggle(id) {
3031
+ if (hidden.has(id)) hidden.delete(id);
3032
+ else hidden.add(id);
3033
+ }
3034
+ function reset() {
3035
+ for (const ticket of /* @__PURE__ */ extractLeaves(cols.values())) {
3036
+ const id = String(ticket.id);
3037
+ sizes.set(id, share(id));
3038
+ }
3039
+ cols.reorder([...order]);
3040
+ hidden.clear();
3041
+ for (const ticket of /* @__PURE__ */ extractLeaves(cols.values())) {
3042
+ const id = String(ticket.id);
3043
+ group.unselect(id);
3044
+ group.unmix(id);
3045
+ }
3046
+ for (const [id, config] of configs) if (config.pinned === "left") group.select(id);
3047
+ else if (config.pinned === "right") group.mix(id);
3048
+ }
3049
+ return {
3050
+ pinned,
3051
+ columns,
3052
+ all: allColumns,
3053
+ pin,
3054
+ resize,
3055
+ reorder,
3056
+ distribute,
3057
+ show,
3058
+ hide,
3059
+ toggle,
3060
+ reset
3061
+ };
3062
+ }
3063
+ //#endregion
3064
+ //#region src/composables/createDataGrid/spanning.ts
3065
+ /**
3066
+ * @module createDataGrid/spanning
3067
+ *
3068
+ * @remarks
3069
+ * Computes a row span map from visible items. For each cell, determines
3070
+ * rowSpan and whether it's hidden (covered by a span from a previous row).
3071
+ * Spans do not cross page boundaries. Each row is addressed by a key: either a
3072
+ * `key(item, index)` projection (the grid passes the registry ticket id) or,
3073
+ * absent that, the row's `itemKey` value. Rows whose resolved key is
3074
+ * `undefined` cannot be addressed in the resulting map and are skipped.
3075
+ *
3076
+ * @internal Consumed only by `createDataGrid`. Fully decoupled (items ref +
3077
+ * columns + key + span fn — no grid/table types), so it is a latent composable.
3078
+ * Promote it to a standalone `createRowSpanning/` composable on a second
3079
+ * consumer. See `.claude/rules/composables.md` §"Sub-modules: inline, private
3080
+ * sibling, or promote".
3081
+ */
3082
+ /**
3083
+ * Computes a row span map from visible items.
3084
+ *
3085
+ * @param options Row spanning configuration
3086
+ * @returns A computed map of item ID to column to SpanEntry
3087
+ *
3088
+ * @example
3089
+ * ```ts
3090
+ * const spans = createRowSpanning({
3091
+ * items: computed(() => grid.items.value),
3092
+ * columns: ['group', 'ticker', 'value'],
3093
+ * span: (item, column) => column === 'group' ? 3 : 1,
3094
+ * })
3095
+ *
3096
+ * spans.value.get(id)?.get('group') // { rowSpan: 3, hidden: false }
3097
+ * ```
3098
+ */
3099
+ function createRowSpanning(options) {
3100
+ const { items, columns, key, itemKey = "id", span } = options;
3101
+ return computed(() => {
3102
+ const result = /* @__PURE__ */ new Map();
3103
+ if (!span) return result;
3104
+ const list = items.value;
3105
+ const resolved = toValue(columns);
3106
+ const covered = Array.from({ length: resolved.length }).fill(0);
3107
+ for (let row = 0; row < list.length; row++) {
3108
+ const item = list[row];
3109
+ const id = key ? key(item, row) : item[itemKey];
3110
+ if (/* @__PURE__ */ isUndefined(id)) continue;
3111
+ const cells = /* @__PURE__ */ new Map();
3112
+ for (const [col, column] of resolved.entries()) if (covered[col] > 0) {
3113
+ cells.set(column, {
3114
+ rowSpan: 1,
3115
+ hidden: true
3116
+ });
3117
+ covered[col]--;
3118
+ } else {
3119
+ const raw = span(item, column);
3120
+ const rowSpan = Math.min(Math.max(1, /* @__PURE__ */ isNaN(raw) ? 1 : raw), list.length - row);
3121
+ cells.set(column, {
3122
+ rowSpan,
3123
+ hidden: false
3124
+ });
3125
+ if (rowSpan > 1) covered[col] = rowSpan - 1;
3126
+ }
3127
+ result.set(id, cells);
3128
+ }
3129
+ return result;
3130
+ });
3131
+ }
3132
+ //#endregion
3133
+ //#region src/composables/createDataGrid/index.ts
3134
+ /**
3135
+ * @module createDataGrid
3136
+ *
3137
+ * @see https://0.vuetifyjs.com/composables/data/create-data-grid
3138
+ *
3139
+ * @remarks
3140
+ * Main factory that wires together column layout, cell editing, row ordering,
3141
+ * and row spanning on top of a createDataTable pipeline. Row ordering is
3142
+ * provided by a createSortable instance synced to the table's row registry
3143
+ * via register / unregister events; the ordered ids are applied post-sort,
3144
+ * pre-pagination by overriding the `items` projection.
3145
+ *
3146
+ * Rows are registered through the inherited registry surface (`register`,
3147
+ * `onboard`, `unregister`, `clear`) — they are not passed as an `items`
3148
+ * option to the factory.
3149
+ *
3150
+ * Follows the trinity pattern for dependency injection.
3151
+ *
3152
+ * @example
3153
+ * ```ts
3154
+ * const grid = createDataGrid()
3155
+ *
3156
+ * grid.columns.onboard([
3157
+ * { id: 'name', sortable: true },
3158
+ * { id: 'progress', editable: true },
3159
+ * ])
3160
+ *
3161
+ * grid.onboard(rows.map(value => ({ id: value.id, value })))
3162
+ * grid.layout.pin('name', 'left')
3163
+ * grid.editing.edit(row.id, 'progress')
3164
+ * ```
3165
+ */
3166
+ function recordId(row) {
3167
+ return row.id;
3168
+ }
3169
+ /**
3170
+ * Creates a data grid instance with layout, editing, row ordering, and spanning
3171
+ * layered on top of the createDataTable pipeline.
3172
+ *
3173
+ * Rows live on the inherited registry surface — register them with
3174
+ * `grid.onboard(rows.map(value => ({ id: value.id, value })))` or
3175
+ * `grid.register({ id, value })`.
3176
+ *
3177
+ * @param options Data grid options
3178
+ * @returns Data grid context
3179
+ *
3180
+ * @example
3181
+ * ```ts
3182
+ * const grid = createDataGrid({ pagination: { page: 1 } })
3183
+ *
3184
+ * grid.columns.onboard([
3185
+ * { id: 'name', sortable: true },
3186
+ * { id: 'progress', editable: true, validate: v => Number(v) >= 0 || 'must be positive' },
3187
+ * ])
3188
+ *
3189
+ * grid.onboard(rows.map(value => ({ id: value.id, value })))
3190
+ * grid.layout.pin('name', 'left')
3191
+ * grid.rows.move(rowId, 0)
3192
+ * grid.editing.edit(row.id, 'progress')
3193
+ * ```
3194
+ */
3195
+ function createDataGrid(_options = {}) {
3196
+ const { editing: editingOptions, preserveRowOrder = false, rowSpanning, ...options } = _options;
3197
+ const table = createDataTable(options);
3198
+ const sortable = createSortable();
3199
+ const sortableProxy = useProxyRegistry(sortable);
3200
+ const dirty = shallowRef(false);
3201
+ table.on("register:ticket", (ticket) => {
3202
+ sortable.register({
3203
+ id: ticket.id,
3204
+ value: ticket.id
3205
+ });
3206
+ });
3207
+ table.on("unregister:ticket", (ticket) => {
3208
+ sortable.unregister(ticket.id);
3209
+ });
3210
+ table.on("clear:registry", () => {
3211
+ sortable.clear();
3212
+ dirty.value = false;
3213
+ });
3214
+ const order = toRef(() => sortableProxy.keys);
3215
+ const sortedIds = computed(() => {
3216
+ const buckets = /* @__PURE__ */ new Map();
3217
+ for (const ticket of table.values()) {
3218
+ const value = ticket.value;
3219
+ const queue = buckets.get(value);
3220
+ if (queue) queue.push(ticket.id);
3221
+ else buckets.set(value, [ticket.id]);
3222
+ }
3223
+ const ids = [];
3224
+ for (const value of table.sortedItems.value) {
3225
+ const id = buckets.get(value)?.shift();
3226
+ if (!/* @__PURE__ */ isUndefined(id)) ids.push(id);
3227
+ }
3228
+ return ids;
3229
+ });
3230
+ function move(id, toIndex) {
3231
+ if (!dirty.value) {
3232
+ const sorted = sortedIds.value;
3233
+ const seen = new Set(sorted);
3234
+ const rest = sortable.keys().filter((key) => !seen.has(key));
3235
+ sortable.reorder([...sorted, ...rest]);
3236
+ }
3237
+ sortable.move(id, toIndex);
3238
+ dirty.value = true;
3239
+ }
3240
+ function reset() {
3241
+ if (!dirty.value) return;
3242
+ if (sortable.size > 0) sortable.reorder(table.keys());
3243
+ dirty.value = false;
3244
+ }
3245
+ useToggleScope(() => !toValue(preserveRowOrder), () => {
3246
+ watch(table.sort.columns, reset, { flush: "sync" });
3247
+ });
3248
+ const ordered = computed(() => {
3249
+ if (!dirty.value) return {
3250
+ items: table.sortedItems.value,
3251
+ ids: sortedIds.value
3252
+ };
3253
+ const sorted = table.sortedItems.value;
3254
+ const ids = sortedIds.value;
3255
+ const byId = /* @__PURE__ */ new Map();
3256
+ for (let i = 0; i < sorted.length; i++) byId.set(ids[i], sorted[i]);
3257
+ const seen = new Set(order.value);
3258
+ const items = [];
3259
+ const orderedIds = [];
3260
+ for (const id of order.value) {
3261
+ const value = byId.get(id);
3262
+ if (!/* @__PURE__ */ isUndefined(value)) {
3263
+ items.push(value);
3264
+ orderedIds.push(id);
3265
+ }
3266
+ }
3267
+ for (const [i, element] of sorted.entries()) {
3268
+ const id = ids[i];
3269
+ if (!seen.has(id)) {
3270
+ items.push(element);
3271
+ orderedIds.push(id);
3272
+ }
3273
+ }
3274
+ return {
3275
+ items,
3276
+ ids: orderedIds
3277
+ };
3278
+ });
3279
+ const page = computed(() => {
3280
+ if (!dirty.value) {
3281
+ const start = table.pagination.pageStart.value;
3282
+ return {
3283
+ items: table.items.value,
3284
+ ids: table.total.value > table.sortedItems.value.length ? sortedIds.value : sortedIds.value.slice(start, start + table.items.value.length)
3285
+ };
3286
+ }
3287
+ const full = ordered.value;
3288
+ if (table.total.value > table.sortedItems.value.length) return full;
3289
+ const start = table.pagination.pageStart.value;
3290
+ const stop = table.pagination.pageStop.value;
3291
+ return {
3292
+ items: full.items.slice(start, stop),
3293
+ ids: full.ids.slice(start, stop)
3294
+ };
3295
+ });
3296
+ const pageOrderedItems = toRef(() => page.value.items);
3297
+ const orderedItems = toRef(() => ordered.value.items);
3298
+ function rank(source) {
3299
+ const ranked = orderedItems.value;
3300
+ const positions = /* @__PURE__ */ new Map();
3301
+ for (const [index, row] of ranked.entries()) {
3302
+ positions.set(row, index);
3303
+ const id = recordId(row);
3304
+ if (!/* @__PURE__ */ isNullOrUndefined(id)) positions.set(id, index);
3305
+ }
3306
+ return [...source].toSorted((a, b) => {
3307
+ return (positions.get(a) ?? positions.get(recordId(a)) ?? Infinity) - (positions.get(b) ?? positions.get(recordId(b)) ?? Infinity);
3308
+ });
3309
+ }
3310
+ const layout = createColumnLayout(table.columns);
3311
+ function lookup(row) {
3312
+ return table.get(row)?.value;
3313
+ }
3314
+ const editing = createCellEditing({
3315
+ columns: () => table.leaves.value.filter((col) => col.editable === true || /* @__PURE__ */ isFunction(col.editable)).map((col) => ({
3316
+ id: String(col.id),
3317
+ editable: col.editable,
3318
+ validate: col.validate
3319
+ })),
3320
+ lookup,
3321
+ registry: table,
3322
+ onEdit: editingOptions?.onEdit ? (row, column, value) => {
3323
+ const item = lookup(row);
3324
+ if (!/* @__PURE__ */ isUndefined(item)) editingOptions.onEdit(row, column, value, item);
3325
+ } : void 0
3326
+ });
3327
+ const spans = createRowSpanning({
3328
+ items: pageOrderedItems,
3329
+ columns: () => table.leaves.value.map((col) => col.id),
3330
+ key: (_item, index) => page.value.ids[index],
3331
+ span(item, column) {
3332
+ const col = table.leaves.value.find((leaf) => String(leaf.id) === column);
3333
+ if (col && /* @__PURE__ */ isFunction(col.span)) return col.span(item);
3334
+ return rowSpanning?.(item, column) ?? 1;
3335
+ }
3336
+ });
3337
+ return {
3338
+ ...table,
3339
+ items: pageOrderedItems,
3340
+ orderedItems,
3341
+ rank,
3342
+ layout,
3343
+ rows: {
3344
+ order,
3345
+ move,
3346
+ reset
3347
+ },
3348
+ editing,
3349
+ spans,
3350
+ get size() {
3351
+ return table.size;
3352
+ }
3353
+ };
3354
+ }
3355
+ /**
3356
+ * Creates a data grid context with dependency injection support.
3357
+ *
3358
+ * @param options Data grid context options including namespace
3359
+ * @returns A trinity tuple: [useDataGridContext, provideDataGridContext, defaultContext]
3360
+ *
3361
+ * @example
3362
+ * ```ts
3363
+ * const [useDataGrid, provideDataGrid, grid] = createDataGridContext({
3364
+ * namespace: 'v0:projects',
3365
+ * })
3366
+ *
3367
+ * grid.columns.onboard(columns)
3368
+ * grid.onboard(rows.map(value => ({ id: value.id, value })))
3369
+ * provideDataGrid()
3370
+ * const ctx = useDataGrid()
3371
+ * ```
3372
+ */
3373
+ function createDataGridContext(_options = {}) {
3374
+ const { namespace = "v0:data-grid", ...options } = _options;
3375
+ const [useDataGridContext, _provideDataGridContext] = createContext(namespace);
3376
+ const context = createDataGrid(options);
3377
+ function provideDataGridContext(_context = context, app) {
3378
+ return _provideDataGridContext(_context, app);
3379
+ }
3380
+ return createTrinity(useDataGridContext, provideDataGridContext, context);
3381
+ }
3382
+ /**
3383
+ * Returns the current data grid context from dependency injection.
3384
+ *
3385
+ * @param namespace The namespace for the data grid context. @default 'v0:data-grid'
3386
+ * @returns The current data grid context
3387
+ *
3388
+ * @example
3389
+ * ```ts
3390
+ * const grid = useDataGrid<Project>()
3391
+ *
3392
+ * grid.layout.pin('id', 'left')
3393
+ * grid.rows.reset()
3394
+ * ```
3395
+ */
3396
+ function useDataGrid(namespace = "v0:data-grid") {
3397
+ return useContext(namespace);
3398
+ }
3399
+ //#endregion
3400
+ //#region src/composables/useRaf/index.ts
3401
+ /**
3402
+ * @module useRaf
3403
+ *
3404
+ * @see https://0.vuetifyjs.com/composables/system/use-raf
3405
+ *
3406
+ * @remarks
3407
+ * Scope-disposed safe requestAnimationFrame composable.
3408
+ *
3409
+ * Key features:
3410
+ * - Cancel-then-request pattern (deduplicates rapid calls)
3411
+ * - Automatic cleanup on scope disposal
3412
+ * - SSR-safe (no-op in non-browser environments)
3413
+ * - Callable function with cancel method
3414
+ *
3415
+ * Perfect for throttling updates to the next animation frame.
3416
+ *
3417
+ * @example
3418
+ * ```ts
3419
+ * import { useRaf } from '@vuetify/v0'
3420
+ *
3421
+ * const update = useRaf(timestamp => {
3422
+ * // runs on next animation frame
3423
+ * })
3424
+ * update()
3425
+ * ```
3426
+ */
3427
+ /**
3428
+ * Scope-disposed safe requestAnimationFrame.
3429
+ *
3430
+ * @param callback Function to call on animation frame.
3431
+ * @returns Callable that requests frame (canceling pending), with cancel method.
3432
+ *
3433
+ * @see https://0.vuetifyjs.com/composables/system/use-raf
3434
+ *
3435
+ * @example
3436
+ * ```ts
3437
+ * // Basic usage
3438
+ * const update = useRaf((timestamp) => {
3439
+ * // Runs on next animation frame
3440
+ * })
3441
+ *
3442
+ * update() // Request frame
3443
+ * update() // Cancels previous, requests new (deduplicated)
3444
+ *
3445
+ * // Manual cancel
3446
+ * update.cancel()
3447
+ *
3448
+ * // Check if pending
3449
+ * if (update.isActive.value) { ... }
3450
+ * ```
3451
+ */
3452
+ function useRaf(callback) {
3453
+ const id = shallowRef();
3454
+ const isActive = toRef(() => !/* @__PURE__ */ isUndefined(id.value));
3455
+ function cancel() {
3456
+ if (!/* @__PURE__ */ isUndefined(id.value)) {
3457
+ cancelAnimationFrame(id.value);
3458
+ id.value = void 0;
3459
+ }
3460
+ }
3461
+ function request() {
3462
+ if (!IN_BROWSER) return;
3463
+ cancel();
3464
+ id.value = requestAnimationFrame((timestamp) => {
3465
+ id.value = void 0;
3466
+ callback(timestamp);
3467
+ });
3468
+ }
3469
+ onScopeDispose(cancel, true);
3470
+ return Object.assign(request, {
3471
+ cancel,
3472
+ isActive
3473
+ });
3474
+ }
3475
+ //#endregion
3476
+ //#region src/composables/createForm/index.ts
3477
+ /**
3478
+ * @module createForm
3479
+ *
3480
+ * @see https://0.vuetifyjs.com/composables/forms/create-form
3481
+ *
3482
+ * @remarks
3483
+ * Form composable that coordinates validation across multiple fields.
3484
+ *
3485
+ * Key features:
3486
+ * - Pure registry of ValidationContext instances
3487
+ * - Validations auto-register via useForm() injection
3488
+ * - Form-level submit, reset, and aggregate state
3489
+ * - Exposes disabled/readonly refs for component consumption
3490
+ * - Trinity pattern for DI
3491
+ *
3492
+ * Per-field validation logic lives in createValidation.
3493
+ * createForm is the mothership — it coordinates, not creates.
3494
+ *
3495
+ * @example
3496
+ * ```ts
3497
+ * import { createForm } from '@vuetify/v0'
3498
+ *
3499
+ * const form = createForm()
3500
+ * const isValid = await form.validate()
3501
+ * if (isValid) form.submit()
3502
+ * ```
3503
+ */
3504
+ /**
3505
+ * Creates a new form instance.
3506
+ *
3507
+ * A form is a pure registry of validation contexts. Validations register
3508
+ * themselves via `useForm()` injection. The form coordinates submit, reset,
3509
+ * and aggregate state across all registered validations.
3510
+ *
3511
+ * @param options The options for the form instance.
3512
+ * @returns A new form context.
3513
+ *
3514
+ * @see https://0.vuetifyjs.com/composables/forms/create-form
3515
+ *
3516
+ * @example
3517
+ * ```ts
3518
+ * import { createForm, createValidation } from '@vuetify/v0'
3519
+ * import { shallowRef } from 'vue'
3520
+ *
3521
+ * const form = createForm()
3522
+ *
3523
+ * // Each validation is one input — form is just the coordinator
3524
+ * const email = shallowRef('')
3525
+ * const validation = createValidation({
3526
+ * value: email,
3527
+ * rules: ['required', 'email'],
3528
+ * })
3529
+ *
3530
+ * form.register({ value: validation })
3531
+ *
3532
+ * await form.submit()
3533
+ * form.reset()
3534
+ * ```
3535
+ */
3536
+ function createForm(_options = {}) {
3537
+ const { disabled = false, readonly = false, ...options } = _options;
3538
+ const registry = createRegistry({
3539
+ ...options,
3540
+ reactive: true
3541
+ });
3542
+ const isValidating = computed(() => {
3543
+ for (const ticket of registry.values()) if (ticket.value.isValidating.value) return true;
3544
+ return false;
3545
+ });
3546
+ const isValid = computed(() => {
3547
+ let hasNull = false;
3548
+ let hasFields = false;
3549
+ for (const ticket of registry.values()) {
3550
+ hasFields = true;
3551
+ if (ticket.value.isValid.value === false) return false;
3552
+ if (/* @__PURE__ */ isNull(ticket.value.isValid.value)) hasNull = true;
3553
+ }
3554
+ if (!hasFields) return null;
3555
+ return hasNull ? null : true;
3556
+ });
3557
+ function register(registration) {
3558
+ return registry.register(registration);
3559
+ }
3560
+ function reset() {
3561
+ for (const ticket of registry.values()) ticket.value.reset();
3562
+ }
3563
+ async function submit(id) {
3564
+ const ids = /* @__PURE__ */ isUndefined(id) ? [...registry.keys()] : /* @__PURE__ */ toArray(id);
3565
+ return (await Promise.all(ids.map(async (key) => {
3566
+ const ticket = registry.get(key);
3567
+ if (!ticket) return true;
3568
+ return ticket.value.validate();
3569
+ }))).every(Boolean);
3570
+ }
3571
+ return {
3572
+ ...registry,
3573
+ register,
3574
+ submit,
3575
+ reset,
3576
+ disabled,
3577
+ readonly,
3578
+ isValid,
3579
+ isValidating,
3580
+ get size() {
3581
+ return registry.size;
3582
+ }
3583
+ };
3584
+ }
3585
+ /**
3586
+ * Creates a new form context using the Trinity pattern.
3587
+ *
3588
+ * @param options The options for the form context.
3589
+ * @returns A Trinity tuple: [useFormContext, provideFormContext, formContext]
3590
+ *
3591
+ * @see https://0.vuetifyjs.com/composables/forms/create-form
3592
+ *
3593
+ * @example
3594
+ * ```ts
3595
+ * import { createFormContext } from '@vuetify/v0'
2934
3596
  *
2935
3597
  * export const [useMyForm, provideMyForm, myForm] = createFormContext()
2936
3598
  * ```
@@ -3511,7 +4173,7 @@ function createNumeric(options = {}) {
3511
4173
  * ```
3512
4174
  */
3513
4175
  function createNumberField(options = {}) {
3514
- const { value = ref(null), locale = "en-US", format: formatOptions, clamp: shouldClamp = true, disabled = false, readonly: _readonly = false, min, max, step, leap, wrap, id, label, name, rules, error, errorMessages } = options;
4176
+ const { value = ref(null), locale = "en-US", format: formatOptions, clamp: shouldClamp = true, commitOn = "change", disabled = false, readonly: _readonly = false, min, max, step, leap, wrap, id, label, name, rules, error, errorMessages } = options;
3515
4177
  const numeric = createNumeric({
3516
4178
  min,
3517
4179
  max,
@@ -3601,6 +4263,10 @@ function createNumberField(options = {}) {
3601
4263
  }
3602
4264
  value.value = numeric.snap(val);
3603
4265
  }
4266
+ function write(text) {
4267
+ if (isLocked()) return;
4268
+ value.value = parse(text);
4269
+ }
3604
4270
  return {
3605
4271
  value,
3606
4272
  display,
@@ -3614,86 +4280,12 @@ function createNumberField(options = {}) {
3614
4280
  ceil,
3615
4281
  formatValue,
3616
4282
  parse,
3617
- commit
4283
+ commit,
4284
+ commitOn,
4285
+ write
3618
4286
  };
3619
4287
  }
3620
4288
  //#endregion
3621
- //#region src/composables/useRaf/index.ts
3622
- /**
3623
- * @module useRaf
3624
- *
3625
- * @see https://0.vuetifyjs.com/composables/system/use-raf
3626
- *
3627
- * @remarks
3628
- * Scope-disposed safe requestAnimationFrame composable.
3629
- *
3630
- * Key features:
3631
- * - Cancel-then-request pattern (deduplicates rapid calls)
3632
- * - Automatic cleanup on scope disposal
3633
- * - SSR-safe (no-op in non-browser environments)
3634
- * - Callable function with cancel method
3635
- *
3636
- * Perfect for throttling updates to the next animation frame.
3637
- *
3638
- * @example
3639
- * ```ts
3640
- * import { useRaf } from '@vuetify/v0'
3641
- *
3642
- * const update = useRaf(timestamp => {
3643
- * // runs on next animation frame
3644
- * })
3645
- * update()
3646
- * ```
3647
- */
3648
- /**
3649
- * Scope-disposed safe requestAnimationFrame.
3650
- *
3651
- * @param callback Function to call on animation frame.
3652
- * @returns Callable that requests frame (canceling pending), with cancel method.
3653
- *
3654
- * @see https://0.vuetifyjs.com/composables/system/use-raf
3655
- *
3656
- * @example
3657
- * ```ts
3658
- * // Basic usage
3659
- * const update = useRaf((timestamp) => {
3660
- * // Runs on next animation frame
3661
- * })
3662
- *
3663
- * update() // Request frame
3664
- * update() // Cancels previous, requests new (deduplicated)
3665
- *
3666
- * // Manual cancel
3667
- * update.cancel()
3668
- *
3669
- * // Check if pending
3670
- * if (update.isActive.value) { ... }
3671
- * ```
3672
- */
3673
- function useRaf(callback) {
3674
- const id = shallowRef();
3675
- const isActive = toRef(() => !/* @__PURE__ */ isUndefined(id.value));
3676
- function cancel() {
3677
- if (!/* @__PURE__ */ isUndefined(id.value)) {
3678
- cancelAnimationFrame(id.value);
3679
- id.value = void 0;
3680
- }
3681
- }
3682
- function request() {
3683
- if (!IN_BROWSER) return;
3684
- cancel();
3685
- id.value = requestAnimationFrame((timestamp) => {
3686
- id.value = void 0;
3687
- callback(timestamp);
3688
- });
3689
- }
3690
- onScopeDispose(cancel, true);
3691
- return Object.assign(request, {
3692
- cancel,
3693
- isActive
3694
- });
3695
- }
3696
- //#endregion
3697
4289
  //#region src/composables/usePresence/index.ts
3698
4290
  /**
3699
4291
  * @module usePresence
@@ -3876,9 +4468,8 @@ function createProgress(options = {}) {
3876
4468
  return (total.value - min) / extent * 100;
3877
4469
  });
3878
4470
  const isIndeterminate = toRef(() => {
3879
- if (_hasInitialValue) return false;
3880
4471
  const segs = segments.value;
3881
- if (segs.length === 0) return true;
4472
+ if (segs.length === 0) return !_hasInitialValue;
3882
4473
  for (const seg of segs) if ((toValue(seg.value) ?? 0) > 0) return false;
3883
4474
  return true;
3884
4475
  });
@@ -3904,10 +4495,9 @@ function createProgress(options = {}) {
3904
4495
  pending = clamped;
3905
4496
  return;
3906
4497
  }
3907
- for (const [index, element] of clamped.entries()) {
3908
- const ticket = segments.value[index];
3909
- if (!ticket || !isRef(ticket.value) || isReadonly(ticket.value)) continue;
3910
- ticket.value.value = element;
4498
+ for (const [index, ticket] of segments.value.entries()) {
4499
+ if (!isRef(ticket.value) || isReadonly(ticket.value)) continue;
4500
+ ticket.value.value = clamped[index] ?? min;
3911
4501
  }
3912
4502
  }
3913
4503
  return {
@@ -4347,7 +4937,8 @@ function createTooltip(options = {}) {
4347
4937
  isAnyOpen,
4348
4938
  shouldSkipOpenDelay,
4349
4939
  register: registry.register,
4350
- unregister: registry.unregister
4940
+ unregister: registry.unregister,
4941
+ adapter: options.adapter
4351
4942
  };
4352
4943
  }
4353
4944
  /**
@@ -5129,4 +5720,4 @@ function useRovingFocus(items, options = {}) {
5129
5720
  };
5130
5721
  }
5131
5722
  //#endregion
5132
- export { createHydrationPlugin as $, useCombobox as A, createBreadcrumbs as B, useIntersectionObserver as C, useLazy as D, useForm as E, useDelay as F, createOverflowContext as G, useBreadcrumbs as H, createStep as I, useResizeObserver as J, useOverflow as K, createStepContext as L, ComboboxAdapter as M, useVirtualFocus as N, createCombobox as O, usePopover as P, createHydrationContext as Q, useStep as R, useElementIntersection as S, createFormContext as T, useImage as U, createBreadcrumbsContext as V, createOverflow as W, createFallbackHydration as X, createObserver as Y, createHydration as Z, useRaf as _, createTooltipContext as a, useStack as at, createInput as b, useTooltip as c, createRatingContext as d, useHydration as et, useRating as f, usePresence as g, useProgress as h, useNested as i, createStackPlugin as it, ClientComboboxAdapter as j, createComboboxContext as k, createSlider as l, createProgressContext as m, createNested as n, createStack as nt, createTooltipFallback as o, toElement as ot, createProgress as p, useElementSize as q, createNestedContext as r, createStackContext as rt, createTooltipPlugin as s, useClickOutside as st, useRovingFocus as t, useToggleScope as tt, createRating as u, createNumberField as v, createForm as w, createValidation as x, createNumeric as y, useProxyModel as z };
5723
+ export { createStack as $, createSortable as A, useStep as B, createForm as C, createDataGrid as D, useRaf as E, ClientComboboxAdapter as F, useImage as G, createBreadcrumbs as H, ComboboxAdapter as I, useOverflow as J, createOverflow as K, useVirtualFocus as L, createCombobox as M, createComboboxContext as N, createDataGridContext as O, useCombobox as P, useToggleScope as Q, createStep as R, useIntersectionObserver as S, useForm as T, createBreadcrumbsContext as U, useProxyModel as V, useBreadcrumbs as W, useResizeObserver as X, useElementSize as Y, createObserver as Z, createNumberField as _, createTooltipContext as a, createValidation as b, useTooltip as c, createRatingContext as d, createStackContext as et, useRating as f, usePresence as g, useProgress as h, useNested as i, useClickOutside as it, useLazy as j, useDataGrid as k, createSlider as l, createProgressContext as m, createNested as n, useStack as nt, createTooltipFallback as o, createProgress as p, createOverflowContext as q, createNestedContext as r, toElement as rt, createTooltipPlugin as s, useRovingFocus as t, createStackPlugin as tt, createRating as u, createNumeric as v, createFormContext as w, useElementIntersection as x, createInput as y, createStepContext as z };