@vuetify/v0 1.0.0-alpha.2 → 1.0.0-alpha.4

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 (171) hide show
  1. package/README.md +6 -1
  2. package/dist/adapter-BSKx9WQ7.mjs +4 -0
  3. package/dist/{index-CT5SCLzW.d.mts → adapter-C6JmOyYo.d.mts} +6 -9
  4. package/dist/{adapter-gQ2SNKZq.mjs → adapter-C9cR4AcY.mjs} +248 -5
  5. package/dist/adapter-Chznb9Ju.mjs +4 -0
  6. package/dist/{adapter-DlX3FA3A.d.mts → adapter-Cr7Bl4la.d.mts} +3 -7
  7. package/dist/adapter-D8zizQVP.mjs +4 -0
  8. package/dist/adapter-DgBBe_BW.d.mts +17 -0
  9. package/dist/adapter-Dzyrlbz7.d.mts +98 -0
  10. package/dist/adapter-o8r-2vUn.d.mts +11 -0
  11. package/dist/adapters-B25KaYU4.mjs +2 -0
  12. package/dist/adapters-BcddLSNs.mjs +4 -0
  13. package/dist/adapters-D64TORDu.mjs +2 -0
  14. package/dist/adapters-Dv74Jk6A.mjs +5 -0
  15. package/dist/adapters-t6QC-zE8.mjs +3 -0
  16. package/dist/browser/index.js +5167 -597
  17. package/dist/components/index.d.mts +3 -3
  18. package/dist/components/index.mjs +2 -2
  19. package/dist/{components-CqDewYTU.mjs → components-BWxMSWr7.mjs} +3780 -417
  20. package/dist/composables/index.d.mts +19 -19
  21. package/dist/composables/index.mjs +31 -29
  22. package/dist/{consola-BdgyPEfx.d.mts → consola-Crr4qbeI.d.mts} +2 -2
  23. package/dist/{consola-Ceox8BWi.mjs → consola-DYvbPNbF.mjs} +3 -1
  24. package/dist/constants/index.d.mts +1 -1
  25. package/dist/constants/index.mjs +2 -2
  26. package/dist/{createDataTable-rXBqGglI.mjs → createDataTable-DDr2yY-A.mjs} +30 -29
  27. package/dist/{createGroup-BU4olYtB.mjs → createGroup-BSGtfBUE.mjs} +3 -3
  28. package/dist/{createRegistry-Dl-bSSTr.mjs → createRegistry-Db_juawd.mjs} +78 -41
  29. package/dist/{createSelection-Csaa-C8p.mjs → createSelection-BeKZMxha.mjs} +3 -3
  30. package/dist/{createSingle-8xbHAIpY.mjs → createSingle-Dms2efxr.mjs} +1 -1
  31. package/dist/{createTokens-lNg2M_P_.mjs → createTokens-B1lwHZvO.mjs} +2 -2
  32. package/dist/data-table/adapters/client/index.d.mts +2 -2
  33. package/dist/data-table/adapters/client/index.mjs +2 -2
  34. package/dist/data-table/adapters/index.d.mts +5 -5
  35. package/dist/data-table/adapters/index.mjs +6 -6
  36. package/dist/data-table/adapters/server/index.d.mts +2 -2
  37. package/dist/data-table/adapters/server/index.mjs +2 -2
  38. package/dist/data-table/adapters/virtual/index.d.mts +2 -2
  39. package/dist/data-table/adapters/virtual/index.mjs +2 -2
  40. package/dist/data-table/index.d.mts +6 -6
  41. package/dist/data-table/index.mjs +6 -6
  42. package/dist/date/index.d.mts +3 -3
  43. package/dist/date/index.mjs +11 -4
  44. package/dist/features/adapters/flagsmith/index.d.mts +2 -2
  45. package/dist/features/adapters/flagsmith/index.mjs +2 -2
  46. package/dist/features/adapters/launchdarkly/index.d.mts +2 -2
  47. package/dist/features/adapters/launchdarkly/index.mjs +2 -2
  48. package/dist/features/adapters/posthog/index.d.mts +2 -2
  49. package/dist/features/adapters/posthog/index.mjs +2 -2
  50. package/dist/features/index.d.mts +3 -3
  51. package/dist/features/index.mjs +2 -1
  52. package/dist/{flagsmith-Dbh1h8WF.mjs → flagsmith-Cu93hUIF.mjs} +6 -4
  53. package/dist/{index-BzFHTW59.d.mts → flagsmith-ygKMImos.d.mts} +4 -4
  54. package/dist/{globals-CKdzjeLe.mjs → globals-BAe6tA_x.mjs} +1 -1
  55. package/dist/{index-GOpjKzaJ.d.mts → index-B3-FstpY.d.mts} +25 -2
  56. package/dist/{index-BJGcVy7e.d.mts → index-BA7YU2xY.d.mts} +5 -5
  57. package/dist/{index-BxKMMWnQ.d.mts → index-BfpLkDR_.d.mts} +1113 -74
  58. package/dist/{index-DE3bWw11.d.mts → index-Cw04PFVO.d.mts} +92 -48
  59. package/dist/index-DewYtDsc.d.mts +4 -0
  60. package/dist/{index-B7cqgTBL.d.mts → index-jQPkwo3G.d.mts} +598 -363
  61. package/dist/index.d.mts +23 -23
  62. package/dist/index.mjs +35 -33
  63. package/dist/json/attributes.json +57 -1
  64. package/dist/json/importMap.json +16 -0
  65. package/dist/json/tags.json +29 -0
  66. package/dist/json/web-types.json +181 -3
  67. package/dist/launchdarkly-BJR4vbbV.d.mts +13 -0
  68. package/dist/{launchdarkly-BKtW-idu.mjs → launchdarkly-CLGtJCsI.mjs} +6 -4
  69. package/dist/locale/adapters/index.d.mts +3 -3
  70. package/dist/locale/adapters/index.mjs +4 -3
  71. package/dist/locale/adapters/v0/index.d.mts +2 -2
  72. package/dist/locale/adapters/v0/index.mjs +2 -2
  73. package/dist/locale/adapters/vue-i18n/index.d.mts +3 -3
  74. package/dist/locale/adapters/vue-i18n/index.mjs +4 -2
  75. package/dist/locale/index.d.mts +4 -4
  76. package/dist/locale/index.mjs +4 -3
  77. package/dist/logger/adapters/consola/index.d.mts +1 -1
  78. package/dist/logger/adapters/consola/index.mjs +1 -1
  79. package/dist/logger/adapters/index.d.mts +5 -5
  80. package/dist/logger/adapters/index.mjs +6 -5
  81. package/dist/logger/adapters/pino/index.d.mts +1 -1
  82. package/dist/logger/adapters/pino/index.mjs +1 -1
  83. package/dist/logger/adapters/v0/index.d.mts +2 -2
  84. package/dist/logger/adapters/v0/index.mjs +2 -2
  85. package/dist/logger/index.d.mts +6 -6
  86. package/dist/logger/index.mjs +6 -5
  87. package/dist/{memory-CeSQE-QV.d.mts → memory-B0w39kno.d.mts} +9 -9
  88. package/dist/{memory-oYBvtkgx.mjs → memory-BMpJcQjQ.mjs} +13 -3
  89. package/dist/notifications/index.d.mts +30 -4
  90. package/dist/notifications/index.mjs +187 -126
  91. package/dist/palettes/ant/index.d.mts +1 -1
  92. package/dist/palettes/index.d.mts +3 -3
  93. package/dist/palettes/leonardo/generate/index.mjs +5 -1
  94. package/dist/palettes/material/index.d.mts +1 -1
  95. package/dist/palettes/md1/index.d.mts +1 -1
  96. package/dist/palettes/md2/index.d.mts +1 -1
  97. package/dist/palettes/radix/index.d.mts +1 -1
  98. package/dist/palettes/tailwind/index.d.mts +1 -1
  99. package/dist/permissions/adapters/index.d.mts +2 -2
  100. package/dist/permissions/adapters/index.mjs +3 -3
  101. package/dist/permissions/adapters/v0/index.d.mts +2 -2
  102. package/dist/permissions/adapters/v0/index.mjs +2 -2
  103. package/dist/permissions/index.d.mts +2 -2
  104. package/dist/permissions/index.mjs +3 -3
  105. package/dist/{pino-9L79n6y6.mjs → pino-CIcYlW3u.mjs} +3 -1
  106. package/dist/{pino-DNfCEb0p.d.mts → pino-NmR7X0s4.d.mts} +2 -2
  107. package/dist/{posthog-DZNnPXxS.mjs → posthog-B3go2aCh.mjs} +7 -5
  108. package/dist/posthog-CQntUXFO.d.mts +13 -0
  109. package/dist/rules/index.d.mts +1 -1
  110. package/dist/rules/index.mjs +1 -1
  111. package/dist/{server-3ilWFwqe.d.mts → server-DQK305lc.d.mts} +5 -5
  112. package/dist/{server-y7zLSCSx.mjs → server-ZFXbgQoY.mjs} +4 -3
  113. package/dist/storage/adapters/index.d.mts +3 -3
  114. package/dist/storage/adapters/index.mjs +2 -2
  115. package/dist/storage/adapters/memory/index.d.mts +2 -2
  116. package/dist/storage/adapters/memory/index.mjs +2 -2
  117. package/dist/storage/index.d.mts +4 -4
  118. package/dist/storage/index.mjs +3 -3
  119. package/dist/theme/adapters/index.d.mts +2 -2
  120. package/dist/theme/adapters/index.mjs +4 -4
  121. package/dist/theme/adapters/unhead/index.d.mts +1 -1
  122. package/dist/theme/adapters/unhead/index.mjs +1 -1
  123. package/dist/theme/adapters/v0/index.d.mts +1 -1
  124. package/dist/theme/adapters/v0/index.mjs +1 -1
  125. package/dist/theme/index.d.mts +1 -1
  126. package/dist/theme/index.mjs +3 -3
  127. package/dist/types/index.d.mts +1 -1
  128. package/dist/{unhead-DZiD-4Tx.mjs → unhead-dPyVkgiJ.mjs} +2 -2
  129. package/dist/{useClickOutside-CmCUD_Pf.mjs → useClickOutside-B8naChaQ.mjs} +1405 -288
  130. package/dist/{useFeatures-ZIvzzuEV.mjs → useFeatures-x2iKp0C8.mjs} +7 -10
  131. package/dist/{useLocale-DTJx3aKR.mjs → useLocale-BUms59Bw.mjs} +6 -6
  132. package/dist/{useLogger-BBTcMN61.mjs → useLogger-DPSSa-YR.mjs} +4 -4
  133. package/dist/{useNotifications-H4knpvNw.mjs → useNotifications-BPTmZWQy.mjs} +22 -11
  134. package/dist/{usePermissions-B_G12Dwd.mjs → usePermissions-CTi-3pBk.mjs} +6 -6
  135. package/dist/{useRules--7Qoo60I.mjs → useRules-DNhljkyO.mjs} +2 -2
  136. package/dist/{useTheme-Co05TEeD.mjs → useTheme-3vW3kFNg.mjs} +5 -5
  137. package/dist/utilities/index.d.mts +2 -2
  138. package/dist/utilities/index.mjs +27 -2
  139. package/dist/{v0-DrOub-e1.d.mts → v0-CRGArJim.d.mts} +4 -4
  140. package/dist/{v0-D0Mh3EVG.mjs → v0-CotC1PiS.mjs} +5 -3
  141. package/dist/{v0-Dq3zhexO.mjs → v0-DAlKpCR8.mjs} +3 -4
  142. package/dist/{v0-D7Fm2C3Y.d.mts → v0-DDELiSI6.d.mts} +3 -3
  143. package/dist/v0-DYsCJPIu.d.mts +8 -0
  144. package/dist/{v0-BrTEO5sv.mjs → v0-DadOcxFI.mjs} +2 -2
  145. package/dist/{v0-Bmow1q_x.mjs → v0-Dvu8fMl3.mjs} +3 -3
  146. package/dist/{v0-DeiZuexg.mjs → v0-YJQoTLsp.mjs} +6 -4
  147. package/dist/virtual-BqCRw0ly.d.mts +8 -0
  148. package/dist/{virtual-C0yHf1DC.mjs → virtual-Dnno49F8.mjs} +3 -4
  149. package/package.json +10 -10
  150. package/dist/adapter-58IvIfAZ.d.mts +0 -96
  151. package/dist/adapter-BvWEd9w-.d.mts +0 -17
  152. package/dist/adapter-Dhq68tz0.d.mts +0 -11
  153. package/dist/adapters-BJ5n7rgh.mjs +0 -3
  154. package/dist/adapters-BnKW0BF_.mjs +0 -2
  155. package/dist/adapters-CBrvizWb.mjs +0 -4
  156. package/dist/adapters-CMJGA1k7.mjs +0 -2
  157. package/dist/adapters-Dse4bPxk.mjs +0 -5
  158. package/dist/createPagination-CHF7SmmK.mjs +0 -247
  159. package/dist/index-1XdASTjp.d.mts +0 -13
  160. package/dist/index-C6rdXZ7j.d.mts +0 -11
  161. package/dist/index-Dp1HjBxJ.d.mts +0 -13
  162. package/dist/v0-CdusXjY_.d.mts +0 -8
  163. package/dist/virtual-D4fckewC.d.mts +0 -8
  164. /package/dist/{adapter-Be9zUc-n.mjs → adapter-oM18AdUc.mjs} +0 -0
  165. /package/dist/{htmlElements-D4oMQZPJ.mjs → htmlElements-CHXZfsZu.mjs} +0 -0
  166. /package/dist/{index-CFhUdAHM.d.mts → index-CKKoxA9V.d.mts} +0 -0
  167. /package/dist/{index-DuddF-NR.d.mts → index-CLR1QMM3.d.mts} +0 -0
  168. /package/dist/{index-BgCkepP_.d.mts → index-DJQwYeyx.d.mts} +0 -0
  169. /package/dist/{index-CROz1VSV.d.mts → index-DWfPppPU.d.mts} +0 -0
  170. /package/dist/{index-DTvqw22G.d.mts → index-H_hl6-4b.d.mts} +0 -0
  171. /package/dist/{toArray-as9BWxBK.mjs → toArray-CD2-OxAz.mjs} +0 -0
