@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.
- package/README.md +3 -0
- package/dist/adapter-BSKx9WQ7.mjs +4 -0
- package/dist/{index-CT5SCLzW.d.mts → adapter-C6JmOyYo.d.mts} +6 -9
- package/dist/{adapter-gQ2SNKZq.mjs → adapter-C9cR4AcY.mjs} +248 -5
- package/dist/adapter-Chznb9Ju.mjs +4 -0
- package/dist/{adapter-DlX3FA3A.d.mts → adapter-Cr7Bl4la.d.mts} +3 -7
- package/dist/adapter-D8zizQVP.mjs +4 -0
- package/dist/adapter-DgBBe_BW.d.mts +17 -0
- package/dist/adapter-Dzyrlbz7.d.mts +98 -0
- package/dist/adapter-o8r-2vUn.d.mts +11 -0
- package/dist/adapters-B25KaYU4.mjs +2 -0
- package/dist/adapters-BcddLSNs.mjs +4 -0
- package/dist/adapters-D64TORDu.mjs +2 -0
- package/dist/adapters-Dv74Jk6A.mjs +5 -0
- package/dist/adapters-t6QC-zE8.mjs +3 -0
- package/dist/browser/index.js +1510 -388
- package/dist/components/index.d.mts +2 -2
- package/dist/components/index.mjs +1 -1
- package/dist/{components-C4jI-78d.mjs → components-BWxMSWr7.mjs} +138 -90
- package/dist/composables/index.d.mts +19 -19
- package/dist/composables/index.mjs +31 -29
- package/dist/{consola-BdgyPEfx.d.mts → consola-Crr4qbeI.d.mts} +2 -2
- package/dist/{consola-Ceox8BWi.mjs → consola-DYvbPNbF.mjs} +3 -1
- package/dist/constants/index.d.mts +1 -1
- package/dist/constants/index.mjs +2 -2
- package/dist/{createDataTable-iODP6aKB.mjs → createDataTable-DDr2yY-A.mjs} +30 -29
- package/dist/{createGroup-CGk6hY_Y.mjs → createGroup-BSGtfBUE.mjs} +3 -3
- package/dist/{createRegistry-BXzdXAjq.mjs → createRegistry-Db_juawd.mjs} +78 -41
- package/dist/{createSelection-B3HJEPNg.mjs → createSelection-BeKZMxha.mjs} +3 -3
- package/dist/{createSingle-Dql8mRi-.mjs → createSingle-Dms2efxr.mjs} +1 -1
- package/dist/{createTokens-DgFYsOrv.mjs → createTokens-B1lwHZvO.mjs} +2 -2
- package/dist/data-table/adapters/client/index.d.mts +2 -2
- package/dist/data-table/adapters/client/index.mjs +2 -2
- package/dist/data-table/adapters/index.d.mts +5 -5
- package/dist/data-table/adapters/index.mjs +6 -6
- package/dist/data-table/adapters/server/index.d.mts +2 -2
- package/dist/data-table/adapters/server/index.mjs +2 -2
- package/dist/data-table/adapters/virtual/index.d.mts +2 -2
- package/dist/data-table/adapters/virtual/index.mjs +2 -2
- package/dist/data-table/index.d.mts +6 -6
- package/dist/data-table/index.mjs +6 -6
- package/dist/date/index.d.mts +3 -3
- package/dist/date/index.mjs +11 -4
- package/dist/features/adapters/flagsmith/index.d.mts +2 -2
- package/dist/features/adapters/flagsmith/index.mjs +2 -2
- package/dist/features/adapters/launchdarkly/index.d.mts +2 -2
- package/dist/features/adapters/launchdarkly/index.mjs +2 -2
- package/dist/features/adapters/posthog/index.d.mts +2 -2
- package/dist/features/adapters/posthog/index.mjs +2 -2
- package/dist/features/index.d.mts +3 -3
- package/dist/features/index.mjs +2 -1
- package/dist/{flagsmith-Dbh1h8WF.mjs → flagsmith-Cu93hUIF.mjs} +6 -4
- package/dist/{index-BzFHTW59.d.mts → flagsmith-ygKMImos.d.mts} +4 -4
- package/dist/{globals-zEjWBKCr.mjs → globals-BAe6tA_x.mjs} +1 -1
- package/dist/{index-GOpjKzaJ.d.mts → index-B3-FstpY.d.mts} +25 -2
- package/dist/{index-BJGcVy7e.d.mts → index-BA7YU2xY.d.mts} +5 -5
- package/dist/{index-B76iGRE3.d.mts → index-BfpLkDR_.d.mts} +949 -59
- package/dist/{index-DE3bWw11.d.mts → index-Cw04PFVO.d.mts} +92 -48
- package/dist/index-DewYtDsc.d.mts +4 -0
- package/dist/{index-DNDuacmH.d.mts → index-jQPkwo3G.d.mts} +15 -5
- package/dist/index.d.mts +23 -23
- package/dist/index.mjs +35 -33
- package/dist/json/attributes.json +1 -1
- package/dist/json/web-types.json +2 -2
- package/dist/launchdarkly-BJR4vbbV.d.mts +13 -0
- package/dist/{launchdarkly-BKtW-idu.mjs → launchdarkly-CLGtJCsI.mjs} +6 -4
- package/dist/locale/adapters/index.d.mts +3 -3
- package/dist/locale/adapters/index.mjs +4 -3
- package/dist/locale/adapters/v0/index.d.mts +2 -2
- package/dist/locale/adapters/v0/index.mjs +2 -2
- package/dist/locale/adapters/vue-i18n/index.d.mts +3 -3
- package/dist/locale/adapters/vue-i18n/index.mjs +4 -2
- package/dist/locale/index.d.mts +4 -4
- package/dist/locale/index.mjs +4 -3
- package/dist/logger/adapters/consola/index.d.mts +1 -1
- package/dist/logger/adapters/consola/index.mjs +1 -1
- package/dist/logger/adapters/index.d.mts +5 -5
- package/dist/logger/adapters/index.mjs +6 -5
- package/dist/logger/adapters/pino/index.d.mts +1 -1
- package/dist/logger/adapters/pino/index.mjs +1 -1
- package/dist/logger/adapters/v0/index.d.mts +2 -2
- package/dist/logger/adapters/v0/index.mjs +2 -2
- package/dist/logger/index.d.mts +6 -6
- package/dist/logger/index.mjs +6 -5
- package/dist/{memory-CeSQE-QV.d.mts → memory-B0w39kno.d.mts} +9 -9
- package/dist/{memory-oYBvtkgx.mjs → memory-BMpJcQjQ.mjs} +13 -3
- package/dist/notifications/index.d.mts +30 -4
- package/dist/notifications/index.mjs +187 -126
- package/dist/palettes/ant/index.d.mts +1 -1
- package/dist/palettes/index.d.mts +3 -3
- package/dist/palettes/leonardo/generate/index.mjs +3 -0
- package/dist/palettes/material/index.d.mts +1 -1
- package/dist/palettes/md1/index.d.mts +1 -1
- package/dist/palettes/md2/index.d.mts +1 -1
- package/dist/palettes/radix/index.d.mts +1 -1
- package/dist/palettes/tailwind/index.d.mts +1 -1
- package/dist/permissions/adapters/index.d.mts +2 -2
- package/dist/permissions/adapters/index.mjs +3 -3
- package/dist/permissions/adapters/v0/index.d.mts +2 -2
- package/dist/permissions/adapters/v0/index.mjs +2 -2
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +3 -3
- package/dist/{pino-9L79n6y6.mjs → pino-CIcYlW3u.mjs} +3 -1
- package/dist/{pino-DNfCEb0p.d.mts → pino-NmR7X0s4.d.mts} +2 -2
- package/dist/{posthog-DmuyycaR.mjs → posthog-B3go2aCh.mjs} +7 -5
- package/dist/posthog-CQntUXFO.d.mts +13 -0
- package/dist/rules/index.d.mts +1 -1
- package/dist/rules/index.mjs +1 -1
- package/dist/{server-3ilWFwqe.d.mts → server-DQK305lc.d.mts} +5 -5
- package/dist/{server-y7zLSCSx.mjs → server-ZFXbgQoY.mjs} +4 -3
- package/dist/storage/adapters/index.d.mts +3 -3
- package/dist/storage/adapters/index.mjs +2 -2
- package/dist/storage/adapters/memory/index.d.mts +2 -2
- package/dist/storage/adapters/memory/index.mjs +2 -2
- package/dist/storage/index.d.mts +4 -4
- package/dist/storage/index.mjs +3 -3
- package/dist/theme/adapters/index.d.mts +2 -2
- package/dist/theme/adapters/index.mjs +4 -4
- package/dist/theme/adapters/unhead/index.d.mts +1 -1
- package/dist/theme/adapters/unhead/index.mjs +1 -1
- package/dist/theme/adapters/v0/index.d.mts +1 -1
- package/dist/theme/adapters/v0/index.mjs +1 -1
- package/dist/theme/index.d.mts +1 -1
- package/dist/theme/index.mjs +3 -3
- package/dist/types/index.d.mts +1 -1
- package/dist/{unhead-B-I2OS6i.mjs → unhead-dPyVkgiJ.mjs} +2 -2
- package/dist/{useClickOutside-CY4U2J9W.mjs → useClickOutside-B8naChaQ.mjs} +1224 -228
- package/dist/{useFeatures-BPOke6iP.mjs → useFeatures-x2iKp0C8.mjs} +7 -10
- package/dist/{useLocale-BV8e3imF.mjs → useLocale-BUms59Bw.mjs} +6 -6
- package/dist/{useLogger-DvdRTrqC.mjs → useLogger-DPSSa-YR.mjs} +4 -4
- package/dist/{useNotifications-vSJE6z6t.mjs → useNotifications-BPTmZWQy.mjs} +4 -4
- package/dist/{usePermissions-D3zPwKQc.mjs → usePermissions-CTi-3pBk.mjs} +6 -6
- package/dist/{useRules-BT6cgoTj.mjs → useRules-DNhljkyO.mjs} +2 -2
- package/dist/{useTheme-cUZb2A9O.mjs → useTheme-3vW3kFNg.mjs} +5 -5
- package/dist/utilities/index.d.mts +2 -2
- package/dist/utilities/index.mjs +26 -2
- package/dist/{v0-DrOub-e1.d.mts → v0-CRGArJim.d.mts} +4 -4
- package/dist/{v0-C49OI_wc.mjs → v0-CotC1PiS.mjs} +5 -3
- package/dist/{v0-Dq3zhexO.mjs → v0-DAlKpCR8.mjs} +3 -4
- package/dist/{v0-D7Fm2C3Y.d.mts → v0-DDELiSI6.d.mts} +3 -3
- package/dist/v0-DYsCJPIu.d.mts +8 -0
- package/dist/{v0-gpExNexS.mjs → v0-DadOcxFI.mjs} +2 -2
- package/dist/{v0-Bmow1q_x.mjs → v0-Dvu8fMl3.mjs} +3 -3
- package/dist/{v0-CHZpnMeP.mjs → v0-YJQoTLsp.mjs} +6 -4
- package/dist/virtual-BqCRw0ly.d.mts +8 -0
- package/dist/{virtual-C0yHf1DC.mjs → virtual-Dnno49F8.mjs} +3 -4
- package/package.json +1 -1
- package/dist/adapter-58IvIfAZ.d.mts +0 -96
- package/dist/adapter-BvWEd9w-.d.mts +0 -17
- package/dist/adapter-Dhq68tz0.d.mts +0 -11
- package/dist/adapters-BSiqsTZN.mjs +0 -3
- package/dist/adapters-C3QXhf4I.mjs +0 -2
- package/dist/adapters-CMJGA1k7.mjs +0 -2
- package/dist/adapters-Dse4bPxk.mjs +0 -5
- package/dist/adapters-_ZqxCD8f.mjs +0 -4
- package/dist/createPagination-CHF7SmmK.mjs +0 -247
- package/dist/index-1XdASTjp.d.mts +0 -13
- package/dist/index-C6rdXZ7j.d.mts +0 -11
- package/dist/index-Dp1HjBxJ.d.mts +0 -13
- package/dist/v0-CdusXjY_.d.mts +0 -8
- package/dist/virtual-D4fckewC.d.mts +0 -8
- /package/dist/{adapter-Be9zUc-n.mjs → adapter-oM18AdUc.mjs} +0 -0
- /package/dist/{htmlElements-D4oMQZPJ.mjs → htmlElements-CHXZfsZu.mjs} +0 -0
- /package/dist/{index-CFhUdAHM.d.mts → index-CKKoxA9V.d.mts} +0 -0
- /package/dist/{index-DuddF-NR.d.mts → index-CLR1QMM3.d.mts} +0 -0
- /package/dist/{index-BgCkepP_.d.mts → index-DJQwYeyx.d.mts} +0 -0
- /package/dist/{index-CROz1VSV.d.mts → index-DWfPppPU.d.mts} +0 -0
- /package/dist/{index-DTvqw22G.d.mts → index-H_hl6-4b.d.mts} +0 -0
- /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-
|
|
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-
|
|
5
|
-
import { t as createRegistry } from "./createRegistry-
|
|
6
|
-
import { i as createModel, t as createSelection } from "./createSelection-
|
|
7
|
-
import { t as createSingle } from "./createSingle-
|
|
8
|
-
import "./createTokens-
|
|
9
|
-
import { a as useLocale } from "./useLocale-
|
|
10
|
-
import { c as useTimer } from "./useNotifications-
|
|
11
|
-
import { t as toArray } from "./toArray-
|
|
12
|
-
import {
|
|
13
|
-
import { t as createGroup } from "./createGroup-
|
|
14
|
-
import "./
|
|
15
|
-
import "./
|
|
16
|
-
import
|
|
17
|
-
import "./
|
|
18
|
-
import "./
|
|
19
|
-
import
|
|
20
|
-
import "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
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
|
|
4637
|
+
* import { DateAdapter } from '@vuetify/v0'
|
|
4304
4638
|
*
|
|
4305
|
-
* class DateFnsAdapter
|
|
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 {
|
|
4688
|
+
* import { V0DateAdapter } from '@vuetify/v0/date'
|
|
4354
4689
|
*
|
|
4355
|
-
* const { adapter } = createDate({ adapter: new
|
|
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
|
|
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 {
|
|
4750
|
+
* import { V0DateAdapter } from '@vuetify/v0/date'
|
|
4416
4751
|
*
|
|
4417
4752
|
* const [useAppDate, provideAppDate] = createDateContext({
|
|
4418
|
-
* adapter: new
|
|
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 {
|
|
4774
|
+
* import { V0DateAdapter } from '@vuetify/v0/date'
|
|
4440
4775
|
*
|
|
4441
4776
|
* const app = createApp(App)
|
|
4442
|
-
* app.use(createDatePlugin({ adapter: new
|
|
4777
|
+
* app.use(createDatePlugin({ adapter: new V0DateAdapter() }))
|
|
4443
4778
|
*
|
|
4444
4779
|
* // With locale options
|
|
4445
4780
|
* app.use(createDatePlugin({
|
|
4446
|
-
* adapter: new
|
|
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 {
|
|
4815
|
+
* import { V0DateAdapter } from '@vuetify/v0/date'
|
|
4481
4816
|
* import { createDatePlugin } from '@vuetify/v0'
|
|
4482
4817
|
*
|
|
4483
|
-
* app.use(createDatePlugin({ adapter: new
|
|
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 {
|
|
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/
|
|
4953
|
+
//#region src/composables/useMutationObserver/index.ts
|
|
4619
4954
|
/**
|
|
4620
|
-
*
|
|
4955
|
+
* @module useMutationObserver
|
|
4621
4956
|
*
|
|
4622
|
-
*
|
|
4623
|
-
*
|
|
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
|
-
*
|
|
4986
|
+
* A composable that uses the Mutation Observer API to detect changes in the DOM.
|
|
4661
4987
|
*
|
|
4662
|
-
*
|
|
4663
|
-
*
|
|
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
|
-
* @
|
|
4666
|
-
* @
|
|
4667
|
-
*
|
|
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
|
|
4670
|
-
const
|
|
4671
|
-
|
|
4672
|
-
|
|
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
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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 {
|
|
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 };
|