@vuetify/v0 0.1.13 → 0.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 (125) hide show
  1. package/dist/{adapter-DvT8lTup.d.mts → adapter-DIZk5xrU.d.mts} +1 -1
  2. package/dist/adapter-Drq1v5wV.mjs +27 -0
  3. package/dist/{adapter-D3dUnE__.d.mts → adapter-MT5TiIjj.d.mts} +2 -2
  4. package/dist/browser/index.js +3912 -2932
  5. package/dist/components/index.d.mts +3 -2
  6. package/dist/components/index.mjs +22 -20
  7. package/dist/{components-C6stt5NV.mjs → components-lme-T9bN.mjs} +771 -177
  8. package/dist/composables/index.d.mts +19 -19
  9. package/dist/composables/index.mjs +22 -20
  10. package/dist/{consola-C1OjyZVf.d.mts → consola-bPHDMwrK.d.mts} +1 -1
  11. package/dist/constants/index.d.mts +1 -1
  12. package/dist/constants/index.mjs +1 -1
  13. package/dist/{useClickOutside-CBsF7hh4.mjs → createCombobox-DgFUWLKw.mjs} +1091 -790
  14. package/dist/{createDataTable-DHxv--aj.mjs → createDataTable-DGG3Kgau.mjs} +2 -2
  15. package/dist/{createGroup-BqN4Ve83.mjs → createGroup-C38Mc-se.mjs} +1 -1
  16. package/dist/{createSelection-BsHsL_qW.mjs → createSelection-GS1yfwki.mjs} +1 -1
  17. package/dist/{createSingle-BBwE5j_k.mjs → createSingle-Dh2gp4kv.mjs} +1 -1
  18. package/dist/{createTokens-CpapxkYX.mjs → createTokens-Ckczb95n.mjs} +1 -1
  19. package/dist/data-table/adapters/client/index.d.mts +1 -1
  20. package/dist/data-table/adapters/client/index.mjs +1 -1
  21. package/dist/data-table/adapters/index.d.mts +4 -4
  22. package/dist/data-table/adapters/index.mjs +1 -1
  23. package/dist/data-table/adapters/server/index.d.mts +1 -1
  24. package/dist/data-table/adapters/server/index.mjs +1 -1
  25. package/dist/data-table/adapters/virtual/index.d.mts +1 -1
  26. package/dist/data-table/adapters/virtual/index.mjs +1 -1
  27. package/dist/data-table/index.d.mts +5 -5
  28. package/dist/data-table/index.mjs +10 -10
  29. package/dist/date/index.d.mts +1 -1
  30. package/dist/date/index.mjs +15 -3
  31. package/dist/features/adapters/flagsmith/index.d.mts +1 -1
  32. package/dist/features/adapters/launchdarkly/index.d.mts +1 -1
  33. package/dist/features/adapters/posthog/index.d.mts +1 -1
  34. package/dist/features/adapters/posthog/index.mjs +2 -2
  35. package/dist/features/index.d.mts +2 -2
  36. package/dist/features/index.mjs +8 -8
  37. package/dist/{globals-DCznFHqN.mjs → globals-B6VQTLVM.mjs} +1 -1
  38. package/dist/{index-B_pScNDG.d.mts → index-A6Vo3pb6.d.mts} +28 -1
  39. package/dist/{index-CBER-Sxt.d.mts → index-BAix61Ke.d.mts} +275 -98
  40. package/dist/{index-vcUKp9YE.d.mts → index-CuUuJj3-.d.mts} +884 -205
  41. package/dist/{index-CdUIc68E.d.mts → index-D5ehwqoP.d.mts} +3 -3
  42. package/dist/{index-YyqYAay2.d.mts → index-Y0Bo7jx-.d.mts} +2 -2
  43. package/dist/{index-dJKXAO5D.d.mts → index-pwVD-IDb.d.mts} +1 -1
  44. package/dist/index.d.mts +23 -23
  45. package/dist/index.mjs +23 -21
  46. package/dist/json/attributes.json +199 -7
  47. package/dist/json/importMap.json +44 -0
  48. package/dist/json/tags.json +100 -2
  49. package/dist/json/web-types.json +592 -10
  50. package/dist/locale/adapters/index.d.mts +2 -2
  51. package/dist/locale/adapters/index.mjs +2 -2
  52. package/dist/locale/adapters/v0/index.d.mts +1 -1
  53. package/dist/locale/adapters/v0/index.mjs +2 -2
  54. package/dist/locale/adapters/vue-i18n/index.d.mts +1 -1
  55. package/dist/locale/adapters/vue-i18n/index.mjs +1 -1
  56. package/dist/locale/index.d.mts +3 -3
  57. package/dist/locale/index.mjs +8 -8
  58. package/dist/logger/adapters/consola/index.d.mts +1 -1
  59. package/dist/logger/adapters/index.d.mts +4 -4
  60. package/dist/logger/adapters/index.mjs +2 -2
  61. package/dist/logger/adapters/pino/index.d.mts +1 -1
  62. package/dist/logger/adapters/pino/index.mjs +1 -1
  63. package/dist/logger/adapters/v0/index.d.mts +1 -1
  64. package/dist/logger/adapters/v0/index.mjs +2 -2
  65. package/dist/logger/index.d.mts +5 -5
  66. package/dist/logger/index.mjs +3 -3
  67. package/dist/notifications/index.d.mts +1 -1
  68. package/dist/notifications/index.mjs +1 -1
  69. package/dist/palettes/ant/index.d.mts +1 -1
  70. package/dist/palettes/index.d.mts +3 -3
  71. package/dist/palettes/material/index.d.mts +1 -1
  72. package/dist/palettes/md1/index.d.mts +1 -1
  73. package/dist/palettes/md2/index.d.mts +1 -1
  74. package/dist/palettes/radix/index.d.mts +1 -1
  75. package/dist/palettes/tailwind/index.d.mts +1 -1
  76. package/dist/permissions/adapters/index.d.mts +2 -2
  77. package/dist/permissions/adapters/index.mjs +1 -1
  78. package/dist/permissions/adapters/v0/index.d.mts +1 -1
  79. package/dist/permissions/adapters/v0/index.mjs +1 -1
  80. package/dist/permissions/index.d.mts +1 -1
  81. package/dist/permissions/index.mjs +5 -5
  82. package/dist/{pino-CM1iLXmi.d.mts → pino-BUGULcfS.d.mts} +1 -1
  83. package/dist/{posthog-BugD9w3g.mjs → posthog-BUVv9Rky.mjs} +1 -1
  84. package/dist/rules/index.d.mts +1 -1
  85. package/dist/rules/index.mjs +9 -9
  86. package/dist/{server-CSLVeyyI.d.mts → server-Ciw0ZhhL.d.mts} +1 -1
  87. package/dist/storage/adapters/index.d.mts +2 -2
  88. package/dist/storage/adapters/memory/index.d.mts +1 -1
  89. package/dist/storage/index.d.mts +3 -3
  90. package/dist/storage/index.mjs +20 -18
  91. package/dist/theme/adapters/index.d.mts +2 -2
  92. package/dist/theme/adapters/index.mjs +6 -4
  93. package/dist/theme/adapters/unhead/index.d.mts +2 -0
  94. package/dist/theme/adapters/unhead/index.mjs +4 -0
  95. package/dist/theme/adapters/v0/index.d.mts +2 -2
  96. package/dist/theme/adapters/v0/index.mjs +4 -3
  97. package/dist/theme/index.d.mts +2 -2
  98. package/dist/theme/index.mjs +12 -10
  99. package/dist/types/index.d.mts +1 -1
  100. package/dist/unhead-CMUN7pv4.mjs +63 -0
  101. package/dist/{useFeatures-CkIfpj7l.mjs → useFeatures-_uwRwuBy.mjs} +3 -3
  102. package/dist/{useLocale-D3btFSZv.mjs → useLocale-BNecStw9.mjs} +9 -5
  103. package/dist/{useLogger-C3crtWEd.mjs → useLogger-B3ijAgn0.mjs} +29 -7
  104. package/dist/{usePermissions-DmaYLhw5.mjs → usePermissions-5g7JSb3-.mjs} +2 -2
  105. package/dist/{useRules-Dkj_-0ay.mjs → useRules-BnQZP8n2.mjs} +2 -2
  106. package/dist/{useTheme-MJA1p7YK.mjs → useTheme-CKcE6z3t.mjs} +10 -7
  107. package/dist/utilities/index.d.mts +1 -1
  108. package/dist/utilities/index.mjs +11 -11
  109. package/dist/{v0-pzmWhctY.mjs → v0-3e8B6fd8.mjs} +7 -32
  110. package/dist/{v0-B6ILFiMN.d.mts → v0-BRnEUnx1.d.mts} +2 -2
  111. package/dist/{v0-CQ2qHo5-.d.mts → v0-B_8xEDpf.d.mts} +1 -1
  112. package/dist/{v0-JPf99NKg.d.mts → v0-C3cGdshz.d.mts} +1 -1
  113. package/dist/{v0-2IcFDTW5.mjs → v0-C64RQgkl.mjs} +1 -1
  114. package/dist/{v0-CB-GCB-s.mjs → v0-Dab-mQua.mjs} +1 -1
  115. package/dist/{v0-3zDdS4II.d.mts → v0-WOPq3YTM.d.mts} +1 -1
  116. package/dist/{virtual-Dg0Cm5Hn.d.mts → virtual-By1prSSj.d.mts} +1 -1
  117. package/package.json +2 -1
  118. /package/dist/{adapter-CJQav2JB.d.mts → adapter-CST7kApg.d.mts} +0 -0
  119. /package/dist/{adapter-BWMm0JAQ.d.mts → adapter-cc0r4Pfu.d.mts} +0 -0
  120. /package/dist/{adapters-C66XaKkw.mjs → adapters-BvwLemDt.mjs} +0 -0
  121. /package/dist/{index-CeNfBYuo.d.mts → index-BsNeOyXe.d.mts} +0 -0
  122. /package/dist/{index-BvTD7kU8.d.mts → index-BvlxDVXg.d.mts} +0 -0
  123. /package/dist/{index-DSao7i57.d.mts → index-C9Wj_it8.d.mts} +0 -0
  124. /package/dist/{index-Dor6eAEc.d.mts → index-D7Yh-7yN.d.mts} +0 -0
  125. /package/dist/{memory-DyBabFV1.d.mts → memory-CC5pKIAp.d.mts} +0 -0
@@ -1,14 +1,15 @@
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-DCznFHqN.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-B6VQTLVM.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-C3crtWEd.mjs";
5
- import { a as createRegistry } from "./createTokens-CpapxkYX.mjs";
6
- import { i as createModel, t as createSelection } from "./createSelection-BsHsL_qW.mjs";
7
- import { t as createSingle } from "./createSingle-BBwE5j_k.mjs";
8
- import { t as createGroup } from "./createGroup-BqN4Ve83.mjs";
4
+ import { a as createPlugin, i as useLogger, o as createPluginContext } from "./useLogger-B3ijAgn0.mjs";
5
+ import { a as createRegistry } from "./createTokens-Ckczb95n.mjs";
6
+ import { i as createModel, t as createSelection } from "./createSelection-GS1yfwki.mjs";
7
+ import { t as createSingle } from "./createSingle-Dh2gp4kv.mjs";
8
+ import { t as createGroup } from "./createGroup-C38Mc-se.mjs";
9
9
  import { t as toArray } from "./toArray-VdROPpy-.mjs";
10
- import { a as useLocale } from "./useLocale-D3btFSZv.mjs";
11
- import { a as useRules, o as isStandardSchema } from "./useRules-Dkj_-0ay.mjs";
10
+ import { a as useLocale } from "./useLocale-BNecStw9.mjs";
11
+ import { n as createFilter } from "./adapter-DXRKF27V.mjs";
12
+ import { a as useRules, o as isStandardSchema } from "./useRules-BnQZP8n2.mjs";
12
13
  import { t as MemoryAdapter } from "./memory-XCMtkld1.mjs";
13
14
  import { computed, effectScope, isRef, nextTick, onMounted, onScopeDispose, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRef, toValue, unref, watch, watchEffect } from "vue";
