@vuetify/v0 1.0.0-alpha.3 → 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 (169) hide show
  1. package/README.md +3 -0
  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 +1510 -388
  17. package/dist/components/index.d.mts +2 -2
  18. package/dist/components/index.mjs +1 -1
  19. package/dist/{components-C4jI-78d.mjs → components-BWxMSWr7.mjs} +138 -90
  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-iODP6aKB.mjs → createDataTable-DDr2yY-A.mjs} +30 -29
  27. package/dist/{createGroup-CGk6hY_Y.mjs → createGroup-BSGtfBUE.mjs} +3 -3
  28. package/dist/{createRegistry-BXzdXAjq.mjs → createRegistry-Db_juawd.mjs} +78 -41
  29. package/dist/{createSelection-B3HJEPNg.mjs → createSelection-BeKZMxha.mjs} +3 -3
  30. package/dist/{createSingle-Dql8mRi-.mjs → createSingle-Dms2efxr.mjs} +1 -1
  31. package/dist/{createTokens-DgFYsOrv.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-zEjWBKCr.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-B76iGRE3.d.mts → index-BfpLkDR_.d.mts} +949 -59
  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-DNDuacmH.d.mts → index-jQPkwo3G.d.mts} +15 -5
  61. package/dist/index.d.mts +23 -23
  62. package/dist/index.mjs +35 -33
  63. package/dist/json/attributes.json +1 -1
  64. package/dist/json/web-types.json +2 -2
  65. package/dist/launchdarkly-BJR4vbbV.d.mts +13 -0
  66. package/dist/{launchdarkly-BKtW-idu.mjs → launchdarkly-CLGtJCsI.mjs} +6 -4
  67. package/dist/locale/adapters/index.d.mts +3 -3
  68. package/dist/locale/adapters/index.mjs +4 -3
  69. package/dist/locale/adapters/v0/index.d.mts +2 -2
  70. package/dist/locale/adapters/v0/index.mjs +2 -2
  71. package/dist/locale/adapters/vue-i18n/index.d.mts +3 -3
  72. package/dist/locale/adapters/vue-i18n/index.mjs +4 -2
  73. package/dist/locale/index.d.mts +4 -4
  74. package/dist/locale/index.mjs +4 -3
  75. package/dist/logger/adapters/consola/index.d.mts +1 -1
  76. package/dist/logger/adapters/consola/index.mjs +1 -1
  77. package/dist/logger/adapters/index.d.mts +5 -5
  78. package/dist/logger/adapters/index.mjs +6 -5
  79. package/dist/logger/adapters/pino/index.d.mts +1 -1
  80. package/dist/logger/adapters/pino/index.mjs +1 -1
  81. package/dist/logger/adapters/v0/index.d.mts +2 -2
  82. package/dist/logger/adapters/v0/index.mjs +2 -2
  83. package/dist/logger/index.d.mts +6 -6
  84. package/dist/logger/index.mjs +6 -5
  85. package/dist/{memory-CeSQE-QV.d.mts → memory-B0w39kno.d.mts} +9 -9
  86. package/dist/{memory-oYBvtkgx.mjs → memory-BMpJcQjQ.mjs} +13 -3
  87. package/dist/notifications/index.d.mts +30 -4
  88. package/dist/notifications/index.mjs +187 -126
  89. package/dist/palettes/ant/index.d.mts +1 -1
  90. package/dist/palettes/index.d.mts +3 -3
  91. package/dist/palettes/leonardo/generate/index.mjs +3 -0
  92. package/dist/palettes/material/index.d.mts +1 -1
  93. package/dist/palettes/md1/index.d.mts +1 -1
  94. package/dist/palettes/md2/index.d.mts +1 -1
  95. package/dist/palettes/radix/index.d.mts +1 -1
  96. package/dist/palettes/tailwind/index.d.mts +1 -1
  97. package/dist/permissions/adapters/index.d.mts +2 -2
  98. package/dist/permissions/adapters/index.mjs +3 -3
  99. package/dist/permissions/adapters/v0/index.d.mts +2 -2
  100. package/dist/permissions/adapters/v0/index.mjs +2 -2
  101. package/dist/permissions/index.d.mts +2 -2
  102. package/dist/permissions/index.mjs +3 -3
  103. package/dist/{pino-9L79n6y6.mjs → pino-CIcYlW3u.mjs} +3 -1
  104. package/dist/{pino-DNfCEb0p.d.mts → pino-NmR7X0s4.d.mts} +2 -2
  105. package/dist/{posthog-DmuyycaR.mjs → posthog-B3go2aCh.mjs} +7 -5
  106. package/dist/posthog-CQntUXFO.d.mts +13 -0
  107. package/dist/rules/index.d.mts +1 -1
  108. package/dist/rules/index.mjs +1 -1
  109. package/dist/{server-3ilWFwqe.d.mts → server-DQK305lc.d.mts} +5 -5
  110. package/dist/{server-y7zLSCSx.mjs → server-ZFXbgQoY.mjs} +4 -3
  111. package/dist/storage/adapters/index.d.mts +3 -3
  112. package/dist/storage/adapters/index.mjs +2 -2
  113. package/dist/storage/adapters/memory/index.d.mts +2 -2
  114. package/dist/storage/adapters/memory/index.mjs +2 -2
  115. package/dist/storage/index.d.mts +4 -4
  116. package/dist/storage/index.mjs +3 -3
  117. package/dist/theme/adapters/index.d.mts +2 -2
  118. package/dist/theme/adapters/index.mjs +4 -4
  119. package/dist/theme/adapters/unhead/index.d.mts +1 -1
  120. package/dist/theme/adapters/unhead/index.mjs +1 -1
  121. package/dist/theme/adapters/v0/index.d.mts +1 -1
  122. package/dist/theme/adapters/v0/index.mjs +1 -1
  123. package/dist/theme/index.d.mts +1 -1
  124. package/dist/theme/index.mjs +3 -3
  125. package/dist/types/index.d.mts +1 -1
  126. package/dist/{unhead-B-I2OS6i.mjs → unhead-dPyVkgiJ.mjs} +2 -2
  127. package/dist/{useClickOutside-CY4U2J9W.mjs → useClickOutside-B8naChaQ.mjs} +1224 -228
  128. package/dist/{useFeatures-BPOke6iP.mjs → useFeatures-x2iKp0C8.mjs} +7 -10
  129. package/dist/{useLocale-BV8e3imF.mjs → useLocale-BUms59Bw.mjs} +6 -6
  130. package/dist/{useLogger-DvdRTrqC.mjs → useLogger-DPSSa-YR.mjs} +4 -4
  131. package/dist/{useNotifications-vSJE6z6t.mjs → useNotifications-BPTmZWQy.mjs} +4 -4
  132. package/dist/{usePermissions-D3zPwKQc.mjs → usePermissions-CTi-3pBk.mjs} +6 -6
  133. package/dist/{useRules-BT6cgoTj.mjs → useRules-DNhljkyO.mjs} +2 -2
  134. package/dist/{useTheme-cUZb2A9O.mjs → useTheme-3vW3kFNg.mjs} +5 -5
  135. package/dist/utilities/index.d.mts +2 -2
  136. package/dist/utilities/index.mjs +26 -2
  137. package/dist/{v0-DrOub-e1.d.mts → v0-CRGArJim.d.mts} +4 -4
  138. package/dist/{v0-C49OI_wc.mjs → v0-CotC1PiS.mjs} +5 -3
  139. package/dist/{v0-Dq3zhexO.mjs → v0-DAlKpCR8.mjs} +3 -4
  140. package/dist/{v0-D7Fm2C3Y.d.mts → v0-DDELiSI6.d.mts} +3 -3
  141. package/dist/v0-DYsCJPIu.d.mts +8 -0
  142. package/dist/{v0-gpExNexS.mjs → v0-DadOcxFI.mjs} +2 -2
  143. package/dist/{v0-Bmow1q_x.mjs → v0-Dvu8fMl3.mjs} +3 -3
  144. package/dist/{v0-CHZpnMeP.mjs → v0-YJQoTLsp.mjs} +6 -4
  145. package/dist/virtual-BqCRw0ly.d.mts +8 -0
  146. package/dist/{virtual-C0yHf1DC.mjs → virtual-Dnno49F8.mjs} +3 -4
  147. package/package.json +1 -1
  148. package/dist/adapter-58IvIfAZ.d.mts +0 -96
  149. package/dist/adapter-BvWEd9w-.d.mts +0 -17
  150. package/dist/adapter-Dhq68tz0.d.mts +0 -11
  151. package/dist/adapters-BSiqsTZN.mjs +0 -3
  152. package/dist/adapters-C3QXhf4I.mjs +0 -2
  153. package/dist/adapters-CMJGA1k7.mjs +0 -2
  154. package/dist/adapters-Dse4bPxk.mjs +0 -5
  155. package/dist/adapters-_ZqxCD8f.mjs +0 -4
  156. package/dist/createPagination-CHF7SmmK.mjs +0 -247
  157. package/dist/index-1XdASTjp.d.mts +0 -13
  158. package/dist/index-C6rdXZ7j.d.mts +0 -11
  159. package/dist/index-Dp1HjBxJ.d.mts +0 -13
  160. package/dist/v0-CdusXjY_.d.mts +0 -8
  161. package/dist/virtual-D4fckewC.d.mts +0 -8
  162. /package/dist/{adapter-Be9zUc-n.mjs → adapter-oM18AdUc.mjs} +0 -0
  163. /package/dist/{htmlElements-D4oMQZPJ.mjs → htmlElements-CHXZfsZu.mjs} +0 -0
  164. /package/dist/{index-CFhUdAHM.d.mts → index-CKKoxA9V.d.mts} +0 -0
  165. /package/dist/{index-DuddF-NR.d.mts → index-CLR1QMM3.d.mts} +0 -0
  166. /package/dist/{index-BgCkepP_.d.mts → index-DJQwYeyx.d.mts} +0 -0
  167. /package/dist/{index-CROz1VSV.d.mts → index-DWfPppPU.d.mts} +0 -0
  168. /package/dist/{index-DTvqw22G.d.mts → index-H_hl6-4b.d.mts} +0 -0
  169. /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-zEjWBKCr.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-DvdRTrqC.mjs";
