@mantine-vue/hooks 3.4.0 → 3.6.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
@@ -117,9 +117,10 @@ 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 useClickOutside(callback, events = ["mousedown", "touchstart"], nodes, enabled = true, capture = false) {
121
121
  const element = ref(null);
122
122
  let activeEvents = [];
123
+ let activeCapture = false;
123
124
  const listener = (event) => {
124
125
  const target = event.target;
125
126
  if (!target || !document.body.contains(target) && target.tagName !== "HTML") return;
@@ -130,17 +131,22 @@ function useClickOutside(callback, events = ["mousedown", "touchstart"], nodes,
130
131
  } else if (element.value && !path.includes(element.value)) callback(event);
131
132
  };
132
133
  const cleanup = () => {
133
- activeEvents.forEach((event) => document.removeEventListener(event, listener));
134
+ activeEvents.forEach((event) => document.removeEventListener(event, listener, activeCapture));
134
135
  activeEvents = [];
135
136
  };
136
137
  const bind = () => {
137
138
  cleanup();
138
139
  if (!toValue(enabled)) return;
139
140
  activeEvents = toValue(events) || ["mousedown", "touchstart"];
140
- activeEvents.forEach((event) => document.addEventListener(event, listener));
141
+ activeCapture = toValue(capture);
142
+ activeEvents.forEach((event) => document.addEventListener(event, listener, activeCapture));
141
143
  };
142
144
  onMounted(bind);
143
- watch([() => toValue(events)?.join(","), () => toValue(enabled)], bind);
145
+ watch([
146
+ () => toValue(events)?.join(","),
147
+ () => toValue(enabled),
148
+ () => toValue(capture)
149
+ ], bind);
144
150
  onBeforeUnmount(cleanup);
145
151
  return element;
146
152
  }