14
15
  //#region src/composables/createBreadcrumbs/index.ts
@@ -692,222 +693,6 @@ function useOverflow(namespace = "v0:overflow") {
692
693
  return useContext(namespace);
693
694
  }
694
695
  //#endregion
695
- //#region src/composables/useProxyModel/index.ts
696
- /**
697
- * @module useProxyModel
698
- *
699
- * @remarks
700
- * Proxy composable for bidirectional sync between a model context and v-model.
701
- *
702
- * Key features:
703
- * - Bidirectional synchronization
704
- * - Array and single-value modes
705
- * - Automatic cleanup on scope disposal
706
- * - Works with any ModelContext (Selection, Slider, etc.)
707
- *
708
- * Bridges the gap between model composables and Vue's v-model.
709
- */
710
- /**
711
- * Syncs a ref with a model context bidirectionally.
712
- *
713
- * @param context The model context to bind to.
714
- * @param model The ref to sync.
715
- * @param options The options for the proxy model.
716
- * @returns A function to stop the sync.
717
- *
718
- * @see https://0.vuetifyjs.com/composables/forms/use-proxy-model
719
- *
720
- * @example
721
- * ```ts
722
- * import { createSelection, useProxyModel } from '@vuetify/v0'
723
- *
724
- * const model = ref()
725
- * const registry = createSelection({ events: true })
726
- * registry.onboard([
727
- * { id: 'item-1', value: 'Item 1' },
728
- * { id: 'item-2', value: 'Item 2' },
729
- * ])
730
- *
731
- * const stop = useProxyModel(registry, model)
732
- * ```
733
- */
734
- function useProxyModel(context, model, options) {
735
- const multiple = options?.multiple ?? context.multiple ?? false;
736
- const _transformIn = options?.transformIn;
737
- const _transformOut = options?.transformOut;
738
- function transformIn(val) {
739
- const value = toValue(val);
740
- return toArray(/* @__PURE__ */ isFunction(_transformIn) ? _transformIn(value) : value);
741
- }
742
- function transformOut(val) {
743
- if (/* @__PURE__ */ isFunction(_transformOut)) return _transformOut(val);
744
- return toValue(multiple) ? val : val[0];
745
- }
746
- const applyOptions = toValue(multiple) === toValue(context.multiple ?? false) ? void 0 : { multiple: toValue(multiple) };
747
- const modelAsArray = transformIn(model);
748
- const pending = new Set(modelAsArray);
749
- context.apply(modelAsArray, applyOptions);
750
- const selected = new Set(context.selectedValues.value);
751
- for (const value of modelAsArray) if (selected.has(value)) pending.delete(value);
752
- const contextWatch = watch(context.selectedValues, (val) => {
753
- modelWatch.pause();
754
- model.value = transformOut(Array.from(toValue(val)));
755
- modelWatch.resume();
756
- }, { flush: "sync" });
757
- const modelWatch = watch(model, (val) => {
758
- contextWatch.pause();
759
- context.apply(transformIn(val), applyOptions);
760
- contextWatch.resume();
761
- }, {
762
- flush: "sync",
763
- deep: toValue(multiple)
764
- });
765
- function onRegister(data) {
766
- const ticket = data;
767
- if (!pending.has(ticket.value) || toValue(ticket.disabled)) return;
768
- contextWatch.pause();
769
- modelWatch.pause();
770
- context.select?.(ticket.id);
771
- pending.delete(ticket.value);
772
- modelWatch.resume();
773
- contextWatch.resume();
774
- }
775
- context.on?.("register:ticket", onRegister);
776
- function stop() {
777
- contextWatch();
778
- modelWatch();
779
- context.off?.("register:ticket", onRegister);
780
- }
781
- onScopeDispose(stop, true);
782
- return stop;
783
- }
784
- //#endregion
785
- //#region src/composables/useTimer/index.ts
786
- /**
787
- * @module useTimer
788
- *
789
- * @remarks
790
- * A reactive timer composable with pause/resume support.
791
- *
792
- * Key features:
793
- * - Start/stop/pause/resume controls
794
- * - Reactive remaining time tracking
795
- * - One-shot (default) or repeating mode
796
- * - Automatic cleanup on scope disposal
797
- * - SSR-safe
798
- */
799
- /**
800
- * Creates a reactive timer with pause/resume support.
801
- *
802
- * @param handler Callback invoked when the timer fires.
803
- * @param options Timer configuration.
804
- * @returns Timer controls and reactive state.
805
- *
806
- * @see https://0.vuetifyjs.com/composables/system/use-timer
807
- *
808
- * @example
809
- * ```ts
810
- * const timer = useTimer(() => console.log('fired'), { duration: 5000 })
811
- *
812
- * timer.start()
813
- * timer.pause()
814
- * timer.resume()
815
- * timer.stop()
816
- * ```
817
- */
818
- function useTimer(handler, options = {}) {
819
- const { duration = 1e3, repeat = false } = options;
820
- const remaining = shallowRef(duration);
821
- const isActive = shallowRef(false);
822
- const isPaused = shallowRef(false);
823
- let timeoutId;
824
- let trackingId;
825
- let startedAt = 0;
826
- let budget = duration;
827
- function clearTimer() {
828
- if (!/* @__PURE__ */ isUndefined(timeoutId)) {
829
- globalThis.clearTimeout(timeoutId);
830
- timeoutId = void 0;
831
- }
832
- }
833
- function startTracking() {
834
- stopTracking();
835
- if (!IN_BROWSER) return;
836
- trackingId = globalThis.setInterval(() => {
837
- if (!isActive.value || isPaused.value) return;
838
- const elapsed = Date.now() - startedAt;
839
- remaining.value = Math.max(0, budget - elapsed);
840
- }, 100);
841
- }
842
- function stopTracking() {
843
- if (!/* @__PURE__ */ isUndefined(trackingId)) {
844
- globalThis.clearInterval(trackingId);
845
- trackingId = void 0;
846
- }
847
- }
848
- function fire() {
849
- clearTimer();
850
- stopTracking();
851
- handler();
852
- if (repeat) begin(duration);
853
- else {
854
- isActive.value = false;
855
- remaining.value = 0;
856
- }
857
- }
858
- function begin(ms) {
859
- clearTimer();
860
- stopTracking();
861
- budget = ms;
862
- startedAt = Date.now();
863
- remaining.value = ms;
864
- isActive.value = true;
865
- isPaused.value = false;
866
- timeoutId = globalThis.setTimeout(fire, ms);
867
- startTracking();
868
- }
869
- function start() {
870
- if (duration < 0) return;
871
- begin(duration);
872
- }
873
- function stop() {
874
- clearTimer();
875
- stopTracking();
876
- isActive.value = false;
877
- isPaused.value = false;
878
- remaining.value = duration;
879
- }
880
- function pause() {
881
- if (!isActive.value || isPaused.value) return;
882
- const elapsed = Date.now() - startedAt;
883
- budget = Math.max(0, budget - elapsed);
884
- remaining.value = budget;
885
- clearTimer();
886
- stopTracking();
887
- isPaused.value = true;
888
- }
889
- function resume() {
890
- if (!isPaused.value) return;
891
- isPaused.value = false;
892
- startedAt = Date.now();
893
- timeoutId = globalThis.setTimeout(fire, budget);
894
- startTracking();
895
- }
896
- onScopeDispose(() => {
897
- clearTimer();
898
- stopTracking();
899
- }, true);
900
- return {
901
- start,
902
- stop,
903
- pause,
904
- resume,
905
- remaining,
906
- isActive,
907
- isPaused
908
- };
909
- }
910
- //#endregion
911
696
  //#region src/composables/toReactive/index.ts
912
697
  /**
913
698
  * @module toReactive
@@ -924,6 +709,115 @@ function useTimer(handler, options = {}) {
924
709
  *
925
710
  * Perfect for creating reactive versions of plain objects while automatically unwrapping refs.
926
711
  */
712
+ function createMapProxy(refObject) {
713
+ let mapProxy;
714
+ const mapHandlers = {
715
+ get: (key) => unref(refObject.value.get(key)),
716
+ set: (key, value) => {
717
+ const map = refObject.value;
718
+ const existingValue = map.get(key);
719
+ if (isRef(existingValue)) existingValue.value = unref(value);
720
+ else map.set(key, value);
721
+ return mapProxy;
722
+ },
723
+ has: (key) => refObject.value.has(key),
724
+ delete: (key) => refObject.value.delete(key),
725
+ clear: () => refObject.value.clear(),
726
+ keys: () => refObject.value.keys(),
727
+ *values() {
728
+ for (const value of refObject.value.values()) yield unref(value);
729
+ },
730
+ *entries() {
731
+ for (const [key, value] of refObject.value.entries()) yield [key, unref(value)];
732
+ },
733
+ forEach: (callback, thisArg) => {
734
+ for (const [key, value] of refObject.value.entries()) callback.call(thisArg, unref(value), key, mapProxy);
735
+ }
736
+ };
737
+ mapHandlers[Symbol.iterator] = mapHandlers.entries;
738
+ mapProxy = new Proxy(/* @__PURE__ */ new Map(), { get(_, p) {
739
+ if (p === "size") return refObject.value.size;
740
+ if (Object.prototype.hasOwnProperty.call(mapHandlers, p)) return mapHandlers[p];
741
+ const map = refObject.value;
742
+ const value = Reflect.get(map, p);
743
+ return typeof value === "function" ? value.bind(map) : value;
744
+ } });
745
+ return reactive(mapProxy);
746
+ }
747
+ function createSetProxy(refObject) {
748
+ let setProxy;
749
+ const setHandlers = {
750
+ add: (value) => {
751
+ refObject.value.add(value);
752
+ return setProxy;
753
+ },
754
+ has: (value) => refObject.value.has(value),
755
+ delete: (value) => refObject.value.delete(value),
756
+ clear: () => refObject.value.clear(),
757
+ *keys() {
758
+ for (const value of refObject.value.values()) yield unref(value);
759
+ },
760
+ *values() {
761
+ for (const value of refObject.value.values()) yield unref(value);
762
+ },
763
+ *entries() {
764
+ for (const value of refObject.value.values()) {
765
+ const unreffedValue = unref(value);
766
+ yield [unreffedValue, unreffedValue];
767
+ }
768
+ },
769
+ forEach: (callback, thisArg) => {
770
+ for (const value of refObject.value) {
771
+ const unreffedValue = unref(value);
772
+ callback.call(thisArg, unreffedValue, unreffedValue, setProxy);
773
+ }
774
+ }
775
+ };
776
+ setHandlers[Symbol.iterator] = setHandlers.values;
777
+ setProxy = new Proxy(/* @__PURE__ */ new Set(), { get(_, p) {
778
+ if (p === "size") return refObject.value.size;
779
+ if (Object.prototype.hasOwnProperty.call(setHandlers, p)) return setHandlers[p];
780
+ const set = refObject.value;
781
+ const value = Reflect.get(set, p);
782
+ return typeof value === "function" ? value.bind(set) : value;
783
+ } });
784
+ return reactive(setProxy);
785
+ }
786
+ function createObjectProxy(refObject) {
787
+ return reactive(new Proxy({}, {
788
+ get(_, p, receiver) {
789
+ return unref(Reflect.get(refObject.value, p, receiver));
790
+ },
791
+ set(_, p, value) {
792
+ return Reflect.set(refObject.value, p, value);
793
+ },
794
+ deleteProperty(_, p) {
795
+ return Reflect.deleteProperty(refObject.value, p);
796
+ },
797
+ has(_, p) {
798
+ return Reflect.has(refObject.value, p);
799
+ },
800
+ ownKeys() {
801
+ return Reflect.ownKeys(refObject.value);
802
+ },
803
+ getOwnPropertyDescriptor(_, p) {
804
+ const desc = Reflect.getOwnPropertyDescriptor(refObject.value, p);
805
+ if (!desc) return;
806
+ const newDesc = {
807
+ configurable: true,
808
+ enumerable: desc.enumerable
809
+ };
810
+ if ("value" in desc) {
811
+ newDesc.value = unref(desc.value);
812
+ newDesc.writable = desc.writable;
813
+ } else {
814
+ newDesc.get = desc.get;
815
+ newDesc.set = desc.set;
816
+ }
817
+ return newDesc;
818
+ }
819
+ }));
820
+ }
927
821
  /**
928
822
  * Converts a `MaybeRef` to a `UnwrapNestedRefs`.
929
823
  *
@@ -946,100 +840,11 @@ function useTimer(handler, options = {}) {
946
840
  */
