@mantine-vue/hooks 3.5.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { computed, getCurrentInstance, isRef, nextTick, onBeforeUnmount, onMounted, onUpdated, reactive, readonly, ref, shallowRef, toValue, triggerRef, watch, watchEffect } from "vue";
1
+ import { computed, getCurrentInstance, isRef, nextTick, onBeforeUnmount, onMounted, onUpdated, reactive, readonly, ref, shallowRef, toValue, triggerRef, useId as useId$1, watch, watchEffect } from "vue";
2
2
  //#region src/utils/clamp/clamp.ts
3
3
  function clamp(value, min, max) {
4
4
  if (min === void 0 && max === void 0) return value;
@@ -117,9 +117,14 @@ function useDebouncedCallback(callback, waitOrOptions) {
117
117
  }
118
118
  //#endregion
119
119
  //#region src/use-click-outside/use-click-outside.ts
120
- function useClickOutside(callback, events = ["mousedown", "touchstart"], nodes, enabled = true) {
120
+ function resolveElement(value) {
121
+ if (!value) return null;
122
+ return "$el" in value ? value.$el ?? null : value;
123
+ }
124
+ function useClickOutside(callback, events = ["mousedown", "touchstart"], nodes, enabled = true, capture = false) {
121
125
  const element = ref(null);
122
126
  let activeEvents = [];
127
+ let activeCapture = false;
123
128
  const listener = (event) => {
124
129
  const target = event.target;
125
130
  if (!target || !document.body.contains(target) && target.tagName !== "HTML") return;
@@ -127,20 +132,28 @@ function useClickOutside(callback, events = ["mousedown", "touchstart"], nodes,
127
132
  const resolvedNodes = nodes ? toValue(nodes) : void 0;
128
133
  if (resolvedNodes) {
129
134
  if (resolvedNodes.every((node) => !!node && !path.includes(node))) callback(event);
130
- } else if (element.value && !path.includes(element.value)) callback(event);
135
+ } else {
136
+ const targetElement = resolveElement(element.value);
137
+ if (targetElement && !path.includes(targetElement)) callback(event);
138
+ }
131
139
  };
132
140
  const cleanup = () => {
133
- activeEvents.forEach((event) => document.removeEventListener(event, listener));
141
+ activeEvents.forEach((event) => document.removeEventListener(event, listener, activeCapture));
134
142
  activeEvents = [];
135
143
  };
136
144
  const bind = () => {
137
145
  cleanup();
138
146
  if (!toValue(enabled)) return;
139
147
  activeEvents = toValue(events) || ["mousedown", "touchstart"];
140
- activeEvents.forEach((event) => document.addEventListener(event, listener));
148
+ activeCapture = toValue(capture);
149
+ activeEvents.forEach((event) => document.addEventListener(event, listener, activeCapture));
141
150
  };
142
151
  onMounted(bind);
143
- watch([() => toValue(events)?.join(","), () => toValue(enabled)], bind);
152
+ watch([
153
+ () => toValue(events)?.join(","),
154
+ () => toValue(enabled),
155
+ () => toValue(capture)
156
+ ], bind);
144
157
  onBeforeUnmount(cleanup);
145
158
  return element;
146
159
  }
@@ -184,7 +197,11 @@ function useClipboard({ timeout = 2e3 } = {}) {
184
197
  //#region src/use-media-query/use-media-query.ts
185
198
  function getInitialMediaQueryValue(query, initialValue) {
186
199
  if (typeof initialValue === "boolean") return initialValue;
187
- if (typeof window !== "undefined" && typeof window.matchMedia === "function") return window.matchMedia(query).matches;
200
+ if (typeof window !== "undefined" && typeof window.matchMedia === "function") try {
201
+ return window.matchMedia(query).matches;
202
+ } catch {
203
+ return false;
204
+ }
188
205
  return false;
189
206
  }
190
207
  function useMediaQuery(query, initialValue = false, options = { getInitialValueInEffect: true }) {
@@ -254,6 +271,10 @@ function useDebouncedState(initialValue, wait, options = {}) {
254
271
  if (options.leading && !leadingCalled) {
255
272
  value.value = next;
256
273
  leadingCalled = true;
274
+ clear();
275
+ timeout = setTimeout(() => {
276
+ leadingCalled = false;
277
+ }, wait);
257
278
  } else {
258
279
  clear();
259
280
  timeout = setTimeout(() => {
@@ -503,12 +524,8 @@ function useForceUpdate() {
503
524
  //#region src/use-id/use-id.ts
504
525
  var idCounter = 0;
505
526
  function useId(staticId) {
506
- const id = ref(staticId || "");
507
- onMounted(() => {
508
- if (!id.value) id.value = `mantine-vue-${++idCounter}`;
509
- });
510
- if (staticId) id.value = staticId;
511
- return id;
527
+ if (staticId) return ref(staticId);
528
+ return ref(`mantine-vue-${getCurrentInstance() ? useId$1() : `fallback-${++idCounter}`}`);
512
529
  }
513
530
  //#endregion
514
531
  //#region src/use-idle/use-idle.ts
@@ -1025,7 +1042,7 @@ function usePagination({ total, siblings = 1, boundaries = 1, page, initialPage,
1025
1042
  const leftSiblingIndex = Math.max(active.value - siblings, start.value + boundaries - 1);
1026
1043
  const rightSiblingIndex = Math.min(active.value + siblings, end.value - boundaries);
1027
1044
  const shouldShowLeftDots = leftSiblingIndex > start.value + boundaries + 1;
1028
- const shouldShowRightDots = rightSiblingIndex < end.value - boundaries;
1045
+ const shouldShowRightDots = rightSiblingIndex < end.value - (boundaries + 1);
1029
1046
  if (!shouldShowLeftDots && shouldShowRightDots) {
1030
1047
  const leftItemCount = siblings * 2 + boundaries + 2;
1031
1048
  return [
@@ -1431,8 +1448,8 @@ function useIntersection(options) {
1431
1448
  entry.value = null;
1432
1449
  return;
1433
1450
  }
1434
- observer = new IntersectionObserver(([_entry]) => {
1435
- entry.value = _entry;
1451
+ observer = new IntersectionObserver((entries) => {
1452
+ entry.value = entries[entries.length - 1];
1436
1453
  }, options);
1437
1454
  observer.observe(node);
1438
1455
  }, { immediate: true });
@@ -1535,12 +1552,10 @@ function getHotkeyMatcher(hotkey, usePhysicalKeys) {
1535
1552
  }
1536
1553
  function getHotkeyHandler(hotkeys) {
1537
1554
  return (event) => {
1538
- hotkeys.forEach(([hotkey, handler, options = {
1539
- preventDefault: true,
1540
- usePhysicalKeys: false
1541
- }]) => {
1542
- if (getHotkeyMatcher(hotkey, options.usePhysicalKeys)(event)) {
1543
- if (options.preventDefault) event.preventDefault();
1555
+ hotkeys.forEach(([hotkey, handler, options]) => {
1556
+ const { preventDefault = true, usePhysicalKeys = false } = options || {};
1557
+ if (getHotkeyMatcher(hotkey, usePhysicalKeys)(event)) {
1558
+ if (preventDefault) event.preventDefault();
1544
1559
  handler(event);
1545
1560
  }
1546
1561
  });
@@ -1571,16 +1586,14 @@ function useHotkeys(hotkeys, tagsToIgnore = [
1571
1586
  "select"
1572
1587
  ], triggerOnContentEditable = false) {
1573
1588
  const handleKeydown = (event) => {
1574
- toValue(hotkeys).forEach(([hotkey, handler, options = {
1575
- preventDefault: true,
1576
- usePhysicalKeys: false
1577
- }]) => {
1578
- if (getHotkeyMatcher(hotkey, options.usePhysicalKeys)(event) && shouldFireEvent(event, toValue(tagsToIgnore) ?? [
1589
+ toValue(hotkeys).forEach(([hotkey, handler, options]) => {
1590
+ const { preventDefault = true, usePhysicalKeys = false } = options || {};
1591
+ if (getHotkeyMatcher(hotkey, usePhysicalKeys)(event) && shouldFireEvent(event, toValue(tagsToIgnore) ?? [
1579
1592
  "input",
1580
1593
  "textarea",
1581
1594
  "select"
1582
1595
  ], toValue(triggerOnContentEditable))) {
1583
- if (options.preventDefault) event.preventDefault();
1596
+ if (preventDefault) event.preventDefault();
1584
1597
  handler(event);
1585
1598
  }
1586
1599
  });
@@ -1699,20 +1712,25 @@ function useLogger(componentName, props) {
1699
1712
  function useHover() {
1700
1713
  const refTarget = ref(null);
1701
1714
  const hovered = ref(false);
1702
- let cleanup;
1703
- onMounted(() => {
1704
- const node = refTarget.value;
1715
+ watch(refTarget, (node, _, onCleanup) => {
1716
+ hovered.value = false;
1705
1717
  if (!node) return;
1706
- const onEnter = () => hovered.value = true;
1707
- const onLeave = () => hovered.value = false;
1718
+ const onEnter = () => {
1719
+ hovered.value = true;
1720
+ };
1721
+ const onLeave = () => {
1722
+ hovered.value = false;
1723
+ };
1708
1724
  node.addEventListener("mouseenter", onEnter);
1709
1725
  node.addEventListener("mouseleave", onLeave);
1710
- cleanup = () => {
1726
+ onCleanup(() => {
1711
1727
  node.removeEventListener("mouseenter", onEnter);
1712
1728
  node.removeEventListener("mouseleave", onLeave);
1713
- };
1729
+ });
1730
+ }, {
1731
+ immediate: true,
1732
+ flush: "sync"
1714
1733
  });
1715
- onBeforeUnmount(() => cleanup?.());
1716
1734
  return {
1717
1735
  ref: refTarget,
1718
1736
  hovered
@@ -1838,27 +1856,35 @@ function useDisclosure(initialState = false, options = {}) {
1838
1856
  function useFocusWithin(options = {}) {
1839
1857
  const refTarget = ref(null);
1840
1858
  const focused = ref(false);
1841
- let cleanup;
1842
- onMounted(() => {
1843
- const node = refTarget.value;
1859
+ watch(refTarget, (node, _, onCleanup) => {
1860
+ focused.value = false;
1844
1861
  if (!node) return;
1862
+ let active = true;
1845
1863
  const onFocusIn = (event) => {
1846
- focused.value = true;
1847
- options.onFocus?.(event);
1864
+ if (!focused.value) {
1865
+ focused.value = true;
1866
+ options.onFocus?.(event);
1867
+ }
1868
+ };
1869
+ const onFocusOut = (event) => {
1870
+ nextTick(() => {
1871
+ if (!active) return;
1872
+ const isFocused = node.matches(":focus-within");
1873
+ if (!isFocused && focused.value) options.onBlur?.(event);
1874
+ focused.value = isFocused;
1875
+ });
1848
1876
  };
1849
- const onFocusOut = (event) => nextTick(() => {
1850
- const isFocused = node.matches(":focus-within");
1851
- if (!isFocused && focused.value) options.onBlur?.(event);
1852
- focused.value = isFocused;
1853
- });
1854
1877
  node.addEventListener("focusin", onFocusIn);
1855
1878
  node.addEventListener("focusout", onFocusOut);
1856
- cleanup = () => {
1879
+ onCleanup(() => {
1880
+ active = false;
1857
1881
  node.removeEventListener("focusin", onFocusIn);
1858
1882
  node.removeEventListener("focusout", onFocusOut);
1859
- };
1883
+ });
1884
+ }, {
1885
+ immediate: true,
1886
+ flush: "sync"
1860
1887
  });
1861
- onBeforeUnmount(() => cleanup?.());
1862
1888
  return {
1863
1889
  ref: refTarget,
1864
1890
  focused
@@ -2278,7 +2304,13 @@ function useThrottledCallbackWithClearTimeout(callback, wait) {
2278
2304
  let latestOutArgs = null;
2279
2305
  let active = true;
2280
2306
  let timeoutId = -1;
2281
- const clear = () => window.clearTimeout(timeoutId);
2307
+ const clear = () => {
2308
+ window.clearTimeout(timeoutId);
2309
+ timeoutId = -1;
2310
+ active = true;
2311
+ latestInArgs = null;
2312
+ latestOutArgs = null;
2313
+ };
2282
2314
  const callThrottledCallback = (...args) => {
2283
2315
  callback(...args);
2284
2316
  latestInArgs = args;
@@ -3300,7 +3332,7 @@ function findPrevTokenIndex(slots, from) {
3300
3332
  for (let i = from; i >= 0; i--) if (slots[i].type === "token") return i;
3301
3333
  return -1;
3302
3334
  }
3303
- function processInput(inputValue, slots, _slotCharOption) {
3335
+ function processInput(inputValue, slots, _slotCharOption, transform) {
3304
3336
  let result = "";
3305
3337
  let inputIndex = 0;
3306
3338
  for (let slotIndex = 0; slotIndex < slots.length && inputIndex <= inputValue.length; slotIndex++) {
@@ -3312,7 +3344,7 @@ function processInput(inputValue, slots, _slotCharOption) {
3312
3344
  }
3313
3345
  if (inputIndex >= inputValue.length) break;
3314
3346
  while (inputIndex < inputValue.length) {
3315
- const ch = inputValue[inputIndex];
3347
+ const ch = transform ? transform(inputValue[inputIndex]) : inputValue[inputIndex];
3316
3348
  inputIndex++;
3317
3349
  if (slot.pattern.test(ch)) {
3318
3350
  result += ch;
@@ -3434,8 +3466,8 @@ function useMask(options) {
3434
3466
  const initializeInputValue = (inputNode) => {
3435
3467
  if (!inputNode.value) return false;
3436
3468
  const isOwnValue = inputNode.value === display;
3437
- const parse = (slots, slotChar) => isOwnValue ? applyMaskToRaw(rawInternal, slots, slotChar, options.transform) : processInput(inputNode.value, slots, slotChar);
3438
- const { slots: initialSlots, slotChar: initialSlotChar } = getResolvedOptions(options, "");
3469
+ const { slots: initialSlots, slotChar: initialSlotChar, transform } = getResolvedOptions(options, "");
3470
+ const parse = (slots, slotChar) => isOwnValue ? applyMaskToRaw(rawInternal, slots, slotChar, options.transform) : processInput(inputNode.value, slots, slotChar, transform);
3439
3471
  const { slots: resolvedSlots, slotChar } = getResolvedOptions(options, extractRaw(parse(initialSlots, initialSlotChar), initialSlots));
3440
3472
  const reprocessed = parse(resolvedSlots, slotChar);
3441
3473
  const newRaw = extractRaw(reprocessed, resolvedSlots);
@@ -3780,6 +3812,13 @@ function findLastEnabled(total, isItemDisabled) {
3780
3812
  for (let i = total - 1; i >= 0; i -= 1) if (!isItemDisabled(i)) return i;
3781
3813
  return 0;
3782
3814
  }
3815
+ function findEnabledInRow(row, startCol, step, columns, total, isItemDisabled) {
3816
+ for (let col = startCol; col >= 0 && col < columns; col += step) {
3817
+ const candidate = row * columns + col;
3818
+ if (candidate < total && !isItemDisabled(candidate)) return candidate;
3819
+ }
3820
+ return null;
3821
+ }
3783
3822
  var defaultIsItemDisabled = () => false;
3784
3823
  function useRovingIndex(input) {
3785
3824
  const { total, orientation = "horizontal", loop = true, dir = "ltr", activateOnFocus = false, columns, focusedIndex, initialIndex, onFocusChange, isItemDisabled = defaultIsItemDisabled } = input;
@@ -3818,19 +3857,13 @@ function useRovingIndex(input) {
3818
3857
  const isRtl = dir === "rtl";
3819
3858
  switch (event.key) {
3820
3859
  case "ArrowRight": {
3821
- const targetCol = isRtl ? col - 1 : col + 1;
3822
- if (targetCol >= 0 && targetCol < cols && row * cols + targetCol < total_) {
3823
- const candidate = row * cols + targetCol;
3824
- if (!isItemDisabled(candidate)) nextIndex = candidate;
3825
- }
3860
+ const step = isRtl ? -1 : 1;
3861
+ nextIndex = findEnabledInRow(row, col + step, step, cols, total_, isItemDisabled);
3826
3862
  break;
3827
3863
  }
3828
3864
  case "ArrowLeft": {
3829
- const targetCol = isRtl ? col + 1 : col - 1;
3830
- if (targetCol >= 0 && targetCol < cols && row * cols + targetCol < total_) {
3831
- const candidate = row * cols + targetCol;
3832
- if (!isItemDisabled(candidate)) nextIndex = candidate;
3833
- }
3865
+ const step = isRtl ? 1 : -1;
3866
+ nextIndex = findEnabledInRow(row, col + step, step, cols, total_, isItemDisabled);
3834
3867
  break;
3835
3868
  }
3836
3869
  case "ArrowDown":
@@ -4168,6 +4201,7 @@ function useDrag(handler, options = {}) {
4168
4201
  resetDrag();
4169
4202
  };
4170
4203
  const refCallback = (newNode) => {
4204
+ if (newNode === node) return;
4171
4205
  cleanupNode?.();
4172
4206
  cleanupNode = void 0;
4173
4207
  node = newNode;