@@ -1,24 +1,23 @@
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-CKdzjeLe.mjs";
2
- import { clamp, instanceExists, isArray, isElement, isFunction, isNull, isNullOrUndefined, isNumber, isObject, isString, isUndefined, mergeDeep, range, resolveIds, resolveIndexes, useId as useId$1 } from "./utilities/index.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-BAe6tA_x.mjs";
2
+ import { clamp, instanceExists, isArray, isElement, isFunction, isNull, isNullOrUndefined, isNumber, isObject, isString, isThenable, isUndefined, mergeDeep, range, resolveIds, resolveIndexes, useId as useId$1 } from "./utilities/index.mjs";
3
3
  import { i as useContext, t as createTrinity } from "./createTrinity-BTCs1pYx.mjs";
4
- import { a as createPlugin, i as useLogger, o as createPluginContext } from "./useLogger-BBTcMN61.mjs";
5
- import { t as createRegistry } from "./createRegistry-Dl-bSSTr.mjs";
6
- import { i as createModel, t as createSelection } from "./createSelection-Csaa-C8p.mjs";
7
- import { t as createSingle } from "./createSingle-8xbHAIpY.mjs";
8
- import "./createTokens-lNg2M_P_.mjs";
9
- import { a as useLocale } from "./useLocale-DTJx3aKR.mjs";
10
- import { c as useTimer } from "./useNotifications-H4knpvNw.mjs";
11
- import { t as toArray } from "./toArray-as9BWxBK.mjs";
12
- import { n as createFilter } from "./adapter-gQ2SNKZq.mjs";
13
- import { t as createGroup } from "./createGroup-BU4olYtB.mjs";
14
- import "./createPagination-CHF7SmmK.mjs";
15
- import "./createDataTable-rXBqGglI.mjs";
16
- import { a as useRules, o as isStandardSchema } from "./useRules--7Qoo60I.mjs";
17
- import "./useFeatures-ZIvzzuEV.mjs";
18
- import "./usePermissions-B_G12Dwd.mjs";
19
- import { t as MemoryAdapter } from "./memory-oYBvtkgx.mjs";
20
- import "./useTheme-Co05TEeD.mjs";
21
- import { computed, effectScope, hasInjectionContext, isRef, nextTick, onMounted, onScopeDispose, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRef, toValue, unref, watch, watchEffect } from "vue";
4
+ import { a as createPlugin, i as useLogger, o as createPluginContext } from "./useLogger-DPSSa-YR.mjs";
5
+ import { t as createRegistry } from "./createRegistry-Db_juawd.mjs";
6
+ import { i as createModel, t as createSelection } from "./createSelection-BeKZMxha.mjs";
7
+ import { t as createSingle } from "./createSingle-Dms2efxr.mjs";
8
+ import "./createTokens-B1lwHZvO.mjs";
9
+ import { a as useLocale } from "./useLocale-BUms59Bw.mjs";
10
+ import { c as useTimer } from "./useNotifications-BPTmZWQy.mjs";
11
+ import { t as toArray } from "./toArray-CD2-OxAz.mjs";
12
+ import { a as createFilter } from "./adapter-C9cR4AcY.mjs";
13
+ import { t as createGroup } from "./createGroup-BSGtfBUE.mjs";
14
+ import "./createDataTable-DDr2yY-A.mjs";
15
+ import { a as useRules, o as isStandardSchema } from "./useRules-DNhljkyO.mjs";
16
+ import "./useFeatures-x2iKp0C8.mjs";
17
+ import "./usePermissions-CTi-3pBk.mjs";
18
+ import { t as MemoryStorageAdapter } from "./memory-BMpJcQjQ.mjs";
19
+ import "./useTheme-3vW3kFNg.mjs";
20
+ import { computed, effectScope, hasInjectionContext, isReadonly, isRef, nextTick, onMounted, onScopeDispose, onWatcherCleanup, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRef, toValue, unref, watch, watchEffect } from "vue";
22
21
  //#region src/composables/createBreadcrumbs/index.ts
23
22
  /**
24
23
  * @module createBreadcrumbs
@@ -351,6 +350,7 @@ function createFocusTraversal(items, activate, options = {}) {
351
350
  }
352
351
  /** Focus first non-disabled item in the current row (grid mode) */