5
- import { t as createRegistry } from "./createRegistry-BXzdXAjq.mjs";
6
- import { i as createModel, t as createSelection } from "./createSelection-B3HJEPNg.mjs";
7
- import { t as createSingle } from "./createSingle-Dql8mRi-.mjs";
8
- import "./createTokens-DgFYsOrv.mjs";
9
- import { a as useLocale } from "./useLocale-BV8e3imF.mjs";
10
- import { c as useTimer } from "./useNotifications-vSJE6z6t.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-CGk6hY_Y.mjs";
14
- import "./createPagination-CHF7SmmK.mjs";
15
- import "./createDataTable-iODP6aKB.mjs";
16
- import { a as useRules, o as isStandardSchema } from "./useRules-BT6cgoTj.mjs";
17
- import "./useFeatures-BPOke6iP.mjs";
18
- import "./usePermissions-D3zPwKQc.mjs";
19
- import { t as MemoryAdapter } from "./memory-oYBvtkgx.mjs";
20
- import "./useTheme-cUZb2A9O.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
  });
@@ -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
@@ -2468,6 +2794,7 @@ function useElementSize(target) {
2468
2794
  const height = shallowRef(0);
2469
2795
  const { pause: _pause, resume, stop, isActive, isPaused } = useResizeObserver(target, (entries) => {
2470
2796
  const entry = entries[0];
2797
+ /* v8 ignore next 4 -- defensive: ResizeObserver always passes an entry array with at least one entry */
2471
2798
  if (entry) {
2472
2799
  width.value = entry.contentRect.width;
2473
2800
  height.value = entry.contentRect.height;
@@ -2626,6 +2953,7 @@ function createOverflow(options = {}) {
2626
2953
  }),
2627
2954
  total,
2628
2955
  isOverflowing: toRef(() => {
2956
+ if (width.value === 0) return false;
2629
2957
  return total.value > width.value - toValue(reserved);
2630
2958
  }),
2631
2959
  measure,
@@ -2778,7 +3106,7 @@ function createProgress(options = {}) {
2778
3106
  }
2779
3107
  for (const [index, element] of clamped.entries()) {
2780
3108
  const ticket = segments.value[index];
2781
- if (!ticket || !isRef(ticket.value)) continue;
3109
+ if (!ticket || !isRef(ticket.value) || isReadonly(ticket.value)) continue;
2782
3110
  ticket.value.value = element;
2783
3111
  }
2784
3112
  }
@@ -3636,6 +3964,7 @@ function createVirtual(items, _options = {}) {
3636
3964
  /* v8 ignore stop */
3637
3965
  });
3638
3966
  useResizeObserver(element, (entries) => {
3967
+ /* v8 ignore next -- defensive: ResizeObserver always emits at least one entry */
3639
3968
  if (!entries[0]) return;
3640
3969
  viewportHeight.value = entries[0].contentRect.height;
3641
3970
  update();
@@ -3648,6 +3977,7 @@ function createVirtual(items, _options = {}) {
3648
3977
  rebuild();
3649
3978
  }, { immediate: true });
3650
3979
  watch(element, () => {
3980
+ /* v8 ignore next -- defensive: watch fires only when element is present */
3651
3981
  if (!element.value) return;
3652
3982
  if (direction === "reverse" && items.value.length > 0) {
3653
3983
  const lastIndex = items.value.length - 1;
@@ -3666,6 +3996,7 @@ function createVirtual(items, _options = {}) {
3666
3996
  anchorOffset = 0;
3667
3997
  } else if (/* @__PURE__ */ isFunction(anchor)) {
3668
3998
  const result = anchor(items.value);
3999
+ /* v8 ignore next 4 -- function-anchor: tested with valid number return; non-number rejection branch is implicit */
3669
4000
  if (/* @__PURE__ */ isNumber(result)) {
3670
4001
  anchorIndex = result;
3671
4002
  anchorOffset = element.value.scrollTop - (offsets.value[result] || 0);
@@ -3749,8 +4080,10 @@ function createVirtual(items, _options = {}) {
3749
4080
  /* v8 ignore stop */
3750
4081
  }
3751
4082
  function resize(index, height) {
4083
+ /* v8 ignore next -- early bail when height is already correct */
3752
4084
  if (heights.value[index] === height) return;
3753
4085
  heights.value[index] = height;
4086
+ /* v8 ignore next -- first-call only: itemHeight bootstrapped from initial measurement */
3754
4087
  if (!itemHeight.value) itemHeight.value = height;
3755
4088
  if (IN_BROWSER) {
3756
4089
  cancelAnimationFrame(rebuildRaf);
@@ -3767,6 +4100,7 @@ function createVirtual(items, _options = {}) {
3767
4100
  /* v8 ignore stop */
3768
4101
  }
3769
4102
  function scrollTo(index, scrollOptions) {
4103
+ /* v8 ignore next -- defensive: scrollTo only invoked after element mounts */
3770
4104
  if (!element.value) return;
3771
4105
  const targetOffset = offsets.value[index] || 0;
3772
4106
  const behavior = scrollOptions?.behavior ?? "auto";
@@ -4286,11 +4620,11 @@ const [createBreakpointsContext, createBreakpointsPlugin, useBreakpoints] = crea
4286
4620
  *
4287
4621
  * @example Using the built-in Temporal adapter
4288
4622
  * ```ts
4289
- * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
4623
+ * import { V0DateAdapter } from '@vuetify/v0/date'
4290
4624
  * import { createDatePlugin } from '@vuetify/v0'
4291
4625
  *
4292
4626
  * const app = createApp(App)
4293
- * app.use(createDatePlugin({ adapter: new Vuetify0DateAdapter() }))
4627
+ * app.use(createDatePlugin({ adapter: new V0DateAdapter() }))
4294
4628
  *
4295
4629
  * // In a component:
4296
4630
  * const { adapter } = useDate()
@@ -4300,9 +4634,9 @@ const [createBreakpointsContext, createBreakpointsPlugin, useBreakpoints] = crea
4300
4634
  *
4301
4635
  * @example Custom adapter
4302
4636
  * ```ts
4303
- * import type { DateAdapter } from '@vuetify/v0'
4637
+ * import { DateAdapter } from '@vuetify/v0'
4304
4638
  *
4305
- * class DateFnsAdapter implements DateAdapter<Date> {
4639
+ * class DateFnsAdapter extends DateAdapter<Date> {
4306
4640
  * // ... implementation
4307
4641
  * }
4308
4642
  *
@@ -4333,6 +4667,7 @@ const defaultLocales = {
4333
4667
  function deriveFirstDayOfWeek(locale) {
4334
4668
  try {
4335
4669
  const info = new Intl.Locale(locale).getWeekInfo?.();
4670
+ /* v8 ignore next -- defensive: getWeekInfo always returns info in Node 22+ */
4336
4671
  return info ? info.firstDay % 7 : 0;
4337
4672
  } catch {
4338
4673
  return 0;
@@ -4350,14 +4685,14 @@ function deriveFirstDayOfWeek(locale) {
4350
4685
  *
4351
4686
  * @example
4352
4687
  * ```ts
4353
- * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
4688
+ * import { V0DateAdapter } from '@vuetify/v0/date'
4354
4689
  *
4355
- * const { adapter } = createDate({ adapter: new Vuetify0DateAdapter() })
4690
+ * const { adapter } = createDate({ adapter: new V0DateAdapter() })
4356
4691
  * const today = adapter.date()
4357
4692
  *
4358
4693
  * // With locale options
4359
4694
  * const { adapter } = createDate({
4360
- * adapter: new Vuetify0DateAdapter(),
4695
+ * adapter: new V0DateAdapter(),
4361
4696
  * locale: 'de-DE',
4362
4697
  * })
4363
4698
  * ```
@@ -4412,10 +4747,10 @@ function createDate(options) {
4412
4747
  *
4413
4748
  * @example
4414
4749
  * ```ts
4415
- * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
4750
+ * import { V0DateAdapter } from '@vuetify/v0/date'
4416
4751
  *
4417
4752
  * const [useAppDate, provideAppDate] = createDateContext({
4418
- * adapter: new Vuetify0DateAdapter(),
4753
+ * adapter: new V0DateAdapter(),
4419
4754
  * namespace: 'app:date',
4420
4755
  * })
4421
4756
  * ```
@@ -4436,14 +4771,14 @@ function createDateContext(options) {
4436
4771
  *
4437
4772
  * @example
4438
4773
  * ```ts
4439
- * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
4774
+ * import { V0DateAdapter } from '@vuetify/v0/date'
4440
4775
  *
4441
4776
  * const app = createApp(App)
4442
- * app.use(createDatePlugin({ adapter: new Vuetify0DateAdapter() }))
4777
+ * app.use(createDatePlugin({ adapter: new V0DateAdapter() }))
4443
4778
  *
4444
4779
  * // With locale options
4445
4780
  * app.use(createDatePlugin({
4446
- * adapter: new Vuetify0DateAdapter(),
4781
+ * adapter: new V0DateAdapter(),
4447
4782
  * locale: 'de-DE',
4448
4783
  * }))
4449
4784
  * ```
@@ -4477,10 +4812,10 @@ function createDatePlugin(options) {
4477
4812
  * @example
4478
4813
  * ```ts
4479
4814
  * // main.ts
4480
- * import { Vuetify0DateAdapter } from '@vuetify/v0/date'
4815
+ * import { V0DateAdapter } from '@vuetify/v0/date'
4481
4816
  * import { createDatePlugin } from '@vuetify/v0'
4482
4817
  *
4483
- * app.use(createDatePlugin({ adapter: new Vuetify0DateAdapter() }))
4818
+ * app.use(createDatePlugin({ adapter: new V0DateAdapter() }))
4484
4819
  * ```
4485
4820
  *
4486
4821
  * @example
@@ -4494,7 +4829,7 @@ function createDatePlugin(options) {
4494
4829
  * ```
4495
4830
  */
4496
4831
  function useDate(namespace = "v0:date") {
4497
- 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() }))");
4498
4833
  return useContext(namespace);
4499
4834
  }
4500
4835
  //#endregion
@@ -4615,70 +4950,842 @@ function useDelay(callback, options = {}) {
4615
4950
  };
4616
4951
  }
4617
4952
  //#endregion
4618
- //#region src/composables/useHotkey/aliases.ts
4953
+ //#region src/composables/useMutationObserver/index.ts
4619
4954
  /**
4620
- * Key alias mapping for consistent key normalization across the hotkey system.
4955
+ * @module useMutationObserver
4621
4956
  *
4622
- * Maps user-friendly aliases to canonical key names that match
4623
- * KeyboardEvent.key values (in lowercase) where possible.
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
+ * ```
4624
4984
  */
4625
- const keyAliasMap = {
4626
- control: "ctrl",
4627
- command: "cmd",
4628
- option: "alt",
4629
- up: "arrowup",
4630
- down: "arrowdown",
4631
- left: "arrowleft",
4632
- right: "arrowright",
4633
- esc: "escape",
4634
- spacebar: " ",
4635
- space: " ",
4636
- return: "enter",
4637
- del: "delete",
4638
- plus: "+",
4639
- slash: "/",
4640
- underscore: "_",
4641
- minus: "-",
4642
- hyphen: "-"
4643
- };
4644
- /* @__NO_SIDE_EFFECTS__ */
4645
- function normalizeKey(key) {
4646
- const lowerKey = key.toLowerCase();
4647
- return keyAliasMap[lowerKey] ?? lowerKey;
4648
- }
4649
- //#endregion
4650
- //#region src/composables/useHotkey/parsing.ts
4651
- const logger = /* @__PURE__ */ useLogger();
4652
- const MODIFIERS = [
4653
- "ctrl",
4654
- "shift",
4655
- "alt",
4656
- "meta",
4657
- "cmd"
4658
- ];
4659
4985
  /**
4660
- * Splits a single combination string into individual key parts.
4986
+ * A composable that uses the Mutation Observer API to detect changes in the DOM.
4661
4987
  *
4662
- * A combination is a set of keys that must be pressed simultaneously.
4663
- * e.g. `ctrl+k`, `shift+-`
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.
4664
4992
  *
4665
- * @param combination - The combination string to parse
4666
- * @param isInternal - Whether this is an internal call (suppresses warnings)
4667
- * @returns Object with keys array and separators array
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
+ * ```
4668
5027
  */
4669
- function splitKeyCombination(combination, isInternal = false) {
4670
- const emptyResult = {
4671
- keys: [],
4672
- separators: []
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
4673
5037
  };
4674
- if (!combination) {
4675
- if (!isInternal) logger.warn("Invalid hotkey combination: empty string provided");
4676
- return emptyResult;
4677
- }
4678
- if (combination.length > 1 && [
4679
- "+",
4680
- "/",
4681
- "_"
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
+ "_"
4682
5789
  ].some((v) => combination.startsWith(v)) && ![
4683
5790
  "++",
4684
5791
  "//",
@@ -4875,9 +5982,11 @@ function useHotkey(keys, callback, options = {}, _platform) {
4875
5982
  let isSequence = false;
4876
5983
  let groupIndex = 0;
4877
5984
  function isInputFocused() {
5985
+ /* v8 ignore next -- IN_BROWSER always true in happy-dom test env */
4878
5986
  if (!IN_BROWSER) return false;
4879
5987
  if (toValue(inputs)) return false;
4880
5988
  const activeElement = document.activeElement;
5989
+ /* v8 ignore next -- defensive: document.activeElement is always set in happy-dom */
4881
5990
  if (!activeElement) return false;
4882
5991
  const role = activeElement.getAttribute("role");
4883
5992
  if (role === "textbox" || role === "searchbox" || role === "combobox" || role === "spinbutton") return true;
@@ -4931,6 +6040,7 @@ function useHotkey(keys, callback, options = {}, _platform) {
4931
6040
  const currentKeys = toValue(keys);
4932
6041
  if (!currentKeys) return;
4933
6042
  const groups = splitKeySequence(currentKeys.toLowerCase());
6043
+ /* v8 ignore next -- defensive: splitKeySequence rejects empty strings earlier */
4934
6044
  if (groups.length === 0) return;
4935
6045
  isSequence = groups.length > 1;
4936
6046
  keyGroups = groups;
@@ -4983,6 +6093,7 @@ function isModifier(key) {
4983
6093
  function parseKeyGroup(group) {
4984
6094
  const { keys: parts } = splitKeyCombination(group.toLowerCase());
4985
6095
  const modifiers = Object.fromEntries(MODIFIERS.map((m) => [m, false]));
6096
+ /* v8 ignore next 3 -- defensive: parseKeyGroup is not invoked with empty parts */
4986
6097
  if (parts.length === 0) return {
4987
6098
  modifiers,
4988
6099
  actualKey: void 0
@@ -5208,11 +6319,13 @@ function useIntersectionObserver(target, callback, options = {}) {
5208
6319
  observe: (obs, el) => obs.observe(el),
5209
6320
  onEntry: (entries) => {
5210
6321
  const latest = entries.at(-1);
6322
+ /* v8 ignore next -- defensive: IntersectionObserver always emits at least one entry */
5211
6323
  if (latest) isIntersecting.value = latest.isIntersecting;
5212
6324
  },
5213
6325
  onPause: () => {
5214
6326
  isIntersecting.value = false;
5215
6327
  },
6328
+ /* v8 ignore next -- defensive nullish coalesce: entries always populated when shouldStop fires */
5216
6329
  shouldStop: (entries) => entries.at(-1)?.isIntersecting ?? false,
5217
6330
  immediate: options.immediate ? (el) => {
5218
6331
  return [{
@@ -5262,6 +6375,7 @@ function useElementIntersection(target, options = {}) {
5262
6375
  const intersectionRatio = shallowRef(0);
5263
6376
  const { pause: _pause, resume, stop, isActive, isPaused } = useIntersectionObserver(target, (entries) => {
5264
6377
  const entry = entries.at(-1);
6378
+ /* v8 ignore next 4 -- defensive: IntersectionObserver always emits at least one entry */
5265
6379
  if (entry) {
5266
6380
  isIntersecting.value = entry.isIntersecting;
5267
6381
  intersectionRatio.value = entry.intersectionRatio;
@@ -5511,131 +6625,6 @@ function usePrefersContrast() {
5511
6625
  return useMediaQuery("(prefers-contrast: more)");
5512
6626
  }
5513
6627
  //#endregion
5514
- //#region src/composables/useMutationObserver/index.ts
5515
- /**
5516
- * @module useMutationObserver
5517
- *
5518
- * @see https://0.vuetifyjs.com/composables/system/use-mutation-observer
5519
- *
5520
- * @remarks
5521
- * MutationObserver composable with lifecycle management.
5522
- *
5523
- * Key features:
5524
- * - MutationObserver API wrapper
5525
- * - `isActive` reactive flag
5526
- * - `once` option for single-fire observation
5527
- * - Pause/resume/stop functionality
5528
- * - Automatic cleanup on unmount
5529
- * - SSR-safe (checks SUPPORTS_MUTATION_OBSERVER)
5530
- * - Hydration-aware
5531
- * - Configurable observation options (childList, attributes, characterData, etc.)
5532
- *
5533
- * Perfect for detecting DOM changes and responding to mutations.
5534
- *
5535
- * @example
5536
- * ```ts
5537
- * import { useTemplateRef } from 'vue'
5538
- * import { useMutationObserver } from '@vuetify/v0'
5539
- *
5540
- * const target = useTemplateRef('target')
5541
- * useMutationObserver(target, records => {
5542
- * console.log(records)
5543
- * }, { childList: true })
5544
- * ```
5545
- */
5546
- /**
5547
- * A composable that uses the Mutation Observer API to detect changes in the DOM.
5548
- *
5549
- * @param target The element to observe.
5550
- * @param callback The callback to execute when a mutation is observed.
5551
- * @param options The options for the Mutation Observer.
5552
- * @returns An object with methods to control the observer.
5553
- *
5554
- * @see https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
5555
- * @see https://0.vuetifyjs.com/composables/system/use-mutation-observer
5556
- *
5557
- * @example
5558
- * ```ts
5559
- * import { ref } from 'vue'
5560
- * import { useMutationObserver } from '@vuetify/v0'
5561
- *
5562
- * const container = ref<HTMLElement>()
5563
- *
5564
- * const { pause, resume, isPaused } = useMutationObserver(
5565
- * container,
5566
- * (mutations) => {
5567
- * mutations.forEach((mutation) => {
5568
- * if (mutation.type === 'childList') {
5569
- * console.log('Children changed:', mutation.addedNodes, mutation.removedNodes)
5570
- * } else if (mutation.type === 'attributes') {
5571
- * console.log('Attribute changed:', mutation.attributeName)
5572
- * }
5573
- * })
5574
- * },
5575
- * {
5576
- * childList: true,
5577
- * attributes: true,
5578
- * subtree: true
5579
- * }
5580
- * )
5581
- *
5582
- * // Pause observation
5583
- * pause()
5584
- *
5585
- * // Resume observation
5586
- * resume()
5587
- * ```
5588
- */
5589
- function useMutationObserver(target, callback, options = {}) {
5590
- const observerOptions = {
5591
- childList: options.childList ?? true,
5592
- attributes: options.attributes ?? false,
5593
- characterData: options.characterData ?? false,
5594
- subtree: options.subtree ?? false,
5595
- attributeOldValue: options.attributeOldValue ?? false,
5596
- characterDataOldValue: options.characterDataOldValue ?? false,
5597
- attributeFilter: options.attributeFilter
5598
- };
5599
- return createObserver(target, callback, {
5600
- supports: SUPPORTS_MUTATION_OBSERVER,
5601
- once: options.once,
5602
- create: (cb) => new MutationObserver((mutations) => {
5603
- cb(mutations.map((m) => ({
5604
- type: m.type,
5605
- target: m.target,
5606
- addedNodes: m.addedNodes,
5607
- removedNodes: m.removedNodes,
5608
- previousSibling: m.previousSibling,
5609
- nextSibling: m.nextSibling,
5610
- attributeName: m.attributeName,
5611
- attributeNamespace: m.attributeNamespace,
5612
- oldValue: m.oldValue
5613
- })));
5614
- }),
5615
- observe: (obs, el) => obs.observe(el, observerOptions),
5616
- immediate: options.immediate ? (el) => {
5617
- const emptyNodeList = {
5618
- length: 0,
5619
- item: () => null,
5620
- forEach: () => {},
5621
- *[Symbol.iterator]() {}
5622
- };
5623
- return [{
5624
- type: "childList",
5625
- target: el,
5626
- addedNodes: emptyNodeList,
5627
- removedNodes: emptyNodeList,
5628
- previousSibling: null,
5629
- nextSibling: null,
5630
- attributeName: null,
5631
- attributeNamespace: null,
5632
- oldValue: null
5633
- }];
5634
- } : void 0,
5635
- onceIncludesImmediate: true
5636
- });
5637
- }
5638
- //#endregion
5639
6628
  //#region src/composables/usePresence/index.ts
5640
6629
  /**
5641
6630
  * @module usePresence
@@ -6019,8 +7008,11 @@ function useRovingFocus(items, options = {}) {
6019
7008
  };
6020
7009
  }
6021
7010
  //#endregion
7011
+ //#region src/composables/useRtl/adapters/adapter.ts
7012
+ var RtlAdapter = class {};
7013
+ //#endregion
6022
7014
  //#region src/composables/useRtl/adapters/v0.ts
6023
- var Vuetify0RtlAdapter = class {
7015
+ var V0RtlAdapter = class extends RtlAdapter {
6024
7016
  setup(app, context, target) {
6025
7017
  if (IN_BROWSER) {
6026
7018
  if (/* @__PURE__ */ isNull(target)) return;
@@ -6088,7 +7080,7 @@ function createRtlFallback() {
6088
7080
  }
6089
7081
  const [createRtlContext, createRtlPlugin, useRtl] = createPluginContext("v0:rtl", (options) => createRtl(options), {
6090
7082
  fallback: () => createRtlFallback(),
6091
- setup: (context, app, { adapter = new Vuetify0RtlAdapter(), target }) => {
7083
+ setup: (context, app, { adapter = new V0RtlAdapter(), target }) => {
6092
7084
  adapter.setup(app, context, target);
6093
7085
  },
6094
7086
  persist: (ctx) => ctx.isRtl.value,
@@ -6380,7 +7372,7 @@ function useStack(namespace = "v0:stack") {
6380
7372
  * ```
6381
7373
  */
6382
7374
  function createStorage(options = {}) {
6383
- const { adapter = IN_BROWSER ? window.localStorage : new MemoryAdapter(), prefix = "v0:", serializer = {
7375
+ const { adapter = IN_BROWSER ? window.localStorage : new MemoryStorageAdapter(), prefix = "v0:", serializer = {
6384
7376
  read: JSON.parse,
6385
7377
  write: JSON.stringify
6386
7378
  }, ttl } = options;
@@ -6395,9 +7387,9 @@ function createStorage(options = {}) {
6395
7387
  if (/* @__PURE__ */ isNullOrUndefined(raw)) return void 0;
6396
7388
  try {
6397
7389
  const parsed = serializer.read(raw);
6398
- if (ttl && /* @__PURE__ */ isObject(parsed) && "__v0" in parsed && "__v" in parsed) {
7390
+ if (/* @__PURE__ */ isObject(parsed) && "__v0" in parsed && "__v" in parsed) {
6399
7391
  const envelope = parsed;
6400
- if (Date.now() - envelope.__t > ttl) {
7392
+ if (ttl && Date.now() - envelope.__t > ttl) {
6401
7393
  adapter?.removeItem(prefixedKey);
6402
7394
  return;
6403
7395
  }
@@ -6477,7 +7469,7 @@ function createStorage(options = {}) {
6477
7469
  };
6478
7470
  }
6479
7471
  function createStorageFallback() {
6480
- return createStorage({ adapter: new MemoryAdapter() });
7472
+ return createStorage({ adapter: new MemoryStorageAdapter() });
6481
7473
  }
6482
7474
  const [createStorageContext, createStoragePlugin, useStorage] = createPluginContext("v0:storage", (options) => createStorage(options), { fallback: () => createStorageFallback() });
6483
7475
  //#endregion
@@ -6824,6 +7816,7 @@ function useClickOutside(target, handler, options = {}) {
6824
7816
  * Check if an element matches resolved ignore targets.
6825
7817
  */
6826
7818
  function isIgnored(el, selectors, elements) {
7819
+ /* v8 ignore next -- defensive guard, callers always pass an Element */
6827
7820
  if (!el) return false;
6828
7821
  for (const selector of selectors) try {
6829
7822
  if (el.matches(selector) || !/* @__PURE__ */ isNull(el.closest(selector))) return true;
@@ -6850,6 +7843,7 @@ function useClickOutside(target, handler, options = {}) {
6850
7843
  const targets = getTargets();
6851
7844
  if (targets.length === 0) return false;
6852
7845
  return targets.every((el) => {
7846
+ /* v8 ignore next -- defensive: getTargets filters to Element-typed values */
6853
7847
  if (/* @__PURE__ */ isNullOrUndefined(el) || !/* @__PURE__ */ isFunction(el.contains)) return false;
6854
7848
  return el !== eventTarget && !el.contains(eventTarget);
6855
7849
  });
@@ -6859,6 +7853,7 @@ function useClickOutside(target, handler, options = {}) {
6859
7853
  */
6860
7854
  function isOutsideBounds(x, y) {
6861
7855
  const targets = getTargets();
7856
+ /* v8 ignore next -- edge case: bounds check only invoked when targets exist */
6862
7857
  if (targets.length === 0) return false;
6863
7858
  return targets.every((el) => {
6864
7859
  const { left, right, top, bottom } = el.getBoundingClientRect();
@@ -6869,6 +7864,7 @@ function useClickOutside(target, handler, options = {}) {
6869
7864
  * Validate that the target is still in the DOM.
6870
7865
  */
6871
7866
  function isValidTarget(eventTarget) {
7867
+ /* v8 ignore next -- type guard for non-Element targets (e.g., document, window) */
6872
7868
  if (!/* @__PURE__ */ isElement(eventTarget)) return false;
6873
7869
  if (!eventTarget.isConnected) return false;
6874
7870
  return true;
@@ -6961,4 +7957,4 @@ function useClickOutside(target, handler, options = {}) {
6961
7957
  };
6962
7958
  }
6963
7959
  //#endregion
6964
- export { createSlider as $, useElementIntersection as A, ServerAdapter as At, createBreakpointsContext as B, usePresence as C, createValidation as Ct, usePrefersDark as D, createCombobox as Dt, usePrefersContrast as E, useForm as Et, createDate as F, createBreadcrumbsContext as Ft, createVirtualContext as G, useBreakpoints as H, createDateContext as I, useBreadcrumbs as It, createTimelineContext as J, useVirtual as K, createDatePlugin as L, useImage as M, useVirtualFocus as Mt, useHotkey as N, usePopover as Nt, usePrefersReducedMotion as O, createComboboxContext as Ot, useDelay as P, createBreadcrumbs as Pt, useStep as Q, useDate as R, useProxyModel as S, createInput as St, useMediaQuery as T, createFormContext as Tt, toReactive as U, createBreakpointsPlugin as V, createVirtual as W, createStep as X, useTimeline as Y, createStepContext as Z, createRtlPlugin as _, createNumberField as _t, useToggleScope as a, useProgress as at, useRovingFocus as b, createNestedContext as bt, createStoragePlugin as c, useOverflow as ct, createStackContext as d, toElement as dt, createRating as et, createStackPlugin as f, createFallbackHydration as ft, createRtlFallback as g, useHydration as gt, createRtlContext as h, createHydrationPlugin as ht, useWindowEventListener as i, createProgressContext as it, useIntersectionObserver as j, ClientAdapter as jt, useLazy as k, useCombobox as kt, useStorage as l, useElementSize as lt, createRtl as m, createHydrationContext as mt, useDocumentEventListener as n, useRating as nt, createStorage as o, createOverflow as ot, useStack as p, createHydration as pt, createTimeline as q, useEventListener as r, createProgress as rt, createStorageContext as s, createOverflowContext as st, useClickOutside as t, createRatingContext as tt, createStack as u, useResizeObserver as ut, useRtl as v, createNumeric as vt, useMutationObserver as w, createForm as wt, useRaf as x, useNested as xt, Vuetify0RtlAdapter as y, createNested as yt, createBreakpoints 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 };