947
841
  function toReactive(objectRef) {
948
842
  if (!isRef(objectRef)) return reactive(objectRef);
949
- const target = objectRef.value;
950
- if (target instanceof Map) {
951
- const mapProxy = new Proxy(/* @__PURE__ */ new Map(), { get(_, p) {
952
- const map = objectRef.value;
953
- if (p === "get") return (key) => unref(map.get(key));
954
- if (p === "set") return (key, value) => {
955
- const existingValue = map.get(key);
956
- if (isRef(existingValue)) existingValue.value = unref(value);
957
- else map.set(key, value);
958
- return mapProxy;
959
- };
960
- if (p === "has") return (key) => map.has(key);
961
- if (p === "delete") return (key) => map.delete(key);
962
- if (p === "clear") return () => map.clear();
963
- if (p === "size") return map.size;
964
- if (p === "keys") return () => map.keys();
965
- if (p === "values") return function* () {
966
- for (const value of map.values()) yield unref(value);
967
- };
968
- if (p === "entries") return function* () {
969
- for (const [key, value] of map.entries()) yield [key, unref(value)];
970
- };
971
- if (p === "forEach") return (callback, thisArg) => {
972
- for (const [key, value] of map.entries()) callback.call(thisArg, unref(value), key, mapProxy);
973
- };
974
- if (p === Symbol.iterator) return function* () {
975
- for (const [key, value] of map.entries()) yield [key, unref(value)];
976
- };
977
- return Reflect.get(map, p);
978
- } });
979
- return reactive(mapProxy);
980
- }
981
- if (target instanceof Set) {
982
- const setProxy = new Proxy(/* @__PURE__ */ new Set(), { get(_, p) {
983
- const set = objectRef.value;
984
- if (p === "add") return (value) => {
985
- set.add(value);
986
- return setProxy;
987
- };
988
- if (p === "has") return (value) => set.has(value);
989
- if (p === "delete") return (value) => set.delete(value);
990
- if (p === "clear") return () => set.clear();
991
- if (p === "size") return set.size;
992
- if (p === "keys" || p === "values") return function* () {
993
- for (const value of set.values()) yield unref(value);
994
- };
995
- if (p === "entries") return function* () {
996
- for (const value of set.values()) {
997
- const unreffedValue = unref(value);
998
- yield [unreffedValue, unreffedValue];
999
- }
1000
- };
1001
- if (p === "forEach") return (callback, thisArg) => {
1002
- for (const value of set) {
1003
- const unreffedValue = unref(value);
1004
- callback.call(thisArg, unreffedValue, unreffedValue, setProxy);
1005
- }
1006
- };
1007
- if (p === Symbol.iterator) return function* () {
1008
- for (const value of set.values()) yield unref(value);
1009
- };
1010
- return Reflect.get(set, p);
1011
- } });
1012
- return reactive(setProxy);
1013
- }
1014
- return reactive(new Proxy({}, {
1015
- get(_, p, receiver) {
1016
- return unref(Reflect.get(objectRef.value, p, receiver));
1017
- },
1018
- set(_, p, value) {
1019
- const currentTarget = objectRef.value;
1020
- currentTarget[p] = value;
1021
- return true;
1022
- },
1023
- deleteProperty(_, p) {
1024
- return Reflect.deleteProperty(objectRef.value, p);
1025
- },
1026
- has(_, p) {
1027
- return Reflect.has(objectRef.value, p);
1028
- },
1029
- ownKeys() {
1030
- return Object.keys(objectRef.value);
1031
- },
1032
- getOwnPropertyDescriptor(_, p) {
1033
- const desc = Reflect.getOwnPropertyDescriptor(objectRef.value, p);
1034
- if (!desc) return;
1035
- const newDesc = {
1036
- ...desc,
1037
- configurable: true
1038
- };
1039
- if ("value" in newDesc) newDesc.value = unref(newDesc.value);
1040
- return newDesc;
1041
- }
1042
- }));
843
+ const refObject = objectRef;
844
+ const target = refObject.value;
845
+ if (target instanceof Map) return createMapProxy(refObject);
846
+ if (target instanceof Set) return createSetProxy(refObject);
847
+ return createObjectProxy(refObject);
1043
848
  }
1044
849
  //#endregion
1045
850
  //#region src/composables/useBreakpoints/index.ts
@@ -1243,202 +1048,459 @@ const [createBreakpointsContext, createBreakpointsPlugin, useBreakpoints] = crea
1243
1048
  }
1244
1049
  });
1245
1050
  //#endregion
1246
- //#region src/composables/useDate/index.ts
1051
+ //#region src/composables/useClickOutside/index.ts
1247
1052
  /**
1248
- * @module useDate
1053
+ * @module useClickOutside
1249
1054
  *
1250
1055
  * @remarks
1251
- * Date manipulation composable with adapter pattern for date operations.
1056
+ * Detects clicks outside of specified element(s) with automatic cleanup.
1252
1057
  *
1253
1058
  * Key features:
1254
- * - Adapter pattern for date library abstraction
1255
- * - Locale-aware formatting via Intl.DateTimeFormat
1256
- * - Integration with useLocale for automatic locale sync
1257
- *
1258
- * @example Using the built-in Temporal adapter
1259
- * ```ts
1260
- * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
1261
- * import { createDatePlugin } from '@vuetify/v0'
1262
- *
1263
- * const app = createApp(App)
1264
- * app.use(createDatePlugin({ adapter: new Vuetify0DateAdapter() }))
1265
- *
1266
- * // In a component:
1267
- * const { adapter } = useDate()
1268
- * const today = adapter.date()
1269
- * const formatted = adapter.format(today, 'fullDate')
1270
- * ```
1271
- *
1272
- * @example Custom adapter
1273
- * ```ts
1274
- * import type { DateAdapter } from '@vuetify/v0'
1059
+ * - Two-phase detection (pointerdown pointerup) prevents drag-out false positives
1060
+ * - Touch scroll threshold ignores swipes/scrolls on mobile
1061
+ * - Capture phase listeners work with stopPropagation
1062
+ * - Pause/resume/stop functionality
1063
+ * - Optional iframe focus detection
1064
+ * - SSR-safe (no-op when not in browser)
1275
1065
  *
1276
- * class DateFnsAdapter implements DateAdapter<Date> {
1277
- * // ... implementation
1278
- * }
1066
+ * Common use cases: closing popovers, dropdowns, modals, and menus.
1279
1067
  *
1280
- * app.use(createDatePlugin({ adapter: new DateFnsAdapter() }))
1281
- * ```
1282
- */
1283
- /**
1284
- * Default short locale codes mapped to full Intl locale strings.
1285
- * Used when useLocale provides a short code without region (e.g., 'en' instead of 'en-US').
1286
- */
1287
- const defaultLocales = {
1288
- en: "en-US",
1289
- es: "es-ES",
1290
- fr: "fr-FR",
1291
- de: "de-DE",
1292
- it: "it-IT",
1293
- pt: "pt-BR",
1294
- ja: "ja-JP",
1295
- ko: "ko-KR",
1296
- zh: "zh-CN",
1297
- ru: "ru-RU",
1298
- ar: "ar-SA"
1299
- };
1300
- /**
1301
- * Derive firstDayOfWeek from an Intl locale string.
1302
- * Returns 0-6 (0=Sun) or 0 as fallback.
1068
+ * Accessibility: This composable handles pointer interactions only. For accessible
1069
+ * components (dialogs, popovers, menus), pair with `useHotkey` for Escape key
1070
+ * dismissal per WCAG/APG requirements.
1303
1071
  */
1304
- function deriveFirstDayOfWeek(locale) {
1305
- try {
1306
- const info = new Intl.Locale(locale).getWeekInfo?.();
1307
- return info ? info.firstDay % 7 : 0;
1308
- } catch {
1309
- return 0;
1310
- }
1311
- }
1312
1072
  /**
1313
- * Creates a new date context.
1073
+ * Detects clicks outside of the specified element(s).
1314
1074
  *
1315
- * @param options Adapter and locale configuration.
1316
- * @template Z The date type used by the adapter.
1317
- * @template E The date context type.
1318
- * @returns A date context.
1075
+ * Uses two-phase detection (pointerdown pointerup) to prevent false positives
1076
+ * when users drag from inside to outside an element.
1319
1077
  *
1320
- * @see https://0.vuetifyjs.com/composables/plugins/use-date
1078
+ * @param target Element ref(s) to detect clicks outside of. Accepts a single ref/getter or array of refs/getters.
1079
+ * @param handler Callback invoked when a click outside is detected.
1080
+ * @param options Configuration options.
1081
+ * @returns An object with methods to control the listener.
1321
1082
  *
1322
- * @example
1323
- * ```ts
1324
- * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
1083
+ * @see https://0.vuetifyjs.com/composables/system/use-click-outside
1325
1084
  *
1326
- * const { adapter } = createDate({ adapter: new Vuetify0DateAdapter() })
1327
- * const today = adapter.date()
1085
+ * @example Native element ref
1086
+ * ```ts
1087
+ * const menuRef = useTemplateRef<HTMLElement>('menu')
1328
1088
  *
1329
- * // With locale options
1330
- * const { adapter } = createDate({
1331
- * adapter: new Vuetify0DateAdapter(),
1332
- * locale: 'de-DE',
1333
- * })
1089
+ * useClickOutside(menuRef, () => { isOpen.value = false })
1334
1090
  * ```
1335
- */
1336
- function createDate(options) {
1337
- const { locales = defaultLocales, adapter, locale: initialLocale, firstDayOfWeek: explicitFirstDay } = options;
1338
- let selectedId;
1339
- try {
1340
- if (instanceExists()) selectedId = useLocale().selectedId;
1341
- } catch {}
1342
- const locale = computed(() => {
1343
- const selected = selectedId?.value;
1344
- if (!/* @__PURE__ */ isNullOrUndefined(selected)) {
1345
- const str = String(selected);
1346
- return str.includes("-") ? str : locales[str] ?? str;
1347
- }
1348
- const fallback = initialLocale ?? adapter.locale ?? "en-US";
1349
- return fallback.includes("-") ? fallback : locales[fallback] ?? fallback;
1350
- });
1351
- const firstDayOfWeek = computed(() => {
1352
- if (!/* @__PURE__ */ isUndefined(explicitFirstDay)) return explicitFirstDay;
1353
- const loc = locale.value;
1354
- return loc ? deriveFirstDayOfWeek(loc) : 0;
1355
- });
1356
- if (instanceExists()) onScopeDispose(watchEffect(() => {
1357
- const loc = locale.value;
1358
- if (loc && adapter.locale !== loc) adapter.locale = loc;
1359
- const fdow = firstDayOfWeek.value;
1360
- if (adapter.firstDayOfWeek !== fdow) adapter.firstDayOfWeek = fdow;
1361
- }));
1362
- else {
1363
- const loc = locale.value;
1364
- if (loc && adapter.locale !== loc) adapter.locale = loc;
1365
- const fdow = firstDayOfWeek.value;
1366
- if (adapter.firstDayOfWeek !== fdow) adapter.firstDayOfWeek = fdow;
1367
- }
1368
- return {
1369
- adapter,
1370
- locale,
1371
- firstDayOfWeek
1372
- };
1373
- }
1374
- /**
1375
- * Creates a new date context trinity.
1376
- *
1377
- * @param options Adapter, locale, and namespace configuration.
1378
- * @template Z The date type used by the adapter.
1379
- * @template E The date context type.
1380
- * @returns A trinity [useContext, provideContext, defaultContext].
1381
- *
1382
- * @see https://0.vuetifyjs.com/composables/plugins/use-date
1383
1091
  *
1384
- * @example
1092
+ * @example Component ref (e.g., Atom)
1385
1093
  * ```ts
1386
- * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
1094
+ * const atomRef = useTemplateRef<AtomExpose>('atom')
1387
1095
  *
1388
- * const [useAppDate, provideAppDate] = createDateContext({
1389
- * adapter: new Vuetify0DateAdapter(),
1390
- * namespace: 'app:date',
1391
- * })
1096
+ * // Pass the exposed element TemplateRef via getter
1097
+ * useClickOutside(
1098
+ * () => atomRef.value?.element,
1099
+ * () => { isOpen.value = false }
1100
+ * )
1392
1101
  * ```
1393
- */
1394
- function createDateContext(options) {
1395
- const { namespace = "v0:date", ...dateOptions } = options;
1396
- const [useDateContext, _provideDateContext] = createContext(namespace);
1397
- const context = createDate(dateOptions);
1398
- function provideDateContext(_context = context, app) {
1399
- return _provideDateContext(_context, app);
1400
- }
1401
- return createTrinity(useDateContext, provideDateContext, context);
1402
- }
1403
- /**
1404
- * Creates a new date plugin.
1405
1102
  *
1406
- * @param options Adapter, locale, and namespace configuration.
1407
- * @template Z The date type used by the adapter.
1408
- * @template E The date context type.
1409
- * @returns A Vue plugin.
1103
+ * @example Multiple targets
1104
+ * ```ts
1105
+ * const popoverRef = useTemplateRef<AtomExpose>('popover')
1106
+ * const anchorRef = useTemplateRef<HTMLElement>('anchor')
1410
1107
  *
1411
- * @see https://0.vuetifyjs.com/composables/plugins/use-date
1108
+ * useClickOutside(
1109
+ * [() => popoverRef.value?.element, anchorRef],
1110
+ * () => { isOpen.value = false }
1111
+ * )
1112
+ * ```
1412
1113
  *
1413
- * @example
1114
+ * @example Ignoring elements (CSS selectors or refs)
1414
1115
  * ```ts
1415
- * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
1116
+ * useClickOutside(
1117
+ * () => navRef.value?.element,
1118
+ * () => { isOpen.value = false },
1119
+ * { ignore: ['[data-app-bar]'] }
1120
+ * )
1121
+ * ```
1416
1122
  *
1417
- * const app = createApp(App)
1418
- * app.use(createDatePlugin({ adapter: new Vuetify0DateAdapter() }))
1123
+ * @example Native dialog with bounds detection
1124
+ * ```ts
1125
+ * // For native <dialog> elements, use bounds mode to detect backdrop clicks
1126
+ * const dialogRef = useTemplateRef<HTMLDialogElement>('dialog')
1419
1127
  *
1420
- * // With locale options
1421
- * app.use(createDatePlugin({
1422
- * adapter: new Vuetify0DateAdapter(),
1423
- * locale: 'de-DE',
1424
- * }))
1128
+ * useClickOutside(
1129
+ * dialogRef,
1130
+ * () => { dialogRef.value?.close() },
1131
+ * { bounds: true }
1132
+ * )
1425
1133
  * ```
1426
1134
  */