353
352
  function rowFirst() {
353
+ /* v8 ignore next 2 -- defensive: rowFirst is only invoked in grid mode (cols > 0) */
354
354
  const cols = toValue(_columns) ?? 0;
355
355
  if (!cols) return first();
356
356
  const all = items();
@@ -367,6 +367,7 @@ function createFocusTraversal(items, activate, options = {}) {
367
367
  }
368
368
  /** Focus last non-disabled item in the current row (grid mode) */
369
369
  function rowLast() {
370
+ /* v8 ignore next 2 -- defensive: rowLast is only invoked in grid mode (cols > 0) */
370
371
  const cols = toValue(_columns) ?? 0;
371
372
  if (!cols) return last();
372
373
  const all = items();
@@ -538,6 +539,9 @@ function useVirtualFocus(items, options) {
538
539
  };
539
540
  }
540
541
  //#endregion
542
+ //#region src/composables/createCombobox/adapters/adapter.ts
543
+ var ComboboxAdapter = class {};
544
+ //#endregion
541
545
  //#region src/composables/createCombobox/adapters/client.ts
542
546
  /**
543
547
  * @module createCombobox/adapters/client
@@ -546,9 +550,10 @@ function useVirtualFocus(items, options) {
546
550
  * Default combobox adapter. Filters items client-side using createFilter.
547
551
  * Synchronous, zero debounce — items update immediately as the user types.
548
552
  */
549
- var ClientAdapter = class {
553
+ var ClientComboboxAdapter = class extends ComboboxAdapter {
550
554
  options;
551
555
  constructor(options = {}) {
556
+ super();
552
557
  this.options = options;
553
558
  }
554
559
  setup(context) {
@@ -588,7 +593,7 @@ var ClientAdapter = class {
588
593
  * The consumer watches `query` via useComboboxContext() or slot props
589
594
  * and provides filtered items reactively.
590
595
  */
591
- var ServerAdapter = class {
596
+ var ServerComboboxAdapter = class extends ComboboxAdapter {
592
597
  setup(context) {
593
598
  const isLoading = shallowRef(false);
594
599
  return {
@@ -613,7 +618,7 @@ var ServerAdapter = class {
613
618
  * chain — it composes existing primitives instead.
614
619
  *
615
620
  * Key features:
616
- * - Adapter-based filtering (client-side or server-side via ClientAdapter/ServerAdapter)
621
+ * - Adapter-based filtering (client-side or server-side via ClientComboboxAdapter/ServerComboboxAdapter)
617
622
  * - Single or multi-select mode
618
623
  * - Virtual focus keyboard navigation (aria-activedescendant pattern)
619
624
  * - Strict mode: reverts query to selected value on close if no match
@@ -690,7 +695,7 @@ function createCombobox(options = {}) {
690
695
  version.value;
691
696
  return [...selection.values()];
692
697
  });
693
- const { filtered, isLoading, isEmpty } = (adapter ?? new ClientAdapter()).setup({
698
+ const { filtered, isLoading, isEmpty } = (adapter ?? new ClientComboboxAdapter()).setup({
694
699
  query,
695
700
  items
696
701
  });
@@ -1069,7 +1074,7 @@ function createValidation(_options = {}) {
1069
1074
  if (!silent) isValidating.value = true;
1070
1075
  try {
1071
1076
  const results = await Promise.all(activeRules.map((rule) => rule(val)));
1072
- if (gen !== generation) return isValid.value ?? false;
1077
+ if (gen !== generation) return false;
1073
1078
  const errorMessages = results.filter((result) => /* @__PURE__ */ isString(result) || result === false).map((result) => result === false ? "Validation failed" : result);
1074
1079
  if (!silent) {
1075
1080
  errors.value = errorMessages;
@@ -1077,7 +1082,7 @@ function createValidation(_options = {}) {
1077
1082
  }
1078
1083
  return errorMessages.length === 0;
1079
1084
  } catch (error) {
1080
- if (gen !== generation) return isValid.value ?? false;
1085
+ if (gen !== generation) return false;
1081
1086
  if (!silent) {
1082
1087
  errors.value = [error instanceof Error ? error.message : "Validation error"];
1083
1088
  isValid.value = false;
@@ -1215,6 +1220,327 @@ function createInput(options) {
1215
1220
  };
1216
1221
  }
1217
1222
  //#endregion
1223
+ //#region src/composables/createSortable/index.ts
1224
+ /**
1225
+ * @module createSortable
1226
+ *
1227
+ * @see https://0.vuetifyjs.com/composables/data/create-sortable
1228
+ *
1229
+ * @remarks
1230
+ * Headless ordered-list primitive. Extends `createModel` with mutation primitives
1231
+ * (`swap`, `reorder`) and an enriched `move:ticket` event on the registry event
1232
+ * bus. Honors `disabled` as a real mutation gate at both root and per-ticket scope.
1233
+ *
1234
+ * Pure logical composable — no DnD, no DOM, no keyboard. Drag-and-drop integration
1235
+ * composes with `useDragDrop`; keyboard reorder composes with `useVirtualFocus`.
1236
+ *
1237
+ * @example
1238
+ * ```ts
1239
+ * import { createSortable } from '@vuetify/v0'
1240
+ * import type { SortableTicketInput } from '@vuetify/v0'
1241
+ *
1242
+ * interface TaskTicket extends SortableTicketInput {
1243
+ * value: { id: number; label: string }
1244
+ * }
1245
+ *
1246
+ * const sortable = createSortable<TaskTicket>()
1247
+ * const a = sortable.register({ value: { id: 1, label: 'A' } })
1248
+ * const b = sortable.register({ value: { id: 2, label: 'B' } })
1249
+ *
1250
+ * sortable.move(a.id, 1)
1251
+ * sortable.on('move:ticket', ({ ticket, from, to }) => {
1252
+ * console.log(`${ticket.value.label} moved ${from} → ${to}`)
1253
+ * })
1254
+ * ```
1255
+ */
1256
+ /**
1257
+ * Create an ordered-list state primitive.
1258
+ *
1259
+ * @param options Sortable options.
1260
+ * @returns A {@link SortableContext} that extends {@link ModelContext}.
1261
+ *
1262
+ * @example
1263
+ * ```ts
1264
+ * const sortable = createSortable<Todo>()
1265
+ * sortable.onboard(todos.value.map(value => ({ value })))
1266
+ * ```
1267
+ */
1268
+ function createSortable(_options = {}) {
1269
+ const { disabled, ...options } = _options;
1270
+ const model = createModel({
1271
+ ...options,
1272
+ events: true,
1273
+ disabled
1274
+ });
1275
+ const logger = useLogger();
1276
+ function move(id, toIndex) {
1277
+ if (toValue(disabled)) return void 0;
1278
+ const ticket = model.get(id);
1279
+ if (!ticket) return void 0;
1280
+ if (toValue(ticket.disabled)) return void 0;
1281
+ const from = ticket.index;
1282
+ const result = model.move(id, toIndex);
1283
+ if (result && result.index !== from) model.emit("move:ticket", {
1284
+ ticket: result,
1285
+ from,
1286
+ to: result.index
1287
+ });
1288
+ return result;
1289
+ }
1290
+ function swap(a, b) {
1291
+ if (toValue(disabled)) return;
1292
+ if (a === b) return;
1293
+ const ta = model.get(a);
1294
+ const tb = model.get(b);
1295
+ if (!ta || !tb) return;
1296
+ if (toValue(ta.disabled) || toValue(tb.disabled)) return;
1297
+ const indexA = ta.index;
1298
+ const indexB = tb.index;
1299
+ model.batch(() => {
1300
+ const ra = model.move(a, indexB);
1301
+ if (ra && ra.index !== indexA) model.emit("move:ticket", {
1302
+ ticket: ra,
1303
+ from: indexA,
1304
+ to: ra.index
1305
+ });
1306
+ const rb = model.move(b, indexA);
1307
+ if (rb && rb.index !== indexB) model.emit("move:ticket", {
1308
+ ticket: rb,
1309
+ from: indexB,
1310
+ to: rb.index
1311
+ });
1312
+ });
1313
+ }
1314
+ function reorder(ids) {
1315
+ if (toValue(disabled)) return;
1316
+ const currentSize = model.size;
1317
+ if (ids.length !== currentSize) {
1318
+ logger.warn(`Reorder expected ${currentSize} ids, got ${ids.length}.`);
1319
+ return;
1320
+ }
1321
+ const known = new Set(model.keys());
1322
+ const seen = /* @__PURE__ */ new Set();
1323
+ for (const id of ids) {
1324
+ if (!known.has(id)) {
1325
+ logger.warn(`Reorder contains unknown id "${String(id)}".`);
1326
+ return;
1327
+ }
1328
+ if (seen.has(id)) {
1329
+ logger.warn(`Reorder contains duplicate id "${String(id)}".`);
1330
+ return;
1331
+ }
1332
+ seen.add(id);
1333
+ }
1334
+ const froms = /* @__PURE__ */ new Map();
1335
+ for (const id of ids) {
1336
+ const ticket = model.get(id);
1337
+ if (ticket) froms.set(id, ticket.index);
1338
+ }
1339
+ model.batch(() => {
1340
+ model.reorder(ids);
1341
+ for (const [index, id] of ids.entries()) {
1342
+ const from = froms.get(id);
1343
+ if (from === index) continue;
1344
+ const ticket = model.get(id);
1345
+ if (ticket) model.emit("move:ticket", {
1346
+ ticket,
1347
+ from,
1348
+ to: index
1349
+ });
1350
+ }
1351
+ });
1352
+ }
1353
+ return {
1354
+ ...model,
1355
+ move,
1356
+ swap,
1357
+ reorder,
1358
+ get size() {
1359
+ return model.size;
1360
+ }
1361
+ };
1362
+ }
1363
+ //#endregion
1364
+ //#region src/composables/createKanban/index.ts
1365
+ /**
1366
+ * @module createKanban
1367
+ *
1368
+ * @see https://0.vuetifyjs.com/composables/data/create-kanban
1369
+ *
1370
+ * @remarks
1371
+ * Headless data-flow management for two-level sortable boards. Wraps a column
1372
+ * sortable; each column ticket carries its own inner sortable on `column.items`.
1373
+ * Adds a `transfer` primitive for cross-column moves with reused `disabled`
1374
+ * gating and a destination-side `accept` predicate. Pure logic — no DnD, no DOM.
1375
+ *
1376
+ * @example
1377
+ * ```ts
1378
+ * import { createKanban } from '@vuetify/v0'
1379
+ * import type { SortableTicketInput } from '@vuetify/v0'
1380
+ *
1381
+ * interface CardTicket extends SortableTicketInput {
1382
+ * value: { title: string }
1383
+ * }
1384
+ *
1385
+ * const kanban = createKanban<CardTicket>()
1386
+ *
1387
+ * const todo = kanban.columns.register({ value: { title: 'Todo' } })
1388
+ * const done = kanban.columns.register({ value: { title: 'Done' } })
1389
+ *
1390
+ * const card = todo.items.register({ value: { title: 'Write spec' } })
1391
+ *
1392
+ * kanban.on('transfer:ticket', ({ ticket, from, to }) => {
1393
+ * console.log(ticket.value.title, from, '→', to)
1394
+ * })
1395
+ *
1396
+ * kanban.transfer(card.id, done.id, 0)
1397
+ * ```
1398
+ */
1399
+ /**
1400
+ * Create a headless two-level sortable orchestrator.
1401
+ *
1402
+ * @param options Kanban options (currently only `disabled`).
1403
+ * @returns A {@link KanbanContext}.
1404
+ *
1405
+ * @example
1406
+ * ```ts
1407
+ * import { createKanban } from '@vuetify/v0'
1408
+ * import type { KanbanColumnTicketInput, SortableTicketInput } from '@vuetify/v0'
1409
+ *
1410
+ * interface CardTicket extends SortableTicketInput {
1411
+ * value: { title: string }
1412
+ * }
1413
+ *
1414
+ * interface BoardColumn extends KanbanColumnTicketInput<CardTicket> {
1415
+ * value: { title: string }
1416
+ * }
1417
+ *
1418
+ * const kanban = createKanban<CardTicket, BoardColumn>()
1419
+ *
1420
+ * const todo = kanban.columns.register({ value: { title: 'Todo' } })
1421
+ * const review = kanban.columns.register({
1422
+ * value: { title: 'Review' },
1423
+ * accept: card => card.value.title.length > 0,
1424
+ * })
1425
+ *
1426
+ * const spec = todo.items.register({ value: { title: 'Write spec' } })
1427
+ * kanban.transfer(spec.id, review.id, 0)
1428
+ * ```
1429
+ */
1430
+ function createKanban(_options = {}) {
1431
+ const { disabled, ...options } = _options;
1432
+ const logger = useLogger();
1433
+ const _columns = createSortable({
1434
+ ...options,
1435
+ disabled
1436
+ });
1437
+ const lookup = createRegistry({ events: true });
1438
+ const bus = createRegistry({ events: true });
1439
+ const on = bus.on;
1440
+ const off = bus.off;
1441
+ const columns = {
1442
+ ..._columns,
1443
+ register(registration) {
1444
+ let ticket;
1445
+ const items = createSortable({ disabled: () => !!toValue(disabled) || !!toValue(ticket?.disabled) });
1446
+ items.on("register:ticket", (t) => {
1447
+ lookup.upsert(t.id, { value: ticket.id });
1448
+ });
1449
+ items.on("unregister:ticket", (t) => {
1450
+ lookup.get(t.id)?.unregister();
1451
+ });
1452
+ ticket = _columns.register({
1453
+ ...registration,
1454
+ items
1455
+ });
1456
+ return ticket;
1457
+ },
1458
+ onboard(registrations) {
1459
+ return _columns.batch(() => registrations.map((reg) => columns.register(reg)));
1460
+ },
1461
+ upsert(id, patch = {}, event) {
1462
+ return /* @__PURE__ */ isUndefined(_columns.get(id)) ? columns.register({
1463
+ ...patch,
1464
+ id
1465
+ }) : _columns.upsert(id, patch, event);
1466
+ },
1467
+ get size() {
1468
+ return _columns.size;
1469
+ }
1470
+ };
1471
+ function isAccepted(accept, ticket, from, toIndex) {
1472
+ if (/* @__PURE__ */ isUndefined(accept)) return true;
1473
+ try {
1474
+ const result = accept(ticket, from, toIndex);
1475
+ if (/* @__PURE__ */ isThenable(result)) {
1476
+ logger.warn("accept predicate returned a thenable; async predicates are not supported — treating as reject");
1477
+ return false;
1478
+ }
1479
+ return Boolean(result);
1480
+ } catch (error) {
1481
+ logger.error("accept predicate threw; treating as reject", error);
1482
+ return false;
1483
+ }
1484
+ }
1485
+ columns.on("unregister:ticket", (column) => {
1486
+ column.items.dispose();
1487
+ for (const t of lookup.values()) if (t.value === column.id) t.unregister();
1488
+ });
1489
+ function transfer(id, toColumnId, toIndex) {
1490
+ const fromColumnId = lookup.get(id)?.value;
1491
+ if (/* @__PURE__ */ isUndefined(fromColumnId)) {
1492
+ logger.warn(`unknown ticket id "${String(id)}"`);
1493
+ return;
1494
+ }
1495
+ const source = columns.get(fromColumnId);
1496
+ if (!source) {
1497
+ logger.warn(`unknown source column id "${String(fromColumnId)}"`);
1498
+ return;
1499
+ }
1500
+ const destination = columns.get(toColumnId);
1501
+ if (!destination) {
1502
+ logger.warn(`unknown destination column id "${String(toColumnId)}"`);
1503
+ return;
1504
+ }
1505
+ const ticket = source.items.get(id);
1506
+ if (!ticket) {
1507
+ logger.warn(`ticket "${String(id)}" missing from source column "${String(fromColumnId)}"`);
1508
+ return;
1509
+ }
1510
+ const fromIndex = ticket.index;
1511
+ if (toColumnId === fromColumnId) return source.items.move(id, toIndex);
1512
+ if (toValue(disabled) || toValue(source.disabled) || toValue(destination.disabled) || toValue(ticket.disabled)) return;
1513
+ if (!isAccepted(destination.accept, ticket, fromColumnId, toIndex)) return void 0;
1514
+ if (!/* @__PURE__ */ isUndefined(destination.items.get(id))) {
1515
+ logger.warn(`id "${String(id)}" already exists in destination column "${String(toColumnId)}"`);
1516
+ return;
1517
+ }
1518
+ let registered;
1519
+ let moved;
1520
+ destination.items.batch(() => {
1521
+ const [input] = source.items.offboard([id]);
1522
+ if (/* @__PURE__ */ isUndefined(input)) return;
1523
+ registered = destination.items.register(input);
1524
+ moved = registered.index === toIndex ? registered : destination.items.move(registered.id, toIndex);
1525
+ });
1526
+ const final = moved ?? registered;
1527
+ if (final) bus.emit("transfer:ticket", {
1528
+ ticket: final,
1529
+ from: fromColumnId,
1530
+ to: toColumnId,
1531
+ fromIndex,
1532
+ toIndex: final.index
1533
+ });
1534
+ return final;
1535
+ }
1536
+ return {
1537
+ columns,
1538
+ transfer,
1539
+ on,
1540
+ off
1541
+ };
1542
+ }
1543
+ //#endregion
1218
1544
  //#region src/composables/createNested/index.ts
1219
1545
  /**
1220
1546
  * @module createNested
@@ -2016,7 +2342,7 @@ function createNumberField(options = {}) {
2016
2342
  }
2017
2343
  function increment(multiplier) {
2018
2344
  if (isLocked()) return;
2019
- if (/* @__PURE__ */ isNull(value.value)) {
2345
+ if (/* @__PURE__ */ isNull(value.value) || !Number.isFinite(value.value)) {
2020
2346
  value.value = initialize();
2021
2347
  return;
2022
2348
  }
@@ -2024,7 +2350,7 @@ function createNumberField(options = {}) {
2024
2350
  }
2025
2351
  function decrement(multiplier) {
2026
2352
  if (isLocked()) return;
2027
- if (/* @__PURE__ */ isNull(value.value)) {
2353
+ if (/* @__PURE__ */ isNull(value.value) || !Number.isFinite(value.value)) {
2028
2354
  value.value = initialize();
2029
2355
  return;
2030
2356
  }
@@ -2231,10 +2557,12 @@ const [createHydrationContext, createHydrationPlugin, useHydration] = createPlug
2231
2557
  */
2232
2558
  /* @__NO_SIDE_EFFECTS__ */
2233
2559
  function toElement(target) {
2234
- const raw = /* @__PURE__ */ isFunction(target) ? target() : target && /* @__PURE__ */ isObject(target) && "value" in target ? target.value : target;
2560
+ if (!target) return void 0;
2561
+ if (/* @__PURE__ */ isElement(target)) return target;
2562
+ const raw = /* @__PURE__ */ isFunction(target) ? target() : /* @__PURE__ */ isObject(target) && "value" in target ? target.value : target;
2235
2563
  if (!raw) return void 0;
2236
2564
  if (/* @__PURE__ */ isElement(raw)) return raw;
2237
- if ("$el" in raw) return raw.$el;
2565
+ if (/* @__PURE__ */ isObject(raw) && "$el" in raw) return raw.$el;
2238
2566
  }
2239
2567
  //#endregion
2240
2568
  //#region src/composables/createObserver/index.ts
@@ -2466,6 +2794,7 @@ function useElementSize(target) {
2466
2794
  const height = shallowRef(0);
2467
2795
  const { pause: _pause, resume, stop, isActive, isPaused } = useResizeObserver(target, (entries) => {
2468
2796
  const entry = entries[0];
2797
+ /* v8 ignore next 4 -- defensive: ResizeObserver always passes an entry array with at least one entry */
2469
2798
  if (entry) {
2470
2799
  width.value = entry.contentRect.width;
2471
2800
  height.value = entry.contentRect.height;
@@ -2624,6 +2953,7 @@ function createOverflow(options = {}) {
2624
2953
  }),
2625
2954
  total,
2626
2955
  isOverflowing: toRef(() => {
2956
+ if (width.value === 0) return false;
2627
2957
  return total.value > width.value - toValue(reserved);
2628
2958
  }),
2629
2959
  measure,
@@ -2776,7 +3106,7 @@ function createProgress(options = {}) {
2776
3106
  }
2777
3107
  for (const [index, element] of clamped.entries()) {
2778
3108
  const ticket = segments.value[index];
2779
- if (!ticket || !isRef(ticket.value)) continue;
3109
+ if (!ticket || !isRef(ticket.value) || isReadonly(ticket.value)) continue;
2780
3110
  ticket.value.value = element;
2781
3111
  }
2782
3112
  }
@@ -3071,8 +3401,8 @@ function createSlider(options = {}) {
3071
3401
  return inverted.value ? 100 - p : p;
3072
3402
  }
3073
3403
  function fromPercent(p) {
3074
- const adjusted = inverted.value ? 100 - p : p;
3075
- return numeric.fromPercent(adjusted);
3404
+ const value = numeric.fromPercent(p);
3405
+ return inverted.value ? numeric.max - (value - numeric.min) : value;
3076
3406
  }
3077
3407
  function register(input) {
3078
3408
  const initialValue = /* @__PURE__ */ isObject(input) ? input.value : input;
@@ -3634,6 +3964,7 @@ function createVirtual(items, _options = {}) {
3634
3964
  /* v8 ignore stop */
3635
3965
  });
3636
3966
  useResizeObserver(element, (entries) => {
3967
+ /* v8 ignore next -- defensive: ResizeObserver always emits at least one entry */
3637
3968
  if (!entries[0]) return;
3638
3969
  viewportHeight.value = entries[0].contentRect.height;
3639
3970
  update();
@@ -3646,6 +3977,7 @@ function createVirtual(items, _options = {}) {
3646
3977
  rebuild();
3647
3978
  }, { immediate: true });
3648
3979
  watch(element, () => {
3980
+ /* v8 ignore next -- defensive: watch fires only when element is present */
3649
3981
  if (!element.value) return;
3650
3982
  if (direction === "reverse" && items.value.length > 0) {
3651
3983
  const lastIndex = items.value.length - 1;
@@ -3664,6 +3996,7 @@ function createVirtual(items, _options = {}) {
3664
3996
  anchorOffset = 0;
3665
3997
  } else if (/* @__PURE__ */ isFunction(anchor)) {
3666
3998
  const result = anchor(items.value);
3999
+ /* v8 ignore next 4 -- function-anchor: tested with valid number return; non-number rejection branch is implicit */
3667
4000
  if (/* @__PURE__ */ isNumber(result)) {
3668
4001
  anchorIndex = result;
3669
4002
  anchorOffset = element.value.scrollTop - (offsets.value[result] || 0);
@@ -3747,8 +4080,10 @@ function createVirtual(items, _options = {}) {
3747
4080
  /* v8 ignore stop */
3748
4081
  }
3749
4082
  function resize(index, height) {
4083
+ /* v8 ignore next -- early bail when height is already correct */
3750
4084
  if (heights.value[index] === height) return;
3751
4085
  heights.value[index] = height;
4086
+ /* v8 ignore next -- first-call only: itemHeight bootstrapped from initial measurement */
3752
4087
  if (!itemHeight.value) itemHeight.value = height;
3753
4088
  if (IN_BROWSER) {
3754
4089
  cancelAnimationFrame(rebuildRaf);
@@ -3765,6 +4100,7 @@ function createVirtual(items, _options = {}) {
3765
4100
  /* v8 ignore stop */
3766
4101
  }
3767
4102
  function scrollTo(index, scrollOptions) {
4103
+ /* v8 ignore next -- defensive: scrollTo only invoked after element mounts */
3768
4104
  if (!element.value) return;
3769
4105
  const targetOffset = offsets.value[index] || 0;
3770
4106
  const behavior = scrollOptions?.behavior ?? "auto";
@@ -4284,11 +4620,11 @@ const [createBreakpointsContext, createBreakpointsPlugin, useBreakpoints] = crea
4284
4620
  *
4285
4621
  * @example Using the built-in Temporal adapter
4286
4622
  * ```ts
4287
- * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
4623
+ * import { V0DateAdapter } from '@vuetify/v0/date'
4288
4624
  * import { createDatePlugin } from '@vuetify/v0'
4289
4625
  *
4290
4626
  * const app = createApp(App)
4291
- * app.use(createDatePlugin({ adapter: new Vuetify0DateAdapter() }))
4627
+ * app.use(createDatePlugin({ adapter: new V0DateAdapter() }))
4292
4628
  *
4293
4629
  * // In a component:
4294
4630
  * const { adapter } = useDate()
@@ -4298,9 +4634,9 @@ const [createBreakpointsContext, createBreakpointsPlugin, useBreakpoints] = crea
4298
4634
  *
4299
4635
  * @example Custom adapter
4300
4636
  * ```ts
4301
- * import type { DateAdapter } from '@vuetify/v0'
4637
+ * import { DateAdapter } from '@vuetify/v0'
4302
4638
  *
4303
- * class DateFnsAdapter implements DateAdapter<Date> {
4639
+ * class DateFnsAdapter extends DateAdapter<Date> {
4304
4640
  * // ... implementation
4305
4641
  * }
4306
4642
  *
@@ -4331,6 +4667,7 @@ const defaultLocales = {
4331
4667
  function deriveFirstDayOfWeek(locale) {
4332
4668
  try {
4333
4669
  const info = new Intl.Locale(locale).getWeekInfo?.();
4670
+ /* v8 ignore next -- defensive: getWeekInfo always returns info in Node 22+ */
4334
4671
  return info ? info.firstDay % 7 : 0;
4335
4672
  } catch {
4336
4673
  return 0;
@@ -4348,14 +4685,14 @@ function deriveFirstDayOfWeek(locale) {
4348
4685
  *
4349
4686
  * @example
4350
4687
  * ```ts
4351
- * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
4688
+ * import { V0DateAdapter } from '@vuetify/v0/date'
4352
4689
  *
4353
- * const { adapter } = createDate({ adapter: new Vuetify0DateAdapter() })
4690
+ * const { adapter } = createDate({ adapter: new V0DateAdapter() })
4354
4691
  * const today = adapter.date()
4355
4692
  *
4356
4693
  * // With locale options
4357
4694
  * const { adapter } = createDate({
4358
- * adapter: new Vuetify0DateAdapter(),
4695
+ * adapter: new V0DateAdapter(),
4359
4696
  * locale: 'de-DE',
4360
4697
  * })
4361
4698
  * ```
@@ -4410,10 +4747,10 @@ function createDate(options) {
4410
4747
  *
4411
4748
  * @example
4412
4749
  * ```ts
4413
- * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
4750
+ * import { V0DateAdapter } from '@vuetify/v0/date'
4414
4751
  *
4415
4752
  * const [useAppDate, provideAppDate] = createDateContext({
4416
- * adapter: new Vuetify0DateAdapter(),
4753
+ * adapter: new V0DateAdapter(),
4417
4754
  * namespace: 'app:date',
4418
4755
  * })
4419
4756
  * ```
@@ -4434,14 +4771,14 @@ function createDateContext(options) {
4434
4771
  *
4435
4772
  * @example
4436
4773
  * ```ts
4437
- * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
4774
+ * import { V0DateAdapter } from '@vuetify/v0/date'
4438
4775
  *
4439
4776
  * const app = createApp(App)
4440
- * app.use(createDatePlugin({ adapter: new Vuetify0DateAdapter() }))
4777
+ * app.use(createDatePlugin({ adapter: new V0DateAdapter() }))
4441
4778
  *
4442
4779
  * // With locale options
4443
4780
  * app.use(createDatePlugin({
4444
- * adapter: new Vuetify0DateAdapter(),
4781
+ * adapter: new V0DateAdapter(),
4445
4782
  * locale: 'de-DE',
4446
4783
  * }))
4447
4784
  * ```
@@ -4475,10 +4812,10 @@ function createDatePlugin(options) {
4475
4812
  * @example
4476
4813
  * ```ts
4477
4814
  * // main.ts
4478
- * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
4815
+ * import { V0DateAdapter } from '@vuetify/v0/date'
4479
4816
  * import { createDatePlugin } from '@vuetify/v0'
4480
4817
  *
4481
- * app.use(createDatePlugin({ adapter: new Vuetify0DateAdapter() }))
4818
+ * app.use(createDatePlugin({ adapter: new V0DateAdapter() }))
4482
4819
  * ```
4483
4820
  *
4484
4821
  * @example
@@ -4492,127 +4829,1016 @@ function createDatePlugin(options) {
4492
4829
  * ```
4493
4830
  */
4494
4831
  function useDate(namespace = "v0:date") {
4495
- if (!/* @__PURE__ */ instanceExists()) throw new Error("[v0] useDate() must be called inside a Vue component with createDatePlugin installed.\n\nExample:\n import { Vuetify0DateAdapter } from '@vuetify/v0/date'\n import { createDatePlugin } from '@vuetify/v0'\n\n app.use(createDatePlugin({ adapter: new Vuetify0DateAdapter() }))");
4832
+ if (!/* @__PURE__ */ instanceExists()) throw new Error("[v0] useDate() must be called inside a Vue component with createDatePlugin installed.\n\nExample:\n import { V0DateAdapter } from '@vuetify/v0/date'\n import { createDatePlugin } from '@vuetify/v0'\n\n app.use(createDatePlugin({ adapter: new V0DateAdapter() }))");
4496
4833
  return useContext(namespace);
4497
4834
  }
4498
4835
  //#endregion
4499
- //#region src/composables/useHotkey/aliases.ts
4836
+ //#region src/composables/useDelay/index.ts
4500
4837
  /**
4501
- * Key alias mapping for consistent key normalization across the hotkey system.
4838
+ * @module useDelay
4502
4839
  *
4503
- * Maps user-friendly aliases to canonical key names that match
4504
- * KeyboardEvent.key values (in lowercase) where possible.
4840
+ * @see https://0.vuetifyjs.com/composables/system/use-delay
4841
+ *
4842
+ * @remarks
4843
+ * Schedule open and close transitions with configurable delays.
4844
+ *
4845
+ * Composes `useTimer` for the underlying lifecycle (start / stop / pause / resume,
4846
+ * plus reactive `isActive`, `isPaused`, `remaining`) and adds direction tracking
4847
+ * (`isOpening`) and promise-based resolution.
4848
+ *
4849
+ * Key features:
4850
+ * - Reactive `openDelay` and `closeDelay` via `MaybeRefOrGetter`
4851
+ * - `start(isOpening)` returns a `Promise<boolean>` that resolves once the delay elapses
4852
+ * - Per-call `minDelay` floor enforces a minimum delay
4853
+ * - Automatic cleanup on scope disposal (timer cleared, pending promise settled with current direction)
4854
+ * - SSR-safe
4855
+ *
4856
+ * @example
4857
+ * ```ts
4858
+ * import { useDelay } from '@vuetify/v0'
4859
+ *
4860
+ * const delay = useDelay(isOpening => isVisible.value = isOpening, {
4861
+ * openDelay: 300,
4862
+ * closeDelay: 200,
4863
+ * })
4864
+ *
4865
+ * delay.start(true)
4866
+ * delay.start(false, { minDelay: 500 })
4867
+ * ```
4505
4868
  */
4506
- const keyAliasMap = {
4507
- control: "ctrl",
4508
- command: "cmd",
4509
- option: "alt",
4510
- up: "arrowup",
4511
- down: "arrowdown",
4512
- left: "arrowleft",
4513
- right: "arrowright",
4514
- esc: "escape",
4515
- spacebar: " ",
4516
- space: " ",
4517
- return: "enter",
4518
- del: "delete",
4519
- plus: "+",
4520
- slash: "/",
4521
- underscore: "_",
4522
- minus: "-",
4523
- hyphen: "-"
4524
- };
4525
- /* @__NO_SIDE_EFFECTS__ */
4526
- function normalizeKey(key) {
4527
- const lowerKey = key.toLowerCase();
4528
- return keyAliasMap[lowerKey] ?? lowerKey;
4529
- }
4530
- //#endregion
4531
- //#region src/composables/useHotkey/parsing.ts
4532
- const logger = /* @__PURE__ */ useLogger();
4533
- const MODIFIERS = [
4534
- "ctrl",
4535
- "shift",
4536
- "alt",
4537
- "meta",
4538
- "cmd"
4539
- ];
4540
4869
  /**
4541
- * Splits a single combination string into individual key parts.
4870
+ * Schedule open and close transitions with configurable delays.
4542
4871
  *
4543
- * A combination is a set of keys that must be pressed simultaneously.
4544
- * e.g. `ctrl+k`, `shift+-`
4872
+ * Composes `useTimer` for the lifecycle (`start` / `stop` / `pause` / `resume` plus
4873
+ * reactive `isActive`, `isPaused`, `remaining`) and adds direction tracking
4874
+ * (`isOpening`) and promise-based resolution.
4545
4875
  *
4546
- * @param combination - The combination string to parse
4547
- * @param isInternal - Whether this is an internal call (suppresses warnings)
4548
- * @returns Object with keys array and separators array
4876
+ * @param callback Optional callback invoked once a started delay elapses; receives the resolved direction.
4877
+ * @param options Reactive delay settings.
4878
+ * @returns Lifecycle controls and reactive state.
4879
+ *
4880
+ * @see https://0.vuetifyjs.com/composables/system/use-delay
4881
+ *
4882
+ * @example
4883
+ * ```ts
4884
+ * const delay = useDelay(isOpening => isVisible.value = isOpening, {
4885
+ * openDelay: 300,
4886
+ * closeDelay: 200,
4887
+ * })
4888
+ *
4889
+ * delay.start(true)
4890
+ * delay.start(false, { minDelay: 500 })
4891
+ * delay.pause()
4892
+ * delay.resume()
4893
+ * delay.stop()
4894
+ * ```
4549
4895
  */
4550
- function splitKeyCombination(combination, isInternal = false) {
4551
- const emptyResult = {
4552
- keys: [],
4553
- separators: []
4554
- };
4555
- if (!combination) {
4556
- if (!isInternal) logger.warn("Invalid hotkey combination: empty string provided");
4557
- return emptyResult;
4558
- }
4559
- if (combination.length > 1 && [
4560
- "+",
4561
- "/",
4562
- "_"
4563
- ].some((v) => combination.startsWith(v)) && ![
4564
- "++",
4565
- "//",
4566
- "__"
4567
- ].some((v) => combination.startsWith(v)) || combination.includes("++") || combination.includes("//") || combination.includes("__") || combination === "+" || combination === "/" || combination === "_" || combination.length > 1 && (combination.endsWith("+") || combination.endsWith("/") || combination.endsWith("_")) && combination.at(-2) !== combination.at(-1) || combination === "++" || combination === "--" || combination === "__") {
4568
- if (!isInternal) logger.warn(`Invalid hotkey combination: "${combination}" has invalid structure`);
4569
- return emptyResult;
4570
- }
4571
- const keys = [];
4572
- const separators = [];
4573
- let buffer = "";
4574
- function flushBuffer(separator) {
4575
- if (buffer) {
4576
- if (separator) separators.push(separator);
4577
- keys.push(/* @__PURE__ */ normalizeKey(buffer));
4578
- buffer = "";
4579
- }
4580
- }
4581
- for (let i = 0; i < combination.length; i++) {
4582
- const char = combination[i];
4583
- const nextChar = combination[i + 1];
4584
- if ([
4585
- "+",
4586
- "/",
4587
- "_",
4588
- "-"
4589
- ].includes(char))
4590
- /* v8 ignore start -- edge case: doubled special chars */
4591
- if (char === nextChar) {
4592
- flushBuffer(char);
4593
- keys.push(char);
4594
- i++;
4595
- } else if ([
4596
- "+",
4597
- "/",
4598
- "_"
4599
- ].includes(char)) flushBuffer(char);
4600
- else buffer += char;
4601
- else buffer += char;
4896
+ function useDelay(callback, options = {}) {
4897
+ const { openDelay, closeDelay } = options;
4898
+ const isOpening = shallowRef(false);
4899
+ let minDelay = 0;
4900
+ let resolver;
4901
+ function fire() {
4902
+ const direction = isOpening.value;
4903
+ const resolve = resolver;
4904
+ resolver = void 0;
4905
+ resolve?.(direction);
4906
+ callback?.(direction);
4907
+ }
4908
+ function duration() {
4909
+ const raw = isOpening.value ? toValue(openDelay) : toValue(closeDelay);
4910
+ return Math.max(minDelay, raw ?? 0);
4911
+ }
4912
+ const timer = useTimer(fire, { duration });
4913
+ function start(direction, options = {}) {
4914
+ const previous = resolver;
4915
+ resolver = void 0;
4916
+ isOpening.value = direction;
4917
+ minDelay = options.minDelay ?? 0;
4918
+ previous?.(direction);
4919
+ const ms = duration();
4920
+ return new Promise((resolve) => {
4921
+ resolver = resolve;
4922
+ if (ms <= 0) {
4923
+ timer.stop();
4924
+ fire();
4925
+ return;
4926
+ }
4927
+ timer.start();
4928
+ });
4602
4929
  }
4603
- flushBuffer();
4604
- /* v8 ignore start -- edge case: invalid minus in combination */
4605
- if (keys.some((key) => key.length > 1 && key.includes("-") && key !== "--")) {
4606
- if (!isInternal) logger.warn(`Invalid hotkey combination: "${combination}" has invalid structure`);
4607
- return emptyResult;
4930
+ function stop() {
4931
+ const resolve = resolver;
4932
+ resolver = void 0;
4933
+ timer.stop();
4934
+ resolve?.(isOpening.value);
4608
4935
  }
4609
- if (keys.length === 0 && combination) return {
4610
- keys: [/* @__PURE__ */ normalizeKey(combination)],
4611
- separators
4612
- };
4613
- /* v8 ignore stop */
4936
+ onScopeDispose(() => {
4937
+ const resolve = resolver;
4938
+ resolver = void 0;
4939
+ resolve?.(isOpening.value);
4940
+ }, true);
4614
4941
  return {
4615
- keys,
4942
+ start,
4943
+ stop,
4944
+ pause: timer.pause,
4945
+ resume: timer.resume,
4946
+ isActive: shallowReadonly(timer.isActive),
4947
+ isPaused: shallowReadonly(timer.isPaused),
4948
+ remaining: shallowReadonly(timer.remaining),
4949
+ isOpening: shallowReadonly(isOpening)
4950
+ };
4951
+ }
4952
+ //#endregion
4953
+ //#region src/composables/useMutationObserver/index.ts
4954
+ /**
4955
+ * @module useMutationObserver
4956
+ *
4957
+ * @see https://0.vuetifyjs.com/composables/system/use-mutation-observer
4958
+ *
4959
+ * @remarks
4960
+ * MutationObserver composable with lifecycle management.
4961
+ *
4962
+ * Key features:
4963
+ * - MutationObserver API wrapper
4964
+ * - `isActive` reactive flag
4965
+ * - `once` option for single-fire observation
4966
+ * - Pause/resume/stop functionality
4967
+ * - Automatic cleanup on unmount
4968
+ * - SSR-safe (checks SUPPORTS_MUTATION_OBSERVER)
4969
+ * - Hydration-aware
4970
+ * - Configurable observation options (childList, attributes, characterData, etc.)
4971
+ *
4972
+ * Perfect for detecting DOM changes and responding to mutations.
4973
+ *
4974
+ * @example
4975
+ * ```ts
4976
+ * import { useTemplateRef } from 'vue'
4977
+ * import { useMutationObserver } from '@vuetify/v0'
4978
+ *
4979
+ * const target = useTemplateRef('target')
4980
+ * useMutationObserver(target, records => {
4981
+ * console.log(records)
4982
+ * }, { childList: true })
4983
+ * ```
4984
+ */
4985
+ /**
4986
+ * A composable that uses the Mutation Observer API to detect changes in the DOM.
4987
+ *
4988
+ * @param target The element to observe.
4989
+ * @param callback The callback to execute when a mutation is observed.
4990
+ * @param options The options for the Mutation Observer.
4991
+ * @returns An object with methods to control the observer.
4992
+ *
4993
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
4994
+ * @see https://0.vuetifyjs.com/composables/system/use-mutation-observer
4995
+ *
4996
+ * @example
4997
+ * ```ts
4998
+ * import { ref } from 'vue'
4999
+ * import { useMutationObserver } from '@vuetify/v0'
5000
+ *
5001
+ * const container = ref<HTMLElement>()
5002
+ *
5003
+ * const { pause, resume, isPaused } = useMutationObserver(
5004
+ * container,
5005
+ * (mutations) => {
5006
+ * mutations.forEach((mutation) => {
5007
+ * if (mutation.type === 'childList') {
5008
+ * console.log('Children changed:', mutation.addedNodes, mutation.removedNodes)
5009
+ * } else if (mutation.type === 'attributes') {
5010
+ * console.log('Attribute changed:', mutation.attributeName)
5011
+ * }
5012
+ * })
5013
+ * },
5014
+ * {
5015
+ * childList: true,
5016
+ * attributes: true,
5017
+ * subtree: true
5018
+ * }
5019
+ * )
5020
+ *
5021
+ * // Pause observation
5022
+ * pause()
5023
+ *
5024
+ * // Resume observation
5025
+ * resume()
5026
+ * ```
5027
+ */
5028
+ function useMutationObserver(target, callback, options = {}) {
5029
+ const observerOptions = {
5030
+ childList: options.childList ?? true,
5031
+ attributes: options.attributes ?? false,
5032
+ characterData: options.characterData ?? false,
5033
+ subtree: options.subtree ?? false,
5034
+ attributeOldValue: options.attributeOldValue ?? false,
5035
+ characterDataOldValue: options.characterDataOldValue ?? false,
5036
+ attributeFilter: options.attributeFilter
5037
+ };
5038
+ return createObserver(target, callback, {
5039
+ supports: SUPPORTS_MUTATION_OBSERVER,
5040
+ once: options.once,
5041
+ create: (cb) => new MutationObserver((mutations) => {
5042
+ cb(mutations.map((m) => ({
5043
+ type: m.type,
5044
+ target: m.target,
5045
+ addedNodes: m.addedNodes,
5046
+ removedNodes: m.removedNodes,
5047
+ previousSibling: m.previousSibling,
5048
+ nextSibling: m.nextSibling,
5049
+ attributeName: m.attributeName,
5050
+ attributeNamespace: m.attributeNamespace,
5051
+ oldValue: m.oldValue
5052
+ })));
5053
+ }),
5054
+ observe: (obs, el) => obs.observe(el, observerOptions),
5055
+ immediate: options.immediate ? (el) => {
5056
+ const emptyNodeList = {
5057
+ length: 0,
5058
+ item: () => null,
5059
+ forEach: () => {},
5060
+ *[Symbol.iterator]() {}
5061
+ };
5062
+ return [{
5063
+ type: "childList",
5064
+ target: el,
5065
+ addedNodes: emptyNodeList,
5066
+ removedNodes: emptyNodeList,
5067
+ previousSibling: null,
5068
+ nextSibling: null,
5069
+ attributeName: null,
5070
+ attributeNamespace: null,
5071
+ oldValue: null
5072
+ }];
5073
+ } : void 0,
5074
+ onceIncludesImmediate: true
5075
+ });
5076
+ }
5077
+ //#endregion
5078
+ //#region src/composables/useDragDrop/adapters/adapter.ts
5079
+ /**
5080
+ * @module useDragDrop/adapters/adapter
5081
+ *
5082
+ * @remarks
5083
+ * Adapter contract and abstract base for useDragDrop. Concrete adapters extend
5084
+ * `DragDropAdapter<Z>` to share the `cleanup` field, `dispose()` lifecycle, and
5085
+ * the `locate()` DOM-walk helper.
5086
+ */
5087
+ /**
5088
+ * Abstract base class for useDragDrop adapters. Provides a `cleanup` slot, a
5089
+ * `dispose()` lifecycle, and a `locate()` helper that walks the DOM to find the
5090
+ * draggable ticket whose `el` ancestors a given event target.
5091
+ *
5092
+ * @example
5093
+ * ```ts
5094
+ * import { DragDropAdapter, type DragDropAdapterContext } from '@vuetify/v0'
5095
+ *
5096
+ * class MyAdapter extends DragDropAdapter {
5097
+ * setup (context: DragDropAdapterContext) {
5098
+ * const stop = useEventListener(document, 'mousedown', event => {
5099
+ * const ticket = this.locate(event.target, context)
5100
+ * if (ticket) context.emit.start(ticket, { x: event.clientX, y: event.clientY }, 'pointer')
5101
+ * })
5102
+ * this.cleanup = () => stop()
5103
+ * }
5104
+ * }
5105
+ * ```
5106
+ */
5107
+ var DragDropAdapter = class {
5108
+ cleanup = null;
5109
+ dispose() {
5110
+ this.cleanup?.();
5111
+ this.cleanup = null;
5112
+ }
5113
+ locate(target, context) {
5114
+ let element = /* @__PURE__ */ isElement(target) ? target : null;
5115
+ while (element) {
5116
+ for (const ticket of context.draggables.values()) if (ticket.el.value === element) return ticket;
5117
+ element = element.parentElement;
5118
+ }
5119
+ return null;
5120
+ }
5121
+ };
5122
+ //#endregion
5123
+ //#region src/composables/useDragDrop/adapters/pointer.ts
5124
+ /**
5125
+ * @module useDragDrop/adapters/pointer
5126
+ *
5127
+ * @remarks
5128
+ * Default pointer adapter for useDragDrop. Listens for `pointerdown` /
5129
+ * `pointermove` / `pointerup` / `pointercancel` on the document and emits the
5130
+ * drag lifecycle. Activation distance is configurable via `threshold`.
5131
+ */
5132
+ /**
5133
+ * Default pointer adapter. Wires `pointerdown` / `pointermove` / `pointerup` /
5134
+ * `pointercancel` on the document and converts them into drag lifecycle calls.
5135
+ *
5136
+ * @example
5137
+ * ```ts
5138
+ * import { PointerAdapter, useDragDrop } from '@vuetify/v0'
5139
+ *
5140
+ * useDragDrop({ adapters: [new PointerAdapter({ threshold: 10 })] })
5141
+ * ```
5142
+ */
5143
+ var PointerAdapter = class extends DragDropAdapter {
5144
+ threshold;
5145
+ constructor(options = {}) {
5146
+ super();
5147
+ this.threshold = options.threshold ?? 0;
5148
+ }
5149
+ setup(context) {
5150
+ const logger = useLogger();
5151
+ if (this.cleanup) {
5152
+ logger.warn("PointerAdapter setup called twice; previous registration will be replaced");
5153
+ this.dispose();
5154
+ }
5155
+ if (!IN_BROWSER) return;
5156
+ let downSource = null;
5157
+ let downOrigin = null;
5158
+ let downId = null;
5159
+ let started = false;
5160
+ function reset() {
5161
+ downSource = null;
5162
+ downOrigin = null;
5163
+ downId = null;
5164
+ started = false;
5165
+ }
5166
+ const onDown = (event) => {
5167
+ if (event.button !== 0) return;
5168
+ if (downSource) return;
5169
+ const ticket = this.locate(event.target, context);
5170
+ if (!ticket) return;
5171
+ downSource = ticket;
5172
+ downOrigin = {
5173
+ x: event.clientX,
5174
+ y: event.clientY
5175
+ };
5176
+ downId = event.pointerId;
5177
+ started = false;
5178
+ };
5179
+ const onMove = (event) => {
5180
+ if (!downSource || !downOrigin || event.pointerId !== downId) return;
5181
+ const point = {
5182
+ x: event.clientX,
5183
+ y: event.clientY
5184
+ };
5185
+ if (!started) {
5186
+ const dx = point.x - downOrigin.x;
5187
+ const dy = point.y - downOrigin.y;
5188
+ if (this.threshold > 0 && Math.hypot(dx, dy) < this.threshold) return;
5189
+ context.emit.start(downSource, downOrigin, "pointer");
5190
+ started = true;
5191
+ }
5192
+ context.emit.move(point);
5193
+ };
5194
+ function onUp(event) {
5195
+ if (event.pointerId !== downId) return;
5196
+ if (downSource && started) context.emit.drop();
5197
+ reset();
5198
+ }
5199
+ function onCancel(event) {
5200
+ if (event.pointerId !== downId) return;
5201
+ if (downSource && started) context.emit.cancel();
5202
+ reset();
5203
+ }
5204
+ const stopDown = useEventListener(document, "pointerdown", onDown);
5205
+ const stopMove = useEventListener(document, "pointermove", onMove);
5206
+ const stopUp = useEventListener(document, "pointerup", onUp);
5207
+ const stopCancel = useEventListener(document, "pointercancel", onCancel);
5208
+ this.cleanup = () => {
5209
+ stopDown();
5210
+ stopMove();
5211
+ stopUp();
5212
+ stopCancel();
5213
+ };
5214
+ }
5215
+ };
5216
+ //#endregion
5217
+ //#region src/composables/useDragDrop/adapters/keyboard.ts
5218
+ /**
5219
+ * @module useDragDrop/adapters/keyboard
5220
+ *
5221
+ * @remarks
5222
+ * Default keyboard adapter for useDragDrop. Listens for `keydown` on the
5223
+ * document. Activation keys (default Space and Enter) start and drop drags;
5224
+ * arrow keys nudge the drag point by `step` px; Escape cancels.
5225
+ */
5226
+ function isEditable(element) {
5227
+ if (/* @__PURE__ */ isNull(element)) return false;
5228
+ const tag = element.tagName;
5229
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
5230
+ if (element.isContentEditable) return true;
5231
+ const role = element.getAttribute("role");
5232
+ return role === "textbox" || role === "searchbox" || role === "combobox" || role === "spinbutton";
5233
+ }
5234
+ /**
5235
+ * Default keyboard adapter. Listens for `keydown` on the document; activation
5236
+ * keys start/drop drags, arrow keys nudge the drag point, Escape cancels.
5237
+ *
5238
+ * @example
5239
+ * ```ts
5240
+ * import { KeyboardAdapter, useDragDrop } from '@vuetify/v0'
5241
+ *
5242
+ * useDragDrop({ adapters: [new KeyboardAdapter({ activate: ['x'], step: 32 })] })
5243
+ * ```
5244
+ */
5245
+ var KeyboardAdapter = class extends DragDropAdapter {
5246
+ activate;
5247
+ step;
5248
+ constructor(options = {}) {
5249
+ super();
5250
+ this.activate = options.activate ?? [" ", "Enter"];
5251
+ this.step = options.step ?? 16;
5252
+ }
5253
+ setup(context) {
5254
+ const logger = useLogger();
5255
+ if (this.cleanup) {
5256
+ logger.warn("KeyboardAdapter setup called twice; previous registration will be replaced");
5257
+ this.dispose();
5258
+ }
5259
+ if (!IN_BROWSER) return;
5260
+ const onKeyDown = (event) => {
5261
+ const focused = document.activeElement;
5262
+ if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) return;
5263
+ if (isEditable(focused)) return;
5264
+ const ticket = this.locate(focused, context);
5265
+ const isActive = !/* @__PURE__ */ isNull(context.active.value);
5266
+ if (this.activate.includes(event.key)) {
5267
+ if (isActive) {
5268
+ event.preventDefault();
5269
+ context.emit.drop();
5270
+ } else if (ticket) {
5271
+ const el = ticket.el.value;
5272
+ if (!el) {
5273
+ logger.warn("KeyboardAdapter: cannot start drag — element is not mounted");
5274
+ return;
5275
+ }
5276
+ event.preventDefault();
5277
+ const rect = el.getBoundingClientRect();
5278
+ context.emit.start(ticket, {
5279
+ x: rect.left + rect.width / 2,
5280
+ y: rect.top + rect.height / 2
5281
+ }, "keyboard");
5282
+ }
5283
+ return;
5284
+ }
5285
+ if (event.key === "Escape" && isActive) {
5286
+ event.preventDefault();
5287
+ context.emit.cancel();
5288
+ return;
5289
+ }
5290
+ if (isActive && event.key.startsWith("Arrow")) {
5291
+ const active = context.active.value;
5292
+ if (/* @__PURE__ */ isNull(active)) return;
5293
+ const current = active.current;
5294
+ let point = null;
5295
+ switch (event.key) {
5296
+ case "ArrowUp":
5297
+ point = {
5298
+ x: current.x,
5299
+ y: current.y - this.step
5300
+ };
5301
+ break;
5302
+ case "ArrowDown":
5303
+ point = {
5304
+ x: current.x,
5305
+ y: current.y + this.step
5306
+ };
5307
+ break;
5308
+ case "ArrowLeft":
5309
+ point = {
5310
+ x: current.x - this.step,
5311
+ y: current.y
5312
+ };
5313
+ break;
5314
+ case "ArrowRight":
5315
+ point = {
5316
+ x: current.x + this.step,
5317
+ y: current.y
5318
+ };
5319
+ break;
5320
+ }
5321
+ if (point) {
5322
+ event.preventDefault();
5323
+ context.emit.move(point);
5324
+ }
5325
+ }
5326
+ };
5327
+ const stop = useEventListener(document, "keydown", onKeyDown);
5328
+ this.cleanup = () => stop();
5329
+ }
5330
+ };
5331
+ //#endregion
5332
+ //#region src/composables/useDragDrop/index.ts
5333
+ /**
5334
+ * @module useDragDrop
5335
+ *
5336
+ * @see https://0.vuetifyjs.com/composables/system/use-drag-drop
5337
+ *
5338
+ * @remarks
5339
+ * Headless drag-and-drop primitive. Owns two registries (draggables and zones)
5340
+ * plus the active-drag state. Pointer and keyboard adapters ship by default;
5341
+ * lifecycle hooks and a plugin array provide extension points.
5342
+ *
5343
+ * @example
5344
+ * ```ts
5345
+ * import { useDragDrop } from '@vuetify/v0'
5346
+ *
5347
+ * const dnd = useDragDrop()
5348
+ * ```
5349
+ */
5350
+ /**
5351
+ * Pure math for resolving where in an oriented zone a pointer would drop.
5352
+ * Internal — consumers rely on the `position.index` / `position.indicator`
5353
+ * fields delivered to `onDrop`.
5354
+ */
5355
+ function resolveDropPosition(point, rects, orientation) {
5356
+ if (rects.length === 0) return null;
5357
+ const axis = orientation === "vertical" ? "y" : "x";
5358
+ const start = orientation === "vertical" ? "top" : "left";
5359
+ const end = orientation === "vertical" ? "bottom" : "right";
5360
+ const coord = point[axis];
5361
+ if (coord < rects[0][start]) return {
5362
+ index: 0,
5363
+ edge: "before",
5364
+ rect: rects[0]
5365
+ };
5366
+ const last = rects.at(-1);
5367
+ if (coord > last[end]) return {
5368
+ index: rects.length,
5369
+ edge: "after",
5370
+ rect: last
5371
+ };
5372
+ for (const [index, rect] of rects.entries()) if (coord >= rect[start] && coord <= rect[end]) {
5373
+ if (coord < rect[start] + (rect[end] - rect[start]) / 2) return {
5374
+ index,
5375
+ edge: "before",
5376
+ rect
5377
+ };
5378
+ return {
5379
+ index: index + 1,
5380
+ edge: "after",
5381
+ rect
5382
+ };
5383
+ }
5384
+ for (let index = 0; index < rects.length - 1; index++) {
5385
+ const a = rects[index];
5386
+ const b = rects[index + 1];
5387
+ if (coord > a[end] && coord < b[start]) {
5388
+ if (coord <= (a[end] + b[start]) / 2) return {
5389
+ index: index + 1,
5390
+ edge: "after",
5391
+ rect: a
5392
+ };
5393
+ return {
5394
+ index: index + 1,
5395
+ edge: "before",
5396
+ rect: b
5397
+ };
5398
+ }
5399
+ }
5400
+ return {
5401
+ index: rects.length,
5402
+ edge: "after",
5403
+ rect: last
5404
+ };
5405
+ }
5406
+ /**
5407
+ * Create a headless drag-and-drop context.
5408
+ *
5409
+ * @param options - Adapters, plugins, and lifecycle hooks. All optional.
5410
+ * @returns A `DragDropContext<Z>` exposing `draggables` / `zones` registries,
5411
+ * the `active` drag, the `isDragging` boolean, and a programmatic `cancel()`.
5412
+ *
5413
+ * @see https://0.vuetifyjs.com/composables/system/use-drag-drop
5414
+ *
5415
+ * @example
5416
+ * ```ts
5417
+ * import { useDragDrop } from '@vuetify/v0'
5418
+ * import { useTemplateRef } from 'vue'
5419
+ *
5420
+ * const dnd = useDragDrop<{ type: 'card', value: string }>()
5421
+ *
5422
+ * const draggable = useTemplateRef<HTMLElement>('draggable')
5423
+ * const dropzone = useTemplateRef<HTMLElement>('dropzone')
5424
+ *
5425
+ * dnd.draggables.register({ el: draggable, type: 'card', value: 'card-1' })
5426
+ * dnd.zones.register({
5427
+ * el: dropzone,
5428
+ * accept: ['card'],
5429
+ * onDrop: (drag, position) => console.log(drag.value, position.index),
5430
+ * })
5431
+ * ```
5432
+ */
5433
+ function useDragDrop(options = {}) {
5434
+ const { plugins = [] } = options;
5435
+ const logger = useLogger();
5436
+ const _draggables = createRegistry({ events: true });
5437
+ const _zones = createRegistry({ events: true });
5438
+ const active = shallowRef(null);
5439
+ const isDragging = toRef(() => !/* @__PURE__ */ isNull(active.value));
5440
+ const scopes = /* @__PURE__ */ new Map();
5441
+ const nodes = /* @__PURE__ */ new Map();
5442
+ function next(fn, ...args) {
5443
+ if (/* @__PURE__ */ isUndefined(fn)) return;
5444
+ try {
5445
+ fn(...args);
5446
+ } catch (error) {
5447
+ logger.error("useDragDrop hook threw", error);
5448
+ }
5449
+ }
5450
+ function safeAccept(accept, drag) {
5451
+ try {
5452
+ if (/* @__PURE__ */ isNull(drag)) return false;
5453
+ if (/* @__PURE__ */ isUndefined(accept)) return true;
5454
+ if (/* @__PURE__ */ isArray(accept)) return accept.includes(drag.type);
5455
+ const result = accept(drag);
5456
+ if (/* @__PURE__ */ isThenable(result)) {
5457
+ logger.warn("useDragDrop accept predicate returned a thenable; async predicates are not supported — treating as reject");
5458
+ return false;
5459
+ }
5460
+ return Boolean(result);
5461
+ } catch (error) {
5462
+ logger.error("useDragDrop accept predicate threw; treating as reject", error);
5463
+ return false;
5464
+ }
5465
+ }
5466
+ const draggables = {
5467
+ ..._draggables,
5468
+ register(registration) {
5469
+ const id = registration.id ?? /* @__PURE__ */ useId$1();
5470
+ const el = toRef(() => toValue(registration.el));
5471
+ const dragging = toRef(() => active.value?.id === id);
5472
+ const input = {
5473
+ ...registration,
5474
+ id,
5475
+ el,
5476
+ isDragging: dragging
5477
+ };
5478
+ return _draggables.register(input);
5479
+ }
5480
+ };
5481
+ const zones = {
5482
+ ..._zones,
5483
+ register(registration) {
5484
+ const id = registration.id ?? /* @__PURE__ */ useId$1();
5485
+ const el = toRef(() => toValue(registration.el));
5486
+ const isOver = toRef(() => active.value?.over === id);
5487
+ const willAccept = toRef(() => safeAccept(registration.accept, active.value));
5488
+ const rects = shallowRef([]);
5489
+ function refresh() {
5490
+ const zoneEl = el.value;
5491
+ rects.value = zoneEl ? Array.from(zoneEl.children).map((child) => child.getBoundingClientRect()) : [];
5492
+ }
5493
+ const scope = effectScope();
5494
+ scopes.set(id, scope);
5495
+ scope.run(() => {
5496
+ if (registration.orientation) {
5497
+ watch(el, refresh, {
5498
+ immediate: true,
5499
+ flush: "post"
5500
+ });
5501
+ useResizeObserver(el, refresh);
5502
+ useMutationObserver(el, refresh, { childList: true });
5503
+ watch(() => active.value?.id, (id) => {
5504
+ if (id) refresh();
5505
+ });
5506
+ }
5507
+ watch(el, (current) => {
5508
+ if (/* @__PURE__ */ isNull(current)) return;
5509
+ nodes.set(current, id);
5510
+ onWatcherCleanup(() => nodes.delete(current));
5511
+ }, { immediate: true });
5512
+ });
5513
+ const indicator = computed(() => {
5514
+ if (!registration.orientation || !isOver.value || !active.value) return null;
5515
+ return resolveDropPosition(active.value.current, rects.value, registration.orientation);
5516
+ });
5517
+ const input = {
5518
+ ...registration,
5519
+ id,
5520
+ el,
5521
+ isOver,
5522
+ willAccept,
5523
+ indicator
5524
+ };
5525
+ return _zones.register(input);
5526
+ }
5527
+ };
5528
+ function at(point) {
5529
+ if (!IN_BROWSER) return null;
5530
+ let element = document.elementFromPoint(point.x, point.y);
5531
+ while (element) {
5532
+ const id = nodes.get(element);
5533
+ if (!/* @__PURE__ */ isUndefined(id)) {
5534
+ const zone = _zones.get(id);
5535
+ if (zone && !toValue(zone.disabled)) return id;
5536
+ }
5537
+ element = element.parentElement;
5538
+ }
5539
+ return null;
5540
+ }
5541
+ function position(zoneId, drag) {
5542
+ const zone = _zones.get(zoneId);
5543
+ const out = { pointer: drag.current };
5544
+ if (zone?.orientation) {
5545
+ const resolved = zone.indicator?.value ?? null;
5546
+ if (resolved) {
5547
+ out.index = resolved.index;
5548
+ out.indicator = resolved;
5549
+ } else out.index = 0;
5550
+ }
5551
+ return out;
5552
+ }
5553
+ function bail(drag, reason = "cancel") {
5554
+ active.value = null;
5555
+ if (!/* @__PURE__ */ isNull(drag.over)) next(_zones.get(drag.over)?.onLeave, drag);
5556
+ next(_draggables.get(drag.id)?.onCancel, drag, reason);
5557
+ next(options.onCancel, drag, reason);
5558
+ }
5559
+ function onStart(source, origin, via) {
5560
+ if (toValue(_draggables.get(source.id)?.disabled)) return;
5561
+ const draft = {
5562
+ id: source.id,
5563
+ type: source.type,
5564
+ value: source.value,
5565
+ origin,
5566
+ current: origin,
5567
+ delta: {
5568
+ x: 0,
5569
+ y: 0
5570
+ },
5571
+ over: null,
5572
+ willAccept: false,
5573
+ via
5574
+ };
5575
+ try {
5576
+ if (_draggables.get(source.id)?.onBeforeStart?.(draft) === false) return;
5577
+ } catch (error) {
5578
+ logger.error("useDragDrop onBeforeStart threw; treating as veto", error);
5579
+ return;
5580
+ }
5581
+ try {
5582
+ if (options.onBeforeStart?.(draft) === false) return;
5583
+ } catch (error) {
5584
+ logger.error("useDragDrop onBeforeStart threw; treating as veto", error);
5585
+ return;
5586
+ }
5587
+ active.value = draft;
5588
+ }
5589
+ function onMove(point) {
5590
+ if (!active.value) return;
5591
+ const over = at(point);
5592
+ const draft = {
5593
+ ...active.value,
5594
+ current: point,
5595
+ delta: {
5596
+ x: point.x - active.value.origin.x,
5597
+ y: point.y - active.value.origin.y
5598
+ },
5599
+ over,
5600
+ willAccept: false
5601
+ };
5602
+ draft.willAccept = !/* @__PURE__ */ isNull(over) && safeAccept(_zones.get(over)?.accept, draft);
5603
+ const previous = active.value.over;
5604
+ active.value = draft;
5605
+ if (previous !== over) {
5606
+ if (!/* @__PURE__ */ isNull(previous)) next(_zones.get(previous)?.onLeave, draft);
5607
+ if (!/* @__PURE__ */ isNull(over)) next(_zones.get(over)?.onEnter, draft);
5608
+ }
5609
+ next(_draggables.get(draft.id)?.onMove, draft);
5610
+ next(options.onMove, draft);
5611
+ }
5612
+ function onDrop() {
5613
+ if (!active.value) return;
5614
+ const drag = active.value;
5615
+ const zoneId = drag.over;
5616
+ if (/* @__PURE__ */ isNull(zoneId)) {
5617
+ bail(drag);
5618
+ return;
5619
+ }
5620
+ const dropAt = position(zoneId, drag);
5621
+ const zone = _zones.get(zoneId);
5622
+ function vetoed(fn, label) {
5623
+ if (!fn) return false;
5624
+ try {
5625
+ return fn(drag, dropAt) === false;
5626
+ } catch (error) {
5627
+ logger.error(`useDragDrop ${label} threw; treating as veto`, error);
5628
+ return true;
5629
+ }
5630
+ }
5631
+ if (vetoed(zone?.onBeforeDrop, "zone onBeforeDrop") || vetoed(options.onBeforeDrop, "options onBeforeDrop")) {
5632
+ bail(drag, "reject");
5633
+ return;
5634
+ }
5635
+ active.value = null;
5636
+ next(zone?.onDrop, drag, dropAt);
5637
+ next(options.onDrop, drag, dropAt);
5638
+ }
5639
+ function cancel() {
5640
+ if (!active.value) return;
5641
+ bail(active.value);
5642
+ }
5643
+ const context = {
5644
+ draggables,
5645
+ zones,
5646
+ active: shallowReadonly(active),
5647
+ isDragging,
5648
+ cancel
5649
+ };
5650
+ const disposers = [];
5651
+ const adapters = options.adapters ?? [new PointerAdapter(), new KeyboardAdapter()];
5652
+ const emit = {
5653
+ start: (source, origin, via) => onStart(source, origin, via),
5654
+ move: (point) => onMove(point),
5655
+ drop: () => onDrop(),
5656
+ cancel: () => cancel()
5657
+ };
5658
+ const adapterContext = {
5659
+ ...context,
5660
+ emit
5661
+ };
5662
+ for (const adapter of adapters) try {
5663
+ adapter.setup(adapterContext);
5664
+ disposers.push(() => adapter.dispose());
5665
+ } catch (error) {
5666
+ logger.error("useDragDrop adapter setup threw; skipping adapter", error);
5667
+ try {
5668
+ adapter.dispose();
5669
+ } catch (disposeError) {
5670
+ logger.error("useDragDrop adapter dispose threw after setup failure", disposeError);
5671
+ }
5672
+ }
5673
+ for (const plugin of plugins) try {
5674
+ const dispose = plugin(context);
5675
+ if (/* @__PURE__ */ isFunction(dispose)) disposers.push(dispose);
5676
+ } catch (error) {
5677
+ logger.error("useDragDrop plugin install threw; skipping plugin", error);
5678
+ }
5679
+ _draggables.on("unregister:ticket", (ticket) => {
5680
+ if (active.value?.id === ticket.id) cancel();
5681
+ });
5682
+ _zones.on("unregister:ticket", (ticket) => {
5683
+ const scope = scopes.get(ticket.id);
5684
+ if (scope) {
5685
+ scope.stop();
5686
+ scopes.delete(ticket.id);
5687
+ }
5688
+ if (active.value?.over === ticket.id) {
5689
+ next(ticket.onLeave, active.value);
5690
+ active.value = {
5691
+ ...active.value,
5692
+ over: null,
5693
+ willAccept: false
5694
+ };
5695
+ }
5696
+ });
5697
+ onScopeDispose(() => {
5698
+ if (!/* @__PURE__ */ isNull(active.value)) cancel();
5699
+ for (const dispose of disposers.toReversed()) try {
5700
+ dispose();
5701
+ } catch (error) {
5702
+ logger.error("useDragDrop disposer threw", error);
5703
+ }
5704
+ for (const scope of scopes.values()) try {
5705
+ scope.stop();
5706
+ } catch (error) {
5707
+ logger.error("useDragDrop zone scope dispose threw", error);
5708
+ }
5709
+ scopes.clear();
5710
+ nodes.clear();
5711
+ try {
5712
+ _draggables.dispose();
5713
+ } catch (error) {
5714
+ logger.error("useDragDrop draggables dispose threw", error);
5715
+ }
5716
+ try {
5717
+ _zones.dispose();
5718
+ } catch (error) {
5719
+ logger.error("useDragDrop zones dispose threw", error);
5720
+ }
5721
+ }, true);
5722
+ return context;
5723
+ }
5724
+ //#endregion
5725
+ //#region src/composables/useHotkey/aliases.ts
5726
+ /**
5727
+ * Key alias mapping for consistent key normalization across the hotkey system.
5728
+ *
5729
+ * Maps user-friendly aliases to canonical key names that match
5730
+ * KeyboardEvent.key values (in lowercase) where possible.
5731
+ */
5732
+ const keyAliasMap = {
5733
+ control: "ctrl",
5734
+ command: "cmd",
5735
+ option: "alt",
5736
+ up: "arrowup",
5737
+ down: "arrowdown",
5738
+ left: "arrowleft",
5739
+ right: "arrowright",
5740
+ esc: "escape",
5741
+ spacebar: " ",
5742
+ space: " ",
5743
+ return: "enter",
5744
+ del: "delete",
5745
+ plus: "+",
5746
+ slash: "/",
5747
+ underscore: "_",
5748
+ minus: "-",
5749
+ hyphen: "-"
5750
+ };
5751
+ /* @__NO_SIDE_EFFECTS__ */
5752
+ function normalizeKey(key) {
5753
+ const lowerKey = key.toLowerCase();
5754
+ return keyAliasMap[lowerKey] ?? lowerKey;
5755
+ }
5756
+ //#endregion
5757
+ //#region src/composables/useHotkey/parsing.ts
5758
+ const logger = /* @__PURE__ */ useLogger();
5759
+ const MODIFIERS = [
5760
+ "ctrl",
5761
+ "shift",
5762
+ "alt",
5763
+ "meta",
5764
+ "cmd"
5765
+ ];
5766
+ /**
5767
+ * Splits a single combination string into individual key parts.
5768
+ *
5769
+ * A combination is a set of keys that must be pressed simultaneously.
5770
+ * e.g. `ctrl+k`, `shift+-`
5771
+ *
5772
+ * @param combination - The combination string to parse
5773
+ * @param isInternal - Whether this is an internal call (suppresses warnings)
5774
+ * @returns Object with keys array and separators array
5775
+ */
5776
+ function splitKeyCombination(combination, isInternal = false) {
5777
+ const emptyResult = {
5778
+ keys: [],
5779
+ separators: []
5780
+ };
5781
+ if (!combination) {
5782
+ if (!isInternal) logger.warn("Invalid hotkey combination: empty string provided");
5783
+ return emptyResult;
5784
+ }
5785
+ if (combination.length > 1 && [
5786
+ "+",
5787
+ "/",
5788
+ "_"
5789
+ ].some((v) => combination.startsWith(v)) && ![
5790
+ "++",
5791
+ "//",
5792
+ "__"
5793
+ ].some((v) => combination.startsWith(v)) || combination.includes("++") || combination.includes("//") || combination.includes("__") || combination === "+" || combination === "/" || combination === "_" || combination.length > 1 && (combination.endsWith("+") || combination.endsWith("/") || combination.endsWith("_")) && combination.at(-2) !== combination.at(-1) || combination === "++" || combination === "--" || combination === "__") {
5794
+ if (!isInternal) logger.warn(`Invalid hotkey combination: "${combination}" has invalid structure`);
5795
+ return emptyResult;
5796
+ }
5797
+ const keys = [];
5798
+ const separators = [];
5799
+ let buffer = "";
5800
+ function flushBuffer(separator) {
5801
+ if (buffer) {
5802
+ if (separator) separators.push(separator);
5803
+ keys.push(/* @__PURE__ */ normalizeKey(buffer));
5804
+ buffer = "";
5805
+ }
5806
+ }
5807
+ for (let i = 0; i < combination.length; i++) {
5808
+ const char = combination[i];
5809
+ const nextChar = combination[i + 1];
5810
+ if ([
5811
+ "+",
5812
+ "/",
5813
+ "_",
5814
+ "-"
5815
+ ].includes(char))
5816
+ /* v8 ignore start -- edge case: doubled special chars */
5817
+ if (char === nextChar) {
5818
+ flushBuffer(char);
5819
+ keys.push(char);
5820
+ i++;
5821
+ } else if ([
5822
+ "+",
5823
+ "/",
5824
+ "_"
5825
+ ].includes(char)) flushBuffer(char);
5826
+ else buffer += char;
5827
+ else buffer += char;
5828
+ }
5829
+ flushBuffer();
5830
+ /* v8 ignore start -- edge case: invalid minus in combination */
5831
+ if (keys.some((key) => key.length > 1 && key.includes("-") && key !== "--")) {
5832
+ if (!isInternal) logger.warn(`Invalid hotkey combination: "${combination}" has invalid structure`);
5833
+ return emptyResult;
5834
+ }
5835
+ if (keys.length === 0 && combination) return {
5836
+ keys: [/* @__PURE__ */ normalizeKey(combination)],
5837
+ separators
5838
+ };
5839
+ /* v8 ignore stop */
5840
+ return {
5841
+ keys,
4616
5842
  separators
4617
5843
  };
4618
5844
  }
@@ -4756,9 +5982,11 @@ function useHotkey(keys, callback, options = {}, _platform) {
4756
5982
  let isSequence = false;
4757
5983
  let groupIndex = 0;
4758
5984
  function isInputFocused() {
5985
+ /* v8 ignore next -- IN_BROWSER always true in happy-dom test env */
4759
5986
  if (!IN_BROWSER) return false;
4760
5987
  if (toValue(inputs)) return false;
4761
5988
  const activeElement = document.activeElement;
5989
+ /* v8 ignore next -- defensive: document.activeElement is always set in happy-dom */
4762
5990
  if (!activeElement) return false;
4763
5991
  const role = activeElement.getAttribute("role");
4764
5992
  if (role === "textbox" || role === "searchbox" || role === "combobox" || role === "spinbutton") return true;
@@ -4812,6 +6040,7 @@ function useHotkey(keys, callback, options = {}, _platform) {
4812
6040
  const currentKeys = toValue(keys);
4813
6041
  if (!currentKeys) return;
4814
6042
  const groups = splitKeySequence(currentKeys.toLowerCase());
6043
+ /* v8 ignore next -- defensive: splitKeySequence rejects empty strings earlier */
4815
6044
  if (groups.length === 0) return;
4816
6045
  isSequence = groups.length > 1;
4817
6046
  keyGroups = groups;
@@ -4864,6 +6093,7 @@ function isModifier(key) {
4864
6093
  function parseKeyGroup(group) {
4865
6094
  const { keys: parts } = splitKeyCombination(group.toLowerCase());
4866
6095
  const modifiers = Object.fromEntries(MODIFIERS.map((m) => [m, false]));
6096
+ /* v8 ignore next 3 -- defensive: parseKeyGroup is not invoked with empty parts */
4867
6097
  if (parts.length === 0) return {
4868
6098
  modifiers,
4869
6099
  actualKey: void 0
@@ -5089,11 +6319,13 @@ function useIntersectionObserver(target, callback, options = {}) {
5089
6319
  observe: (obs, el) => obs.observe(el),
5090
6320
  onEntry: (entries) => {
5091
6321
  const latest = entries.at(-1);
6322
+ /* v8 ignore next -- defensive: IntersectionObserver always emits at least one entry */
5092
6323
  if (latest) isIntersecting.value = latest.isIntersecting;
5093
6324
  },
5094
6325
  onPause: () => {
5095
6326
  isIntersecting.value = false;
5096
6327
  },
6328
+ /* v8 ignore next -- defensive nullish coalesce: entries always populated when shouldStop fires */
5097
6329
  shouldStop: (entries) => entries.at(-1)?.isIntersecting ?? false,
5098
6330
  immediate: options.immediate ? (el) => {
5099
6331
  return [{
@@ -5143,6 +6375,7 @@ function useElementIntersection(target, options = {}) {
5143
6375
  const intersectionRatio = shallowRef(0);
5144
6376
  const { pause: _pause, resume, stop, isActive, isPaused } = useIntersectionObserver(target, (entries) => {
5145
6377
  const entry = entries.at(-1);
6378
+ /* v8 ignore next 4 -- defensive: IntersectionObserver always emits at least one entry */
5146
6379
  if (entry) {
5147
6380
  isIntersecting.value = entry.isIntersecting;
5148
6381
  intersectionRatio.value = entry.intersectionRatio;
@@ -5392,131 +6625,6 @@ function usePrefersContrast() {
5392
6625
  return useMediaQuery("(prefers-contrast: more)");
5393
6626
  }
5394
6627
  //#endregion
5395
- //#region src/composables/useMutationObserver/index.ts
5396
- /**
5397
- * @module useMutationObserver
5398
- *
5399
- * @see https://0.vuetifyjs.com/composables/system/use-mutation-observer
5400
- *
5401
- * @remarks
5402
- * MutationObserver composable with lifecycle management.
5403
- *
5404
- * Key features:
5405
- * - MutationObserver API wrapper
5406
- * - `isActive` reactive flag
5407
- * - `once` option for single-fire observation
5408
- * - Pause/resume/stop functionality
5409
- * - Automatic cleanup on unmount
5410
- * - SSR-safe (checks SUPPORTS_MUTATION_OBSERVER)
5411
- * - Hydration-aware
5412
- * - Configurable observation options (childList, attributes, characterData, etc.)
5413
- *
5414
- * Perfect for detecting DOM changes and responding to mutations.
5415
- *
5416
- * @example
5417
- * ```ts
5418
- * import { useTemplateRef } from 'vue'
5419
- * import { useMutationObserver } from '@vuetify/v0'
5420
- *
5421
- * const target = useTemplateRef('target')
5422
- * useMutationObserver(target, records => {
5423
- * console.log(records)
5424
- * }, { childList: true })
5425
- * ```
5426
- */
5427
- /**
5428
- * A composable that uses the Mutation Observer API to detect changes in the DOM.
5429
- *
5430
- * @param target The element to observe.
5431
- * @param callback The callback to execute when a mutation is observed.
5432
- * @param options The options for the Mutation Observer.
5433
- * @returns An object with methods to control the observer.
5434
- *
5435
- * @see https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
5436
- * @see https://0.vuetifyjs.com/composables/system/use-mutation-observer
5437
- *
5438
- * @example
5439
- * ```ts
5440
- * import { ref } from 'vue'
5441
- * import { useMutationObserver } from '@vuetify/v0'
5442
- *
5443
- * const container = ref<HTMLElement>()
5444
- *
5445
- * const { pause, resume, isPaused } = useMutationObserver(
5446
- * container,
5447
- * (mutations) => {
5448
- * mutations.forEach((mutation) => {
5449
- * if (mutation.type === 'childList') {
5450
- * console.log('Children changed:', mutation.addedNodes, mutation.removedNodes)
5451
- * } else if (mutation.type === 'attributes') {
5452
- * console.log('Attribute changed:', mutation.attributeName)
5453
- * }
5454
- * })
5455
- * },
5456
- * {
5457
- * childList: true,
5458
- * attributes: true,
5459
- * subtree: true
5460
- * }
5461
- * )
5462
- *
5463
- * // Pause observation
5464
- * pause()
5465
- *
5466
- * // Resume observation
5467
- * resume()
5468
- * ```
5469
- */
5470
- function useMutationObserver(target, callback, options = {}) {
5471
- const observerOptions = {
5472
- childList: options.childList ?? true,
5473
- attributes: options.attributes ?? false,
5474
- characterData: options.characterData ?? false,
5475
- subtree: options.subtree ?? false,
5476
- attributeOldValue: options.attributeOldValue ?? false,
5477
- characterDataOldValue: options.characterDataOldValue ?? false,
5478
- attributeFilter: options.attributeFilter
5479
- };
5480
- return createObserver(target, callback, {
5481
- supports: SUPPORTS_MUTATION_OBSERVER,
5482
- once: options.once,
5483
- create: (cb) => new MutationObserver((mutations) => {
5484
- cb(mutations.map((m) => ({
5485
- type: m.type,
5486
- target: m.target,
5487
- addedNodes: m.addedNodes,
5488
- removedNodes: m.removedNodes,
5489
- previousSibling: m.previousSibling,
5490
- nextSibling: m.nextSibling,
5491
- attributeName: m.attributeName,
5492
- attributeNamespace: m.attributeNamespace,
5493
- oldValue: m.oldValue
5494
- })));
5495
- }),
5496
- observe: (obs, el) => obs.observe(el, observerOptions),
5497
- immediate: options.immediate ? (el) => {
5498
- const emptyNodeList = {
5499
- length: 0,
5500
- item: () => null,
5501
- forEach: () => {},
5502
- *[Symbol.iterator]() {}
5503
- };
5504
- return [{
5505
- type: "childList",
5506
- target: el,
5507
- addedNodes: emptyNodeList,
5508
- removedNodes: emptyNodeList,
5509
- previousSibling: null,
5510
- nextSibling: null,
5511
- attributeName: null,
5512
- attributeNamespace: null,
5513
- oldValue: null
5514
- }];
5515
- } : void 0,
5516
- onceIncludesImmediate: true
5517
- });
5518
- }
5519
- //#endregion
5520
6628
  //#region src/composables/usePresence/index.ts
5521
6629
  /**
5522
6630
  * @module usePresence
@@ -5900,8 +7008,11 @@ function useRovingFocus(items, options = {}) {
5900
7008
  };
5901
7009
  }
5902
7010
  //#endregion
7011
+ //#region src/composables/useRtl/adapters/adapter.ts
7012
+ var RtlAdapter = class {};
7013
+ //#endregion
5903
7014
  //#region src/composables/useRtl/adapters/v0.ts
5904
- var Vuetify0RtlAdapter = class {
7015
+ var V0RtlAdapter = class extends RtlAdapter {
5905
7016
  setup(app, context, target) {
5906
7017
  if (IN_BROWSER) {
5907
7018
  if (/* @__PURE__ */ isNull(target)) return;
@@ -5969,7 +7080,7 @@ function createRtlFallback() {
5969
7080
  }
5970
7081
  const [createRtlContext, createRtlPlugin, useRtl] = createPluginContext("v0:rtl", (options) => createRtl(options), {
5971
7082
  fallback: () => createRtlFallback(),
5972
- setup: (context, app, { adapter = new Vuetify0RtlAdapter(), target }) => {
7083
+ setup: (context, app, { adapter = new V0RtlAdapter(), target }) => {
5973
7084
  adapter.setup(app, context, target);
5974
7085
  },
5975
7086
  persist: (ctx) => ctx.isRtl.value,
@@ -6261,7 +7372,7 @@ function useStack(namespace = "v0:stack") {
6261
7372
  * ```
6262
7373
  */
6263
7374
  function createStorage(options = {}) {
6264
- const { adapter = IN_BROWSER ? window.localStorage : new MemoryAdapter(), prefix = "v0:", serializer = {
7375
+ const { adapter = IN_BROWSER ? window.localStorage : new MemoryStorageAdapter(), prefix = "v0:", serializer = {
6265
7376
  read: JSON.parse,
6266
7377
  write: JSON.stringify
6267
7378
  }, ttl } = options;
@@ -6276,9 +7387,9 @@ function createStorage(options = {}) {
6276
7387
  if (/* @__PURE__ */ isNullOrUndefined(raw)) return void 0;
6277
7388
  try {
6278
7389
  const parsed = serializer.read(raw);
6279
- if (ttl && /* @__PURE__ */ isObject(parsed) && "__v0" in parsed && "__v" in parsed) {
7390
+ if (/* @__PURE__ */ isObject(parsed) && "__v0" in parsed && "__v" in parsed) {
6280
7391
  const envelope = parsed;
6281
- if (Date.now() - envelope.__t > ttl) {
7392
+ if (ttl && Date.now() - envelope.__t > ttl) {
6282
7393
  adapter?.removeItem(prefixedKey);
6283
7394
  return;
6284
7395
  }
@@ -6358,7 +7469,7 @@ function createStorage(options = {}) {
6358
7469
  };
6359
7470
  }
6360
7471
  function createStorageFallback() {
6361
- return createStorage({ adapter: new MemoryAdapter() });
7472
+ return createStorage({ adapter: new MemoryStorageAdapter() });
6362
7473
  }
6363
7474
  const [createStorageContext, createStoragePlugin, useStorage] = createPluginContext("v0:storage", (options) => createStorage(options), { fallback: () => createStorageFallback() });
6364
7475
  //#endregion
@@ -6675,6 +7786,7 @@ function useClickOutside(target, handler, options = {}) {
6675
7786
  x: 0,
6676
7787
  y: 0
6677
7788
  };
7789
+ let startedOutsideBounds = false;
6678
7790
  let cleanupPointerDown;
6679
7791
  let cleanupPointerUp;
6680
7792
  let cleanupBlur;
@@ -6704,6 +7816,7 @@ function useClickOutside(target, handler, options = {}) {
6704
7816
  * Check if an element matches resolved ignore targets.
6705
7817
  */
6706
7818
  function isIgnored(el, selectors, elements) {
7819
+ /* v8 ignore next -- defensive guard, callers always pass an Element */
6707
7820
  if (!el) return false;
6708
7821
  for (const selector of selectors) try {
6709
7822
  if (el.matches(selector) || !/* @__PURE__ */ isNull(el.closest(selector))) return true;
@@ -6730,6 +7843,7 @@ function useClickOutside(target, handler, options = {}) {
6730
7843
  const targets = getTargets();
6731
7844
  if (targets.length === 0) return false;
6732
7845
  return targets.every((el) => {
7846
+ /* v8 ignore next -- defensive: getTargets filters to Element-typed values */
6733
7847
  if (/* @__PURE__ */ isNullOrUndefined(el) || !/* @__PURE__ */ isFunction(el.contains)) return false;
6734
7848
  return el !== eventTarget && !el.contains(eventTarget);
6735
7849
  });
@@ -6739,6 +7853,7 @@ function useClickOutside(target, handler, options = {}) {
6739
7853
  */
6740
7854
  function isOutsideBounds(x, y) {
6741
7855
  const targets = getTargets();
7856
+ /* v8 ignore next -- edge case: bounds check only invoked when targets exist */
6742
7857
  if (targets.length === 0) return false;
6743
7858
  return targets.every((el) => {
6744
7859
  const { left, right, top, bottom } = el.getBoundingClientRect();
@@ -6749,6 +7864,7 @@ function useClickOutside(target, handler, options = {}) {
6749
7864
  * Validate that the target is still in the DOM.
6750
7865
  */
6751
7866
  function isValidTarget(eventTarget) {
7867
+ /* v8 ignore next -- type guard for non-Element targets (e.g., document, window) */
6752
7868
  if (!/* @__PURE__ */ isElement(eventTarget)) return false;
6753
7869
  if (!eventTarget.isConnected) return false;
6754
7870
  return true;
@@ -6765,6 +7881,7 @@ function useClickOutside(target, handler, options = {}) {
6765
7881
  x: event.clientX,
6766
7882
  y: event.clientY
6767
7883
  };
7884
+ startedOutsideBounds = bounds ? isOutsideBounds(event.clientX, event.clientY) : false;
6768
7885
  }
6769
7886
  /**
6770
7887
  * Handle pointerup - check if it's an outside click.
@@ -6783,7 +7900,7 @@ function useClickOutside(target, handler, options = {}) {
6783
7900
  const dy = Math.abs(event.clientY - startPosition.y);
6784
7901
  if (dx >= touchScrollThreshold || dy >= touchScrollThreshold) return;
6785
7902
  }
6786
- if ((bounds ? isOutsideBounds(startPosition.x, startPosition.y) && isOutsideBounds(event.clientX, event.clientY) : isOutside(pointerdownTarget) && isOutside(pointerupTarget)) && !shouldIgnore(path)) handler(event);
7903
+ if ((bounds ? startedOutsideBounds && isOutsideBounds(event.clientX, event.clientY) : isOutside(pointerdownTarget) && isOutside(pointerupTarget)) && !shouldIgnore(path)) handler(event);
6787
7904
  }
6788
7905
  /**
6789
7906
  * Handle window blur - detect focus moving to iframe.
@@ -6840,4 +7957,4 @@ function useClickOutside(target, handler, options = {}) {
6840
7957
  };
6841
7958
  }
6842
7959
  //#endregion
6843
- export { createRating as $, useElementIntersection as A, ClientAdapter as At, createBreakpointsPlugin as B, usePresence as C, createForm as Ct, usePrefersDark as D, createComboboxContext as Dt, usePrefersContrast as E, createCombobox as Et, createDateContext as F, useBreadcrumbs as Ft, useVirtual as G, toReactive as H, createDatePlugin as I, useTimeline as J, createTimeline as K, useDate as L, useImage as M, usePopover as Mt, useHotkey as N, createBreadcrumbs as Nt, usePrefersReducedMotion as O, useCombobox as Ot, createDate as P, createBreadcrumbsContext as Pt, createSlider as Q, createBreakpoints as R, useProxyModel as S, createValidation as St, useMediaQuery as T, useForm as Tt, createVirtual as U, useBreakpoints as V, createVirtualContext as W, createStepContext as X, createStep as Y, useStep as Z, createRtlPlugin as _, createNumeric as _t, useToggleScope as a, createOverflow as at, useRovingFocus as b, useNested as bt, createStoragePlugin as c, useElementSize as ct, createStackContext as d, createFallbackHydration as dt, createRatingContext as et, createStackPlugin as f, createHydration as ft, createRtlFallback as g, createNumberField as gt, createRtlContext as h, useHydration as ht, useWindowEventListener as i, useProgress as it, useIntersectionObserver as j, useVirtualFocus as jt, useLazy as k, ServerAdapter as kt, useStorage as l, useResizeObserver as lt, createRtl as m, createHydrationPlugin as mt, useDocumentEventListener as n, createProgress as nt, createStorage as o, createOverflowContext as ot, useStack as p, createHydrationContext as pt, createTimelineContext as q, useEventListener as r, createProgressContext as rt, createStorageContext as s, useOverflow as st, useClickOutside as t, useRating as tt, createStack as u, toElement as ut, useRtl as v, createNested as vt, useMutationObserver as w, createFormContext as wt, useRaf as x, createInput as xt, Vuetify0RtlAdapter as y, createNestedContext as yt, createBreakpointsContext as z };
7960
+ export { useTimeline as $, useIntersectionObserver as A, createForm as At, createDateContext as B, usePopover as Bt, usePresence as C, createNested as Ct, usePrefersReducedMotion as D, createSortable as Dt, usePrefersDark as E, createKanban as Et, PointerAdapter as F, useCombobox as Ft, createBreakpointsPlugin as G, useDate as H, createBreadcrumbsContext as Ht, DragDropAdapter as I, ServerComboboxAdapter as It, createVirtual as J, useBreakpoints as K, useMutationObserver as L, ClientComboboxAdapter as Lt, useHotkey as M, useForm as Mt, useDragDrop as N, createCombobox as Nt, useLazy as O, createInput as Ot, KeyboardAdapter as P, createComboboxContext as Pt, createTimelineContext as Q, useDelay as R, ComboboxAdapter as Rt, useProxyModel as S, createNumeric as St, usePrefersContrast as T, useNested as Tt, createBreakpoints as U, useBreadcrumbs as Ut, createDatePlugin as V, createBreadcrumbs as Vt, createBreakpointsContext as W, useVirtual as X, createVirtualContext as Y, createTimeline as Z, createRtlPlugin as _, createHydration as _t, useToggleScope as a, createRatingContext as at, useRovingFocus as b, useHydration as bt, createStoragePlugin as c, createProgressContext as ct, createStackContext as d, createOverflowContext as dt, createStep as et, createStackPlugin as f, useOverflow as ft, createRtlFallback as g, createFallbackHydration as gt, createRtlContext as h, toElement as ht, useWindowEventListener as i, createRating as it, useImage as j, createFormContext as jt, useElementIntersection as k, createValidation as kt, useStorage as l, useProgress as lt, createRtl as m, useResizeObserver as mt, useDocumentEventListener as n, useStep as nt, createStorage as o, useRating as ot, useStack as p, useElementSize as pt, toReactive as q, useEventListener as r, createSlider as rt, createStorageContext as s, createProgress as st, useClickOutside as t, createStepContext as tt, createStack as u, createOverflow as ut, useRtl as v, createHydrationContext as vt, useMediaQuery as w, createNestedContext as wt, useRaf as x, createNumberField as xt, V0RtlAdapter as y, createHydrationPlugin as yt, createDate as z, useVirtualFocus as zt };