@vuetify/v0 0.0.18 → 0.0.20

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.
@@ -1,25 +1,9 @@
1
- import { _ as range, d as isObject, h as isUndefined, l as isNullOrUndefined, m as isSymbol, o as isFunction, p as isString, r as genId, s as isNaN } from "./utilities-JxCe-nF2.mjs";
2
- import { a as SUPPORTS_OBSERVER, s as __LOGGER_ENABLED__, t as IN_BROWSER } from "./globals-BGVqrlN7.mjs";
3
- import { computed, getCurrentInstance, inject, isRef, onScopeDispose, provide, reactive, shallowReactive, shallowReadonly, shallowRef, toRef, toValue, watch } from "vue";
1
+ import { _ as range, d as isObject, h as isUndefined, l as isNullOrUndefined, m as isSymbol, o as isFunction, p as isString, r as genId, s as isNaN } from "./utilities-BrFKLHFS.mjs";
2
+ import { a as SUPPORTS_OBSERVER, s as __LOGGER_ENABLED__, t as IN_BROWSER } from "./globals-exvZ8fiO.mjs";
3
+ import { computed, getCurrentInstance, inject, isRef, onScopeDispose, provide, reactive, shallowReactive, shallowReadonly, shallowRef, toRef, toValue, watch, watchEffect } from "vue";
4
4
 
5
5
  //#region src/composables/createContext/index.ts
6
6
  /**
7
- * @module createContext
8
- *
9
- * @see https://0.vuetifyjs.com/composables/foundation/create-context
10
- *
11
- * @remarks
12
- * Factory for creating type-safe Vue dependency injection contexts.
13
- *
14
- * Provides a wrapper around Vue's provide/inject that throws errors when context is not found,
15
- * eliminating silent failures and improving developer experience. Supports both app-level and
16
- * component-level provision.
17
- *
18
- * Supports two modes:
19
- * - **Static key**: `createContext('my-key')` - key is fixed at creation time
20
- * - **Dynamic key**: `createContext()` or `createContext({ suffix: 'item' })` - key provided at runtime
21
- */
22
- /**
23
7
  * Injects a context provided by an ancestor component.
24
8
  *
25
9
  * @param key The key of the context to inject.
@@ -558,21 +542,6 @@ function useLogger(namespace = "v0:logger") {
558
542
  //#endregion
559
543
  //#region src/composables/useRegistry/index.ts
560
544
  /**
561
- * @module useRegistry
562
- *
563
- * @remarks
564
- * A foundational composable for managing collections of items (tickets) with:
565
- * - Unique ID-based access
566
- * - Index-based ordering
567
- * - Value-based reverse lookup
568
- * - Automatic reindexing
569
- * - Optional event emission
570
- * - Performance-optimized caching
571
- *
572
- * The registry serves as the base for many other composables in the system,
573
- * including useSelection, useForm, useTimeline, and more.
574
- */
575
- /**
576
545
  * Creates a new registry instance.
577
546
  *
578
547
  * @param options The options for the registry instance.
@@ -616,17 +585,21 @@ function useRegistry(options) {
616
585
  }
617
586
  function on(event, cb) {
618
587
  if (!events) {
619
- logger.warn(`Attempted to register event listener for "${event}" but events are disabled.`);
588
+ logger.warn(`Events are disabled. Initialize with \`useRegistry({ events: true })\` to enable.`);
620
589
  return;
621
590
  }
622
591
  if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
623
592
  listeners.get(event).add(cb);
624
593
  }
625
594
  function off(event, cb) {
595
+ if (!events) {
596
+ logger.warn(`Events are disabled. Initialize with \`useRegistry({ events: true })\` to enable.`);
597
+ return;
598
+ }
626
599
  listeners.get(event)?.delete(cb);
627
600
  }
628
601
  function dispose() {
629
- if (listeners.size > 0) listeners.clear();
602
+ listeners.clear();
630
603
  clear();
631
604
  }
632
605
  function get(id) {
@@ -715,9 +688,9 @@ function useRegistry(options) {
715
688
  return entries$1;
716
689
  }
717
690
  function clear() {
718
- if (collection.size > 0) collection.clear();
719
- if (catalog.size > 0) catalog.clear();
720
- if (directory.size > 0) directory.clear();
691
+ collection.clear();
692
+ catalog.clear();
693
+ directory.clear();
721
694
  invalidate();
722
695
  indexDependentCount = 0;
723
696
  needsReindex = false;
@@ -726,7 +699,7 @@ function useRegistry(options) {
726
699
  }
727
700
  function invalidate() {
728
701
  if (batching) return;
729
- if (cache.size > 0) cache.clear();
702
+ cache.clear();
730
703
  }
731
704
  function queueEmit(event, data) {
732
705
  if (batching) pendingEmits.push({
@@ -741,7 +714,7 @@ function useRegistry(options) {
741
714
  pendingEmits = [];
742
715
  try {
743
716
  const result = fn();
744
- if (cache.size > 0) cache.clear();
717
+ cache.clear();
745
718
  for (const { event, data } of pendingEmits) emit(event, data);
746
719
  return result;
747
720
  } finally {
@@ -752,8 +725,8 @@ function useRegistry(options) {
752
725
  function reindex() {
753
726
  const startIndex = minDirtyIndex === Infinity ? 0 : minDirtyIndex;
754
727
  if (startIndex === 0) {
755
- if (catalog.size > 0) catalog.clear();
756
- if (directory.size > 0) directory.clear();
728
+ catalog.clear();
729
+ directory.clear();
757
730
  }
758
731
  invalidate();
759
732
  let index = 0;
@@ -780,7 +753,7 @@ function useRegistry(options) {
780
753
  const size = collection.size;
781
754
  const id = registration.id ?? /* @__PURE__ */ genId();
782
755
  if (has(id)) {
783
- logger.warn(`Ticket with id "${id}" already exists in the registry. Skipping registration.`);
756
+ logger.warn(`Ticket "${id}" already exists. Use \`upsert()\` to update or check \`has()\` before registering.`);
784
757
  return get(id);
785
758
  }
786
759
  const valueIsUndefined = /* @__PURE__ */ isUndefined(registration.value);
@@ -830,12 +803,16 @@ function useRegistry(options) {
830
803
  }
831
804
  if (removed.length === 0) return;
832
805
  invalidate();
833
- if (events) for (const ticket of removed) emit("unregister:ticket", ticket);
806
+ for (const ticket of removed) queueEmit("unregister:ticket", ticket);
834
807
  needsReindex = true;
835
808
  }