1427
- function createDatePlugin(options) {
1428
- const { namespace = "v0:date", ...dateOptions } = options;
1429
- const [, provideDateContext, context] = createDateContext({
1430
- namespace,
1431
- ...dateOptions
1432
- });
1433
- return createPlugin({
1434
- namespace,
1435
- provide: (app) => {
1436
- provideDateContext(context, app);
1437
- }
1438
- });
1439
- }
1440
- /**
1441
- * Returns the current date context.
1135
+ function useClickOutside(target, handler, options = {}) {
1136
+ const { capture = true, touchScrollThreshold = 30, detectIframe = false, ignore = [], bounds = false } = options;
1137
+ const isPaused = shallowRef(false);
1138
+ const isActive = toRef(() => !isPaused.value);
1139
+ let initialTarget = null;
1140
+ let startPosition = {
1141
+ x: 0,
1142
+ y: 0
1143
+ };
1144
+ let cleanupPointerDown;
1145
+ let cleanupPointerUp;
1146
+ let cleanupBlur;
1147
+ /**
1148
+ * Resolve target(s) to an array of HTMLElements.
1149
+ */
1150
+ function getTargets() {
1151
+ return toArray(target).map((source) => toValue(source)).filter((el) => !!el && /* @__PURE__ */ isFunction(el.contains));
1152
+ }
1153
+ /**
1154
+ * Resolve ignore targets to a tuple of [selectors, elements].
1155
+ * Called once per event to avoid repeated toValue() calls in hot path.
1156
+ */
1157
+ function resolveIgnoreTargets() {
1158
+ const ignoreTargets = toValue(ignore);
1159
+ if (ignoreTargets.length === 0) return [[], []];
1160
+ const selectors = [];
1161
+ const elements = [];
1162
+ for (const ignoreTarget of ignoreTargets) if (/* @__PURE__ */ isString(ignoreTarget)) selectors.push(ignoreTarget);
1163
+ else {
1164
+ const ignoreEl = toValue(ignoreTarget);
1165
+ if (ignoreEl) elements.push(ignoreEl);
1166
+ }
1167
+ return [selectors, elements];
1168
+ }
1169
+ /**
1170
+ * Check if an element matches resolved ignore targets.
1171
+ */
1172
+ function isIgnored(el, selectors, elements) {
1173
+ if (!el) return false;
1174
+ for (const selector of selectors) try {
1175
+ if (el.matches(selector) || !/* @__PURE__ */ isNull(el.closest(selector))) return true;
1176
+ } catch {}
1177
+ for (const ignoreEl of elements) if (ignoreEl === el || ignoreEl.contains(el)) return true;
1178
+ return false;
1179
+ }
1180
+ /**
1181
+ * Check if any element in the event path should be ignored.
1182
+ */
1183
+ function shouldIgnore(path) {
1184
+ const [selectors, elements] = resolveIgnoreTargets();
1185
+ if (selectors.length === 0 && elements.length === 0) return false;
1186
+ return path.some((node) => node instanceof Element && isIgnored(node, selectors, elements));
1187
+ }
1188
+ /**
1189
+ * Check if the event target is outside all target elements (DOM containment).
1190
+ */
1191
+ function isOutside(eventTarget) {
1192
+ /* v8 ignore start -- defensive guards */
1193
+ if (!eventTarget) return false;
1194
+ if (!(eventTarget instanceof Node)) return false;
1195
+ /* v8 ignore stop */
1196
+ const targets = getTargets();
1197
+ if (targets.length === 0) return false;
1198
+ return targets.every((el) => {
1199
+ if (/* @__PURE__ */ isNullOrUndefined(el) || !/* @__PURE__ */ isFunction(el.contains)) return false;
1200
+ return el !== eventTarget && !el.contains(eventTarget);
1201
+ });
1202
+ }
1203
+ /**
1204
+ * Check if coordinates are outside all target elements' bounding rects.
1205
+ */
1206
+ function isOutsideBounds(x, y) {
1207
+ const targets = getTargets();
1208
+ if (targets.length === 0) return false;
1209
+ return targets.every((el) => {
1210
+ const { left, right, top, bottom } = el.getBoundingClientRect();
1211
+ return x < left || x > right || y < top || y > bottom;
1212
+ });
1213
+ }
1214
+ /**
1215
+ * Validate that the target is still in the DOM.
1216
+ */
1217
+ function isValidTarget(eventTarget) {
1218
+ if (!(eventTarget instanceof Element)) return false;
1219
+ if (!eventTarget.isConnected) return false;
1220
+ return true;
1221
+ }
1222
+ /* v8 ignore start -- event handlers tested via integration, not unit */
1223
+ /**
1224
+ * Handle pointerdown - store initial target and position.
1225
+ */
1226
+ function onPointerDown(event) {
1227
+ if (isPaused.value) return;
1228
+ if (event.defaultPrevented) return;
1229
+ initialTarget = event.composedPath()[0] ?? event.target;
1230
+ startPosition = {
1231
+ x: event.clientX,
1232
+ y: event.clientY
1233
+ };
1234
+ }
1235
+ /**
1236
+ * Handle pointerup - check if it's an outside click.
1237
+ */
1238
+ function onPointerUp(event) {
1239
+ if (isPaused.value) return;
1240
+ if (event.defaultPrevented) return;
1241
+ if (!initialTarget) return;
1242
+ const pointerdownTarget = initialTarget;
1243
+ initialTarget = null;
1244
+ if (!isValidTarget(pointerdownTarget)) return;
1245
+ const path = event.composedPath();
1246
+ const pointerupTarget = path[0] ?? event.target;
1247
+ if (event.pointerType === "touch") {
1248
+ const dx = Math.abs(event.clientX - startPosition.x);
1249
+ const dy = Math.abs(event.clientY - startPosition.y);
1250
+ if (dx >= touchScrollThreshold || dy >= touchScrollThreshold) return;
1251
+ }
1252
+ if ((bounds ? isOutsideBounds(startPosition.x, startPosition.y) && isOutsideBounds(event.clientX, event.clientY) : isOutside(pointerdownTarget) && isOutside(pointerupTarget)) && !shouldIgnore(path)) handler(event);
1253
+ }
1254
+ /**
1255
+ * Handle window blur - detect focus moving to iframe.
1256
+ */
1257
+ function onBlur(event) {
1258
+ if (isPaused.value) return;
1259
+ if (event.defaultPrevented) return;
1260
+ if (document.activeElement instanceof HTMLIFrameElement) {
1261
+ const iframeIsOutside = getTargets().every((el) => !el.contains(document.activeElement));
1262
+ const [selectors, elements] = resolveIgnoreTargets();
1263
+ if (iframeIsOutside && !isIgnored(document.activeElement, selectors, elements)) handler(event);
1264
+ }
1265
+ }
1266
+ /* v8 ignore stop */
1267
+ function setup() {
1268
+ cleanupPointerDown = useDocumentEventListener("pointerdown", onPointerDown, capture);
1269
+ cleanupPointerUp = useDocumentEventListener("pointerup", onPointerUp, capture);
1270
+ if (!detectIframe) return;
1271
+ cleanupBlur = useWindowEventListener("blur", onBlur, capture);
1272
+ }
1273
+ function cleanup() {
1274
+ cleanupPointerDown?.();
1275
+ cleanupPointerUp?.();
1276
+ cleanupBlur?.();
1277
+ cleanupPointerDown = void 0;
1278
+ cleanupPointerUp = void 0;
1279
+ cleanupBlur = void 0;
1280
+ }
1281
+ function pause() {
1282
+ if (isPaused.value) return;
1283
+ isPaused.value = true;
1284
+ initialTarget = null;
1285
+ cleanup();
1286
+ }
1287
+ function resume() {
1288
+ if (!isPaused.value) return;
1289
+ isPaused.value = false;
1290
+ setup();
1291
+ }
1292
+ function stop() {
1293
+ isPaused.value = true;
1294
+ initialTarget = null;
1295
+ cleanup();
1296
+ }
1297
+ setup();
1298
+ onScopeDispose(stop, true);
1299
+ return {
1300
+ isActive: shallowReadonly(isActive),
1301
+ isPaused: shallowReadonly(isPaused),
1302
+ pause,
1303
+ resume,
1304
+ stop
1305
+ };
1306
+ }
1307
+ //#endregion
1308
+ //#region src/composables/useDate/index.ts
1309
+ /**
1310
+ * @module useDate
1311
+ *
1312
+ * @remarks
1313
+ * Date manipulation composable with adapter pattern for date operations.
1314
+ *
1315
+ * Key features:
1316
+ * - Adapter pattern for date library abstraction
1317
+ * - Locale-aware formatting via Intl.DateTimeFormat
1318
+ * - Integration with useLocale for automatic locale sync
1319
+ *
1320
+ * @example Using the built-in Temporal adapter
1321
+ * ```ts
1322
+ * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
1323
+ * import { createDatePlugin } from '@vuetify/v0'
1324
+ *
1325
+ * const app = createApp(App)
1326
+ * app.use(createDatePlugin({ adapter: new Vuetify0DateAdapter() }))
1327
+ *
1328
+ * // In a component:
1329
+ * const { adapter } = useDate()
1330
+ * const today = adapter.date()
1331
+ * const formatted = adapter.format(today, 'fullDate')
1332
+ * ```
1333
+ *
1334
+ * @example Custom adapter
1335
+ * ```ts
1336
+ * import type { DateAdapter } from '@vuetify/v0'
1337
+ *
1338
+ * class DateFnsAdapter implements DateAdapter<Date> {
1339
+ * // ... implementation
1340
+ * }
1341
+ *
1342
+ * app.use(createDatePlugin({ adapter: new DateFnsAdapter() }))
1343
+ * ```
1344
+ */
1345
+ /**
1346
+ * Default short locale codes mapped to full Intl locale strings.
1347
+ * Used when useLocale provides a short code without region (e.g., 'en' instead of 'en-US').
1348
+ */
1349
+ const defaultLocales = {
1350
+ en: "en-US",
1351
+ es: "es-ES",
1352
+ fr: "fr-FR",
1353
+ de: "de-DE",
1354
+ it: "it-IT",
1355
+ pt: "pt-BR",
1356
+ ja: "ja-JP",
1357
+ ko: "ko-KR",
1358
+ zh: "zh-CN",
1359
+ ru: "ru-RU",
1360
+ ar: "ar-SA"
1361
+ };
1362
+ /**
1363
+ * Derive firstDayOfWeek from an Intl locale string.
1364
+ * Returns 0-6 (0=Sun) or 0 as fallback.
1365
+ */
1366
+ function deriveFirstDayOfWeek(locale) {
1367
+ try {
1368
+ const info = new Intl.Locale(locale).getWeekInfo?.();
1369
+ return info ? info.firstDay % 7 : 0;
1370
+ } catch {
1371
+ return 0;
1372
+ }
1373
+ }
1374
+ /**
1375
+ * Creates a new date context.
1376
+ *
1377
+ * @param options Adapter and locale configuration.
1378
+ * @template Z The date type used by the adapter.
1379
+ * @template E The date context type.
1380
+ * @returns A date context.
1381
+ *
1382
+ * @see https://0.vuetifyjs.com/composables/plugins/use-date
1383
+ *
1384
+ * @example
1385
+ * ```ts
1386
+ * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
1387
+ *
1388
+ * const { adapter } = createDate({ adapter: new Vuetify0DateAdapter() })
1389
+ * const today = adapter.date()
1390
+ *
1391
+ * // With locale options
1392
+ * const { adapter } = createDate({
1393
+ * adapter: new Vuetify0DateAdapter(),
1394
+ * locale: 'de-DE',
1395
+ * })
1396
+ * ```
1397
+ */
1398
+ function createDate(options) {
1399
+ const { locales = defaultLocales, adapter, locale: initialLocale, firstDayOfWeek: explicitFirstDay } = options;
1400
+ let selectedId;
1401
+ try {
1402
+ if (instanceExists()) selectedId = useLocale().selectedId;
1403
+ } catch {}
1404
+ const locale = computed(() => {
1405
+ const selected = selectedId?.value;
1406
+ if (!/* @__PURE__ */ isNullOrUndefined(selected)) {
1407
+ const str = String(selected);
1408
+ return str.includes("-") ? str : locales[str] ?? str;
1409
+ }
1410
+ const fallback = initialLocale ?? adapter.locale ?? "en-US";
1411
+ return fallback.includes("-") ? fallback : locales[fallback] ?? fallback;
1412
+ });
1413
+ const firstDayOfWeek = computed(() => {
1414
+ if (!/* @__PURE__ */ isUndefined(explicitFirstDay)) return explicitFirstDay;
1415
+ const loc = locale.value;
1416
+ return loc ? deriveFirstDayOfWeek(loc) : 0;
1417
+ });
1418
+ if (instanceExists()) onScopeDispose(watchEffect(() => {
1419
+ const loc = locale.value;
1420
+ if (loc && adapter.locale !== loc) adapter.locale = loc;
1421
+ const fdow = firstDayOfWeek.value;
1422
+ if (adapter.firstDayOfWeek !== fdow) adapter.firstDayOfWeek = fdow;
1423
+ }));
1424
+ else {
1425
+ const loc = locale.value;
1426
+ if (loc && adapter.locale !== loc) adapter.locale = loc;
1427
+ const fdow = firstDayOfWeek.value;
1428
+ if (adapter.firstDayOfWeek !== fdow) adapter.firstDayOfWeek = fdow;
1429
+ }
1430
+ return {
1431
+ adapter,
1432
+ locale,
1433
+ firstDayOfWeek
1434
+ };
1435
+ }
1436
+ /**
1437
+ * Creates a new date context trinity.
1438
+ *
1439
+ * @param options Adapter, locale, and namespace configuration.
1440
+ * @template Z The date type used by the adapter.
1441
+ * @template E The date context type.
1442
+ * @returns A trinity [useContext, provideContext, defaultContext].
1443
+ *
1444
+ * @see https://0.vuetifyjs.com/composables/plugins/use-date
1445
+ *
1446
+ * @example
1447
+ * ```ts
1448
+ * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
1449
+ *
1450
+ * const [useAppDate, provideAppDate] = createDateContext({
1451
+ * adapter: new Vuetify0DateAdapter(),
1452
+ * namespace: 'app:date',
1453
+ * })
1454
+ * ```
1455
+ */
1456
+ function createDateContext(options) {
1457
+ const { namespace = "v0:date", ...dateOptions } = options;
1458
+ const [useDateContext, _provideDateContext] = createContext(namespace);
1459
+ const context = createDate(dateOptions);
1460
+ function provideDateContext(_context = context, app) {
1461
+ return _provideDateContext(_context, app);
1462
+ }
1463
+ return createTrinity(useDateContext, provideDateContext, context);
1464
+ }
1465
+ /**
1466
+ * Creates a new date plugin.
1467
+ *
1468
+ * @param options Adapter, locale, and namespace configuration.
1469
+ * @template Z The date type used by the adapter.
1470
+ * @template E The date context type.
1471
+ * @returns A Vue plugin.
1472
+ *
1473
+ * @see https://0.vuetifyjs.com/composables/plugins/use-date
1474
+ *
1475
+ * @example
1476
+ * ```ts
1477
+ * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
1478
+ *
1479
+ * const app = createApp(App)
1480
+ * app.use(createDatePlugin({ adapter: new Vuetify0DateAdapter() }))
1481
+ *
1482
+ * // With locale options
1483
+ * app.use(createDatePlugin({
1484
+ * adapter: new Vuetify0DateAdapter(),
1485
+ * locale: 'de-DE',
1486
+ * }))
1487
+ * ```
1488
+ */
1489
+ function createDatePlugin(options) {
1490
+ const { namespace = "v0:date", ...dateOptions } = options;
1491
+ const [, provideDateContext, context] = createDateContext({
1492
+ namespace,
1493
+ ...dateOptions
1494
+ });
1495
+ return createPlugin({
1496
+ namespace,
1497
+ provide: (app) => {
1498
+ provideDateContext(context, app);
1499
+ }
1500
+ });
1501
+ }
1502
+ /**
1503
+ * Returns the current date context.
1442
1504
  *
1443
1505
  * Requires `createDatePlugin` to be installed with an adapter.
1444
1506
  *
@@ -2156,6 +2218,132 @@ function useElementIntersection(target, options = {}) {
2156
2218
  };
2157
2219
  }
2158
2220
  //#endregion
2221
+ //#region src/composables/useTimer/index.ts
2222
+ /**
2223
+ * @module useTimer
2224
+ *
2225
+ * @remarks
2226
+ * A reactive timer composable with pause/resume support.
2227
+ *
2228
+ * Key features:
2229
+ * - Start/stop/pause/resume controls
2230
+ * - Reactive remaining time tracking
2231
+ * - One-shot (default) or repeating mode
2232
+ * - Automatic cleanup on scope disposal
2233
+ * - SSR-safe
2234
+ */
2235
+ /**
2236
+ * Creates a reactive timer with pause/resume support.
2237
+ *
2238
+ * @param handler Callback invoked when the timer fires.
2239
+ * @param options Timer configuration.
2240
+ * @returns Timer controls and reactive state.
2241
+ *
2242
+ * @see https://0.vuetifyjs.com/composables/system/use-timer
2243
+ *
2244
+ * @example
2245
+ * ```ts
2246
+ * const timer = useTimer(() => console.log('fired'), { duration: 5000 })
2247
+ *
2248
+ * timer.start()
2249
+ * timer.pause()
2250
+ * timer.resume()
2251
+ * timer.stop()
2252
+ * ```
2253
+ */
2254
+ function useTimer(handler, options = {}) {
2255
+ const { duration = 1e3, repeat = false } = options;
2256
+ const remaining = shallowRef(duration);
2257
+ const isActive = shallowRef(false);
2258
+ const isPaused = shallowRef(false);
2259
+ let timeoutId;
2260
+ let trackingId;
2261
+ let startedAt = 0;
2262
+ let budget = duration;
2263
+ function clearTimer() {
2264
+ if (!/* @__PURE__ */ isUndefined(timeoutId)) {
2265
+ globalThis.clearTimeout(timeoutId);
2266
+ timeoutId = void 0;
2267
+ }
2268
+ }
2269
+ function startTracking() {
2270
+ stopTracking();
2271
+ if (!IN_BROWSER) return;
2272
+ trackingId = globalThis.setInterval(() => {
2273
+ if (!isActive.value || isPaused.value) return;
2274
+ const elapsed = Date.now() - startedAt;
2275
+ remaining.value = Math.max(0, budget - elapsed);
2276
+ }, 100);
2277
+ }
2278
+ function stopTracking() {
2279
+ if (!/* @__PURE__ */ isUndefined(trackingId)) {
2280
+ globalThis.clearInterval(trackingId);
2281
+ trackingId = void 0;
2282
+ }
2283
+ }
2284
+ function fire() {
2285
+ clearTimer();
2286
+ stopTracking();
2287
+ handler();
2288
+ if (repeat) begin(duration);
2289
+ else {
2290
+ isActive.value = false;
2291
+ remaining.value = 0;
2292
+ }
2293
+ }
2294
+ function begin(ms) {
2295
+ clearTimer();
2296
+ stopTracking();
2297
+ budget = ms;
2298
+ startedAt = Date.now();
2299
+ remaining.value = ms;
2300
+ isActive.value = true;
2301
+ isPaused.value = false;
2302
+ timeoutId = globalThis.setTimeout(fire, ms);
2303
+ startTracking();
2304
+ }
2305
+ function start() {
2306
+ if (duration < 0) return;
2307
+ begin(duration);
2308
+ }
2309
+ function stop() {
2310
+ clearTimer();
2311
+ stopTracking();
2312
+ isActive.value = false;
2313
+ isPaused.value = false;
2314
+ remaining.value = duration;
2315
+ }
2316
+ function pause() {
2317
+ if (!isActive.value || isPaused.value) return;
2318
+ const elapsed = Date.now() - startedAt;
2319
+ budget = Math.max(0, budget - elapsed);
2320
+ remaining.value = budget;
2321
+ clearTimer();
2322
+ stopTracking();
2323
+ isPaused.value = true;
2324
+ }
2325
+ function resume() {
2326
+ if (!isPaused.value) return;
2327
+ isPaused.value = false;
2328
+ startedAt = Date.now();
2329
+ timeoutId = globalThis.setTimeout(fire, budget);
2330
+ startTracking();
2331
+ }
2332
+ onScopeDispose(() => {
2333
+ clearTimer();
2334
+ stopTracking();
2335
+ }, true);
2336
+ return {
2337
+ start,
2338
+ stop,
2339
+ pause,
2340
+ resume,
2341
+ remaining,
2342
+ isActive,
2343
+ isPaused
2344
+ };
2345
+ }
2346
+ //#endregion
2159
2347
  //#region src/composables/useLazy/index.ts