@@ -184,7 +190,11 @@ function useClipboard({ timeout = 2e3 } = {}) {
184
190
  //#region src/use-media-query/use-media-query.ts
185
191
  function getInitialMediaQueryValue(query, initialValue) {
186
192
  if (typeof initialValue === "boolean") return initialValue;
187
- if (typeof window !== "undefined" && typeof window.matchMedia === "function") return window.matchMedia(query).matches;
193
+ if (typeof window !== "undefined" && typeof window.matchMedia === "function") try {
194
+ return window.matchMedia(query).matches;
195
+ } catch {
196
+ return false;
197
+ }
188
198
  return false;
189
199
  }
190
200
  function useMediaQuery(query, initialValue = false, options = { getInitialValueInEffect: true }) {
@@ -254,6 +264,10 @@ function useDebouncedState(initialValue, wait, options = {}) {
254
264
  if (options.leading && !leadingCalled) {
255
265
  value.value = next;
256
266
  leadingCalled = true;
267
+ clear();
268
+ timeout = setTimeout(() => {
269
+ leadingCalled = false;
270
+ }, wait);
257
271
  } else {
258
272
  clear();
259
273
  timeout = setTimeout(() => {
@@ -1025,7 +1039,7 @@ function usePagination({ total, siblings = 1, boundaries = 1, page, initialPage,
1025
1039
  const leftSiblingIndex = Math.max(active.value - siblings, start.value + boundaries - 1);
1026
1040
  const rightSiblingIndex = Math.min(active.value + siblings, end.value - boundaries);
1027
1041
  const shouldShowLeftDots = leftSiblingIndex > start.value + boundaries + 1;
1028
- const shouldShowRightDots = rightSiblingIndex < end.value - boundaries;
1042
+ const shouldShowRightDots = rightSiblingIndex < end.value - (boundaries + 1);
1029
1043
  if (!shouldShowLeftDots && shouldShowRightDots) {
1030
1044
  const leftItemCount = siblings * 2 + boundaries + 2;
1031
1045
  return [
@@ -1431,8 +1445,8 @@ function useIntersection(options) {
1431
1445
  entry.value = null;
1432
1446
  return;
1433
1447
  }
1434
- observer = new IntersectionObserver(([_entry]) => {
1435
- entry.value = _entry;
1448
+ observer = new IntersectionObserver((entries) => {
1449
+ entry.value = entries[entries.length - 1];
1436
1450
  }, options);
1437
1451
  observer.observe(node);
1438
1452
  }, { immediate: true });
@@ -1535,12 +1549,10 @@ function getHotkeyMatcher(hotkey, usePhysicalKeys) {
1535
1549
  }
1536
1550
  function getHotkeyHandler(hotkeys) {
1537
1551
  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();
1552
+ hotkeys.forEach(([hotkey, handler, options]) => {
1553
+ const { preventDefault = true, usePhysicalKeys = false } = options || {};
1554
+ if (getHotkeyMatcher(hotkey, usePhysicalKeys)(event)) {
1555
+ if (preventDefault) event.preventDefault();
1544
1556
  handler(event);
1545
1557
  }
1546
1558
  });
@@ -1571,16 +1583,14 @@ function useHotkeys(hotkeys, tagsToIgnore = [
1571
1583
  "select"
1572
1584
  ], triggerOnContentEditable = false) {
1573
1585
  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) ?? [
1586
+ toValue(hotkeys).forEach(([hotkey, handler, options]) => {
1587
+ const { preventDefault = true, usePhysicalKeys = false } = options || {};
1588
+ if (getHotkeyMatcher(hotkey, usePhysicalKeys)(event) && shouldFireEvent(event, toValue(tagsToIgnore) ?? [
1579
1589
  "input",
1580
1590
  "textarea",
1581
1591
  "select"
1582
1592
  ], toValue(triggerOnContentEditable))) {
1583
- if (options.preventDefault) event.preventDefault();
1593
+ if (preventDefault) event.preventDefault();
1584
1594
  handler(event);
1585
1595
  }
1586
1596
  });
@@ -2278,7 +2288,13 @@ function useThrottledCallbackWithClearTimeout(callback, wait) {
2278
2288
  let latestOutArgs = null;
2279
2289
  let active = true;
2280
2290
  let timeoutId = -1;
2281
- const clear = () => window.clearTimeout(timeoutId);
2291
+ const clear = () => {
2292
+ window.clearTimeout(timeoutId);
2293
+ timeoutId = -1;
2294
+ active = true;
2295
+ latestInArgs = null;
2296
+ latestOutArgs = null;
2297
+ };
2282
2298
  const callThrottledCallback = (...args) => {
2283
2299
  callback(...args);
2284
2300
  latestInArgs = args;
@@ -2520,6 +2536,10 @@ function useRadialMove(onChange, { step = .01, onChangeEnd, onScrubStart, onScru
2520
2536
  //#endregion
2521
2537
  //#region src/use-scroll-spy/use-scroll-spy.ts
2522
2538
  var scrollSpyId = 0;
2539
+ function resolveHeadingNode(heading, selector, index) {
2540
+ if (heading.isConnected) return heading;
2541
+ return (heading.id ? document.getElementById(heading.id) : null) ?? document.querySelectorAll(selector)[index] ?? heading;
2542
+ }
2523
2543
  function getActiveElement(rects, offset = 0) {
2524
2544
  if (rects.length === 0) return -1;
2525
2545
  return rects.reduce((closest, rect, index) => Math.abs(closest.position - offset) < Math.abs(rect.y - offset) ? closest : {
@@ -2542,11 +2562,12 @@ function useScrollSpy(options = {}) {
2542
2562
  if (typeof document === "undefined") return;
2543
2563
  const getDepth = options.getDepth ?? ((element) => Number(element.tagName[1]));
2544
2564
  const getValue = options.getValue ?? ((element) => element.textContent || "");
2545
- data.value = Array.from(document.querySelectorAll(toValue(options.selector) ?? "h1, h2, h3, h4, h5, h6")).map((heading) => ({
2565
+ const selector = toValue(options.selector) ?? "h1, h2, h3, h4, h5, h6";
2566
+ data.value = Array.from(document.querySelectorAll(selector)).map((heading, index) => ({
2546
2567
  depth: getDepth(heading),
2547
2568
  value: getValue(heading),
2548
2569
  id: heading.id || `mantine-scroll-spy-${++scrollSpyId}`,
2549
- getNode: () => heading.id ? document.getElementById(heading.id) ?? heading : heading
2570
+ getNode: () => resolveHeadingNode(heading, selector, index)
2550
2571
  }));
2551
2572
  initialized.value = true;
2552
2573
  updateActive();
@@ -3040,6 +3061,9 @@ function getElementContentSize(element, dimension) {
3040
3061
  function getCssSize(size) {
3041
3062
  return typeof size === "number" ? `${size}px` : size;
3042
3063
  }
3064
+ function isMeasured(size) {
3065
+ return typeof size === "number" && size > 0;
3066
+ }
3043
3067
  function createCollapse({ dimension, getElementSize, expanded, transitionDuration, transitionTimingFunction = "ease", onTransitionEnd, onTransitionStart, keepMounted }) {
3044
3068
  const isExpanded = () => toValue(expanded);
3045
3069
  const getTransitionDuration = () => toValue(transitionDuration);
@@ -3053,17 +3077,24 @@ function createCollapse({ dimension, getElementSize, expanded, transitionDuratio
3053
3077
  const styles = ref(isExpanded() ? {} : collapsedStyles);
3054
3078
  const state = ref(isExpanded() ? "entered" : "exited");
3055
3079
  const raf = typeof window === "undefined" ? (callback) => callback(0) : window.requestAnimationFrame;
3080
+ let transitionId = 0;
3056
3081
  const mergeStyles = (nextStyles) => {
3057
3082
  styles.value = {
3058
3083
  ...styles.value,
3059
3084
  ...nextStyles
3060
3085
  };
3061
3086
  };
3062
- const measureExpandedSize = (attempt = 0) => {
3063
- if (!isExpanded()) return;
3087
+ const measureExpandedSize = (currentTransitionId, attempt = 0) => {
3088
+ if (transitionId !== currentTransitionId || !isExpanded() || !elementRef.value) return;
3064
3089
  const size = getElementSize(elementRef.value);
3065
3090
  if (size === 0 && elementRef.value?.children.length && attempt < 5) {
3066
- raf(() => measureExpandedSize(attempt + 1));
3091
+ raf(() => measureExpandedSize(currentTransitionId, attempt + 1));
3092
+ return;
3093
+ }
3094
+ if (!isMeasured(size)) {
3095
+ styles.value = {};
3096
+ state.value = "entered";
3097
+ onTransitionEnd?.();
3067
3098
  return;
3068
3099
  }
3069
3100
  mergeStyles({
@@ -3077,8 +3108,12 @@ function createCollapse({ dimension, getElementSize, expanded, transitionDuratio
3077
3108
  };
3078
3109
  watch(() => isExpanded(), (value, oldValue) => {
3079
3110
  if (value === oldValue) return;
3111
+ transitionId += 1;
3112
+ const currentTransitionId = transitionId;
3113
+ const isCurrentTransition = () => transitionId === currentTransitionId && elementRef.value !== null;
3080
3114
  if (getTransitionDuration() !== 0) onTransitionStart?.();
3081
3115
  if (value) raf(() => {
3116
+ if (!isCurrentTransition()) return;
3082
3117
  state.value = "entering";
3083
3118
  mergeStyles({
3084
3119
  willChange: dimension,
@@ -3087,24 +3122,37 @@ function createCollapse({ dimension, getElementSize, expanded, transitionDuratio
3087
3122
  });
3088
3123
  nextTick(() => {
3089
3124
  raf(() => {
3090
- raf(() => measureExpandedSize());
3125
+ if (!isCurrentTransition()) return;
3126
+ raf(() => measureExpandedSize(currentTransitionId));
3091
3127
  });
3092
3128
  });
3093
3129
  });
3094
3130
  else raf(() => {
3131
+ if (!isCurrentTransition()) return;
3095
3132
  state.value = "exiting";
3096
3133
  const size = getElementSize(elementRef.value);
3134
+ if (!isMeasured(size)) {
3135
+ styles.value = collapsedStyles;
3136
+ state.value = "exited";
3137
+ onTransitionEnd?.();
3138
+ return;
3139
+ }
3097
3140
  mergeStyles({
3098
3141
  ...getTransitionStyles(size),
3099
3142
  willChange: dimension,
3100
3143
  [dimension]: getCssSize(size)
3101
3144
  });
3102
- raf(() => mergeStyles({
3103
- [dimension]: 0,
3104
- overflow: "hidden"
3105
- }));
3145
+ raf(() => {
3146
+ if (isCurrentTransition()) mergeStyles({
3147
+ [dimension]: 0,
3148
+ overflow: "hidden"
3149
+ });
3150
+ });
3106
3151
  });
3107
3152
  }, { flush: "post" });
3153
+ onBeforeUnmount(() => {
3154
+ transitionId += 1;
3155
+ });
3108
3156
  const handleTransitionEnd = (event) => {
3109
3157
  if (event.target !== elementRef.value || event.propertyName !== dimension) return;
3110
3158
  if (isExpanded()) {
@@ -3268,7 +3316,7 @@ function findPrevTokenIndex(slots, from) {
3268
3316
  for (let i = from; i >= 0; i--) if (slots[i].type === "token") return i;
3269
3317
  return -1;
3270
3318
  }
3271
- function processInput(inputValue, slots, _slotCharOption) {
3319
+ function processInput(inputValue, slots, _slotCharOption, transform) {
3272
3320
  let result = "";
3273
3321
  let inputIndex = 0;
3274
3322
  for (let slotIndex = 0; slotIndex < slots.length && inputIndex <= inputValue.length; slotIndex++) {
@@ -3280,7 +3328,7 @@ function processInput(inputValue, slots, _slotCharOption) {
3280
3328
  }
3281
3329
  if (inputIndex >= inputValue.length) break;
3282
3330
  while (inputIndex < inputValue.length) {
3283
- const ch = inputValue[inputIndex];
3331
+ const ch = transform ? transform(inputValue[inputIndex]) : inputValue[inputIndex];
3284
3332
  inputIndex++;
3285
3333
  if (slot.pattern.test(ch)) {
3286
3334
  result += ch;
@@ -3401,9 +3449,11 @@ function useMask(options) {
3401
3449
  };
3402
3450
  const initializeInputValue = (inputNode) => {
3403
3451
  if (!inputNode.value) return false;
3404
- const { slots: initialSlots, slotChar: initialSlotChar } = getResolvedOptions(options, "");
3405
- const { slots: resolvedSlots, slotChar } = getResolvedOptions(options, extractRaw(processInput(inputNode.value, initialSlots, initialSlotChar), initialSlots));
3406
- const reprocessed = processInput(inputNode.value, resolvedSlots, slotChar);
3452
+ const isOwnValue = inputNode.value === display;
3453
+ const { slots: initialSlots, slotChar: initialSlotChar, transform } = getResolvedOptions(options, "");
3454
+ const parse = (slots, slotChar) => isOwnValue ? applyMaskToRaw(rawInternal, slots, slotChar, options.transform) : processInput(inputNode.value, slots, slotChar, transform);
3455
+ const { slots: resolvedSlots, slotChar } = getResolvedOptions(options, extractRaw(parse(initialSlots, initialSlotChar), initialSlots));
3456
+ const reprocessed = parse(resolvedSlots, slotChar);
3407
3457
  const newRaw = extractRaw(reprocessed, resolvedSlots);
3408
3458
  const showSlots = options.alwaysShowMask || isFocused;
3409
3459
  const showOnFocus = options.showMaskOnFocus !== false;
@@ -3746,6 +3796,13 @@ function findLastEnabled(total, isItemDisabled) {
3746
3796
  for (let i = total - 1; i >= 0; i -= 1) if (!isItemDisabled(i)) return i;
3747
3797
  return 0;
3748
3798
  }
3799
+ function findEnabledInRow(row, startCol, step, columns, total, isItemDisabled) {
3800
+ for (let col = startCol; col >= 0 && col < columns; col += step) {
3801
+ const candidate = row * columns + col;
3802
+ if (candidate < total && !isItemDisabled(candidate)) return candidate;
3803
+ }
3804
+ return null;
3805
+ }
3749
3806
  var defaultIsItemDisabled = () => false;
3750
3807
  function useRovingIndex(input) {
3751
3808
  const { total, orientation = "horizontal", loop = true, dir = "ltr", activateOnFocus = false, columns, focusedIndex, initialIndex, onFocusChange, isItemDisabled = defaultIsItemDisabled } = input;
@@ -3784,19 +3841,13 @@ function useRovingIndex(input) {
3784
3841
  const isRtl = dir === "rtl";
3785
3842
  switch (event.key) {
3786
3843
  case "ArrowRight": {
3787
- const targetCol = isRtl ? col - 1 : col + 1;
3788
- if (targetCol >= 0 && targetCol < cols && row * cols + targetCol < total_) {
3789
- const candidate = row * cols + targetCol;
3790
- if (!isItemDisabled(candidate)) nextIndex = candidate;
3791
- }
3844
+ const step = isRtl ? -1 : 1;
3845
+ nextIndex = findEnabledInRow(row, col + step, step, cols, total_, isItemDisabled);
3792
3846
  break;
3793
3847
  }
3794
3848
  case "ArrowLeft": {
3795
- const targetCol = isRtl ? col + 1 : col - 1;
3796
- if (targetCol >= 0 && targetCol < cols && row * cols + targetCol < total_) {
3797
- const candidate = row * cols + targetCol;
3798
- if (!isItemDisabled(candidate)) nextIndex = candidate;
3799
- }
3849
+ const step = isRtl ? 1 : -1;
3850
+ nextIndex = findEnabledInRow(row, col + step, step, cols, total_, isItemDisabled);
3800
3851
  break;
3801
3852
  }
3802
3853
  case "ArrowDown":