836
809
  function seek(direction = "first", from, predicate) {
837
810
  if (collection.size === 0) return void 0;
838
811
  if (needsReindex) reindex();
812
+ if (!predicate && /* @__PURE__ */ isUndefined(from)) {
813
+ const tickets$1 = values();
814
+ return direction === "first" ? tickets$1[0] : tickets$1.at(-1);
815
+ }
839
816
  const tickets = values();
840
817
  const index = /* @__PURE__ */ isUndefined(from) ? void 0 : Math.max(0, Math.min(from, tickets.length - 1));
841
818
  if (direction === "last") {
@@ -922,21 +899,6 @@ function createRegistryContext(_options = {}) {
922
899
  //#endregion
923
900
  //#region src/composables/useSelection/index.ts
924
901
  /**
925
- * @module useSelection
926
- *
927
- * @remarks
928
- * Base composable for managing selected items in a collection with Set-based tracking.
929
- *
930
- * Key features:
931
- * - Set-based selectedIds for O(1) selection checks
932
- * - Mandatory selection mode (prevents deselecting last item)
933
- * - Auto-enrollment option (selects non-disabled items on register)
934
- * - Disabled item filtering
935
- * - Computed selectedItems and selectedValues Sets
936
- *
937
- * Extends useRegistry and serves as the base for useSingle, useGroup, useStep, and useFeatures.
938
- */
939
- /**
940
902
  * Creates a new selection instance for managing multiple selected items.
941
903
  *
942
904
  * Extends `useRegistry` with selection tracking via a reactive `Set` of selected IDs.
@@ -1176,20 +1138,6 @@ function toArray(value) {
1176
1138
  //#endregion
1177
1139
  //#region src/composables/useProxyModel/index.ts
1178
1140
  /**
1179
- * @module useProxyModel
1180
- *
1181
- * @remarks
1182
- * Proxy composable for bidirectional sync between selection registry and v-model.
1183
- *
1184
- * Key features:
1185
- * - Bidirectional synchronization
1186
- * - Array and single-value modes
1187
- * - Automatic cleanup on scope disposal
1188
- * - Perfect for form controls with selection backing
1189
- *
1190
- * Bridges the gap between selection composables and Vue's v-model.
1191
- */
1192
- /**
1193
1141
  * Syncs a ref with a selection registry bidirectionally.
1194
1142
  *
1195
1143
  * @param registry The selection registry to bind to.
@@ -1262,7 +1210,8 @@ function useProxyModel(registry, model, options) {
1262
1210
  flush: "sync",
1263
1211
  deep: multiple
1264
1212
  });
1265
- function onRegister(ticket) {
1213
+ function onRegister(data) {
1214
+ const ticket = data;
1266
1215
  if (!pending.has(ticket.value) || ticket.disabled) return;
1267
1216
  registryWatch.pause();
1268
1217
  modelWatch.pause();
@@ -1284,21 +1233,6 @@ function useProxyModel(registry, model, options) {
1284
1233
  //#endregion
1285
1234
  //#region src/composables/useProxyRegistry/index.ts
1286
1235
  /**
1287
- * @module useProxyRegistry
1288
- *
1289
- * @remarks
1290
- * Proxy composable for reactive registry keys, values, entries, and size.
1291
- *
1292
- * Key features:
1293
- * - Reactive proxy for registry data
1294
- * - Deep or shallow reactivity options
1295
- * - Event-based updates
1296
- * - Automatic cleanup on scope disposal
1297
- * - Transforms Map-based registry into reactive refs
1298
- *
1299
- * Perfect for exposing registry data as reactive computed properties.
1300
- */
1301
- /**
1302
1236
  * Creates a proxy registry that provides reactive objects for registry data.
1303
1237
  *
1304
1238
  * @param registry The registry instance to proxy.
@@ -1348,26 +1282,6 @@ function useProxyRegistry(registry, options) {
1348
1282
  //#endregion
1349
1283
  //#region src/composables/useGroup/index.ts
1350
1284
  /**
1351
- * @module useGroup
1352
- *
1353
- * @remarks
1354
- * Multi-selection composable that extends useSelection with batch operations and tri-state support.
1355
- *
1356
- * Key features:
1357
- * - Batch operations (select/unselect/toggle accept ID | ID[])
1358
- * - Tri-state support via mixed/indeterminate state (mix/unmix)
1359
- * - selectedIndexes computed Set for position-based tracking
1360
- * - Perfect for checkbox trees, multi-select dropdowns, filter panels
1361
- *
1362
- * Tri-state behavior:
1363
- * - Items can be selected, mixed (indeterminate), or unselected
1364
- * - select() clears mixed state, mix() clears selected state (mutually exclusive)
1365
- * - toggle() on a mixed item selects it (resolves positively)
1366
- *
1367
- * Inheritance chain: useRegistry → useSelection → useGroup
1368
- * Extended by: useFeatures
1369
- */
1370
- /**
1371
1285
  * Creates a new group instance with batch selection and tri-state support.
1372
1286
  *
1373
1287
  * Extends `createSelection` to support selecting, unselecting, and toggling multiple items
@@ -1632,1364 +1546,1244 @@ function useGroup(namespace = "v0:group") {
1632
1546
  }
1633
1547
 
1634
1548
  //#endregion
1635
- //#region src/composables/useHydration/index.ts
1549
+ //#region src/composables/useLocale/adapters/v0.ts
1636
1550
  /**
1637
- * @module useHydration
1551
+ * Vuetify0.x locale adapter implementation
1638
1552
  *
1639
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
1553
+ * This adapter provides translation and number formatting
1554
+ * capabilities using the Intl API and supports both
1555
+ * numbered ({0}, {1}) and named ({name}) variables in translation strings.
1556
+ */
1557
+ var Vuetify0LocaleAdapter = class {
1558
+ t(message, ...params) {
1559
+ let resolvedMessage = message;
1560
+ if (params.length > 0 && /* @__PURE__ */ isObject(params[0])) {
1561
+ const variables = params[0];
1562
+ resolvedMessage = resolvedMessage.replace(/{([a-zA-Z][a-zA-Z0-9_]*)}/g, (match, name) => {
1563
+ return /* @__PURE__ */ isUndefined(variables[name]) ? match : String(variables[name]);
1564
+ });
1565
+ params = params.slice(1);
1566
+ }
1567
+ resolvedMessage = resolvedMessage.replace(/\{(\d+)\}/g, (match, index) => {
1568
+ const idx = Number.parseInt(index, 10);
1569
+ if (!/* @__PURE__ */ isUndefined(params[idx])) return String(params[idx]);
1570
+ return match;
1571
+ });
1572
+ return resolvedMessage;
1573
+ }
1574
+ n(value, locale, ...params) {
1575
+ if (!IN_BROWSER || !locale) return value.toString();
1576
+ const options = params[0];
1577
+ return new Intl.NumberFormat(String(locale), options).format(value);
1578
+ }
1579
+ };
1580
+
1581
+ //#endregion
1582
+ //#region src/composables/useSingle/index.ts
1583
+ /**
1584
+ * Creates a new single selection instance that enforces only one selected item at a time.
1640
1585
  *
1641
- * @remarks
1642
- * SSR hydration state management composable.
1586
+ * Extends `createSelection` by automatically clearing previous selections when a new item is selected.
1587
+ * Adds computed singular properties: `selectedId`, `selectedItem`, `selectedIndex`, `selectedValue`.
1643
1588
  *
1644
- * Key features:
1645
- * - Hydration state detection (browser vs SSR)
1646
- * - Root component detection
1647
- * - Readonly hydration state refs
1648
- * - Plugin installation support
1649
- * - Perfect for hydration-safe rendering
1589
+ * @param options The options for the single selection instance.
1590
+ * @template Z The type of the single selection ticket.
1591
+ * @template E The type of the single selection context.
1592
+ * @returns A new single selection instance with single-selection enforcement.
1650
1593
  *
1651
- * Essential for composables that need to behave differently during SSR vs client-side.
1652
- */
1653
- /**
1654
- * Creates a new hydration instance.
1594
+ * @remarks
1595
+ * **Key Differences from `createSelection`:**
1596
+ * - Automatically clears `selectedIds` before selecting a new item (enforces single selection)
1597
+ * - Provides singular computed properties instead of plural sets
1598
+ * - Perfect for tabs, radio buttons, theme selectors, and other single-choice UI components
1655
1599
  *
1656
- * @returns A new hydration instance.
1600
+ * **Computed Properties:**
1601
+ * - `selectedId`: The ID of the selected item (undefined if none selected)
1602
+ * - `selectedItem`: The selected ticket object (undefined if none selected)
1603
+ * - `selectedIndex`: The index of the selected item (-1 if none selected)
1604
+ * - `selectedValue`: The value of the selected item (undefined if none selected)
1657
1605
  *
1658
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
1606
+ * **Inheritance Chain:**
1607
+ * `useRegistry` → `createSelection` → `createSingle` → `createStep`
1608
+ *
1609
+ * @see https://0.vuetifyjs.com/composables/selection/use-single
1659
1610
  *
1660
1611
  * @example
1661
1612
  * ```ts
1662
- * import { createHydration } from '@vuetify/v0'
1613
+ * import { createSingle } from '@vuetify/v0'
1663
1614
  *
1664
- * const hydration = createHydration()
1665
- * console.log(hydration.isHydrated.value) // false
1666
- * hydration.hydrate()
1667
- * console.log(hydration.isHydrated.value) // true
1668
- * ```
1669
- */
1670
- function createHydration() {
1671
- const isHydrated = shallowRef(false);
1672
- function hydrate() {
1673
- isHydrated.value = true;
1674
- }
1675
- return {
1676
- isHydrated: shallowReadonly(isHydrated),
1677
- hydrate
1678
- };
1679
- }
1680
- function createFallbackHydration() {
1681
- return {
1682
- isHydrated: shallowReadonly(shallowRef(true)),
1683
- hydrate: () => {}
1684
- };
1685
- }
1686
- /**
1687
- * Creates a new hydration context trinity.
1615
+ * const tabs = createSingle({ mandatory: true })
1688
1616
  *
1689
- * @param options Options for creating the hydration context.
1690
- * @template E The type of the hydration context.
1691
- * @returns A new hydration context trinity.
1617
+ * tabs.onboard([
1618
+ * { id: 'home', value: 'Home' },
1619
+ * { id: 'about', value: 'About' },
1620
+ * { id: 'contact', value: 'Contact' },
1621
+ * ])
1692
1622
  *
1693
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
1623
+ * tabs.first() // Select first tab
1694
1624
  *
1695
- * @example
1696
- * ```ts
1697
- * import { createHydrationContext } from '@vuetify/v0'
1625
+ * console.log(tabs.selectedId.value) // 'home'
1626
+ * console.log(tabs.selectedIndex.value) // 0
1698
1627
  *
1699
- * export const [useHydrationContext, provideHydrationContext, context] = createHydrationContext({
1700
- * namespace: 'app:hydration',
1701
- * })
1628
+ * tabs.select('about') // Switch to about tab
1629
+ * console.log(tabs.selectedId.value) // 'about'
1630
+ * console.log(tabs.selectedIds.size) // 1 (always enforces single selection)
1702
1631
  * ```
1703
1632
  */
1704
- function createHydrationContext(_options = {}) {
1705
- const { namespace = "v0:hydration" } = _options;
1706
- const [useHydrationContext, _provideHydrationContext] = createContext(namespace);
1707
- const context = createHydration();
1708
- function provideHydrationContext(_context = context, app) {
1709
- return _provideHydrationContext(_context, app);
1633
+ function createSingle(_options = {}) {
1634
+ const { mandatory = false, multiple = false, ...options } = _options;
1635
+ const registry = createSelection({
1636
+ ...options,
1637
+ mandatory,
1638
+ multiple
1639
+ });
1640
+ const selectedId = computed(() => registry.selectedIds.values().next().value);
1641
+ const selectedItem = computed(() => registry.selectedItems.value.values().next().value);
1642
+ const selectedIndex = computed(() => selectedItem.value?.index ?? -1);
1643
+ const selectedValue = computed(() => selectedItem.value?.value);
1644
+ function unselect(id) {
1645
+ if (mandatory && registry.selectedIds.size === 1) return;
1646
+ registry.selectedIds.delete(id);
1710
1647
  }
1711
- return createTrinity(useHydrationContext, provideHydrationContext, context);
1648
+ function toggle(id) {
1649
+ if (registry.selectedIds.has(id)) unselect(id);
1650
+ else registry.select(id);
1651
+ }
1652
+ return {
1653
+ ...registry,
1654
+ selectedId,
1655
+ selectedItem,
1656
+ selectedIndex,
1657
+ selectedValue,
1658
+ unselect,
1659
+ toggle,
1660
+ get size() {
1661
+ return registry.size;
1662
+ }
1663
+ };
1712
1664
  }
1713
1665
  /**
1714
- * Creates a new hydration plugin.
1666
+ * Creates a new single selection context.
1715
1667
  *
1716
- * @param options The options for the hydration plugin.
1717
- * @template E The type of the hydration context.
1718
- * @returns A new hydration plugin.
1668
+ * @param options The options for the single selection context.
1669
+ * @template Z The type of the single selection ticket.
1670
+ * @template E The type of the single selection context.
1671
+ * @returns A new single selection context.
1719
1672
  *
1720
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
1673
+ * @see https://0.vuetifyjs.com/composables/selection/use-single
1721
1674
  *
1722
1675
  * @example
1723
1676
  * ```ts
1724
- * import { createApp } from 'vue'
1725
- * import { createHydrationPlugin } from '@vuetify/v0'
1726
- * import App from './App.vue'
1677
+ * import { createSingleContext } from '@vuetify/v0'
1727
1678
  *
1728
- * const app = createApp(App)
1679
+ * // With default namespace 'v0:single'
1680
+ * export const [useSingle, provideSingle, context] = createSingleContext()
1729
1681
  *
1730
- * app.use(createHydrationPlugin())
1682
+ * // In a parent component:
1683
+ * provideSingle()
1731
1684
  *
1732
- * app.mount('#app')
1685
+ * // In a child component:
1686
+ * const single = useSingle()
1687
+ * single.select('tab-1')
1733
1688
  * ```
1734
1689
  */
1735
- function createHydrationPlugin(_options = {}) {
1736
- const { namespace = "v0:hydration", ...options } = _options;
1737
- const [, provideHydrationContext, context] = createHydrationContext({
1738
- ...options,
1739
- namespace
1740
- });
1741
- return createPlugin({
1742
- namespace,
1743
- provide: (app) => {
1744
- provideHydrationContext(context, app);
1745
- },
1746
- setup: (app) => {
1747
- app.mixin({ mounted() {
1748
- if (this.$parent !== null) return;
1749
- context.hydrate();
1750
- } });
1751
- }
1752
- });
1690
+ function createSingleContext(_options = {}) {
1691
+ const { namespace = "v0:single", ...options } = _options;
1692
+ const [useSingleContext, _provideSingleContext] = createContext(namespace);
1693
+ const context = createSingle(options);
1694
+ function provideSingleContext(_context = context, app) {
1695
+ return _provideSingleContext(_context, app);
1696
+ }
1697
+ return createTrinity(useSingleContext, provideSingleContext, context);
1753
1698
  }
1754
1699
  /**
1755
- * Returns the current hydration instance.
1700
+ * Returns the current single selection instance.
1756
1701
  *
1757
- * @param namespace The namespace for the hydration context. Defaults to `v0:hydration`.
1758
- * @returns The current hydration instance.
1702
+ * @param namespace The namespace for the single selection context. Defaults to `'v0:single'`.
1703
+ * @returns The current single selection instance.
1759
1704
  *
1760
- * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
1705
+ * @see https://0.vuetifyjs.com/composables/selection/use-single
1761
1706
  *
1762
1707
  * @example
1763
1708
  * ```vue
1764
1709
  * <script setup lang="ts">
1765
- * import { useHydration } from '@vuetify/v0'
1710
+ * import { useSingle } from '@vuetify/v0'
1766
1711
  *
1767
- * const hydration = useHydration()
1712
+ * const tabs = useSingle()
1768
1713
  * <\/script>
1769
1714
  *
1770
1715
  * <template>
1771
1716
  * <div>
1772
- * <p>Is hydrated: {{ hydration.isHydrated.value }}</p>
1717
+ * <p>Selected: {{ tabs.selectedId }}</p>
1773
1718
  * </div>
1774
1719
  * </template>
1775
1720
  * ```
1776
1721
  */
1777
- function useHydration(namespace = "v0:hydration") {
1778
- const fallback = createFallbackHydration();
1779
- if (!getCurrentInstance()) return fallback;
1780
- try {
1781
- return useContext(namespace, fallback);
1782
- } catch {
1783
- return fallback;
1784
- }
1722
+ function useSingle(namespace = "v0:single") {
1723
+ return useContext(namespace);
1785
1724
  }
1786
1725
 
1787
1726
  //#endregion
1788
- //#region src/composables/useResizeObserver/index.ts
1789
- /**
1790
- * @module useResizeObserver
1791
- *
1792
- * @remarks
1793
- * ResizeObserver composable with lifecycle management.
1794
- *
1795
- * Key features:
1796
- * - ResizeObserver API wrapper
1797
- * - Pause/resume/stop functionality
1798
- * - Automatic cleanup on unmount
1799
- * - SSR-safe (checks SUPPORTS_OBSERVER)
1800
- * - Hydration-aware
1801
- * - Box model options (content-box/border-box)
1802
- *
1803
- * Perfect for responsive components and size-based rendering.
1804
- */
1727
+ //#region src/composables/useTokens/index.ts
1805
1728
  /**
1806
- * A composable that uses the Resize Observer API to detect when an element's
1807
- * size changes.
1729
+ * Creates a new token instance.
1808
1730
  *
1809
- * @param target The element to observe.
1810
- * @param callback The callback to execute when the element's size changes.
1811
- * @param options The options for the Resize Observer.
1812
- * @returns An object with methods to control the observer.
1731
+ * @param tokens The tokens to use.
1732
+ * @param options The options for the token instance.
1733
+ * @template Z The type of the token ticket.
1734
+ * @template E The type of the token context.
1735
+ * @returns A new token instance.
1813
1736
  *
1814
- * @see https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
1815
- * @see https://0.vuetifyjs.com/composables/system/use-resize-observer
1737
+ * @see https://www.designtokens.org/tr/drafts/format/
1738
+ * @see https://0.vuetifyjs.com/composables/registration/use-tokens
1816
1739
  *
1817
1740
  * @example
1818
1741
  * ```ts
1819
- * import { ref } from 'vue'
1820
- * import { useResizeObserver } from '@vuetify/v0'
1821
- *
1822
- * const el = ref<HTMLElement>()
1823
- * const width = ref(0)
1824
- * const height = ref(0)
1742
+ * import { useTokens } from '@vuetify/v0'
1825
1743
  *
1826
- * const { pause, resume, isPaused } = useResizeObserver(
1827
- * el,
1828
- * (entries) => {
1829
- * const entry = entries[0]
1830
- * if (entry) {
1831
- * width.value = entry.contentRect.width
1832
- * height.value = entry.contentRect.height
1833
- * console.log('Size changed:', width.value, 'x', height.value)
1834
- * }
1744
+ * const tokens = useTokens({
1745
+ * colors: {
1746
+ * primary: '#3b82f6',
1747
+ * secondary: '{colors.primary}', // Alias reference
1835
1748
  * },
1836
- * { immediate: true }
1837
- * )
1838
- *
1839
- * // Pause observation
1840
- * pause()
1749
+ * })
1841
1750
  *
1842
- * // Resume observation
1843
- * resume()
1751
+ * console.log(tokens.resolve('{colors.primary}')) // '#3b82f6'
1752
+ * console.log(tokens.resolve('{colors.secondary}')) // '#3b82f6'
1844
1753
  * ```
1845
1754
  */
1846
- function useResizeObserver(target, callback, options = {}) {
1847
- const { isHydrated } = useHydration();
1848
- const observer = shallowRef();
1849
- const isPaused = shallowRef(false);
1850
- const isActive = toRef(() => !!observer.value);
1851
- function setup() {
1852
- if (!isHydrated.value || !SUPPORTS_OBSERVER || !target.value || isPaused.value) return;
1853
- observer.value = new ResizeObserver((entries) => {
1854
- callback(entries.map((entry) => ({
1855
- contentRect: {
1856
- width: entry.contentRect.width,
1857
- height: entry.contentRect.height,
1858
- top: entry.contentRect.top,
1859
- left: entry.contentRect.left
1860
- },
1861
- target: entry.target
1862
- })));
1863
- });
1864
- observer.value.observe(target.value, { box: options.box || "content-box" });
1865
- if (options.immediate) {
1866
- const rect = target.value.getBoundingClientRect();
1867
- callback([{
1868
- contentRect: {
1869
- width: rect.width,
1870
- height: rect.height,
1871
- top: rect.top,
1872
- left: rect.left
1873
- },
1874
- target: target.value
1875
- }]);
1876
- }
1877
- }
1878
- watch([isHydrated, target], () => {
1879
- cleanup();
1880
- setup();
1881
- }, { immediate: true });
1882
- function cleanup() {
1883
- if (observer.value) {
1884
- observer.value.disconnect();
1885
- observer.value = void 0;
1886
- }
1887
- }
1888
- function pause() {
1889
- isPaused.value = true;
1890
- observer.value?.disconnect();
1755
+ function createTokens(tokens = {}, options = {}) {
1756
+ const logger = useLogger();
1757
+ const registry = useRegistry(options);
1758
+ const cache = /* @__PURE__ */ new Map();
1759
+ registry.onboard(flatten(tokens, options.prefix, !!options.flat));
1760
+ function isAlias(token) {
1761
+ return /* @__PURE__ */ isString(token) && token.length > 2 && token[0] === "{" && token.at(-1) === "}";
1891
1762
  }
1892
- function resume() {
1893
- isPaused.value = false;
1894
- setup();
1763
+ function isTokenAlias(value) {
1764
+ return /* @__PURE__ */ isObject(value) && "$value" in value;
1895
1765
  }
1896
- function stop() {
1897
- cleanup();
1766
+ function resolve(token, visited = /* @__PURE__ */ new Set()) {
1767
+ const cacheKey = /* @__PURE__ */ isString(token) ? token : JSON.stringify(token);
1768
+ const cached = cache.get(cacheKey);
1769
+ if (!/* @__PURE__ */ isUndefined(cached)) return cached;
1770
+ const reference = isTokenAlias(token) ? token.$value : token;
1771
+ const isAliasReference = /* @__PURE__ */ isString(reference) && isAlias(reference);
1772
+ const clean = isAliasReference ? reference.slice(1, -1) : String(reference);
1773
+ if (visited.has(clean)) {
1774
+ logger.warn(`Circular alias detected for "${clean}"`);
1775
+ cache.set(cacheKey, void 0);
1776
+ return;
1777
+ }
1778
+ visited.add(clean);
1779
+ let found = registry.get(clean);
1780
+ let segments = [];
1781
+ if (!found && clean.includes(".")) {
1782
+ const parts = clean.split(".");
1783
+ for (let i = parts.length - 1; i > 0; i--) {
1784
+ const prefix = parts.slice(0, i).join(".");
1785
+ const suffix = parts.slice(i);
1786
+ const candidate = registry.get(prefix);
1787
+ if (!/* @__PURE__ */ isUndefined(candidate?.value)) {
1788
+ found = candidate;
1789
+ segments = suffix;
1790
+ break;
1791
+ }
1792
+ }
1793
+ }
1794
+ if (/* @__PURE__ */ isUndefined(found?.value)) {
1795
+ if (isAliasReference) logger.warn(`Alias not found for "${String(reference)}"`);
1796
+ cache.set(cacheKey, void 0);
1797
+ return;
1798
+ }
1799
+ let result;
1800
+ let current = found.value;
1801
+ if (segments.length > 0) {
1802
+ if (isTokenAlias(current)) current = current.$value;
1803
+ for (const segment of segments) {
1804
+ if (!/* @__PURE__ */ isObject(current) || !(segment in current)) {
1805
+ current = void 0;
1806
+ break;
1807
+ }
1808
+ current = current[segment];
1809
+ if (isTokenAlias(current)) current = current.$value;
1810
+ }
1811
+ if (/* @__PURE__ */ isUndefined(current)) {
1812
+ logger.warn(`Path not found inside "${clean}": ${segments.join(".")}`);
1813
+ cache.set(cacheKey, void 0);
1814
+ return;
1815
+ }
1816
+ result = current;
1817
+ } else if (isTokenAlias(current)) {
1818
+ const inner = current.$value;
1819
+ if (/* @__PURE__ */ isString(inner) && isAlias(inner)) return resolve(inner, visited);
1820
+ result = inner;
1821
+ } else if (/* @__PURE__ */ isString(current) && isAlias(current)) return resolve(current, visited);
1822
+ else result = current;
1823
+ cache.set(cacheKey, result);
1824
+ return result;
1898
1825
  }
1899
- onScopeDispose(stop, true);
1900
1826
  return {
1901
- isActive: shallowReadonly(isActive),
1902
- isPaused: shallowReadonly(isPaused),
1903
- pause,
1904
- resume,
1905
- stop
1827
+ ...registry,
1828
+ resolve,
1829
+ isAlias,
1830
+ get size() {
1831
+ return registry.size;
1832
+ }
1906
1833
  };
1907
1834
  }
1908
1835
  /**
1909
- * A convenience composable that uses the Resize Observer API to track an
1910
- * element's size.
1836
+ * Creates a new token context.
1911
1837
  *
1912
- * @param target The element to observe.
1913
- * @returns An object with the element's width and height.
1838
+ * @param namespace The namespace for the token context.
1839
+ * @param tokens The tokens to use.
1840
+ * @template Z The type of the token ticket.
1841
+ * @template E The type of the token context.
1842
+ * @returns A new token context.
1914
1843
  *
1915
- * @see https://0.vuetifyjs.com/composables/system/use-resize-observer#use-element-size
1844
+ * @see https://0.vuetifyjs.com/composables/registration/use-tokens
1916
1845
  *
1917
1846
  * @example
1918
1847
  * ```ts
1919
- * import { ref, watchEffect } from 'vue'
1920
- * import { useElementSize } from '@vuetify/v0'
1921
- *
1922
- * const box = ref<HTMLElement>()
1923
- * const { width, height } = useElementSize(box)
1848
+ * import { createTokensContext } from '@vuetify/v0'
1924
1849
  *
1925
- * // Width and height are reactive refs
1926
- * watchEffect(() => {
1927
- * console.log('Box size:', width.value, 'x', height.value)
1850
+ * export const [useTokens, provideTokens, context] = createTokensContext({
1851
+ * namespace: 'v0:tokens',
1852
+ * tokens: {
1853
+ * colors: {
1854
+ * primary: '#3b82f6',
1855
+ * secondary: '{colors.primary}', // Alias reference
1856
+ * },
1857
+ * },
1928
1858
  * })
1929
1859
  * ```
1930
1860
  */
1931
- function useElementSize(target) {
1932
- const width = shallowRef(0);
1933
- const height = shallowRef(0);
1934
- const { pause: _pause, resume, stop, isActive, isPaused } = useResizeObserver(target, (entries) => {
1935
- const entry = entries[0];
1936
- if (entry) {
1937
- width.value = entry.contentRect.width;
1938
- height.value = entry.contentRect.height;
1939
- }
1940
- }, { immediate: true });
1941
- function pause() {
1942
- width.value = 0;
1943
- height.value = 0;
1944
- _pause();
1861
+ function createTokensContext(_options) {
1862
+ const { namespace = "v0:tokens", tokens = {}, ...options } = _options;
1863
+ const [useTokensContext, _provideTokensContext] = createContext(namespace);
1864
+ const context = createTokens(tokens, options);
1865
+ function provideTokensContext(_context = context, app) {
1866
+ return _provideTokensContext(_context, app);
1945
1867
  }
1946
- return {
1947
- width,
1948
- height,
1949
- isActive,
1950
- isPaused,
1951
- pause,
1952
- resume,
1953
- stop
1954
- };
1868
+ return createTrinity(useTokensContext, provideTokensContext, context);
1955
1869
  }
1956
-
1957
- //#endregion
1958
- //#region src/composables/useOverflow/index.ts
1959
1870
  /**
1960
- * @module useOverflow
1871
+ * Returns the current tokens instance.
1961
1872
  *
1962
- * @remarks
1963
- * Composable for computing how many items fit in a container based on available width.
1964
- * Enables responsive truncation logic for Pagination, Breadcrumbs, and similar components.
1965
- *
1966
- * Key features:
1967
- * - Container width tracking via ResizeObserver
1968
- * - Two modes: variable-width (per-item) or uniform-width (sample-based)
1969
- * - Computes capacity (how many items fit)
1970
- * - SSR-safe with Infinity fallback
1971
- * - Supports reserved space for nav buttons, ellipsis, etc.
1972
- *
1973
- * Use variable mode (default) for items with different widths like Breadcrumbs.
1974
- * Use uniform mode (itemWidth option) for same-width items like Pagination buttons.
1975
- */
1976
- /**
1977
- * Creates a new overflow context for computing how many items fit in a container.
1873
+ * @param namespace The namespace for the tokens context. Defaults to `'v0:tokens'`.
1874
+ * @returns The current tokens instance.
1978
1875
  *
1979
- * @param options Configuration options
1980
- * @returns Overflow context with container ref, capacity, and measurement functions
1876
+ * @see https://0.vuetifyjs.com/composables/registration/use-tokens
1981
1877
  *
1982
- * @example Variable-width mode (Breadcrumbs)
1878
+ * @example
1983
1879
  * ```vue
1984
- * <script lang="ts" setup>
1985
- * import { useTemplateRef } from 'vue'
1986
- * import { createOverflow } from '@vuetify/v0'
1880
+ * <script setup lang="ts">
1881
+ * import { useTokens } from '@vuetify/v0'
1987
1882
  *
1988
- * const containerRef = useTemplateRef('container')
1989
- * const overflow = createOverflow({
1990
- * container: containerRef,
1991
- * gap: 8,
1992
- * reserved: 40,
1993
- * })
1883
+ * const tokens = useTokens()
1994
1884
  * <\/script>
1995
- *
1996
- * <template>
1997
- * <div ref="container">
1998
- * <span
1999
- * v-for="(item, i) in items.slice(0, overflow.capacity.value)"
2000
- * :key="i"
2001
- * :ref="el => overflow.measure(i, el)"
2002
- * >
2003
- * {{ item }}
2004
- * </span>
2005
- * <span v-if="overflow.isOverflowing.value">...</span>
2006
- * </div>
2007
- * </template>
2008
- * ```
2009
- *
2010
- * @example Uniform-width mode (Pagination)
2011
- * ```ts
2012
- * const overflow = createOverflow({
2013
- * container: () => atom.value?.element,
2014
- * itemWidth: buttonWidth,
2015
- * reserved: () => buttonWidth.value * 4,
2016
- * })
2017
1885
  * ```
2018
1886
  */
2019
- function createOverflow(options = {}) {
2020
- const { container: _container, gap = 0, reserved = 0, itemWidth, reverse } = options;
2021
- const container = /* @__PURE__ */ isUndefined(_container) ? shallowRef() : toRef(_container);
2022
- const widths = shallowRef(/* @__PURE__ */ new Map());
2023
- const { width } = useElementSize(container);
2024
- function measure(index, el) {
2025
- if (!el) {
2026
- if (widths.value.has(index)) {
2027
- const next = new Map(widths.value);
2028
- next.delete(index);
2029
- widths.value = next;
1887
+ function useTokens(namespace = "v0:tokens") {
1888
+ return useContext(namespace);
1889
+ }
1890
+ /**
1891
+ * Flattens a nested collection of tokens into a flat array of tokens.
1892
+ * Each token is represented by an object containing its ID & value.
1893
+ * @param tokens The collection of tokens to flatten.
1894
+ * @param prefix An optional prefix to prepend to each token ID.
1895
+ * @returns An array of flattened tokens, each with an ID and value.
1896
+ */
1897
+ function flatten(tokens, prefix = "", flat = false) {
1898
+ const flattened = [];
1899
+ const stack = [{
1900
+ tokens,
1901
+ prefix,
1902
+ flat
1903
+ }];
1904
+ while (stack.length > 0) {
1905
+ const { tokens: currentTokens, prefix: currentPrefix, flat: flat$1 } = stack.pop();
1906
+ const meta = {};
1907
+ for (const k in currentTokens) if (k.startsWith("$")) meta[k] = currentTokens[k];
1908
+ if (Object.keys(meta).length > 0 && currentPrefix) flattened.push({
1909
+ id: currentPrefix,
1910
+ value: meta
1911
+ });
1912
+ for (const key in currentTokens) {
1913
+ if (key.startsWith("$")) continue;
1914
+ const value = currentTokens[key];
1915
+ const id = currentPrefix ? `${currentPrefix}.${key}` : key;
1916
+ if (!/* @__PURE__ */ isObject(value)) {
1917
+ flattened.push({
1918
+ id,
1919
+ value
1920
+ });
1921
+ continue;
2030
1922
  }
2031
- return;
1923
+ if ("$value" in value) {
1924
+ flattened.push({
1925
+ id,
1926
+ value
1927
+ });
1928
+ const inner = value.$value;
1929
+ if (/* @__PURE__ */ isObject(inner) && !flat$1) for (const innerKey in inner) {
1930
+ if (innerKey.startsWith("$")) continue;
1931
+ const child = inner[innerKey];
1932
+ const childId = `${id}.${innerKey}`;
1933
+ if (!/* @__PURE__ */ isObject(child)) flattened.push({
1934
+ id: childId,
1935
+ value: child
1936
+ });
1937
+ else if ("$value" in child) flattened.push({
1938
+ id: childId,
1939
+ value: child
1940
+ });
1941
+ else stack.push({
1942
+ tokens: child,
1943
+ prefix: childId,
1944
+ flat: flat$1
1945
+ });
1946
+ }
1947
+ continue;
1948
+ }
1949
+ if (flat$1) {
1950
+ flattened.push({
1951
+ id,
1952
+ value
1953
+ });
1954
+ continue;
1955
+ }
1956
+ stack.push({
1957
+ tokens: value,
1958
+ prefix: id,
1959
+ flat: flat$1
1960
+ });
2032
1961
  }
2033
- const style = getComputedStyle(el);
2034
- const marginX = Number.parseFloat(style.marginLeft) + Number.parseFloat(style.marginRight);
2035
- const w = el.offsetWidth + marginX;
2036
- if (widths.value.get(index) !== w) widths.value = new Map(widths.value).set(index, w);
2037
1962
  }
2038
- function reset() {
2039
- widths.value = /* @__PURE__ */ new Map();
1963
+ return flattened;
1964
+ }
1965
+
1966
+ //#endregion
1967
+ //#region src/composables/useLocale/index.ts
1968
+ /**
1969
+ * Creates a new locale instance.
1970
+ *
1971
+ * @param options The options for the locale instance.
1972
+ * @template Z The type of the locale ticket.
1973
+ * @template E The type of the locale context.
1974
+ * @returns A new locale instance.
1975
+ *
1976
+ * @see https://0.vuetifyjs.com/composables/plugins/use-locale
1977
+ */
1978
+ function createLocale(_options = {}) {
1979
+ const { adapter = new Vuetify0LocaleAdapter(), messages = {}, ...options } = _options;
1980
+ const tokens = createTokens(messages);
1981
+ const registry = createSingle(options);
1982
+ for (const id in messages) {
1983
+ registry.register({ id });
1984
+ if (id === options.default && !registry.selectedId.value) registry.select(id);
2040
1985
  }
2041
- const total = computed(() => {
2042
- const g = toValue(gap);
2043
- let sum = 0;
2044
- let count = 0;
2045
- for (const w of widths.value.values()) {
2046
- sum += w + (count > 0 ? g : 0);
2047
- count++;
1986
+ function t(key, params, fallback) {
1987
+ const locale = registry.selectedId.value;
1988
+ const args = toArray(params);
1989
+ if (!locale) return adapter.t(fallback ?? key, ...args);
1990
+ const path = `${locale}.${key}`;
1991
+ const message = tokens.get(path)?.value;
1992
+ const template = /* @__PURE__ */ isString(message) ? resolve(locale, message) : fallback ?? key;
1993
+ return adapter.t(template, ...args);
1994
+ }
1995
+ function n(value, ...params) {
1996
+ return adapter.n(value, registry.selectedId.value, ...params);
1997
+ }
1998
+ function resolve(locale, str) {
1999
+ return str.replace(/{([a-zA-Z0-9.-_]+)}/g, (match, key) => {
2000
+ const [prefix, ...rest] = key.split(".");
2001
+ const target = registry.has(prefix) ? prefix : locale;
2002
+ const path = `${target}.${registry.has(prefix) ? rest.join(".") : key}`;
2003
+ const resolved = tokens.get(path)?.value;
2004
+ if (/* @__PURE__ */ isString(resolved)) return resolve(target, resolved);
2005
+ return match;
2006
+ });
2007
+ }
2008
+ return {
2009
+ ...registry,
2010
+ t,
2011
+ n,
2012
+ get size() {
2013
+ return registry.size;
2048
2014
  }
2049
- return sum;
2050
- });
2015
+ };
2016
+ }
2017
+ function createLocaleFallback() {
2051
2018
  return {
2052
- container,
2053
- width,
2054
- capacity: computed(() => {
2055
- const available = width.value - toValue(reserved);
2056
- if (width.value === 0) return Infinity;
2057
- if (available <= 0) return 0;
2058
- const g = toValue(gap);
2059
- const uniformWidth = toValue(itemWidth);
2060
- if (uniformWidth && uniformWidth > 0) {
2061
- const first = uniformWidth;
2062
- const subsequent = uniformWidth + g;
2063
- if (available < first) return 0;
2064
- return Math.max(1, Math.floor((available - first) / subsequent) + 1);
2065
- }
2066
- const entries = [...widths.value.entries()].toSorted((a, b) => a[0] - b[0]);
2067
- if (toValue(reverse)) entries.reverse();
2068
- let sum = 0;
2069
- let count = 0;
2070
- for (const [, w] of entries) {
2071
- const next = sum + w + (count > 0 ? g : 0);
2072
- if (next > available) break;
2073
- sum = next;
2074
- count++;
2075
- }
2076
- return count;
2077
- }),
2078
- total,
2079
- isOverflowing: toRef(() => {
2080
- return total.value > width.value - toValue(reserved);
2081
- }),
2082
- measure,
2083
- reset
2019
+ size: 0,
2020
+ t: (key, _params, fallback) => fallback ?? key,
2021
+ n: String
2084
2022
  };
2085
2023
  }
2086
2024
  /**
2087
- * Creates an overflow context with dependency injection support.
2025
+ * Creates a new locale context.
2088
2026
  *
2089
- * @param options Configuration options including namespace
2090
- * @returns Trinity tuple: [useContext, provideContext, defaultContext]
2027
+ * @param options The options for the locale context.
2028
+ * @template Z The type of the locale ticket.
2029
+ * @template E The type of the locale context.
2030
+ * @returns A new locale context.
2031
+ *
2032
+ * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2091
2033
  *
2092
2034
  * @example
2093
2035
  * ```ts
2094
- * // Create injectable context
2095
- * const [useOverflow, provideOverflow, overflow] = createOverflowContext({
2096
- * namespace: 'my-overflow',
2097
- * gap: 8,
2098
- * reserved: 160,
2036
+ * import { createLocaleContext } from '@vuetify/v0'
2037
+ *
2038
+ * export const [useAppLocale, provideAppLocale, appLocale] = createLocaleContext({
2039
+ * namespace: 'app:locale',
2040
+ * messages: {
2041
+ * en: { hello: 'Hello' },
2042
+ * es: { hello: 'Hola' },
2043
+ * },
2099
2044
  * })
2100
2045
  *
2101
- * // In parent component
2102
- * provideOverflow()
2046
+ * // In a parent component:
2047
+ * provideAppLocale()
2103
2048
  *
2104
- * // In child component
2105
- * const overflow = useOverflow()
2049
+ * // In a child component:
2050
+ * const locale = useAppLocale()
2051
+ * locale.select('es')
2106
2052
  * ```
2107
2053
  */
2108
- function createOverflowContext(_options = {}) {
2109
- const { namespace = "v0:overflow", ...options } = _options;
2110
- const [useOverflowContext, _provideOverflowContext] = createContext(namespace);
2111
- const context = createOverflow(options);
2112
- function provideOverflowContext(_context = context, app) {
2113
- return _provideOverflowContext(_context, app);
2054
+ function createLocaleContext(_options = {}) {
2055
+ const { namespace = "v0:locale", ...options } = _options;
2056
+ const [useLocaleContext, _provideLocaleContext] = createContext(namespace);
2057
+ const context = createLocale(options);
2058
+ function provideLocaleContext(_context = context, app) {
2059
+ return _provideLocaleContext(_context, app);
2114
2060
  }
2115
- return createTrinity(useOverflowContext, provideOverflowContext, context);
2061
+ return createTrinity(useLocaleContext, provideLocaleContext, context);
2116
2062
  }
2117
2063
  /**
2118
- * Returns the current overflow context from dependency injection.
2064
+ * Creates a new locale plugin.
2119
2065
  *
2120
- * @param namespace The namespace for the overflow context. Defaults to `v0:overflow`.
2121
- * @returns The current overflow context.
2066
+ * @param options The options for the locale plugin.
2067
+ * @template Z The type of the locale ticket.
2068
+ * @template E The type of the locale context.
2069
+ * @template R The type of the token ticket.
2070
+ * @template O The type of the token context.
2071
+ * @returns A new locale plugin.
2122
2072
  *
2123
- * @example
2124
- * ```vue
2125
- * <script lang="ts" setup>
2126
- * import { useOverflow } from '@vuetify/v0'
2073
+ * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2074
+ */
2075
+ function createLocalePlugin(_options = {}) {
2076
+ const { namespace = "v0:locale", adapter = new Vuetify0LocaleAdapter(), messages = {}, ...options } = _options;
2077
+ const [, provideLocaleContext, context] = createLocaleContext({
2078
+ ...options,
2079
+ namespace,
2080
+ adapter,
2081
+ messages
2082
+ });
2083
+ return createPlugin({
2084
+ namespace,
2085
+ provide: (app) => {
2086
+ provideLocaleContext(context, app);
2087
+ }
2088
+ });
2089
+ }
2090
+ /**
2091
+ * Returns the current locale instance.
2127
2092
  *
2128
- * // Inject overflow context provided by parent
2129
- * const overflow = useOverflow()
2130
- * <\/script>
2093
+ * @returns The current locale instance.
2131
2094
  *
2132
- * <template>
2133
- * <div>
2134
- * <p>Capacity: {{ overflow.capacity.value }}</p>
2135
- * </div>
2136
- * </template>
2137
- * ```
2095
+ * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2138
2096
  */
2139
- function useOverflow(namespace = "v0:overflow") {
2140
- return useContext(namespace);
2097
+ function useLocale(namespace = "v0:locale") {
2098
+ const fallback = createLocaleFallback();
2099
+ if (!getCurrentInstance()) return fallback;
2100
+ try {
2101
+ return useContext(namespace, fallback);
2102
+ } catch {
2103
+ return fallback;
2104
+ }
2141
2105
  }
2142
2106
 
2143
2107
  //#endregion
2144
- //#region src/composables/usePagination/index.ts
2108
+ //#region src/composables/useHydration/index.ts
2145
2109
  /**
2146
- * @module usePagination
2147
- *
2148
- * @remarks
2149
- * Lightweight pagination composable for navigating through pages.
2110
+ * Creates a new hydration instance.
2150
2111
  *
2151
- * Key features:
2152
- * - No registry overhead - just a bounded integer
2153
- * - Direct ref support for v-model compatibility
2154
- * - Navigation methods: next, prev, first, last
2155
- * - Computed visible items with ellipsis
2156
- * - Trinity pattern for dependency injection
2157
- *
2158
- * Unlike registry-based composables, pagination tracks a single number
2159
- * within a range, making it efficient for large page counts.
2160
- */
2161
- /**
2162
- * Creates a pagination instance.
2112
+ * @returns A new hydration instance.
2163
2113
  *
2164
- * @param options The options for the pagination instance.
2165
- * @returns A pagination context with navigation methods.
2114
+ * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2166
2115
  *
2167
2116
  * @example
2168
2117
  * ```ts
2169
- * import { createPagination } from '@vuetify/v0'
2170
- *
2171
- * // Basic usage
2172
- * const pagination = createPagination({ size: 100 })
2173
- * pagination.next()
2174
- * pagination.items.value // [{ type: 'page', value: 1 }, { type: 'page', value: 2 }, ...]
2175
- *
2176
- * // With v-model (pass a ref)
2177
- * const page = ref(1)
2178
- * const pagination = createPagination({ page, size: 100 })
2179
- * // Mutating pagination.page or the passed ref syncs both
2180
- * ```
2181
- */
2182
- function createPagination(_options = {}) {
2183
- const { page: _page = 1, itemsPerPage: _itemsPerPage = 10, size: _size = 0, visible: _visible = 7, ellipsis = "..." } = _options;
2184
- const page = isRef(_page) ? _page : shallowRef(_page);
2185
- const pages = computed(() => {
2186
- const size = toValue(_size);
2187
- const perPage = toValue(_itemsPerPage);
2188
- if (size <= 0 || /* @__PURE__ */ isNaN(size)) return 0;
2189
- return Math.ceil(size / perPage);
2190
- });
2191
- function first() {
2192
- page.value = 1;
2193
- }
2194
- function last() {
2195
- page.value = Math.max(1, pages.value);
2196
- }
2197
- function next() {
2198
- if (page.value < pages.value) page.value++;
2199
- }
2200
- function prev() {
2201
- if (page.value > 1) page.value--;
2202
- }
2203
- function select(value) {
2204
- if (value < 1) page.value = 1;
2205
- else if (value > pages.value) page.value = Math.max(1, pages.value);
2206
- else page.value = value;
2207
- }
2208
- const isFirst = computed(() => page.value <= 1);
2209
- const isLast = computed(() => page.value >= pages.value);
2210
- const pageStart = computed(() => (page.value - 1) * toValue(_itemsPerPage));
2211
- const pageStop = computed(() => Math.min(pageStart.value + toValue(_itemsPerPage), toValue(_size)));
2212
- function toPage(value) {
2213
- return {
2214
- type: "page",
2215
- value
2216
- };
2217
- }
2218
- function toEllipsis() {
2219
- return ellipsis === false ? false : {
2220
- type: "ellipsis",
2221
- value: ellipsis
2222
- };
2223
- }
2224
- function filter(array) {
2225
- return array.filter((item) => item !== false);
2226
- }
2227
- return {
2228
- page,
2229
- ellipsis,
2230
- items: computed(() => {
2231
- const pageCount = pages.value;
2232
- const visible = toValue(_visible);
2233
- const current = page.value;
2234
- if (pageCount <= 0 || /* @__PURE__ */ isNaN(pageCount) || pageCount > Number.MAX_SAFE_INTEGER) return [];
2235
- if (visible <= 0) return [];
2236
- if (visible <= 2) return [toPage(current)];
2237
- if (pageCount <= visible) return (/* @__PURE__ */ range(pageCount, 1)).map(toPage);
2238
- if (visible === 3) {
2239
- const mid = current <= 1 ? 2 : current >= pageCount ? pageCount - 1 : current;
2240
- return [
2241
- toPage(1),
2242
- toPage(mid),
2243
- toPage(pageCount)
2244
- ];
2245
- }
2246
- const boundary = visible - 2;
2247
- const middle = visible - 4;
2248
- if (middle <= 0) {
2249
- if (current <= boundary) return filter([
2250
- ...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
2251
- toEllipsis(),
2252
- toPage(pageCount)
2253
- ]);
2254
- if (current > pageCount - boundary) return filter([
2255
- toPage(1),
2256
- toEllipsis(),
2257
- ...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
2258
- ]);
2259
- return current <= Math.ceil(pageCount / 2) ? filter([
2260
- toPage(1),
2261
- toPage(current),
2262
- toEllipsis(),
2263
- toPage(pageCount)
2264
- ]) : filter([
2265
- toPage(1),
2266
- toEllipsis(),
2267
- toPage(current),
2268
- toPage(pageCount)
2269
- ]);
2270
- }
2271
- const leftThreshold = boundary - 1;
2272
- const rightThreshold = pageCount - boundary + 2;
2273
- if (current <= leftThreshold) return filter([
2274
- ...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
2275
- toEllipsis(),
2276
- toPage(pageCount)
2277
- ]);
2278
- else if (current >= rightThreshold) return filter([
2279
- toPage(1),
2280
- toEllipsis(),
2281
- ...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
2282
- ]);
2283
- else {
2284
- const start = current - Math.floor(middle / 2);
2285
- return filter([
2286
- toPage(1),
2287
- toEllipsis(),
2288
- ...(/* @__PURE__ */ range(middle, start)).map(toPage),
2289
- toEllipsis(),
2290
- toPage(pageCount)
2291
- ]);
2292
- }
2293
- }),
2294
- pageStart,
2295
- pageStop,
2296
- isFirst,
2297
- isLast,
2298
- first,
2299
- last,
2300
- next,
2301
- prev,
2302
- select,
2303
- get itemsPerPage() {
2304
- return toValue(_itemsPerPage);
2305
- },
2306
- get size() {
2307
- return toValue(_size);
2308
- },
2309
- get pages() {
2310
- return pages.value;
2311
- }
2118
+ * import { createHydration } from '@vuetify/v0'
2119
+ *
2120
+ * const hydration = createHydration()
2121
+ * console.log(hydration.isHydrated.value) // false
2122
+ * hydration.hydrate()
2123
+ * console.log(hydration.isHydrated.value) // true
2124
+ * ```
2125
+ */
2126
+ function createHydration() {
2127
+ const isHydrated = shallowRef(false);
2128
+ function hydrate() {
2129
+ isHydrated.value = true;
2130
+ }
2131
+ return {
2132
+ isHydrated: shallowReadonly(isHydrated),
2133
+ hydrate
2134
+ };
2135
+ }
2136
+ function createFallbackHydration() {
2137
+ return {
2138
+ isHydrated: shallowReadonly(shallowRef(true)),
2139
+ hydrate: () => {}
2312
2140
  };
2313
2141
  }
2314
2142
  /**
2315
- * Creates a pagination context for dependency injection.
2143
+ * Creates a new hydration context trinity.
2316
2144
  *
2317
- * @param options The options including namespace.
2318
- * @returns A trinity: [usePagination, providePagination, defaultContext]
2145
+ * @param options Options for creating the hydration context.
2146
+ * @template E The type of the hydration context.
2147
+ * @returns A new hydration context trinity.
2148
+ *
2149
+ * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2319
2150
  *
2320
2151
  * @example
2321
2152
  * ```ts
2322
- * // With default namespace 'v0:pagination'
2323
- * const [usePagination, providePaginationContext] = createPaginationContext({ size: 50 })
2153
+ * import { createHydrationContext } from '@vuetify/v0'
2324
2154
  *
2325
- * // Or with custom namespace
2326
- * const [usePagination, providePaginationContext] = createPaginationContext({
2327
- * namespace: 'my-pagination',
2328
- * size: 50,
2155
+ * export const [useHydrationContext, provideHydrationContext, context] = createHydrationContext({
2156
+ * namespace: 'app:hydration',
2329
2157
  * })
2158
+ * ```
2159
+ */
2160
+ function createHydrationContext(_options = {}) {
2161
+ const { namespace = "v0:hydration" } = _options;
2162
+ const [useHydrationContext, _provideHydrationContext] = createContext(namespace);
2163
+ const context = createHydration();
2164
+ function provideHydrationContext(_context = context, app) {
2165
+ return _provideHydrationContext(_context, app);
2166
+ }
2167
+ return createTrinity(useHydrationContext, provideHydrationContext, context);
2168
+ }
2169
+ /**
2170
+ * Creates a new hydration plugin.
2330
2171
  *
2331
- * // Parent component
2332
- * providePaginationContext()
2172
+ * @param options The options for the hydration plugin.
2173
+ * @template E The type of the hydration context.
2174
+ * @returns A new hydration plugin.
2333
2175
  *
2334
- * // Child component
2335
- * const pagination = usePagination()
2336
- * pagination.next()
2176
+ * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2177
+ *
2178
+ * @example
2179
+ * ```ts
2180
+ * import { createApp } from 'vue'
2181
+ * import { createHydrationPlugin } from '@vuetify/v0'
2182
+ * import App from './App.vue'
2183
+ *
2184
+ * const app = createApp(App)
2185
+ *
2186
+ * app.use(createHydrationPlugin())
2187
+ *
2188
+ * app.mount('#app')
2337
2189
  * ```
2338
2190
  */
2339
- function createPaginationContext(_options = {}) {
2340
- const { namespace = "v0:pagination", ...options } = _options;
2341
- const [usePaginationContext, _providePaginationContext] = createContext(namespace);
2342
- const context = createPagination(options);
2343
- function providePaginationContext(_context = context, app) {
2344
- return _providePaginationContext(_context, app);
2345
- }
2346
- return createTrinity(usePaginationContext, providePaginationContext, context);
2191
+ function createHydrationPlugin(_options = {}) {
2192
+ const { namespace = "v0:hydration", ...options } = _options;
2193
+ const [, provideHydrationContext, context] = createHydrationContext({
2194
+ ...options,
2195
+ namespace
2196
+ });
2197
+ return createPlugin({
2198
+ namespace,
2199
+ provide: (app) => {
2200
+ provideHydrationContext(context, app);
2201
+ },
2202
+ setup: (app) => {
2203
+ app.mixin({ mounted() {
2204
+ if (this.$parent !== null) return;
2205
+ context.hydrate();
2206
+ } });
2207
+ }
2208
+ });
2347
2209
  }
2348
2210
  /**
2349
- * Returns the current pagination instance from context.
2211
+ * Returns the current hydration instance.
2350
2212
  *
2351
- * @param namespace The namespace. @default 'v0:pagination'
2352
- * @returns The pagination context.
2213
+ * @param namespace The namespace for the hydration context. Defaults to `v0:hydration`.
2214
+ * @returns The current hydration instance.
2215
+ *
2216
+ * @see https://0.vuetifyjs.com/composables/plugins/use-hydration
2353
2217
  *
2354
2218
  * @example
2355
2219
  * ```vue
2356
- * <script setup>
2357
- * import { usePagination } from '@vuetify/v0'
2220
+ * <script setup lang="ts">
2221
+ * import { useHydration } from '@vuetify/v0'
2358
2222
  *
2359
- * const pagination = usePagination()
2223
+ * const hydration = useHydration()
2360
2224
  * <\/script>
2361
2225
  *
2362
2226
  * <template>
2363
- * <button @click="pagination.prev()" :disabled="pagination.isFirst.value">Prev</button>
2364
- * <button @click="pagination.next()" :disabled="pagination.isLast.value">Next</button>
2227
+ * <div>
2228
+ * <p>Is hydrated: {{ hydration.isHydrated.value }}</p>
2229
+ * </div>
2365
2230
  * </template>
2366
2231
  * ```
2367
2232
  */
2368
- function usePagination(namespace = "v0:pagination") {
2369
- return useContext(namespace);
2233
+ function useHydration(namespace = "v0:hydration") {
2234
+ const fallback = createFallbackHydration();
2235
+ if (!getCurrentInstance()) return fallback;
2236
+ try {
2237
+ return useContext(namespace, fallback);
2238
+ } catch {
2239
+ return fallback;
2240
+ }
2370
2241
  }
2371
2242
 
2372
2243
  //#endregion
2373
- //#region src/composables/useSingle/index.ts
2374
- /**
2375
- * @module useSingle
2376
- *
2377
- * @remarks
2378
- * Single-selection composable that extends useSelection to enforce only one selected item.
2379
- *
2380
- * Key features:
2381
- * - Auto-clears previous selection when selecting new item
2382
- * - Singular computed properties (selectedId, selectedItem, selectedIndex, selectedValue)
2383
- * - Perfect for tabs, radio buttons, theme selectors
2384
- *
2385
- * Inheritance chain: useRegistry → useSelection → useSingle
2386
- */
2244
+ //#region src/composables/useResizeObserver/index.ts
2387
2245
  /**
2388
- * Creates a new single selection instance that enforces only one selected item at a time.
2389
- *
2390
- * Extends `createSelection` by automatically clearing previous selections when a new item is selected.
2391
- * Adds computed singular properties: `selectedId`, `selectedItem`, `selectedIndex`, `selectedValue`.
2392
- *
2393
- * @param options The options for the single selection instance.
2394
- * @template Z The type of the single selection ticket.
2395
- * @template E The type of the single selection context.
2396
- * @returns A new single selection instance with single-selection enforcement.
2397
- *
2398
- * @remarks
2399
- * **Key Differences from `createSelection`:**
2400
- * - Automatically clears `selectedIds` before selecting a new item (enforces single selection)
2401
- * - Provides singular computed properties instead of plural sets
2402
- * - Perfect for tabs, radio buttons, theme selectors, and other single-choice UI components
2403
- *
2404
- * **Computed Properties:**
2405
- * - `selectedId`: The ID of the selected item (undefined if none selected)
2406
- * - `selectedItem`: The selected ticket object (undefined if none selected)
2407
- * - `selectedIndex`: The index of the selected item (-1 if none selected)
2408
- * - `selectedValue`: The value of the selected item (undefined if none selected)
2246
+ * A composable that uses the Resize Observer API to detect when an element's
2247
+ * size changes.
2409
2248
  *
2410
- * **Inheritance Chain:**
2411
- * `useRegistry` `createSelection` `createSingle` `createStep`
2249
+ * @param target The element to observe.
2250
+ * @param callback The callback to execute when the element's size changes.
2251
+ * @param options The options for the Resize Observer.
2252
+ * @returns An object with methods to control the observer.
2412
2253
  *
2413
- * @see https://0.vuetifyjs.com/composables/selection/use-single
2254
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
2255
+ * @see https://0.vuetifyjs.com/composables/system/use-resize-observer
2414
2256
  *
2415
2257
  * @example
2416
2258
  * ```ts
2417
- * import { createSingle } from '@vuetify/v0'
2418
- *
2419
- * const tabs = createSingle({ mandatory: true })
2259
+ * import { ref } from 'vue'
2260
+ * import { useResizeObserver } from '@vuetify/v0'
2420
2261
  *
2421
- * tabs.onboard([
2422
- * { id: 'home', value: 'Home' },
2423
- * { id: 'about', value: 'About' },
2424
- * { id: 'contact', value: 'Contact' },
2425
- * ])
2262
+ * const el = ref<HTMLElement>()
2263
+ * const width = ref(0)
2264
+ * const height = ref(0)
2426
2265
  *
2427
- * tabs.first() // Select first tab
2266
+ * const { pause, resume, isPaused } = useResizeObserver(
2267
+ * el,
2268
+ * (entries) => {
2269
+ * const entry = entries[0]
2270
+ * if (entry) {
2271
+ * width.value = entry.contentRect.width
2272
+ * height.value = entry.contentRect.height
2273
+ * console.log('Size changed:', width.value, 'x', height.value)
2274
+ * }
2275
+ * },
2276
+ * { immediate: true }
2277
+ * )
2428
2278
  *
2429
- * console.log(tabs.selectedId.value) // 'home'
2430
- * console.log(tabs.selectedIndex.value) // 0
2279
+ * // Pause observation
2280
+ * pause()
2431
2281
  *
2432
- * tabs.select('about') // Switch to about tab
2433
- * console.log(tabs.selectedId.value) // 'about'
2434
- * console.log(tabs.selectedIds.size) // 1 (always enforces single selection)
2282
+ * // Resume observation
2283
+ * resume()
2435
2284
  * ```
2436
2285
  */
2437
- function createSingle(_options = {}) {
2438
- const { mandatory = false, multiple = false, ...options } = _options;
2439
- const registry = createSelection({
2440
- ...options,
2441
- mandatory,
2442
- multiple
2286
+ function useResizeObserver(target, callback, options = {}) {
2287
+ const { isHydrated } = useHydration();
2288
+ const observer = shallowRef();
2289
+ const isPaused = shallowRef(false);
2290
+ const isActive = toRef(() => !!observer.value);
2291
+ function setup() {
2292
+ if (observer.value === null) return;
2293
+ if (!isHydrated.value || !SUPPORTS_OBSERVER || !target.value || isPaused.value) return;
2294
+ observer.value = new ResizeObserver((entries) => {
2295
+ callback(entries.map((entry) => ({
2296
+ contentRect: {
2297
+ width: entry.contentRect.width,
2298
+ height: entry.contentRect.height,
2299
+ top: entry.contentRect.top,
2300
+ left: entry.contentRect.left
2301
+ },
2302
+ target: entry.target
2303
+ })));
2304
+ if (options.once) stop();
2305
+ });
2306
+ observer.value.observe(target.value, { box: options.box || "content-box" });
2307
+ if (options.immediate) {
2308
+ const rect = target.value.getBoundingClientRect();
2309
+ callback([{
2310
+ contentRect: {
2311
+ width: rect.width,
2312
+ height: rect.height,
2313
+ top: rect.top,
2314
+ left: rect.left
2315
+ },
2316
+ target: target.value
2317
+ }]);
2318
+ }
2319
+ }
2320
+ watchEffect(() => {
2321
+ const hydrated = isHydrated.value;
2322
+ const el = target.value;
2323
+ cleanup();
2324
+ if (hydrated && el) setup();
2443
2325
  });
2444
- const selectedId = computed(() => registry.selectedIds.values().next().value);
2445
- const selectedItem = computed(() => registry.selectedItems.value.values().next().value);
2446
- const selectedIndex = computed(() => selectedItem.value?.index ?? -1);
2447
- const selectedValue = computed(() => selectedItem.value?.value);
2448
- function unselect(id) {
2449
- if (mandatory && registry.selectedIds.size === 1) return;
2450
- registry.selectedIds.delete(id);
2326
+ function cleanup() {
2327
+ if (observer.value) {
2328
+ observer.value.disconnect();
2329
+ observer.value = void 0;
2330
+ }
2331
+ }
2332
+ function pause() {
2333
+ isPaused.value = true;
2334
+ observer.value?.disconnect();
2451
2335
  }
2452
- function toggle(id) {
2453
- if (registry.selectedIds.has(id)) unselect(id);
2454
- else registry.select(id);
2336
+ function resume() {
2337
+ isPaused.value = false;
2338
+ setup();
2455
2339
  }
2340
+ function stop() {
2341
+ cleanup();
2342
+ observer.value = null;
2343
+ }
2344
+ onScopeDispose(stop, true);
2456
2345
  return {
2457
- ...registry,
2458
- selectedId,
2459
- selectedItem,
2460
- selectedIndex,
2461
- selectedValue,
2462
- unselect,
2463
- toggle,
2464
- get size() {
2465
- return registry.size;
2466
- }
2346
+ isActive: shallowReadonly(isActive),
2347
+ isPaused: shallowReadonly(isPaused),
2348
+ pause,
2349
+ resume,
2350
+ stop
2467
2351
  };
2468
2352
  }
2469
2353
  /**
2470
- * Creates a new single selection context.
2354
+ * A convenience composable that uses the Resize Observer API to track an
2355
+ * element's size.
2471
2356
  *
2472
- * @param options The options for the single selection context.
2473
- * @template Z The type of the single selection ticket.
2474
- * @template E The type of the single selection context.
2475
- * @returns A new single selection context.
2357
+ * @param target The element to observe.
2358
+ * @returns An object with the element's width and height.
2476
2359
  *
2477
- * @see https://0.vuetifyjs.com/composables/selection/use-single
2360
+ * @see https://0.vuetifyjs.com/composables/system/use-resize-observer#use-element-size
2478
2361
  *
2479
2362
  * @example
2480
2363
  * ```ts
2481
- * import { createSingleContext } from '@vuetify/v0'
2482
- *
2483
- * // With default namespace 'v0:single'
2484
- * export const [useSingle, provideSingle, context] = createSingleContext()
2364
+ * import { ref, watchEffect } from 'vue'
2365
+ * import { useElementSize } from '@vuetify/v0'
2485
2366
  *
2486
- * // In a parent component:
2487
- * provideSingle()
2367
+ * const box = ref<HTMLElement>()
2368
+ * const { width, height } = useElementSize(box)
2488
2369
  *
2489
- * // In a child component:
2490
- * const single = useSingle()
2491
- * single.select('tab-1')
2370
+ * // Width and height are reactive refs
2371
+ * watchEffect(() => {
2372
+ * console.log('Box size:', width.value, 'x', height.value)
2373
+ * })
2492
2374
  * ```
2493
2375
  */
2494
- function createSingleContext(_options = {}) {
2495
- const { namespace = "v0:single", ...options } = _options;
2496
- const [useSingleContext, _provideSingleContext] = createContext(namespace);
2497
- const context = createSingle(options);
2498
- function provideSingleContext(_context = context, app) {
2499
- return _provideSingleContext(_context, app);
2376
+ function useElementSize(target) {
2377
+ const width = shallowRef(0);
2378
+ const height = shallowRef(0);
2379
+ const { pause: _pause, resume, stop, isActive, isPaused } = useResizeObserver(target, (entries) => {
2380
+ const entry = entries[0];
2381
+ if (entry) {
2382
+ width.value = entry.contentRect.width;
2383
+ height.value = entry.contentRect.height;
2384
+ }
2385
+ }, { immediate: true });
2386
+ function pause() {
2387
+ width.value = 0;
2388
+ height.value = 0;
2389
+ _pause();
2500
2390
  }
2501
- return createTrinity(useSingleContext, provideSingleContext, context);
2391
+ return {
2392
+ width,
2393
+ height,
2394
+ isActive,
2395
+ isPaused,
2396
+ pause,
2397
+ resume,
2398
+ stop
2399
+ };
2502
2400
  }
2401
+
2402
+ //#endregion
2403
+ //#region src/composables/useOverflow/index.ts
2503
2404
  /**
2504
- * Returns the current single selection instance.
2505
- *
2506
- * @param namespace The namespace for the single selection context. Defaults to `'v0:single'`.
2507
- * @returns The current single selection instance.
2405
+ * Creates a new overflow context for computing how many items fit in a container.
2508
2406
  *
2509
- * @see https://0.vuetifyjs.com/composables/selection/use-single
2407
+ * @param options Configuration options
2408
+ * @returns Overflow context with container ref, capacity, and measurement functions
2510
2409
  *
2511
- * @example
2410
+ * @example Variable-width mode (Breadcrumbs)
2512
2411
  * ```vue
2513
- * <script setup lang="ts">
2514
- * import { useSingle } from '@vuetify/v0'
2412
+ * <script lang="ts" setup>
2413
+ * import { useTemplateRef } from 'vue'
2414
+ * import { createOverflow } from '@vuetify/v0'
2515
2415
  *
2516
- * const tabs = useSingle()
2416
+ * const containerRef = useTemplateRef('container')
2417
+ * const overflow = createOverflow({
2418
+ * container: containerRef,
2419
+ * gap: 8,
2420
+ * reserved: 40,
2421
+ * })
2517
2422
  * <\/script>
2518
2423
  *
2519
2424
  * <template>
2520
- * <div>
2521
- * <p>Selected: {{ tabs.selectedId }}</p>
2425
+ * <div ref="container">
2426
+ * <span
2427
+ * v-for="(item, i) in items.slice(0, overflow.capacity.value)"
2428
+ * :key="i"
2429
+ * :ref="el => overflow.measure(i, el)"
2430
+ * >
2431
+ * {{ item }}
2432
+ * </span>
2433
+ * <span v-if="overflow.isOverflowing.value">...</span>
2522
2434
  * </div>
2523
2435
  * </template>
2524
2436
  * ```
2525
- */
2526
- function useSingle(namespace = "v0:single") {
2527
- return useContext(namespace);
2528
- }
2529
-
2530
- //#endregion
2531
- //#region src/composables/useTokens/index.ts
2532
- /**
2533
- * @module useTokens
2534
- *
2535
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
2536
- *
2537
- * @remarks
2538
- * Design token registry with alias resolution and W3C Design Tokens format support.
2539
- *
2540
- * Key features:
2541
- * - Alias resolution with circular reference detection
2542
- * - Nested token flattening with dot notation
2543
- * - W3C Design Tokens format ($value, $type, $description, $extensions)
2544
- * - Path-based resolution (e.g., {colors}.blue.500)
2545
- * - Resolution caching for performance (~28,590 ops/sec)
2546
- *
2547
- * Used by useTheme, useLocale, and useFeatures for token-based configuration.
2548
- */
2549
- /**
2550
- * Creates a new token instance.
2551
- *
2552
- * @param tokens The tokens to use.
2553
- * @param options The options for the token instance.
2554
- * @template Z The type of the token ticket.
2555
- * @template E The type of the token context.
2556
- * @returns A new token instance.
2557
- *
2558
- * @see https://www.designtokens.org/tr/drafts/format/
2559
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
2560
2437
  *
2561
- * @example
2438
+ * @example Uniform-width mode (Pagination)
2562
2439
  * ```ts
2563
- * import { useTokens } from '@vuetify/v0'
2564
- *
2565
- * const tokens = useTokens({
2566
- * colors: {
2567
- * primary: '#3b82f6',
2568
- * secondary: '{colors.primary}', // Alias reference
2569
- * },
2440
+ * const overflow = createOverflow({
2441
+ * container: () => atom.value?.element,
2442
+ * itemWidth: buttonWidth,
2443
+ * reserved: () => buttonWidth.value * 4,
2570
2444
  * })
2571
- *
2572
- * console.log(tokens.resolve('{colors.primary}')) // '#3b82f6'
2573
- * console.log(tokens.resolve('{colors.secondary}')) // '#3b82f6'
2574
2445
  * ```
2575
2446
  */
2576
- function createTokens(tokens = {}, options = {}) {
2577
- const logger = useLogger();
2578
- const registry = useRegistry(options);
2579
- const cache = /* @__PURE__ */ new Map();
2580
- registry.onboard(flatten(tokens, options.prefix, !!options.flat));
2581
- function isAlias(token) {
2582
- return /* @__PURE__ */ isString(token) && token.length > 2 && token[0] === "{" && token.at(-1) === "}";
2583
- }
2584
- function isTokenAlias(value) {
2585
- return /* @__PURE__ */ isObject(value) && "$value" in value;
2586
- }
2587
- function resolve(token, visited = /* @__PURE__ */ new Set()) {
2588
- const cacheKey = /* @__PURE__ */ isString(token) ? token : JSON.stringify(token);
2589
- const cached = cache.get(cacheKey);
2590
- if (!/* @__PURE__ */ isUndefined(cached)) return cached;
2591
- const reference = isTokenAlias(token) ? token.$value : token;
2592
- const isAliasReference = /* @__PURE__ */ isString(reference) && isAlias(reference);
2593
- const clean = isAliasReference ? reference.slice(1, -1) : String(reference);
2594
- if (visited.has(clean)) {
2595
- logger.warn(`Circular alias detected for "${clean}"`);
2596
- cache.set(cacheKey, void 0);
2597
- return;
2598
- }
2599
- visited.add(clean);
2600
- let found = registry.get(clean);
2601
- let segments = [];
2602
- if (!found && clean.includes(".")) {
2603
- const parts = clean.split(".");
2604
- for (let i = parts.length - 1; i > 0; i--) {
2605
- const prefix = parts.slice(0, i).join(".");
2606
- const suffix = parts.slice(i);
2607
- const candidate = registry.get(prefix);
2608
- if (!/* @__PURE__ */ isUndefined(candidate?.value)) {
2609
- found = candidate;
2610
- segments = suffix;
2611
- break;
2612
- }
2447
+ function createOverflow(options = {}) {
2448
+ const { container: _container, gap = 0, reserved = 0, itemWidth, reverse } = options;
2449
+ const container = /* @__PURE__ */ isUndefined(_container) ? shallowRef() : toRef(_container);
2450
+ const widths = shallowRef(/* @__PURE__ */ new Map());
2451
+ const { width } = useElementSize(container);
2452
+ function measure(index, el) {
2453
+ if (!el) {
2454
+ if (widths.value.has(index)) {
2455
+ const next = new Map(widths.value);
2456
+ next.delete(index);
2457
+ widths.value = next;
2613
2458
  }
2614
- }
2615
- if (/* @__PURE__ */ isUndefined(found?.value)) {
2616
- if (isAliasReference) logger.warn(`Alias not found for "${String(reference)}"`);
2617
- cache.set(cacheKey, void 0);
2618
2459
  return;
2619
2460
  }
2620
- let result;
2621
- let current = found.value;
2622
- if (segments.length > 0) {
2623
- if (isTokenAlias(current)) current = current.$value;
2624
- for (const segment of segments) {
2625
- if (!/* @__PURE__ */ isObject(current) || !(segment in current)) {
2626
- current = void 0;
2627
- break;
2628
- }
2629
- current = current[segment];
2630
- if (isTokenAlias(current)) current = current.$value;
2461
+ const style = getComputedStyle(el);
2462
+ const marginX = Number.parseFloat(style.marginLeft) + Number.parseFloat(style.marginRight);
2463
+ const w = el.offsetWidth + marginX;
2464
+ if (widths.value.get(index) !== w) widths.value = new Map(widths.value).set(index, w);
2465
+ }
2466
+ function reset() {
2467
+ widths.value = /* @__PURE__ */ new Map();
2468
+ }
2469
+ const total = computed(() => {
2470
+ const g = toValue(gap);
2471
+ let sum = 0;
2472
+ let count = 0;
2473
+ for (const w of widths.value.values()) {
2474
+ sum += w + (count > 0 ? g : 0);
2475
+ count++;
2476
+ }
2477
+ return sum;
2478
+ });
2479
+ return {
2480
+ container,
2481
+ width,
2482
+ capacity: computed(() => {
2483
+ const available = width.value - toValue(reserved);
2484
+ if (width.value === 0) return Infinity;
2485
+ if (available <= 0) return 0;
2486
+ const g = toValue(gap);
2487
+ const uniformWidth = toValue(itemWidth);
2488
+ if (uniformWidth && uniformWidth > 0) {
2489
+ const first = uniformWidth;
2490
+ const subsequent = uniformWidth + g;
2491
+ if (available < first) return 0;
2492
+ return Math.max(1, Math.floor((available - first) / subsequent) + 1);
2631
2493
  }
2632
- if (/* @__PURE__ */ isUndefined(current)) {
2633
- logger.warn(`Path not found inside "${clean}": ${segments.join(".")}`);
2634
- cache.set(cacheKey, void 0);
2635
- return;
2494
+ const entries = [...widths.value.entries()].toSorted((a, b) => a[0] - b[0]);
2495
+ if (toValue(reverse)) entries.reverse();
2496
+ let sum = 0;
2497
+ let count = 0;
2498
+ for (const [, w] of entries) {
2499
+ const next = sum + w + (count > 0 ? g : 0);
2500
+ if (next > available) break;
2501
+ sum = next;
2502
+ count++;
2636
2503
  }
2637
- result = current;
2638
- } else if (isTokenAlias(current)) {
2639
- const inner = current.$value;
2640
- if (/* @__PURE__ */ isString(inner) && isAlias(inner)) return resolve(inner, visited);
2641
- result = inner;
2642
- } else if (/* @__PURE__ */ isString(current) && isAlias(current)) return resolve(current, visited);
2643
- else result = current;
2644
- cache.set(cacheKey, result);
2645
- return result;
2646
- }
2647
- return {
2648
- ...registry,
2649
- resolve,
2650
- isAlias,
2651
- get size() {
2652
- return registry.size;
2653
- }
2504
+ return count;
2505
+ }),
2506
+ total,
2507
+ isOverflowing: toRef(() => {
2508
+ return total.value > width.value - toValue(reserved);
2509
+ }),
2510
+ measure,
2511
+ reset
2654
2512
  };
2655
2513
  }
2656
2514
  /**
2657
- * Creates a new token context.
2658
- *
2659
- * @param namespace The namespace for the token context.
2660
- * @param tokens The tokens to use.
2661
- * @template Z The type of the token ticket.
2662
- * @template E The type of the token context.
2663
- * @returns A new token context.
2515
+ * Creates an overflow context with dependency injection support.
2664
2516
  *
2665
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
2517
+ * @param options Configuration options including namespace
2518
+ * @returns Trinity tuple: [useContext, provideContext, defaultContext]
2666
2519
  *
2667
2520
  * @example
2668
2521
  * ```ts
2669
- * import { createTokensContext } from '@vuetify/v0'
2670
- *
2671
- * export const [useTokens, provideTokens, context] = createTokensContext({
2672
- * namespace: 'v0:tokens',
2673
- * tokens: {
2674
- * colors: {
2675
- * primary: '#3b82f6',
2676
- * secondary: '{colors.primary}', // Alias reference
2677
- * },
2678
- * },
2522
+ * // Create injectable context
2523
+ * const [useOverflow, provideOverflow, overflow] = createOverflowContext({
2524
+ * namespace: 'my-overflow',
2525
+ * gap: 8,
2526
+ * reserved: 160,
2679
2527
  * })
2528
+ *
2529
+ * // In parent component
2530
+ * provideOverflow()
2531
+ *
2532
+ * // In child component
2533
+ * const overflow = useOverflow()
2680
2534
  * ```
2681
2535
  */
2682
- function createTokensContext(_options) {
2683
- const { namespace = "v0:tokens", tokens = {}, ...options } = _options;
2684
- const [useTokensContext, _provideTokensContext] = createContext(namespace);
2685
- const context = createTokens(tokens, options);
2686
- function provideTokensContext(_context = context, app) {
2687
- return _provideTokensContext(_context, app);
2536
+ function createOverflowContext(_options = {}) {
2537
+ const { namespace = "v0:overflow", ...options } = _options;
2538
+ const [useOverflowContext, _provideOverflowContext] = createContext(namespace);
2539
+ const context = createOverflow(options);
2540
+ function provideOverflowContext(_context = context, app) {
2541
+ return _provideOverflowContext(_context, app);
2688
2542
  }
2689
- return createTrinity(useTokensContext, provideTokensContext, context);
2543
+ return createTrinity(useOverflowContext, provideOverflowContext, context);
2690
2544
  }
2691
2545
  /**
2692
- * Returns the current tokens instance.
2693
- *
2694
- * @param namespace The namespace for the tokens context. Defaults to `'v0:tokens'`.
2695
- * @returns The current tokens instance.
2546
+ * Returns the current overflow context from dependency injection.
2696
2547
  *
2697
- * @see https://0.vuetifyjs.com/composables/registration/use-tokens
2548
+ * @param namespace The namespace for the overflow context. Defaults to `v0:overflow`.
2549
+ * @returns The current overflow context.
2698
2550
  *
2699
2551
  * @example
2700
2552
  * ```vue
2701
- * <script setup lang="ts">
2702
- * import { useTokens } from '@vuetify/v0'
2553
+ * <script lang="ts" setup>
2554
+ * import { useOverflow } from '@vuetify/v0'
2703
2555
  *
2704
- * const tokens = useTokens()
2556
+ * // Inject overflow context provided by parent
2557
+ * const overflow = useOverflow()
2705
2558
  * <\/script>
2559
+ *
2560
+ * <template>
2561
+ * <div>
2562
+ * <p>Capacity: {{ overflow.capacity.value }}</p>
2563
+ * </div>
2564
+ * </template>
2706
2565
  * ```
2707
2566
  */
2708
- function useTokens(namespace = "v0:tokens") {
2567
+ function useOverflow(namespace = "v0:overflow") {
2709
2568
  return useContext(namespace);
2710
2569
  }
2711
- /**
2712
- * Flattens a nested collection of tokens into a flat array of tokens.
2713
- * Each token is represented by an object containing its ID & value.
2714
- * @param tokens The collection of tokens to flatten.
2715
- * @param prefix An optional prefix to prepend to each token ID.
2716
- * @returns An array of flattened tokens, each with an ID and value.
2717
- */
2718
- function flatten(tokens, prefix = "", flat = false) {
2719
- const flattened = [];
2720
- const stack = [{
2721
- tokens,
2722
- prefix,
2723
- flat
2724
- }];
2725
- while (stack.length > 0) {
2726
- const { tokens: currentTokens, prefix: currentPrefix, flat: flat$1 } = stack.pop();
2727
- const meta = {};
2728
- for (const k in currentTokens) if (k.startsWith("$")) meta[k] = currentTokens[k];
2729
- if (Object.keys(meta).length > 0 && currentPrefix) flattened.push({
2730
- id: currentPrefix,
2731
- value: meta
2732
- });
2733
- for (const key in currentTokens) {
2734
- if (key.startsWith("$")) continue;
2735
- const value = currentTokens[key];
2736
- const id = currentPrefix ? `${currentPrefix}.${key}` : key;
2737
- if (!/* @__PURE__ */ isObject(value)) {
2738
- flattened.push({
2739
- id,
2740
- value
2741
- });
2742
- continue;
2743
- }
2744
- if ("$value" in value) {
2745
- flattened.push({
2746
- id,
2747
- value
2748
- });
2749
- const inner = value.$value;
2750
- if (/* @__PURE__ */ isObject(inner) && !flat$1) for (const innerKey in inner) {
2751
- if (innerKey.startsWith("$")) continue;
2752
- const child = inner[innerKey];
2753
- const childId = `${id}.${innerKey}`;
2754
- if (!/* @__PURE__ */ isObject(child)) flattened.push({
2755
- id: childId,
2756
- value: child
2757
- });
2758
- else if ("$value" in child) flattened.push({
2759
- id: childId,
2760
- value: child
2761
- });
2762
- else stack.push({
2763
- tokens: child,
2764
- prefix: childId,
2765
- flat: flat$1
2766
- });
2767
- }
2768
- continue;
2769
- }
2770
- if (flat$1) {
2771
- flattened.push({
2772
- id,
2773
- value
2774
- });
2775
- continue;
2776
- }
2777
- stack.push({
2778
- tokens: value,
2779
- prefix: id,
2780
- flat: flat$1
2781
- });
2782
- }
2783
- }
2784
- return flattened;
2785
- }
2786
-
2787
- //#endregion
2788
- //#region src/composables/useLocale/adapters/v0.ts
2789
- /**
2790
- * Vuetify0.x locale adapter implementation
2791
- *
2792
- * This adapter provides translation and number formatting
2793
- * capabilities using the Intl API and supports both
2794
- * numbered ({0}, {1}) and named ({name}) variables in translation strings.
2795
- */
2796
- var Vuetify0LocaleAdapter = class {
2797
- t(message, ...params) {
2798
- let resolvedMessage = message;
2799
- if (params.length > 0 && /* @__PURE__ */ isObject(params[0])) {
2800
- const variables = params[0];
2801
- resolvedMessage = resolvedMessage.replace(/{([a-zA-Z][a-zA-Z0-9_]*)}/g, (match, name) => {
2802
- return /* @__PURE__ */ isUndefined(variables[name]) ? match : String(variables[name]);
2803
- });
2804
- params = params.slice(1);
2805
- }
2806
- resolvedMessage = resolvedMessage.replace(/\{(\d+)\}/g, (match, index) => {
2807
- const idx = Number.parseInt(index, 10);
2808
- if (!/* @__PURE__ */ isUndefined(params[idx])) return String(params[idx]);
2809
- return match;
2810
- });
2811
- return resolvedMessage;
2812
- }
2813
- n(value, locale, ...params) {
2814
- if (!IN_BROWSER || !locale) return value.toString();
2815
- const options = params[0];
2816
- return new Intl.NumberFormat(String(locale), options).format(value);
2817
- }
2818
- };
2819
2570
 
2820
2571
  //#endregion
2821
- //#region src/composables/useLocale/index.ts
2572
+ //#region src/composables/usePagination/index.ts
2822
2573
  /**
2823
- * @module useLocale
2824
- *
2825
- * @remarks
2826
- * Internationalization (i18n) composable with adapter pattern for message translation.
2574
+ * Creates a pagination instance.
2827
2575
  *
2828
- * Key features:
2829
- * - Locale selection with createSingle
2830
- * - Token-based message storage with useTokens
2831
- * - Numbered and named placeholder support ({0}, {name})
2832
- * - Number formatting with Intl.NumberFormat
2833
- * - Adapter pattern for integration with i18n providers
2576
+ * @param options The options for the pagination instance.
2577
+ * @returns A pagination context with navigation methods.
2834
2578
  *
2835
- * Integrates with createSingle for locale selection and useTokens for message resolution.
2836
- */
2837
- /**
2838
- * Creates a new locale instance.
2579
+ * @example
2580
+ * ```ts
2581
+ * import { createPagination } from '@vuetify/v0'
2839
2582
  *
2840
- * @param options The options for the locale instance.
2841
- * @template Z The type of the locale ticket.
2842
- * @template E The type of the locale context.
2843
- * @returns A new locale instance.
2583
+ * // Basic usage
2584
+ * const pagination = createPagination({ size: 100 })
2585
+ * pagination.next()
2586
+ * pagination.items.value // [{ type: 'page', value: 1 }, { type: 'page', value: 2 }, ...]
2844
2587
  *
2845
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2588
+ * // With v-model (pass a ref)
2589
+ * const page = ref(1)
2590
+ * const pagination = createPagination({ page, size: 100 })
2591
+ * // Mutating pagination.page or the passed ref syncs both
2592
+ * ```
2846
2593
  */
2847
- function createLocale(_options = {}) {
2848
- const { adapter = new Vuetify0LocaleAdapter(), messages = {}, ...options } = _options;
2849
- const tokens = createTokens(messages);
2850
- const registry = createSingle(options);
2851
- for (const id in messages) {
2852
- registry.register({ id });
2853
- if (id === options.default && !registry.selectedId.value) registry.select(id);
2594
+ function createPagination(_options = {}) {
2595
+ const { page: _page = 1, itemsPerPage: _itemsPerPage = 10, size: _size = 0, visible: _visible = 7, ellipsis = "..." } = _options;
2596
+ const page = isRef(_page) ? _page : shallowRef(_page);
2597
+ const pages = computed(() => {
2598
+ const size = toValue(_size);
2599
+ const perPage = toValue(_itemsPerPage);
2600
+ if (size <= 0 || /* @__PURE__ */ isNaN(size)) return 0;
2601
+ return Math.ceil(size / perPage);
2602
+ });
2603
+ function first() {
2604
+ page.value = 1;
2854
2605
  }
2855
- function t(key, params, fallback) {
2856
- const locale = registry.selectedId.value;
2857
- const args = toArray(params);
2858
- if (!locale) return adapter.t(fallback ?? key, ...args);
2859
- const path = `${locale}.${key}`;
2860
- const message = tokens.get(path)?.value;
2861
- const template = /* @__PURE__ */ isString(message) ? resolve(locale, message) : fallback ?? key;
2862
- return adapter.t(template, ...args);
2606
+ function last() {
2607
+ page.value = Math.max(1, pages.value);
2863
2608
  }
2864
- function n(value, ...params) {
2865
- return adapter.n(value, registry.selectedId.value, ...params);
2609
+ function next() {
2610
+ if (page.value < pages.value) page.value++;
2866
2611
  }
2867
- function resolve(locale, str) {
2868
- return str.replace(/{([a-zA-Z0-9.-_]+)}/g, (match, key) => {
2869
- const [prefix, ...rest] = key.split(".");
2870
- const target = registry.has(prefix) ? prefix : locale;
2871
- const path = `${target}.${registry.has(prefix) ? rest.join(".") : key}`;
2872
- const resolved = tokens.get(path)?.value;
2873
- if (/* @__PURE__ */ isString(resolved)) return resolve(target, resolved);
2874
- return match;
2875
- });
2612
+ function prev() {
2613
+ if (page.value > 1) page.value--;
2614
+ }
2615
+ function select(value) {
2616
+ if (value < 1) page.value = 1;
2617
+ else if (value > pages.value) page.value = Math.max(1, pages.value);
2618
+ else page.value = value;
2619
+ }
2620
+ const isFirst = computed(() => page.value <= 1);
2621
+ const isLast = computed(() => page.value >= pages.value);
2622
+ const pageStart = computed(() => (page.value - 1) * toValue(_itemsPerPage));
2623
+ const pageStop = computed(() => Math.min(pageStart.value + toValue(_itemsPerPage), toValue(_size)));
2624
+ function toPage(value) {
2625
+ return {
2626
+ type: "page",
2627
+ value
2628
+ };
2629
+ }
2630
+ function toEllipsis() {
2631
+ return ellipsis === false ? false : {
2632
+ type: "ellipsis",
2633
+ value: ellipsis
2634
+ };
2635
+ }
2636
+ function filter(array) {
2637
+ return array.filter((item) => item !== false);
2876
2638
  }
2877
2639
  return {
2878
- ...registry,
2879
- t,
2880
- n,
2640
+ page,
2641
+ ellipsis,
2642
+ items: computed(() => {
2643
+ const pageCount = pages.value;
2644
+ const visible = toValue(_visible);
2645
+ const current = page.value;
2646
+ if (pageCount <= 0 || /* @__PURE__ */ isNaN(pageCount) || pageCount > Number.MAX_SAFE_INTEGER) return [];
2647
+ if (visible <= 0) return [];
2648
+ if (visible <= 2) return [toPage(current)];
2649
+ if (pageCount <= visible) return (/* @__PURE__ */ range(pageCount, 1)).map(toPage);
2650
+ if (visible === 3) {
2651
+ const mid = current <= 1 ? 2 : current >= pageCount ? pageCount - 1 : current;
2652
+ return [
2653
+ toPage(1),
2654
+ toPage(mid),
2655
+ toPage(pageCount)
2656
+ ];
2657
+ }
2658
+ const boundary = visible - 2;
2659
+ const middle = visible - 4;
2660
+ if (middle <= 0) {
2661
+ if (current <= boundary) return filter([
2662
+ ...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
2663
+ toEllipsis(),
2664
+ toPage(pageCount)
2665
+ ]);
2666
+ if (current > pageCount - boundary) return filter([
2667
+ toPage(1),
2668
+ toEllipsis(),
2669
+ ...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
2670
+ ]);
2671
+ return current <= Math.ceil(pageCount / 2) ? filter([
2672
+ toPage(1),
2673
+ toPage(current),
2674
+ toEllipsis(),
2675
+ toPage(pageCount)
2676
+ ]) : filter([
2677
+ toPage(1),
2678
+ toEllipsis(),
2679
+ toPage(current),
2680
+ toPage(pageCount)
2681
+ ]);
2682
+ }
2683
+ const leftThreshold = boundary - 1;
2684
+ const rightThreshold = pageCount - boundary + 2;
2685
+ if (current <= leftThreshold) return filter([
2686
+ ...(/* @__PURE__ */ range(boundary, 1)).map(toPage),
2687
+ toEllipsis(),
2688
+ toPage(pageCount)
2689
+ ]);
2690
+ else if (current >= rightThreshold) return filter([
2691
+ toPage(1),
2692
+ toEllipsis(),
2693
+ ...(/* @__PURE__ */ range(boundary, pageCount - boundary + 1)).map(toPage)
2694
+ ]);
2695
+ else {
2696
+ const start = current - Math.floor(middle / 2);
2697
+ return filter([
2698
+ toPage(1),
2699
+ toEllipsis(),
2700
+ ...(/* @__PURE__ */ range(middle, start)).map(toPage),
2701
+ toEllipsis(),
2702
+ toPage(pageCount)
2703
+ ]);
2704
+ }
2705
+ }),
2706
+ pageStart,
2707
+ pageStop,
2708
+ isFirst,
2709
+ isLast,
2710
+ first,
2711
+ last,
2712
+ next,
2713
+ prev,
2714
+ select,
2715
+ get itemsPerPage() {
2716
+ return toValue(_itemsPerPage);
2717
+ },
2881
2718
  get size() {
2882
- return registry.size;
2719
+ return toValue(_size);
2720
+ },
2721
+ get pages() {
2722
+ return pages.value;
2883
2723
  }
2884
2724
  };
2885
2725
  }
2886
- function createLocaleFallback() {
2887
- return {
2888
- size: 0,
2889
- t: (key, _params, fallback) => fallback ?? key,
2890
- n: String
2891
- };
2892
- }
2893
2726
  /**
2894
- * Creates a new locale context.
2895
- *
2896
- * @param options The options for the locale context.
2897
- * @template Z The type of the locale ticket.
2898
- * @template E The type of the locale context.
2899
- * @returns A new locale context.
2727
+ * Creates a pagination context for dependency injection.
2900
2728
  *
2901
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2729
+ * @param options The options including namespace.
2730
+ * @returns A trinity: [usePagination, providePagination, defaultContext]
2902
2731
  *
2903
2732
  * @example
2904
2733
  * ```ts
2905
- * import { createLocaleContext } from '@vuetify/v0'
2734
+ * // With default namespace 'v0:pagination'
2735
+ * const [usePagination, providePaginationContext] = createPaginationContext({ size: 50 })
2906
2736
  *
2907
- * export const [useAppLocale, provideAppLocale, appLocale] = createLocaleContext({
2908
- * namespace: 'app:locale',
2909
- * messages: {
2910
- * en: { hello: 'Hello' },
2911
- * es: { hello: 'Hola' },
2912
- * },
2737
+ * // Or with custom namespace
2738
+ * const [usePagination, providePaginationContext] = createPaginationContext({
2739
+ * namespace: 'my-pagination',
2740
+ * size: 50,
2913
2741
  * })
2914
2742
  *
2915
- * // In a parent component:
2916
- * provideAppLocale()
2743
+ * // Parent component
2744
+ * providePaginationContext()
2917
2745
  *
2918
- * // In a child component:
2919
- * const locale = useAppLocale()
2920
- * locale.select('es')
2746
+ * // Child component
2747
+ * const pagination = usePagination()
2748
+ * pagination.next()
2921
2749
  * ```
2922
2750
  */
2923
- function createLocaleContext(_options = {}) {
2924
- const { namespace = "v0:locale", ...options } = _options;
2925
- const [useLocaleContext, _provideLocaleContext] = createContext(namespace);
2926
- const context = createLocale(options);
2927
- function provideLocaleContext(_context = context, app) {
2928
- return _provideLocaleContext(_context, app);
2751
+ function createPaginationContext(_options = {}) {
2752
+ const { namespace = "v0:pagination", ...options } = _options;
2753
+ const [usePaginationContext, _providePaginationContext] = createContext(namespace);
2754
+ const context = createPagination(options);
2755
+ function providePaginationContext(_context = context, app) {
2756
+ return _providePaginationContext(_context, app);
2929
2757
  }
2930
- return createTrinity(useLocaleContext, provideLocaleContext, context);
2758
+ return createTrinity(usePaginationContext, providePaginationContext, context);
2931
2759
  }
2932
2760
  /**
2933
- * Creates a new locale plugin.
2761
+ * Returns the current pagination instance from context.
2934
2762
  *
2935
- * @param options The options for the locale plugin.
2936
- * @template Z The type of the locale ticket.
2937
- * @template E The type of the locale context.
2938
- * @template R The type of the token ticket.
2939
- * @template O The type of the token context.
2940
- * @returns A new locale plugin.
2763
+ * @param namespace The namespace. @default 'v0:pagination'
2764
+ * @returns The pagination context.
2941
2765
  *
2942
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2943
- */
2944
- function createLocalePlugin(_options = {}) {
2945
- const { namespace = "v0:locale", adapter = new Vuetify0LocaleAdapter(), messages = {}, ...options } = _options;
2946
- const [, provideLocaleContext, context] = createLocaleContext({
2947
- ...options,
2948
- namespace,
2949
- adapter,
2950
- messages
2951
- });
2952
- return createPlugin({
2953
- namespace,
2954
- provide: (app) => {
2955
- provideLocaleContext(context, app);
2956
- }
2957
- });
2958
- }
2959
- /**
2960
- * Returns the current locale instance.
2766
+ * @example
2767
+ * ```vue
2768
+ * <script setup lang="ts">
2769
+ * import { usePagination } from '@vuetify/v0'
2961
2770
  *
2962
- * @returns The current locale instance.
2771
+ * const pagination = usePagination()
2772
+ * <\/script>
2963
2773
  *
2964
- * @see https://0.vuetifyjs.com/composables/plugins/use-locale
2774
+ * <template>
2775
+ * <button @click="pagination.prev()" :disabled="pagination.isFirst.value">Prev</button>
2776
+ * <button @click="pagination.next()" :disabled="pagination.isLast.value">Next</button>
2777
+ * </template>
2778
+ * ```
2965
2779
  */
2966
- function useLocale(namespace = "v0:locale") {
2967
- const fallback = createLocaleFallback();
2968
- if (!getCurrentInstance()) return fallback;
2969
- try {
2970
- return useContext(namespace, fallback);
2971
- } catch {
2972
- return fallback;
2973
- }
2780
+ function usePagination(namespace = "v0:pagination") {
2781
+ return useContext(namespace);
2974
2782
  }
2975
2783
 
2976
2784
  //#endregion
2977
2785
  //#region src/composables/useStep/index.ts
2978
2786
  /**
2979
- * @module useStep
2980
- *
2981
- * @remarks
2982
- * Navigation composable that extends useSingle with first/last/next/prev/step methods.
2983
- *
2984
- * Key features:
2985
- * - Configurable circular or bounded navigation
2986
- * - Automatic disabled item skipping
2987
- * - Arbitrary step counts (positive/negative)
2988
- * - Perfect for wizards, carousels, pagination, onboarding flows
2989
- *
2990
- * Inheritance chain: useRegistry → useSelection → useSingle → useStep
2991
- */
2992
- /**
2993
2787
  * Creates a new step instance with navigation through items.
2994
2788
  *
2995
2789
  * Extends `createSingle` with `first()`, `last()`, `next()`, `prev()`, and `step(count)` methods
@@ -3171,4 +2965,4 @@ function useStep(namespace = "v0:step") {
3171
2965
  }
3172
2966
 
3173
2967
  //#endregion
3174
- export { createGroupContext as A, createLogger as B, useResizeObserver as C, createHydrationPlugin as D, createHydrationContext as E, createSelection as F, PinoLoggerAdapter as G, createLoggerPlugin as H, createSelectionContext as I, createTrinity as J, ConsolaLoggerAdapter as K, useSelection as L, useProxyRegistry as M, useProxyModel as N, useHydration as O, toArray as P, createRegistryContext as R, useElementSize as S, createHydration as T, useLogger as U, createLoggerContext as V, Vuetify0LoggerAdapter as W, provideContext as X, createContext as Y, useContext as Z, createPaginationContext as _, createLocaleContext as a, createOverflowContext as b, useLocale as c, createTokensContext as d, useTokens as f, createPagination as g, useSingle as h, createLocale as i, useGroup as j, createGroup as k, Vuetify0LocaleAdapter as l, createSingleContext as m, createStepContext as n, createLocaleFallback as o, createSingle as p, createPlugin as q, useStep as r, createLocalePlugin as s, createStep as t, createTokens as u, usePagination as v, createFallbackHydration as w, useOverflow as x, createOverflow as y, useRegistry as z };
2968
+ export { createGroupContext as A, createLogger as B, createTokensContext as C, useSingle as D, createSingleContext as E, createSelection as F, PinoLoggerAdapter as G, createLoggerPlugin as H, createSelectionContext as I, createTrinity as J, ConsolaLoggerAdapter as K, useSelection as L, useProxyRegistry as M, useProxyModel as N, Vuetify0LocaleAdapter as O, toArray as P, createRegistryContext as R, createTokens as S, createSingle as T, useLogger as U, createLoggerContext as V, Vuetify0LoggerAdapter as W, provideContext as X, createContext as Y, useContext as Z, createLocale as _, createPaginationContext as a, createLocalePlugin as b, createOverflowContext as c, useResizeObserver as d, createFallbackHydration as f, useHydration as g, createHydrationPlugin as h, createPagination as i, useGroup as j, createGroup as k, useOverflow as l, createHydrationContext as m, createStepContext as n, usePagination as o, createHydration as p, createPlugin as q, useStep as r, createOverflow as s, createStep as t, useElementSize as u, createLocaleContext as v, useTokens as w, useLocale as x, createLocaleFallback as y, useRegistry as z };