2160
2348
  /**
2161
2349
  * @module useLazy
@@ -3886,92 +4074,94 @@ const [createNotificationsContext, createNotificationsPlugin, useNotifications]
3886
4074
  }
3887
4075
  });
3888
4076
  //#endregion
3889
- //#region src/composables/usePopover/index.ts
4077
+ //#region src/composables/useProxyModel/index.ts
3890
4078
  /**
3891
- * @module usePopover
4079
+ * @module useProxyModel
3892
4080
  *
3893
4081
  * @remarks
3894
- * Composable for native popover API behavior with CSS anchor positioning.
3895
- * Manages open/close state, anchor styles, content attributes, and
3896
- * bidirectional sync between reactive state and native popover events.
4082
+ * Proxy composable for bidirectional sync between a model context and v-model.
3897
4083
  *
3898
4084
  * Key features:
3899
- * - Native popover API (showPopover/hidePopover)
3900
- * - CSS anchor positioning (position-area, position-try-fallbacks)
3901
- * - Toggle event sync for native state changes
3902
- * - SSR-safe (no DOM ops outside browser)
3903
- * - Optional external isOpen ref for v-model integration
4085
+ * - Bidirectional synchronization
4086
+ * - Array and single-value modes
4087
+ * - Automatic cleanup on scope disposal
4088
+ * - Works with any ModelContext (Selection, Slider, etc.)
4089
+ *
4090
+ * Bridges the gap between model composables and Vue's v-model.
4091
+ */
4092
+ /**
4093
+ * Syncs a ref with a model context bidirectionally.
4094
+ *
4095
+ * @param context The model context to bind to.
4096
+ * @param model The ref to sync.
4097
+ * @param options The options for the proxy model.
4098
+ * @returns A function to stop the sync.
4099
+ *
4100
+ * @see https://0.vuetifyjs.com/composables/forms/use-proxy-model
3904
4101
  *
3905
- * Perfect for building select, combobox, tooltip, and menu components
3906
- * without wrapping the Popover compound component.
4102
+ * @example
4103
+ * ```ts
4104
+ * import { createSelection, useProxyModel } from '@vuetify/v0'
4105
+ *
4106
+ * const model = ref()
4107
+ * const registry = createSelection({ events: true })
4108
+ * registry.onboard([
4109
+ * { id: 'item-1', value: 'Item 1' },
4110
+ * { id: 'item-2', value: 'Item 2' },
4111
+ * ])
4112
+ *
4113
+ * const stop = useProxyModel(registry, model)
4114
+ * ```
3907
4115
  */
3908
- function usePopover(options = {}) {
3909
- const { id: _id, positionArea = "bottom", positionTry = "most-width bottom", showDelay = 0, hideDelay = 0 } = options;
3910
- const id = _id ?? /* @__PURE__ */ useId$1();
3911
- const isOpen = options.isOpen ?? shallowRef(false);
3912
- const showTimer = showDelay > 0 ? useTimer(() => {
3913
- isOpen.value = true;
3914
- }, { duration: showDelay }) : void 0;
3915
- const hideTimer = hideDelay > 0 ? useTimer(() => {
3916
- isOpen.value = false;
3917
- }, { duration: hideDelay }) : void 0;
3918
- function open() {
3919
- hideTimer?.stop();
3920
- if (showTimer) showTimer.start();
3921
- else isOpen.value = true;
4116
+ function useProxyModel(context, model, options) {
4117
+ const multiple = options?.multiple ?? context.multiple ?? false;
4118
+ const _transformIn = options?.transformIn;
4119
+ const _transformOut = options?.transformOut;
4120
+ function transformIn(val) {
4121
+ const value = toValue(val);
4122
+ return toArray(/* @__PURE__ */ isFunction(_transformIn) ? _transformIn(value) : value);
3922
4123
  }
3923
- function close() {
3924
- showTimer?.stop();
3925
- if (hideTimer) hideTimer.start();
3926
- else isOpen.value = false;
4124
+ function transformOut(val) {
4125
+ if (/* @__PURE__ */ isFunction(_transformOut)) return _transformOut(val);
4126
+ return toValue(multiple) ? val : val[0];
3927
4127
  }
3928
- function toggle() {
3929
- if (isOpen.value) close();
3930
- else open();
4128
+ const applyOptions = toValue(multiple) === toValue(context.multiple ?? false) ? void 0 : { multiple: toValue(multiple) };
4129
+ const modelAsArray = transformIn(model);
4130
+ const pending = new Set(modelAsArray);
4131
+ context.apply(modelAsArray, applyOptions);
4132
+ const selected = new Set(context.selectedValues.value);
4133
+ for (const value of modelAsArray) if (selected.has(value)) pending.delete(value);
4134
+ const contextWatch = watch(context.selectedValues, (val) => {
4135
+ modelWatch.pause();
4136
+ model.value = transformOut(Array.from(toValue(val)));
4137
+ modelWatch.resume();
4138
+ }, { flush: "sync" });
4139
+ const modelWatch = watch(model, (val) => {
4140
+ contextWatch.pause();
4141
+ context.apply(transformIn(val), applyOptions);
4142
+ contextWatch.resume();
4143
+ }, {
4144
+ flush: "sync",
4145
+ deep: toValue(multiple)
4146
+ });
4147
+ function onRegister(data) {
4148
+ const ticket = data;
4149
+ if (!pending.has(ticket.value) || toValue(ticket.disabled)) return;
4150
+ contextWatch.pause();
4151
+ modelWatch.pause();
4152
+ context.select?.(ticket.id);
4153
+ pending.delete(ticket.value);
4154
+ modelWatch.resume();
4155
+ contextWatch.resume();
3931
4156
  }
3932
- const anchorStyles = toRef(() => ({ anchorName: `--${id}` }));
3933
- const contentAttrs = toRef(() => ({
3934
- id,
3935
- popover: ""
3936
- }));
3937
- const contentStyles = toRef(() => ({
3938
- "position": "fixed",
3939
- "margin": "unset",
3940
- "inset-area": positionArea,
3941
- "position-area": positionArea,
3942
- "position-anchor": `--${id}`,
3943
- "position-try-fallbacks": positionTry
3944
- }));
3945
- /* v8 ignore start -- browser popover API */
3946
- function attach(el) {
3947
- onMounted(() => {
3948
- const element = toValue(el);
3949
- if (isOpen.value) element?.showPopover();
3950
- });
3951
- watch(isOpen, (value) => {
3952
- const element = toValue(el);
3953
- if (!element?.isConnected) return;
3954
- if (value === element.matches?.(":popover-open")) return;
3955
- if (value) element.showPopover?.();
3956
- else element.hidePopover?.();
3957
- });
3958
- useEventListener(el, "toggle", (e) => {
3959
- if (!toValue(el)?.isConnected) return;
3960
- isOpen.value = e.newState === "open";
3961
- });
4157
+ context.on?.("register:ticket", onRegister);
4158
+ function stop() {
4159
+ contextWatch();
4160
+ modelWatch();
4161
+ context.off?.("register:ticket", onRegister);
3962
4162
  }
3963
- /* v8 ignore stop */
3964
- return {
3965
- isOpen,
3966
- id,
3967
- open,
3968
- close,
3969
- toggle,
3970
- anchorStyles,
3971
- contentAttrs,
3972
- contentStyles,
3973
- attach
3974
- };
4163
+ onScopeDispose(stop, true);
4164
+ return stop;
3975
4165
  }
3976
4166
  //#endregion
3977
4167
  //#region src/composables/useRaf/index.ts
@@ -4331,6 +4521,10 @@ const [createRtlContext, createRtlPlugin, useRtl] = createPluginContext("v0:rtl"
4331
4521
  fallback: () => createRtlFallback(),
4332
4522
  setup: (context, app, { adapter = new Vuetify0RtlAdapter(), target }) => {
4333
4523
  adapter.setup(app, context, target);
4524
+ },
4525
+ persist: (ctx) => ctx.isRtl.value,
4526
+ restore: (ctx, saved) => {
4527
+ ctx.isRtl.value = saved;
4334
4528
  }
4335
4529
  });
4336
4530
  //#endregion
@@ -5976,261 +6170,368 @@ function useDocumentEventListener(event, listener, options) {
5976
6170
  return IN_BROWSER ? useEventListener(document, event, listener, options) : () => {};
5977
6171
  }
5978
6172
  //#endregion
5979
- //#region src/composables/useClickOutside/index.ts
6173
+ //#region src/composables/usePopover/index.ts
5980
6174
  /**
5981
- * @module useClickOutside
6175
+ * @module usePopover
5982
6176
  *
5983
6177
  * @remarks
5984
- * Detects clicks outside of specified element(s) with automatic cleanup.
6178
+ * Composable for native popover API behavior with CSS anchor positioning.
6179
+ * Manages open/close state, anchor styles, content attributes, and
6180
+ * bidirectional sync between reactive state and native popover events.
5985
6181
  *
5986
6182
  * Key features:
5987
- * - Two-phase detection (pointerdown → pointerup) prevents drag-out false positives
5988
- * - Touch scroll threshold ignores swipes/scrolls on mobile
5989
- * - Capture phase listeners work with stopPropagation
5990
- * - Pause/resume/stop functionality
5991
- * - Optional iframe focus detection
5992
- * - SSR-safe (no-op when not in browser)
5993
- *
5994
- * Common use cases: closing popovers, dropdowns, modals, and menus.
6183
+ * - Native popover API (showPopover/hidePopover)
6184
+ * - CSS anchor positioning (position-area, position-try-fallbacks)
6185
+ * - Toggle event sync for native state changes
6186
+ * - SSR-safe (no DOM ops outside browser)
6187
+ * - Optional external isOpen ref for v-model integration
5995
6188
  *
5996
- * Accessibility: This composable handles pointer interactions only. For accessible
5997
- * components (dialogs, popovers, menus), pair with `useHotkey` for Escape key
5998
- * dismissal per WCAG/APG requirements.
6189
+ * Perfect for building select, combobox, tooltip, and menu components
6190
+ * without wrapping the Popover compound component.
5999
6191
  */
6192
+ function usePopover(options = {}) {
6193
+ const { id: _id, positionArea = "bottom", positionTry = "most-width bottom", showDelay = 0, hideDelay = 0 } = options;
6194
+ const id = _id ?? /* @__PURE__ */ useId$1();
6195
+ const isOpen = options.isOpen ?? shallowRef(false);
6196
+ const showTimer = showDelay > 0 ? useTimer(() => {
6197
+ isOpen.value = true;
6198
+ }, { duration: showDelay }) : void 0;
6199
+ const hideTimer = hideDelay > 0 ? useTimer(() => {
6200
+ isOpen.value = false;
6201
+ }, { duration: hideDelay }) : void 0;
6202
+ function open() {
6203
+ hideTimer?.stop();
6204
+ if (showTimer) showTimer.start();
6205
+ else isOpen.value = true;
6206
+ }
6207
+ function close() {
6208
+ showTimer?.stop();
6209
+ if (hideTimer) hideTimer.start();
6210
+ else isOpen.value = false;
6211
+ }
6212
+ function toggle() {
6213
+ if (isOpen.value) close();
6214
+ else open();
6215
+ }
6216
+ const anchorStyles = toRef(() => ({ anchorName: `--${id}` }));
6217
+ const contentAttrs = toRef(() => ({
6218
+ id,
6219
+ popover: ""
6220
+ }));
6221
+ const contentStyles = toRef(() => ({
6222
+ "position": "fixed",
6223
+ "margin": "unset",
6224
+ "inset-area": positionArea,
6225
+ "position-area": positionArea,
6226
+ "position-anchor": `--${id}`,
6227
+ "position-try-fallbacks": positionTry
6228
+ }));
6229
+ /* v8 ignore start -- browser popover API */
6230
+ function attach(el) {
6231
+ onMounted(() => {
6232
+ const element = toValue(el);
6233
+ if (isOpen.value) element?.showPopover();
6234
+ });
6235
+ watch(isOpen, (value) => {
6236
+ const element = toValue(el);
6237
+ if (!element?.isConnected) return;
6238
+ if (value === element.matches?.(":popover-open")) return;
6239
+ if (value) element.showPopover?.();
6240
+ else element.hidePopover?.();
6241
+ });
6242
+ useEventListener(el, "toggle", (e) => {
6243
+ if (!toValue(el)?.isConnected) return;
6244
+ isOpen.value = e.newState === "open";
6245
+ });
6246
+ }
6247
+ /* v8 ignore stop */
6248
+ return {
6249
+ isOpen,
6250
+ id,
6251
+ open,
6252
+ close,
6253
+ toggle,
6254
+ anchorStyles,
6255
+ contentAttrs,
6256
+ contentStyles,
6257
+ attach
6258
+ };
6259
+ }
6260
+ //#endregion
6261
+ //#region src/composables/createCombobox/adapters/client.ts
6000
6262
  /**
6001
- * Detects clicks outside of the specified element(s).
6002
- *
6003
- * Uses two-phase detection (pointerdown → pointerup) to prevent false positives
6004
- * when users drag from inside to outside an element.
6005
- *
6006
- * @param target Element ref(s) to detect clicks outside of. Accepts a single ref/getter or array of refs/getters.
6007
- * @param handler Callback invoked when a click outside is detected.
6008
- * @param options Configuration options.
6009
- * @returns An object with methods to control the listener.
6263
+ * @module createCombobox/adapters/client
6010
6264
  *
6011
- * @see https://0.vuetifyjs.com/composables/system/use-click-outside
6012
- *
6013
- * @example Native element ref
6014
- * ```ts
6015
- * const menuRef = useTemplateRef<HTMLElement>('menu')
6265
+ * @remarks
6266
+ * Default combobox adapter. Filters items client-side using createFilter.
6267
+ * Synchronous, zero debounce — items update immediately as the user types.
6268
+ */
6269
+ var ClientAdapter = class {
6270
+ options;
6271
+ constructor(options = {}) {
6272
+ this.options = options;
6273
+ }
6274
+ setup(context) {
6275
+ const { mode, keys, filter: customFilter } = this.options;
6276
+ const filter = createFilter({
6277
+ mode,
6278
+ keys,
6279
+ customFilter: customFilter ? (query, item) => customFilter(String(query), item) : void 0
6280
+ });
6281
+ const values = toRef(() => context.items.value.map((t) => t.value));
6282
+ const { items: filteredValues } = filter.apply(context.query, values);
6283
+ const filtered = computed(() => {
6284
+ if (!String(context.query.value).trim()) return new Set(context.items.value.map((t) => t.id));
6285
+ const matchedValues = new Set(filteredValues.value);
6286
+ const ids = /* @__PURE__ */ new Set();
6287
+ for (const ticket of context.items.value) if (matchedValues.has(ticket.value)) ids.add(ticket.id);
6288
+ return ids;
6289
+ });
6290
+ const isEmpty = toRef(() => filtered.value.size === 0);
6291
+ return {
6292
+ filtered,
6293
+ isLoading: shallowRef(false),
6294
+ isEmpty
6295
+ };
6296
+ }
6297
+ };
6298
+ //#endregion
6299
+ //#region src/composables/createCombobox/adapters/server.ts
6300
+ /**
6301
+ * @module createCombobox/adapters/server
6016
6302
  *
6017
- * useClickOutside(menuRef, () => { isOpen.value = false })
6018
- * ```
6303
+ * @remarks
6304
+ * Server-side combobox adapter. All filtering is performed externally.
6305
+ * The adapter is a pass-through — it shows all registered items and
6306
+ * exposes loading state for the consumer to control.
6019
6307
  *
6020
- * @example Component ref (e.g., Atom)
6021
- * ```ts
6022
- * const atomRef = useTemplateRef<AtomExpose>('atom')
6308
+ * The consumer watches `query` via useComboboxContext() or slot props
6309
+ * and provides filtered items reactively.
6310
+ */
6311
+ var ServerAdapter = class {
6312
+ setup(context) {
6313
+ const isLoading = shallowRef(false);
6314
+ return {
6315
+ filtered: toRef(() => {
6316
+ return new Set(context.items.value.map((t) => t.id));
6317
+ }),
6318
+ isLoading,
6319
+ isEmpty: toRef(() => context.items.value.length === 0)
6320
+ };
6321
+ }
6322
+ };
6323
+ //#endregion
6324
+ //#region src/composables/createCombobox/index.ts
6325
+ /**
6326
+ * @module createCombobox
6023
6327
  *
6024
- * // Pass the exposed element TemplateRef via getter
6025
- * useClickOutside(
6026
- * () => atomRef.value?.element,
6027
- * () => { isOpen.value = false }
6028
- * )
6029
- * ```
6328
+ * @remarks
6329
+ * Orchestrator composable that coordinates selection, popover, and virtual focus
6330
+ * for a combobox (autocomplete/typeahead) pattern. Does not extend the selection
6331
+ * chain it composes existing primitives instead.
6030
6332
  *
6031
- * @example Multiple targets
6032
- * ```ts
6033
- * const popoverRef = useTemplateRef<AtomExpose>('popover')
6034
- * const anchorRef = useTemplateRef<HTMLElement>('anchor')
6333
+ * Key features:
6334
+ * - Adapter-based filtering (client-side or server-side via ClientAdapter/ServerAdapter)
6335
+ * - Single or multi-select mode
6336
+ * - Virtual focus keyboard navigation (aria-activedescendant pattern)
6337
+ * - Strict mode: reverts query to selected value on close if no match
6338
+ * - Popover-based dropdown positioning
6339
+ * - Context DI via createComboboxContext / useCombobox
6340
+ */
6341
+ /**
6342
+ * Creates a combobox context that orchestrates selection, popover, virtual
6343
+ * focus, and adapter-based filtering into a single coordinated state.
6035
6344
  *
6036
- * useClickOutside(
6037
- * [() => popoverRef.value?.element, anchorRef],
6038
- * () => { isOpen.value = false }
6039
- * )
6040
- * ```
6345
+ * @param options - Configuration for selection mode, filtering adapter, and behavior.
6346
+ * @returns A combobox context with all composed systems and action methods.
6041
6347
  *
6042
- * @example Ignoring elements (CSS selectors or refs)
6043
- * ```ts
6044
- * useClickOutside(
6045
- * () => navRef.value?.element,
6046
- * () => { isOpen.value = false },
6047
- * { ignore: ['[data-app-bar]'] }
6048
- * )
6049
- * ```
6348
+ * @see https://0.vuetifyjs.com/composables/combobox/create-combobox
6050
6349
  *
6051
- * @example Native dialog with bounds detection
6350
+ * @example
6052
6351
  * ```ts
6053
- * // For native <dialog> elements, use bounds mode to detect backdrop clicks
6054
- * const dialogRef = useTemplateRef<HTMLDialogElement>('dialog')
6352
+ * import { createCombobox } from '@vuetify/v0'
6055
6353
  *
6056
- * useClickOutside(
6057
- * dialogRef,
6058
- * () => { dialogRef.value?.close() },
6059
- * { bounds: true }
6060
- * )
6354
+ * const combobox = createCombobox({ strict: true })
6355
+ *
6356
+ * combobox.selection.register({ id: 'a', value: 'Apple' })
6357
+ * combobox.selection.register({ id: 'b', value: 'Banana' })
6358
+ *
6359
+ * combobox.open()
6360
+ * combobox.select('a') // query → 'Apple', dropdown closes
6061
6361
  * ```
6062
6362
  */
6063
- function useClickOutside(target, handler, options = {}) {
6064
- const { capture = true, touchScrollThreshold = 30, detectIframe = false, ignore = [], bounds = false } = options;
6065
- const isPaused = shallowRef(false);
6066
- const isActive = toRef(() => !isPaused.value);
6067
- let initialTarget = null;
6068
- let startPosition = {
6069
- x: 0,
6070
- y: 0
6071
- };
6072
- let cleanupPointerDown;
6073
- let cleanupPointerUp;
6074
- let cleanupBlur;
6075
- /**
6076
- * Resolve target(s) to an array of HTMLElements.
6077
- */
6078
- function getTargets() {
6079
- return toArray(target).map((source) => toValue(source)).filter((el) => !!el && /* @__PURE__ */ isFunction(el.contains));
6080
- }
6081
- /**
6082
- * Resolve ignore targets to a tuple of [selectors, elements].
6083
- * Called once per event to avoid repeated toValue() calls in hot path.
6084
- */
6085
- function resolveIgnoreTargets() {
6086
- const ignoreTargets = toValue(ignore);
6087
- if (ignoreTargets.length === 0) return [[], []];
6088
- const selectors = [];
6089
- const elements = [];
6090
- for (const ignoreTarget of ignoreTargets) if (/* @__PURE__ */ isString(ignoreTarget)) selectors.push(ignoreTarget);
6091
- else {
6092
- const ignoreEl = toValue(ignoreTarget);
6093
- if (ignoreEl) elements.push(ignoreEl);
6094
- }
6095
- return [selectors, elements];
6096
- }
6097
- /**
6098
- * Check if an element matches resolved ignore targets.
6099
- */
6100
- function isIgnored(el, selectors, elements) {
6101
- if (!el) return false;
6102
- for (const selector of selectors) try {
6103
- if (el.matches(selector) || !/* @__PURE__ */ isNull(el.closest(selector))) return true;
6104
- } catch {}
6105
- for (const ignoreEl of elements) if (ignoreEl === el || ignoreEl.contains(el)) return true;
6106
- return false;
6107
- }
6108
- /**
6109
- * Check if any element in the event path should be ignored.
6110
- */
6111
- function shouldIgnore(path) {
6112
- const [selectors, elements] = resolveIgnoreTargets();
6113
- if (selectors.length === 0 && elements.length === 0) return false;
6114
- return path.some((node) => node instanceof Element && isIgnored(node, selectors, elements));
6115
- }
6116
- /**
6117
- * Check if the event target is outside all target elements (DOM containment).
6118
- */
6119
- function isOutside(eventTarget) {
6120
- /* v8 ignore start -- defensive guards */
6121
- if (!eventTarget) return false;
6122
- if (!(eventTarget instanceof Node)) return false;
6123
- /* v8 ignore stop */
6124
- const targets = getTargets();
6125
- if (targets.length === 0) return false;
6126
- return targets.every((el) => {
6127
- if (/* @__PURE__ */ isNullOrUndefined(el) || !/* @__PURE__ */ isFunction(el.contains)) return false;
6128
- return el !== eventTarget && !el.contains(eventTarget);
6129
- });
6130
- }
6131
- /**
6132
- * Check if coordinates are outside all target elements' bounding rects.
6133
- */
6134
- function isOutsideBounds(x, y) {
6135
- const targets = getTargets();
6136
- if (targets.length === 0) return false;
6137
- return targets.every((el) => {
6138
- const { left, right, top, bottom } = el.getBoundingClientRect();
6139
- return x < left || x > right || y < top || y > bottom;
6140
- });
6141
- }
6142
- /**
6143
- * Validate that the target is still in the DOM.
6144
- */
6145
- function isValidTarget(eventTarget) {
6146
- if (!(eventTarget instanceof Element)) return false;
6147
- if (!eventTarget.isConnected) return false;
6148
- return true;
6363
+ function createCombobox(options = {}) {
6364
+ const { multiple = false, mandatory = false, disabled = false, strict = false, error = false, errorMessages, adapter, displayValue = (v) => String(v ?? ""), id: _id, name, form } = options;
6365
+ const id = _id ?? /* @__PURE__ */ useId$1();
6366
+ const inputId = `${id}-input`;
6367
+ const listboxId = `${id}-listbox`;
6368
+ const descriptionId = `${id}-description`;
6369
+ const errorId = `${id}-error`;
6370
+ const hasDescription = shallowRef(false);
6371
+ const hasError = shallowRef(false);
6372
+ const errors = computed(() => {
6373
+ const messages = toValue(errorMessages);
6374
+ return messages ? toArray(messages) : [];
6375
+ });
6376
+ const isValid = toRef(() => {
6377
+ if (toValue(error)) return false;
6378
+ if (errors.value.length > 0) return false;
6379
+ return null;
6380
+ });
6381
+ const selection = createSelection({
6382
+ multiple,
6383
+ mandatory,
6384
+ disabled: toRef(() => toValue(disabled)),
6385
+ events: true
6386
+ });
6387
+ const query = shallowRef("");
6388
+ const pristine = shallowRef(true);
6389
+ const inputEl = shallowRef(null);
6390
+ const version = shallowRef(0);
6391
+ selection.on("register:ticket", () => {
6392
+ version.value++;
6393
+ });
6394
+ selection.on("unregister:ticket", () => {
6395
+ version.value++;
6396
+ });
6397
+ const items = computed(() => {
6398
+ version.value;
6399
+ return [...selection.values()];
6400
+ });
6401
+ const { filtered, isLoading, isEmpty } = (adapter ?? new ClientAdapter()).setup({
6402
+ query,
6403
+ items
6404
+ });
6405
+ const popover = usePopover({ id });
6406
+ const isOpen = popover.isOpen;
6407
+ const cursor = useVirtualFocus(() => selection.values().filter((ticket) => filtered.value.has(ticket.id) && !toValue(ticket.disabled)).map((ticket) => ({
6408
+ id: ticket.id,
6409
+ el: () => document.querySelector(`#${CSS.escape(`${id}-option-${ticket.id}`)}`),
6410
+ disabled: ticket.disabled
6411
+ })), {
6412
+ control: inputEl,
6413
+ target: () => null,
6414
+ orientation: "vertical"
6415
+ });
6416
+ const selectedId = toRef(() => selection.selectedIds.values().next().value);
6417
+ const display = toRef(() => {
6418
+ if (!pristine.value) return query.value;
6419
+ if (toValue(multiple)) return query.value;
6420
+ const ticket = /* @__PURE__ */ isUndefined(selectedId.value) ? void 0 : selection.get(selectedId.value);
6421
+ return ticket ? displayValue(ticket.value) : "";
6422
+ });
6423
+ function open() {
6424
+ if (!isOpen.value && !toValue(disabled)) isOpen.value = true;
6149
6425
  }
6150
- /* v8 ignore start -- event handlers tested via integration, not unit */
6151
- /**
6152
- * Handle pointerdown - store initial target and position.
6153
- */
6154
- function onPointerDown(event) {
6155
- if (isPaused.value) return;
6156
- if (event.defaultPrevented) return;
6157
- initialTarget = event.composedPath()[0] ?? event.target;
6158
- startPosition = {
6159
- x: event.clientX,
6160
- y: event.clientY
6161
- };
6426
+ function close() {
6427
+ query.value = "";
6428
+ pristine.value = true;
6429
+ isOpen.value = false;
6162
6430
  }
6163
- /**
6164
- * Handle pointerup - check if it's an outside click.
6165
- */
6166
- function onPointerUp(event) {
6167
- if (isPaused.value) return;
6168
- if (event.defaultPrevented) return;
6169
- if (!initialTarget) return;
6170
- const pointerdownTarget = initialTarget;
6171
- initialTarget = null;
6172
- if (!isValidTarget(pointerdownTarget)) return;
6173
- const path = event.composedPath();
6174
- const pointerupTarget = path[0] ?? event.target;
6175
- if (event.pointerType === "touch") {
6176
- const dx = Math.abs(event.clientX - startPosition.x);
6177
- const dy = Math.abs(event.clientY - startPosition.y);
6178
- if (dx >= touchScrollThreshold || dy >= touchScrollThreshold) return;
6179
- }
6180
- if ((bounds ? isOutsideBounds(startPosition.x, startPosition.y) && isOutsideBounds(event.clientX, event.clientY) : isOutside(pointerdownTarget) && isOutside(pointerupTarget)) && !shouldIgnore(path)) handler(event);
6431
+ function toggle() {
6432
+ if (isOpen.value) close();
6433
+ else open();
6181
6434
  }
6182
- /**
6183
- * Handle window blur - detect focus moving to iframe.
6184
- */
6185
- function onBlur(event) {
6186
- if (isPaused.value) return;
6187
- if (event.defaultPrevented) return;
6188
- if (document.activeElement instanceof HTMLIFrameElement) {
6189
- const iframeIsOutside = getTargets().every((el) => !el.contains(document.activeElement));
6190
- const [selectors, elements] = resolveIgnoreTargets();
6191
- if (iframeIsOutside && !isIgnored(document.activeElement, selectors, elements)) handler(event);
6435
+ function select(itemId) {
6436
+ if (toValue(multiple)) {
6437
+ selection.toggle(itemId);
6438
+ query.value = "";
6439
+ pristine.value = true;
6440
+ cursor.highlight(itemId);
6441
+ inputEl.value?.focus();
6442
+ } else {
6443
+ selection.select(itemId);
6444
+ query.value = "";
6445
+ pristine.value = true;
6446
+ close();
6192
6447
  }
6193
6448
  }
6194
- /* v8 ignore stop */
6195
- function setup() {
6196
- cleanupPointerDown = useDocumentEventListener("pointerdown", onPointerDown, capture);
6197
- cleanupPointerUp = useDocumentEventListener("pointerup", onPointerUp, capture);
6198
- if (!detectIframe) return;
6199
- cleanupBlur = useWindowEventListener("blur", onBlur, capture);
6200
- }
6201
- function cleanup() {
6202
- cleanupPointerDown?.();
6203
- cleanupPointerUp?.();
6204
- cleanupBlur?.();
6205
- cleanupPointerDown = void 0;
6206
- cleanupPointerUp = void 0;
6207
- cleanupBlur = void 0;
6208
- }
6209
- function pause() {
6210
- if (isPaused.value) return;
6211
- isPaused.value = true;
6212
- initialTarget = null;
6213
- cleanup();
6214
- }
6215
- function resume() {
6216
- if (!isPaused.value) return;
6217
- isPaused.value = false;
6218
- setup();
6219
- }
6220
- function stop() {
6221
- isPaused.value = true;
6222
- initialTarget = null;
6223
- cleanup();
6449
+ function clear() {
6450
+ query.value = "";
6451
+ pristine.value = true;
6452
+ for (const id of Array.from(selection.selectedIds)) selection.unselect(id);
6224
6453
  }
6225
- setup();
6226
- onScopeDispose(stop, true);
6454
+ watch(isOpen, (open) => {
6455
+ if (!open) cursor.clear();
6456
+ });
6227
6457
  return {
6228
- isActive: shallowReadonly(isActive),
6229
- isPaused: shallowReadonly(isPaused),
6230
- pause,
6231
- resume,
6232
- stop
6458
+ selection,
6459
+ popover,
6460
+ cursor,
6461
+ query,
6462
+ display,
6463
+ pristine,
6464
+ filtered,
6465
+ isEmpty,
6466
+ isLoading,
6467
+ isOpen,
6468
+ id,
6469
+ inputId,
6470
+ listboxId,
6471
+ descriptionId,
6472
+ errorId,
6473
+ hasDescription,
6474
+ hasError,
6475
+ errors,
6476
+ isValid,
6477
+ inputEl,
6478
+ multiple: toValue(multiple),
6479
+ strict,
6480
+ disabled,
6481
+ name,
6482
+ form,
6483
+ open,
6484
+ close,
6485
+ toggle,
6486
+ select,
6487
+ clear
6233
6488
  };
6234
6489
  }
6490
+ /**
6491
+ * Creates a combobox context with dependency injection support.
6492
+ *
6493
+ * @param options - Combobox options plus an optional namespace for DI.
6494
+ * @returns A context trinity: `[useCombobox, provideCombobox, defaultCombobox]`.
6495
+ *
6496
+ * @see https://0.vuetifyjs.com/composables/combobox/create-combobox
6497
+ *
6498
+ * @example
6499
+ * ```ts
6500
+ * import { createComboboxContext } from '@vuetify/v0'
6501
+ *
6502
+ * const [useCombobox, provideCombobox, combobox] = createComboboxContext({
6503
+ * namespace: 'my-combobox',
6504
+ * strict: true,
6505
+ * })
6506
+ * ```
6507
+ */
6508
+ function createComboboxContext(options = {}) {
6509
+ const { namespace = "v0:combobox", ...rest } = options;
6510
+ const [useCombobox, _provideCombobox] = createContext(namespace);
6511
+ const context = createCombobox(rest);
6512
+ function provideCombobox(_context = context, app) {
6513
+ return _provideCombobox(_context, app);
6514
+ }
6515
+ return createTrinity(useCombobox, provideCombobox, context);
6516
+ }
6517
+ /**
6518
+ * Injects the current combobox context from a parent provider.
6519
+ *
6520
+ * @param namespace - DI namespace. Defaults to `'v0:combobox'`.
6521
+ * @returns The combobox context from the nearest provider.
6522
+ *
6523
+ * @see https://0.vuetifyjs.com/composables/combobox/create-combobox
6524
+ *
6525
+ * @example
6526
+ * ```ts
6527
+ * import { useCombobox } from '@vuetify/v0'
6528
+ *
6529
+ * const combobox = useCombobox()
6530
+ * combobox.query.value = 'search term'
6531
+ * ```
6532
+ */
6533
+ function useCombobox(namespace = "v0:combobox") {
6534
+ return useContext(namespace);
6535
+ }
6235
6536
  //#endregion
6236
- export { useElementIntersection 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, createDate as at, useStep as b, useResizeObserver as bt, useVirtual as c, useDate as ct, createTimeline as d, createBreakpointsPlugin as dt, useIntersectionObserver as et, createTimelineContext as f, useBreakpoints as ft, createStoragePlugin as g, createOverflow as gt, createStorageContext as h, useProxyModel as ht, useWindowEventListener as i, useForm as it, Vuetify0RtlAdapter as j, createRtlPlugin as k, useBreadcrumbs as kt, useToggleScope as l, createBreakpoints as lt, createStorage as m, useTimer as mt, useDocumentEventListener as n, createForm as nt, createVirtual as o, createDateContext as ot, useTimeline as p, toReactive as pt, useMutationObserver as q, useEventListener as r, createFormContext as rt, createVirtualContext as s, createDatePlugin as st, useClickOutside as t, useHotkey as tt, createValidation as u, createBreakpointsContext 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 };
6537
+ export { useMediaQuery as $, createSlider as A, createHydrationPlugin as At, createNotifications as B, createStep as C, useOverflow as Ct, createStackContext as D, createFallbackHydration as Dt, createStack as E, toElement as Et, useRtl as F, createQueueContext as G, createNotificationsPlugin as H, Vuetify0RtlAdapter as I, createNestedContext as J, useQueue as K, useRovingFocus as L, createRtlContext as M, createBreadcrumbs as Mt, createRtlFallback as N, createBreadcrumbsContext as Nt, createStackPlugin as O, createHydration as Ot, createRtlPlugin as P, useBreadcrumbs as Pt, useMutationObserver as Q, useRaf as R, useStorage as S, createOverflowContext as St, useStep as T, useResizeObserver as Tt, useNotifications as U, createNotificationsContext as V, createQueue as W, multipleOpenStrategy as X, useNested as Y, singleOpenStrategy as Z, createTimelineContext as _, createBreakpointsContext as _t, ClientAdapter as a, useElementIntersection as at, createStorageContext as b, toReactive as bt, useEventListener as c, createForm as ct, createVirtual as d, createDate as dt, usePrefersContrast as et, createVirtualContext as f, createDateContext as ft, createTimeline as g, createBreakpoints as gt, createValidation as h, useClickOutside as ht, ServerAdapter as i, useTimer as it, createRtl as j, useHydration as jt, useStack as k, createHydrationContext as kt, useWindowEventListener as l, createFormContext as lt, useToggleScope as m, useDate as mt, createComboboxContext as n, usePrefersReducedMotion as nt, usePopover as o, useIntersectionObserver as ot, useVirtual as p, createDatePlugin as pt, createNested as q, useCombobox as r, useLazy as rt, useDocumentEventListener as s, useHotkey as st, createCombobox as t, usePrefersDark as tt, useVirtualFocus as u, useForm as ut, useTimeline as v, createBreakpointsPlugin as vt, createStepContext as w, useElementSize as wt, createStoragePlugin as x, createOverflow as xt, createStorage as y, useBreakpoints as yt, useProxyModel as z };