@mantine/hooks 9.0.2 → 9.1.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/cjs/index.cjs +8 -0
- package/cjs/use-click-outside/use-click-outside.cjs +11 -8
- package/cjs/use-click-outside/use-click-outside.cjs.map +1 -1
- package/cjs/use-clipboard/use-clipboard.cjs +13 -6
- package/cjs/use-clipboard/use-clipboard.cjs.map +1 -1
- package/cjs/use-collapse/use-collapse.cjs +1 -1
- package/cjs/use-collapse/use-collapse.cjs.map +1 -1
- package/cjs/use-collapse/use-horizontal-collapse.cjs +4 -3
- package/cjs/use-collapse/use-horizontal-collapse.cjs.map +1 -1
- package/cjs/use-counter/use-counter.cjs +12 -3
- package/cjs/use-counter/use-counter.cjs.map +1 -1
- package/cjs/use-debounced-callback/use-debounced-callback.cjs +28 -4
- package/cjs/use-debounced-callback/use-debounced-callback.cjs.map +1 -1
- package/cjs/use-debounced-state/use-debounced-state.cjs +1 -1
- package/cjs/use-debounced-state/use-debounced-state.cjs.map +1 -1
- package/cjs/use-debounced-value/use-debounced-value.cjs +22 -2
- package/cjs/use-debounced-value/use-debounced-value.cjs.map +1 -1
- package/cjs/use-event-listener/use-event-listener.cjs.map +1 -1
- package/cjs/use-favicon/use-favicon.cjs +3 -1
- package/cjs/use-favicon/use-favicon.cjs.map +1 -1
- package/cjs/use-fetch/use-fetch.cjs +7 -4
- package/cjs/use-fetch/use-fetch.cjs.map +1 -1
- package/cjs/use-file-dialog/use-file-dialog.cjs +1 -0
- package/cjs/use-file-dialog/use-file-dialog.cjs.map +1 -1
- package/cjs/use-floating-window/use-floating-window.cjs +33 -36
- package/cjs/use-floating-window/use-floating-window.cjs.map +1 -1
- package/cjs/use-focus-trap/use-focus-trap.cjs +12 -9
- package/cjs/use-focus-trap/use-focus-trap.cjs.map +1 -1
- package/cjs/use-fullscreen/use-fullscreen.cjs +3 -1
- package/cjs/use-fullscreen/use-fullscreen.cjs.map +1 -1
- package/cjs/use-headroom/use-headroom.cjs +4 -1
- package/cjs/use-headroom/use-headroom.cjs.map +1 -1
- package/cjs/use-hotkeys/parse-hotkey.cjs +1 -2
- package/cjs/use-hotkeys/parse-hotkey.cjs.map +1 -1
- package/cjs/use-hover/use-hover.cjs +1 -0
- package/cjs/use-hover/use-hover.cjs.map +1 -1
- package/cjs/use-in-viewport/use-in-viewport.cjs +11 -7
- package/cjs/use-in-viewport/use-in-viewport.cjs.map +1 -1
- package/cjs/use-interval/use-interval.cjs +15 -6
- package/cjs/use-interval/use-interval.cjs.map +1 -1
- package/cjs/use-local-storage/create-storage.cjs +1 -1
- package/cjs/use-local-storage/create-storage.cjs.map +1 -1
- package/cjs/use-long-press/use-long-press.cjs +6 -2
- package/cjs/use-long-press/use-long-press.cjs.map +1 -1
- package/cjs/use-mask/use-mask.cjs +457 -0
- package/cjs/use-mask/use-mask.cjs.map +1 -0
- package/cjs/use-mouse/use-mouse.cjs +2 -2
- package/cjs/use-mouse/use-mouse.cjs.map +1 -1
- package/cjs/use-move/use-move.cjs +8 -0
- package/cjs/use-move/use-move.cjs.map +1 -1
- package/cjs/use-orientation/use-orientation.cjs +8 -2
- package/cjs/use-orientation/use-orientation.cjs.map +1 -1
- package/cjs/use-os/use-os.cjs +1 -1
- package/cjs/use-os/use-os.cjs.map +1 -1
- package/cjs/use-radial-move/use-radial-move.cjs +4 -4
- package/cjs/use-radial-move/use-radial-move.cjs.map +1 -1
- package/cjs/use-roving-index/use-roving-index.cjs +200 -0
- package/cjs/use-roving-index/use-roving-index.cjs.map +1 -0
- package/cjs/use-scroll-direction/use-scroll-direction.cjs +7 -7
- package/cjs/use-scroll-direction/use-scroll-direction.cjs.map +1 -1
- package/cjs/use-scroll-into-view/use-scroll-into-view.cjs +14 -4
- package/cjs/use-scroll-into-view/use-scroll-into-view.cjs.map +1 -1
- package/cjs/use-scroll-spy/use-scroll-spy.cjs +7 -3
- package/cjs/use-scroll-spy/use-scroll-spy.cjs.map +1 -1
- package/cjs/use-scroller/use-scroller.cjs +3 -3
- package/cjs/use-scroller/use-scroller.cjs.map +1 -1
- package/cjs/use-set/use-set.cjs +6 -1
- package/cjs/use-set/use-set.cjs.map +1 -1
- package/cjs/use-throttled-callback/use-throttled-callback.cjs +3 -1
- package/cjs/use-throttled-callback/use-throttled-callback.cjs.map +1 -1
- package/cjs/use-timeout/use-timeout.cjs +3 -1
- package/cjs/use-timeout/use-timeout.cjs.map +1 -1
- package/cjs/use-viewport-size/use-viewport-size.cjs +3 -3
- package/cjs/use-viewport-size/use-viewport-size.cjs.map +1 -1
- package/cjs/use-window-scroll/use-window-scroll.cjs +2 -2
- package/cjs/use-window-scroll/use-window-scroll.cjs.map +1 -1
- package/esm/index.mjs +3 -1
- package/esm/use-click-outside/use-click-outside.mjs +11 -8
- package/esm/use-click-outside/use-click-outside.mjs.map +1 -1
- package/esm/use-clipboard/use-clipboard.mjs +14 -7
- package/esm/use-clipboard/use-clipboard.mjs.map +1 -1
- package/esm/use-collapse/use-collapse.mjs +1 -1
- package/esm/use-collapse/use-collapse.mjs.map +1 -1
- package/esm/use-collapse/use-horizontal-collapse.mjs +5 -4
- package/esm/use-collapse/use-horizontal-collapse.mjs.map +1 -1
- package/esm/use-counter/use-counter.mjs +12 -3
- package/esm/use-counter/use-counter.mjs.map +1 -1
- package/esm/use-debounced-callback/use-debounced-callback.mjs +28 -4
- package/esm/use-debounced-callback/use-debounced-callback.mjs.map +1 -1
- package/esm/use-debounced-state/use-debounced-state.mjs +1 -1
- package/esm/use-debounced-state/use-debounced-state.mjs.map +1 -1
- package/esm/use-debounced-value/use-debounced-value.mjs +22 -2
- package/esm/use-debounced-value/use-debounced-value.mjs.map +1 -1
- package/esm/use-event-listener/use-event-listener.mjs.map +1 -1
- package/esm/use-favicon/use-favicon.mjs +3 -1
- package/esm/use-favicon/use-favicon.mjs.map +1 -1
- package/esm/use-fetch/use-fetch.mjs +7 -4
- package/esm/use-fetch/use-fetch.mjs.map +1 -1
- package/esm/use-file-dialog/use-file-dialog.mjs +1 -0
- package/esm/use-file-dialog/use-file-dialog.mjs.map +1 -1
- package/esm/use-floating-window/use-floating-window.mjs +33 -36
- package/esm/use-floating-window/use-floating-window.mjs.map +1 -1
- package/esm/use-focus-trap/use-focus-trap.mjs +12 -9
- package/esm/use-focus-trap/use-focus-trap.mjs.map +1 -1
- package/esm/use-fullscreen/use-fullscreen.mjs +3 -1
- package/esm/use-fullscreen/use-fullscreen.mjs.map +1 -1
- package/esm/use-headroom/use-headroom.mjs +4 -1
- package/esm/use-headroom/use-headroom.mjs.map +1 -1
- package/esm/use-hotkeys/parse-hotkey.mjs +1 -2
- package/esm/use-hotkeys/parse-hotkey.mjs.map +1 -1
- package/esm/use-hover/use-hover.mjs +1 -0
- package/esm/use-hover/use-hover.mjs.map +1 -1
- package/esm/use-in-viewport/use-in-viewport.mjs +11 -7
- package/esm/use-in-viewport/use-in-viewport.mjs.map +1 -1
- package/esm/use-interval/use-interval.mjs +15 -6
- package/esm/use-interval/use-interval.mjs.map +1 -1
- package/esm/use-local-storage/create-storage.mjs +1 -1
- package/esm/use-local-storage/create-storage.mjs.map +1 -1
- package/esm/use-long-press/use-long-press.mjs +6 -2
- package/esm/use-long-press/use-long-press.mjs.map +1 -1
- package/esm/use-mask/use-mask.mjs +453 -0
- package/esm/use-mask/use-mask.mjs.map +1 -0
- package/esm/use-mouse/use-mouse.mjs +2 -2
- package/esm/use-mouse/use-mouse.mjs.map +1 -1
- package/esm/use-move/use-move.mjs +8 -0
- package/esm/use-move/use-move.mjs.map +1 -1
- package/esm/use-orientation/use-orientation.mjs +8 -2
- package/esm/use-orientation/use-orientation.mjs.map +1 -1
- package/esm/use-os/use-os.mjs +1 -1
- package/esm/use-os/use-os.mjs.map +1 -1
- package/esm/use-radial-move/use-radial-move.mjs +5 -5
- package/esm/use-radial-move/use-radial-move.mjs.map +1 -1
- package/esm/use-roving-index/use-roving-index.mjs +200 -0
- package/esm/use-roving-index/use-roving-index.mjs.map +1 -0
- package/esm/use-scroll-direction/use-scroll-direction.mjs +7 -7
- package/esm/use-scroll-direction/use-scroll-direction.mjs.map +1 -1
- package/esm/use-scroll-into-view/use-scroll-into-view.mjs +15 -5
- package/esm/use-scroll-into-view/use-scroll-into-view.mjs.map +1 -1
- package/esm/use-scroll-spy/use-scroll-spy.mjs +8 -4
- package/esm/use-scroll-spy/use-scroll-spy.mjs.map +1 -1
- package/esm/use-scroller/use-scroller.mjs +3 -3
- package/esm/use-scroller/use-scroller.mjs.map +1 -1
- package/esm/use-set/use-set.mjs +6 -1
- package/esm/use-set/use-set.mjs.map +1 -1
- package/esm/use-throttled-callback/use-throttled-callback.mjs +3 -1
- package/esm/use-throttled-callback/use-throttled-callback.mjs.map +1 -1
- package/esm/use-timeout/use-timeout.mjs +3 -1
- package/esm/use-timeout/use-timeout.mjs.map +1 -1
- package/esm/use-viewport-size/use-viewport-size.mjs +3 -3
- package/esm/use-viewport-size/use-viewport-size.mjs.map +1 -1
- package/esm/use-window-scroll/use-window-scroll.mjs +2 -2
- package/esm/use-window-scroll/use-window-scroll.mjs.map +1 -1
- package/lib/index.d.mts +5 -1
- package/lib/index.d.ts +5 -1
- package/lib/use-click-outside/use-click-outside.d.ts +1 -1
- package/lib/use-collapse/use-horizontal-collapse.d.ts +5 -0
- package/lib/use-counter/use-counter.d.ts +1 -0
- package/lib/use-debounced-callback/use-debounced-callback.d.ts +4 -0
- package/lib/use-debounced-value/use-debounced-value.d.ts +6 -1
- package/lib/use-event-listener/use-event-listener.d.ts +1 -1
- package/lib/use-hotkeys/parse-hotkey.d.ts +4 -5
- package/lib/use-long-press/use-long-press.d.ts +1 -0
- package/lib/use-mask/use-mask.d.ts +60 -0
- package/lib/use-roving-index/use-roving-index.d.ts +49 -0
- package/lib/use-scroll-into-view/use-scroll-into-view.d.ts +4 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-hover.mjs","names":[],"sources":["../../src/use-hover/use-hover.ts"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\n\nexport interface UseHoverReturnValue<T extends HTMLElement = any> {\n hovered: boolean;\n ref: React.RefCallback<T | null>;\n}\n\nexport function useHover<T extends HTMLElement = any>(): UseHoverReturnValue<T> {\n const [hovered, setHovered] = useState(false);\n const previousNode = useRef<HTMLElement>(null);\n\n const handleMouseEnter = useCallback(() => {\n setHovered(true);\n }, []);\n\n const handleMouseLeave = useCallback(() => {\n setHovered(false);\n }, []);\n\n const ref: React.RefCallback<T | null> = useCallback(\n (node) => {\n if (previousNode.current) {\n previousNode.current.removeEventListener('mouseenter', handleMouseEnter);\n previousNode.current.removeEventListener('mouseleave', handleMouseLeave);\n }\n\n if (node) {\n node.addEventListener('mouseenter', handleMouseEnter);\n node.addEventListener('mouseleave', handleMouseLeave);\n }\n\n previousNode.current = node;\n\n return () => {\n previousNode.current = null;\n };\n },\n [handleMouseEnter, handleMouseLeave]\n );\n\n return { ref, hovered };\n}\n\nexport namespace useHover {\n export type ReturnValue<T extends HTMLElement> = UseHoverReturnValue<T>;\n}\n"],"mappings":";;;AAOA,SAAgB,WAAgE;CAC9E,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;CAC7C,MAAM,eAAe,OAAoB,KAAK;CAE9C,MAAM,mBAAmB,kBAAkB;AACzC,aAAW,KAAK;IACf,EAAE,CAAC;CAEN,MAAM,mBAAmB,kBAAkB;AACzC,aAAW,MAAM;IAChB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"use-hover.mjs","names":[],"sources":["../../src/use-hover/use-hover.ts"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\n\nexport interface UseHoverReturnValue<T extends HTMLElement = any> {\n hovered: boolean;\n ref: React.RefCallback<T | null>;\n}\n\nexport function useHover<T extends HTMLElement = any>(): UseHoverReturnValue<T> {\n const [hovered, setHovered] = useState(false);\n const previousNode = useRef<HTMLElement>(null);\n\n const handleMouseEnter = useCallback(() => {\n setHovered(true);\n }, []);\n\n const handleMouseLeave = useCallback(() => {\n setHovered(false);\n }, []);\n\n const ref: React.RefCallback<T | null> = useCallback(\n (node) => {\n if (previousNode.current) {\n previousNode.current.removeEventListener('mouseenter', handleMouseEnter);\n previousNode.current.removeEventListener('mouseleave', handleMouseLeave);\n }\n\n if (node) {\n node.addEventListener('mouseenter', handleMouseEnter);\n node.addEventListener('mouseleave', handleMouseLeave);\n }\n\n previousNode.current = node;\n\n return () => {\n previousNode.current = null;\n setHovered(false);\n };\n },\n [handleMouseEnter, handleMouseLeave]\n );\n\n return { ref, hovered };\n}\n\nexport namespace useHover {\n export type ReturnValue<T extends HTMLElement> = UseHoverReturnValue<T>;\n}\n"],"mappings":";;;AAOA,SAAgB,WAAgE;CAC9E,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;CAC7C,MAAM,eAAe,OAAoB,KAAK;CAE9C,MAAM,mBAAmB,kBAAkB;AACzC,aAAW,KAAK;IACf,EAAE,CAAC;CAEN,MAAM,mBAAmB,kBAAkB;AACzC,aAAW,MAAM;IAChB,EAAE,CAAC;AAwBN,QAAO;EAAE,KAtBgC,aACtC,SAAS;AACR,OAAI,aAAa,SAAS;AACxB,iBAAa,QAAQ,oBAAoB,cAAc,iBAAiB;AACxE,iBAAa,QAAQ,oBAAoB,cAAc,iBAAiB;;AAG1E,OAAI,MAAM;AACR,SAAK,iBAAiB,cAAc,iBAAiB;AACrD,SAAK,iBAAiB,cAAc,iBAAiB;;AAGvD,gBAAa,UAAU;AAEvB,gBAAa;AACX,iBAAa,UAAU;AACvB,eAAW,MAAM;;KAGrB,CAAC,kBAAkB,iBAAiB,CACrC;EAEa;EAAS"}
|
|
@@ -7,13 +7,17 @@ function useInViewport() {
|
|
|
7
7
|
return {
|
|
8
8
|
ref: useCallback((node) => {
|
|
9
9
|
if (typeof IntersectionObserver !== "undefined") {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
observer.current?.disconnect();
|
|
11
|
+
if (node) {
|
|
12
|
+
observer.current = new IntersectionObserver((entries) => {
|
|
13
|
+
const lastEntry = entries[entries.length - 1];
|
|
14
|
+
setInViewport(lastEntry.isIntersecting);
|
|
15
|
+
});
|
|
16
|
+
observer.current.observe(node);
|
|
17
|
+
} else {
|
|
18
|
+
observer.current = null;
|
|
19
|
+
setInViewport(false);
|
|
20
|
+
}
|
|
17
21
|
}
|
|
18
22
|
}, []),
|
|
19
23
|
inViewport
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-in-viewport.mjs","names":[],"sources":["../../src/use-in-viewport/use-in-viewport.ts"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\n\nexport interface UseInViewportReturnValue<T extends HTMLElement = any> {\n inViewport: boolean;\n ref: React.RefCallback<T | null>;\n}\n\nexport function useInViewport<T extends HTMLElement = any>(): UseInViewportReturnValue<T> {\n const observer = useRef<IntersectionObserver | null>(null);\n const [inViewport, setInViewport] = useState(false);\n\n const ref: React.RefCallback<T | null> = useCallback((node) => {\n if (typeof IntersectionObserver !== 'undefined') {\n if (node
|
|
1
|
+
{"version":3,"file":"use-in-viewport.mjs","names":[],"sources":["../../src/use-in-viewport/use-in-viewport.ts"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\n\nexport interface UseInViewportReturnValue<T extends HTMLElement = any> {\n inViewport: boolean;\n ref: React.RefCallback<T | null>;\n}\n\nexport function useInViewport<T extends HTMLElement = any>(): UseInViewportReturnValue<T> {\n const observer = useRef<IntersectionObserver | null>(null);\n const [inViewport, setInViewport] = useState(false);\n\n const ref: React.RefCallback<T | null> = useCallback((node) => {\n if (typeof IntersectionObserver !== 'undefined') {\n observer.current?.disconnect();\n\n if (node) {\n observer.current = new IntersectionObserver((entries) => {\n const lastEntry = entries[entries.length - 1];\n setInViewport(lastEntry.isIntersecting);\n });\n observer.current.observe(node);\n } else {\n observer.current = null;\n setInViewport(false);\n }\n }\n }, []);\n\n return { ref, inViewport };\n}\n\nexport namespace useInViewport {\n export type ReturnValue<T extends HTMLElement> = UseInViewportReturnValue<T>;\n}\n"],"mappings":";;;AAOA,SAAgB,gBAA0E;CACxF,MAAM,WAAW,OAAoC,KAAK;CAC1D,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;AAmBnD,QAAO;EAAE,KAjBgC,aAAa,SAAS;AAC7D,OAAI,OAAO,yBAAyB,aAAa;AAC/C,aAAS,SAAS,YAAY;AAE9B,QAAI,MAAM;AACR,cAAS,UAAU,IAAI,sBAAsB,YAAY;MACvD,MAAM,YAAY,QAAQ,QAAQ,SAAS;AAC3C,oBAAc,UAAU,eAAe;OACvC;AACF,cAAS,QAAQ,QAAQ,KAAK;WACzB;AACL,cAAS,UAAU;AACnB,mBAAc,MAAM;;;KAGvB,EAAE,CAAC;EAEQ;EAAY"}
|
|
@@ -5,21 +5,30 @@ function useInterval(fn, interval, { autoInvoke = false } = {}) {
|
|
|
5
5
|
const [active, setActive] = useState(false);
|
|
6
6
|
const intervalRef = useRef(null);
|
|
7
7
|
const fnRef = useRef(null);
|
|
8
|
+
const intervalValueRef = useRef(interval);
|
|
9
|
+
intervalValueRef.current = interval;
|
|
8
10
|
const start = useCallback(() => {
|
|
9
11
|
setActive((old) => {
|
|
10
|
-
if (!old &&
|
|
12
|
+
if (!old && !intervalRef.current) intervalRef.current = window.setInterval(fnRef.current, intervalValueRef.current);
|
|
11
13
|
return true;
|
|
12
14
|
});
|
|
13
15
|
}, []);
|
|
14
16
|
const stop = useCallback(() => {
|
|
15
17
|
setActive(false);
|
|
16
|
-
window.clearInterval(intervalRef.current
|
|
17
|
-
intervalRef.current =
|
|
18
|
+
if (intervalRef.current) window.clearInterval(intervalRef.current);
|
|
19
|
+
intervalRef.current = null;
|
|
18
20
|
}, []);
|
|
19
21
|
const toggle = useCallback(() => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
setActive((current) => {
|
|
23
|
+
if (current) {
|
|
24
|
+
if (intervalRef.current) window.clearInterval(intervalRef.current);
|
|
25
|
+
intervalRef.current = null;
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (!intervalRef.current) intervalRef.current = window.setInterval(fnRef.current, intervalValueRef.current);
|
|
29
|
+
return true;
|
|
30
|
+
});
|
|
31
|
+
}, []);
|
|
23
32
|
useEffect(() => {
|
|
24
33
|
fnRef.current = fn;
|
|
25
34
|
active && start();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-interval.mjs","names":[],"sources":["../../src/use-interval/use-interval.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nexport interface UseIntervalOptions {\n /** If set, the interval will start automatically when the component is mounted, `false` by default */\n autoInvoke?: boolean;\n}\n\nexport interface UseIntervalReturnValue {\n /** Starts the interval */\n start: () => void;\n\n /** Stops the interval */\n stop: () => void;\n\n /** Toggles the interval */\n toggle: () => void;\n\n /** Indicates if the interval is active */\n active: boolean;\n}\n\nexport function useInterval(\n fn: () => void,\n interval: number,\n { autoInvoke = false }: UseIntervalOptions = {}\n): UseIntervalReturnValue {\n const [active, setActive] = useState(false);\n const intervalRef = useRef<number | null>(null);\n const fnRef = useRef<() => void>(null);\n\n const start = useCallback(() => {\n setActive((old) => {\n if (!old &&
|
|
1
|
+
{"version":3,"file":"use-interval.mjs","names":[],"sources":["../../src/use-interval/use-interval.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nexport interface UseIntervalOptions {\n /** If set, the interval will start automatically when the component is mounted, `false` by default */\n autoInvoke?: boolean;\n}\n\nexport interface UseIntervalReturnValue {\n /** Starts the interval */\n start: () => void;\n\n /** Stops the interval */\n stop: () => void;\n\n /** Toggles the interval */\n toggle: () => void;\n\n /** Indicates if the interval is active */\n active: boolean;\n}\n\nexport function useInterval(\n fn: () => void,\n interval: number,\n { autoInvoke = false }: UseIntervalOptions = {}\n): UseIntervalReturnValue {\n const [active, setActive] = useState(false);\n const intervalRef = useRef<number | null>(null);\n const fnRef = useRef<() => void>(null);\n const intervalValueRef = useRef(interval);\n intervalValueRef.current = interval;\n\n const start = useCallback(() => {\n setActive((old) => {\n if (!old && !intervalRef.current) {\n intervalRef.current = window.setInterval(fnRef.current!, intervalValueRef.current);\n }\n return true;\n });\n }, []);\n\n const stop = useCallback(() => {\n setActive(false);\n if (intervalRef.current) {\n window.clearInterval(intervalRef.current);\n }\n intervalRef.current = null;\n }, []);\n\n const toggle = useCallback(() => {\n setActive((current) => {\n if (current) {\n if (intervalRef.current) {\n window.clearInterval(intervalRef.current);\n }\n intervalRef.current = null;\n return false;\n }\n if (!intervalRef.current) {\n intervalRef.current = window.setInterval(fnRef.current!, intervalValueRef.current);\n }\n return true;\n });\n }, []);\n\n useEffect(() => {\n fnRef.current = fn;\n active && start();\n return stop;\n }, [fn, active, interval]);\n\n useEffect(() => {\n if (autoInvoke) {\n start();\n }\n }, []);\n\n return { start, stop, toggle, active };\n}\n\nexport namespace useInterval {\n export type Options = UseIntervalOptions;\n export type ReturnValue = UseIntervalReturnValue;\n}\n"],"mappings":";;;AAqBA,SAAgB,YACd,IACA,UACA,EAAE,aAAa,UAA8B,EAAE,EACvB;CACxB,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,cAAc,OAAsB,KAAK;CAC/C,MAAM,QAAQ,OAAmB,KAAK;CACtC,MAAM,mBAAmB,OAAO,SAAS;AACzC,kBAAiB,UAAU;CAE3B,MAAM,QAAQ,kBAAkB;AAC9B,aAAW,QAAQ;AACjB,OAAI,CAAC,OAAO,CAAC,YAAY,QACvB,aAAY,UAAU,OAAO,YAAY,MAAM,SAAU,iBAAiB,QAAQ;AAEpF,UAAO;IACP;IACD,EAAE,CAAC;CAEN,MAAM,OAAO,kBAAkB;AAC7B,YAAU,MAAM;AAChB,MAAI,YAAY,QACd,QAAO,cAAc,YAAY,QAAQ;AAE3C,cAAY,UAAU;IACrB,EAAE,CAAC;CAEN,MAAM,SAAS,kBAAkB;AAC/B,aAAW,YAAY;AACrB,OAAI,SAAS;AACX,QAAI,YAAY,QACd,QAAO,cAAc,YAAY,QAAQ;AAE3C,gBAAY,UAAU;AACtB,WAAO;;AAET,OAAI,CAAC,YAAY,QACf,aAAY,UAAU,OAAO,YAAY,MAAM,SAAU,iBAAiB,QAAQ;AAEpF,UAAO;IACP;IACD,EAAE,CAAC;AAEN,iBAAgB;AACd,QAAM,UAAU;AAChB,YAAU,OAAO;AACjB,SAAO;IACN;EAAC;EAAI;EAAQ;EAAS,CAAC;AAE1B,iBAAgB;AACd,MAAI,WACF,QAAO;IAER,EAAE,CAAC;AAEN,QAAO;EAAE;EAAO;EAAM;EAAQ;EAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-storage.mjs","names":[],"sources":["../../src/use-local-storage/create-storage.ts"],"sourcesContent":["/* oxlint-disable no-console */\nimport { useCallback, useEffect, useState } from 'react';\nimport { useWindowEvent } from '../use-window-event/use-window-event';\n\nexport type StorageType = 'localStorage' | 'sessionStorage';\n\nexport interface UseStorageOptions<T> {\n /** Storage key */\n key: string;\n\n /** Default value that will be set if value is not found in storage */\n defaultValue?: T;\n\n /** If set to true, value will be updated in useEffect after mount. Default value is true. */\n getInitialValueInEffect?: boolean;\n\n /** Determines whether the value must be synced between browser tabs, `true` by default */\n sync?: boolean;\n\n /** Function to serialize value into string to be save in storage */\n serialize?: (value: T) => string;\n\n /** Function to deserialize string value from storage to value */\n deserialize?: (value: string | undefined) => T;\n}\n\nfunction serializeJSON<T>(value: T, hookName: string = 'use-local-storage') {\n try {\n return JSON.stringify(value);\n } catch (error) {\n throw new Error(`@mantine/hooks ${hookName}: Failed to serialize the value`);\n }\n}\n\nfunction deserializeJSON(value: string | undefined) {\n try {\n return value && JSON.parse(value);\n } catch {\n return value;\n }\n}\n\nfunction createStorageHandler(type: StorageType) {\n const getItem = (key: string) => {\n try {\n return window[type].getItem(key);\n } catch (error) {\n console.warn('use-local-storage: Failed to get value from storage, localStorage is blocked');\n return null;\n }\n };\n\n const setItem = (key: string, value: string) => {\n try {\n window[type].setItem(key, value);\n } catch (error) {\n console.warn('use-local-storage: Failed to set value to storage, localStorage is blocked');\n }\n };\n\n const removeItem = (key: string) => {\n try {\n window[type].removeItem(key);\n } catch (error) {\n console.warn(\n 'use-local-storage: Failed to remove value from storage, localStorage is blocked'\n );\n }\n };\n\n return { getItem, setItem, removeItem };\n}\n\nexport type UseStorageReturnValue<T> = [\n T, // current value\n (val: T | ((prevState: T) => T)) => void, // callback to set value in storage\n () => void, // callback to remove value from storage\n];\n\nexport function createStorage<T>(type: StorageType, hookName: string) {\n const eventName = type === 'localStorage' ? 'mantine-local-storage' : 'mantine-session-storage';\n const { getItem, setItem, removeItem } = createStorageHandler(type);\n\n return function useStorage({\n key,\n defaultValue,\n getInitialValueInEffect = true,\n sync = true,\n deserialize = deserializeJSON,\n serialize = (value: T) => serializeJSON(value, hookName),\n }: UseStorageOptions<T>): UseStorageReturnValue<T> {\n const readStorageValue = useCallback(\n (skipStorage?: boolean): T => {\n let storageBlockedOrSkipped;\n\n try {\n storageBlockedOrSkipped =\n typeof window === 'undefined' ||\n !(type in window) ||\n window[type] === null ||\n !!skipStorage;\n } catch (_e) {\n storageBlockedOrSkipped = true;\n }\n\n if (storageBlockedOrSkipped) {\n return defaultValue as T;\n }\n\n const storageValue = getItem(key);\n return storageValue !== null ? deserialize(storageValue) : (defaultValue as T);\n },\n [key, defaultValue]\n );\n\n const [value, setValue] = useState<T>(readStorageValue(getInitialValueInEffect));\n\n const setStorageValue = useCallback(\n (val: T | ((prevState: T) => T)) => {\n if (val instanceof Function) {\n setValue((current) => {\n const result = val(current);\n setItem(key, serialize(result));\n // Defer dispatching this event to avoid the handler being called during render.\n queueMicrotask(() => {\n window.dispatchEvent(
|
|
1
|
+
{"version":3,"file":"create-storage.mjs","names":[],"sources":["../../src/use-local-storage/create-storage.ts"],"sourcesContent":["/* oxlint-disable no-console */\nimport { useCallback, useEffect, useState } from 'react';\nimport { useWindowEvent } from '../use-window-event/use-window-event';\n\nexport type StorageType = 'localStorage' | 'sessionStorage';\n\nexport interface UseStorageOptions<T> {\n /** Storage key */\n key: string;\n\n /** Default value that will be set if value is not found in storage */\n defaultValue?: T;\n\n /** If set to true, value will be updated in useEffect after mount. Default value is true. */\n getInitialValueInEffect?: boolean;\n\n /** Determines whether the value must be synced between browser tabs, `true` by default */\n sync?: boolean;\n\n /** Function to serialize value into string to be save in storage */\n serialize?: (value: T) => string;\n\n /** Function to deserialize string value from storage to value */\n deserialize?: (value: string | undefined) => T;\n}\n\nfunction serializeJSON<T>(value: T, hookName: string = 'use-local-storage') {\n try {\n return JSON.stringify(value);\n } catch (error) {\n throw new Error(`@mantine/hooks ${hookName}: Failed to serialize the value`);\n }\n}\n\nfunction deserializeJSON(value: string | undefined) {\n try {\n return value && JSON.parse(value);\n } catch {\n return value;\n }\n}\n\nfunction createStorageHandler(type: StorageType) {\n const getItem = (key: string) => {\n try {\n return window[type].getItem(key);\n } catch (error) {\n console.warn('use-local-storage: Failed to get value from storage, localStorage is blocked');\n return null;\n }\n };\n\n const setItem = (key: string, value: string) => {\n try {\n window[type].setItem(key, value);\n } catch (error) {\n console.warn('use-local-storage: Failed to set value to storage, localStorage is blocked');\n }\n };\n\n const removeItem = (key: string) => {\n try {\n window[type].removeItem(key);\n } catch (error) {\n console.warn(\n 'use-local-storage: Failed to remove value from storage, localStorage is blocked'\n );\n }\n };\n\n return { getItem, setItem, removeItem };\n}\n\nexport type UseStorageReturnValue<T> = [\n T, // current value\n (val: T | ((prevState: T) => T)) => void, // callback to set value in storage\n () => void, // callback to remove value from storage\n];\n\nexport function createStorage<T>(type: StorageType, hookName: string) {\n const eventName = type === 'localStorage' ? 'mantine-local-storage' : 'mantine-session-storage';\n const { getItem, setItem, removeItem } = createStorageHandler(type);\n\n return function useStorage({\n key,\n defaultValue,\n getInitialValueInEffect = true,\n sync = true,\n deserialize = deserializeJSON,\n serialize = (value: T) => serializeJSON(value, hookName),\n }: UseStorageOptions<T>): UseStorageReturnValue<T> {\n const readStorageValue = useCallback(\n (skipStorage?: boolean): T => {\n let storageBlockedOrSkipped;\n\n try {\n storageBlockedOrSkipped =\n typeof window === 'undefined' ||\n !(type in window) ||\n window[type] === null ||\n !!skipStorage;\n } catch (_e) {\n storageBlockedOrSkipped = true;\n }\n\n if (storageBlockedOrSkipped) {\n return defaultValue as T;\n }\n\n const storageValue = getItem(key);\n return storageValue !== null ? deserialize(storageValue) : (defaultValue as T);\n },\n [key, defaultValue]\n );\n\n const [value, setValue] = useState<T>(readStorageValue(getInitialValueInEffect));\n\n const setStorageValue = useCallback(\n (val: T | ((prevState: T) => T)) => {\n if (val instanceof Function) {\n setValue((current) => {\n const result = val(current);\n setItem(key, serialize(result));\n // Defer dispatching this event to avoid the handler being called during render.\n queueMicrotask(() => {\n window.dispatchEvent(new CustomEvent(eventName, { detail: { key, value: result } }));\n });\n return result;\n });\n } else {\n setItem(key, serialize(val));\n window.dispatchEvent(new CustomEvent(eventName, { detail: { key, value: val } }));\n setValue(val);\n }\n },\n [key]\n );\n\n const removeStorageValue = useCallback(() => {\n removeItem(key);\n setValue(defaultValue as T);\n window.dispatchEvent(new CustomEvent(eventName, { detail: { key, value: defaultValue } }));\n }, [key, defaultValue]);\n\n useWindowEvent('storage', (event) => {\n if (sync) {\n if (event.storageArea === window[type] && event.key === key) {\n setValue(deserialize(event.newValue ?? undefined));\n }\n }\n });\n\n useWindowEvent(eventName, (event) => {\n if (sync) {\n if (event.detail.key === key) {\n setValue(event.detail.value);\n }\n }\n });\n\n useEffect(() => {\n if (defaultValue !== undefined && value === undefined) {\n setStorageValue(defaultValue);\n }\n }, [defaultValue, value, setStorageValue]);\n\n useEffect(() => {\n const val = readStorageValue();\n val !== undefined && setStorageValue(val);\n }, [key]);\n\n return [value === undefined ? (defaultValue as T) : value, setStorageValue, removeStorageValue];\n };\n}\n\nexport function readValue(type: StorageType) {\n const { getItem } = createStorageHandler(type);\n\n return function read<T>({\n key,\n defaultValue,\n deserialize = deserializeJSON,\n }: UseStorageOptions<T>) {\n let storageBlockedOrSkipped;\n\n try {\n storageBlockedOrSkipped =\n typeof window === 'undefined' || !(type in window) || window[type] === null;\n } catch (_e) {\n storageBlockedOrSkipped = true;\n }\n\n if (storageBlockedOrSkipped) {\n return defaultValue as T;\n }\n\n const storageValue = getItem(key);\n return storageValue !== null ? deserialize(storageValue) : (defaultValue as T);\n };\n}\n"],"mappings":";;;;AA0BA,SAAS,cAAiB,OAAU,WAAmB,qBAAqB;AAC1E,KAAI;AACF,SAAO,KAAK,UAAU,MAAM;UACrB,OAAO;AACd,QAAM,IAAI,MAAM,kBAAkB,SAAS,iCAAiC;;;AAIhF,SAAS,gBAAgB,OAA2B;AAClD,KAAI;AACF,SAAO,SAAS,KAAK,MAAM,MAAM;SAC3B;AACN,SAAO;;;AAIX,SAAS,qBAAqB,MAAmB;CAC/C,MAAM,WAAW,QAAgB;AAC/B,MAAI;AACF,UAAO,OAAO,MAAM,QAAQ,IAAI;WACzB,OAAO;AACd,WAAQ,KAAK,+EAA+E;AAC5F,UAAO;;;CAIX,MAAM,WAAW,KAAa,UAAkB;AAC9C,MAAI;AACF,UAAO,MAAM,QAAQ,KAAK,MAAM;WACzB,OAAO;AACd,WAAQ,KAAK,6EAA6E;;;CAI9F,MAAM,cAAc,QAAgB;AAClC,MAAI;AACF,UAAO,MAAM,WAAW,IAAI;WACrB,OAAO;AACd,WAAQ,KACN,kFACD;;;AAIL,QAAO;EAAE;EAAS;EAAS;EAAY;;AASzC,SAAgB,cAAiB,MAAmB,UAAkB;CACpE,MAAM,YAAY,SAAS,iBAAiB,0BAA0B;CACtE,MAAM,EAAE,SAAS,SAAS,eAAe,qBAAqB,KAAK;AAEnE,QAAO,SAAS,WAAW,EACzB,KACA,cACA,0BAA0B,MAC1B,OAAO,MACP,cAAc,iBACd,aAAa,UAAa,cAAc,OAAO,SAAS,IACP;EACjD,MAAM,mBAAmB,aACtB,gBAA6B;GAC5B,IAAI;AAEJ,OAAI;AACF,8BACE,OAAO,WAAW,eAClB,EAAE,QAAQ,WACV,OAAO,UAAU,QACjB,CAAC,CAAC;YACG,IAAI;AACX,8BAA0B;;AAG5B,OAAI,wBACF,QAAO;GAGT,MAAM,eAAe,QAAQ,IAAI;AACjC,UAAO,iBAAiB,OAAO,YAAY,aAAa,GAAI;KAE9D,CAAC,KAAK,aAAa,CACpB;EAED,MAAM,CAAC,OAAO,YAAY,SAAY,iBAAiB,wBAAwB,CAAC;EAEhF,MAAM,kBAAkB,aACrB,QAAmC;AAClC,OAAI,eAAe,SACjB,WAAU,YAAY;IACpB,MAAM,SAAS,IAAI,QAAQ;AAC3B,YAAQ,KAAK,UAAU,OAAO,CAAC;AAE/B,yBAAqB;AACnB,YAAO,cAAc,IAAI,YAAY,WAAW,EAAE,QAAQ;MAAE;MAAK,OAAO;MAAQ,EAAE,CAAC,CAAC;MACpF;AACF,WAAO;KACP;QACG;AACL,YAAQ,KAAK,UAAU,IAAI,CAAC;AAC5B,WAAO,cAAc,IAAI,YAAY,WAAW,EAAE,QAAQ;KAAE;KAAK,OAAO;KAAK,EAAE,CAAC,CAAC;AACjF,aAAS,IAAI;;KAGjB,CAAC,IAAI,CACN;EAED,MAAM,qBAAqB,kBAAkB;AAC3C,cAAW,IAAI;AACf,YAAS,aAAkB;AAC3B,UAAO,cAAc,IAAI,YAAY,WAAW,EAAE,QAAQ;IAAE;IAAK,OAAO;IAAc,EAAE,CAAC,CAAC;KACzF,CAAC,KAAK,aAAa,CAAC;AAEvB,iBAAe,YAAY,UAAU;AACnC,OAAI;QACE,MAAM,gBAAgB,OAAO,SAAS,MAAM,QAAQ,IACtD,UAAS,YAAY,MAAM,YAAY,KAAA,EAAU,CAAC;;IAGtD;AAEF,iBAAe,YAAY,UAAU;AACnC,OAAI;QACE,MAAM,OAAO,QAAQ,IACvB,UAAS,MAAM,OAAO,MAAM;;IAGhC;AAEF,kBAAgB;AACd,OAAI,iBAAiB,KAAA,KAAa,UAAU,KAAA,EAC1C,iBAAgB,aAAa;KAE9B;GAAC;GAAc;GAAO;GAAgB,CAAC;AAE1C,kBAAgB;GACd,MAAM,MAAM,kBAAkB;AAC9B,WAAQ,KAAA,KAAa,gBAAgB,IAAI;KACxC,CAAC,IAAI,CAAC;AAET,SAAO;GAAC,UAAU,KAAA,IAAa,eAAqB;GAAO;GAAiB;GAAmB;;;AAInG,SAAgB,UAAU,MAAmB;CAC3C,MAAM,EAAE,YAAY,qBAAqB,KAAK;AAE9C,QAAO,SAAS,KAAQ,EACtB,KACA,cACA,cAAc,mBACS;EACvB,IAAI;AAEJ,MAAI;AACF,6BACE,OAAO,WAAW,eAAe,EAAE,QAAQ,WAAW,OAAO,UAAU;WAClE,IAAI;AACX,6BAA0B;;AAG5B,MAAI,wBACF,QAAO;EAGT,MAAM,eAAe,QAAQ,IAAI;AACjC,SAAO,iBAAiB,OAAO,YAAY,aAAa,GAAI"}
|
|
@@ -27,14 +27,18 @@ function useLongPress(onLongPress, options = {}) {
|
|
|
27
27
|
}
|
|
28
28
|
isLongPressActive.current = false;
|
|
29
29
|
isPressed.current = false;
|
|
30
|
-
if (timeout.current)
|
|
30
|
+
if (timeout.current !== -1) {
|
|
31
|
+
window.clearTimeout(timeout.current);
|
|
32
|
+
timeout.current = -1;
|
|
33
|
+
}
|
|
31
34
|
};
|
|
32
35
|
return {
|
|
33
36
|
onMouseDown: start,
|
|
34
37
|
onMouseUp: cancel,
|
|
35
38
|
onMouseLeave: cancel,
|
|
36
39
|
onTouchStart: start,
|
|
37
|
-
onTouchEnd: cancel
|
|
40
|
+
onTouchEnd: cancel,
|
|
41
|
+
onTouchCancel: cancel
|
|
38
42
|
};
|
|
39
43
|
}, [
|
|
40
44
|
onLongPress,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-long-press.mjs","names":[],"sources":["../../src/use-long-press/use-long-press.ts"],"sourcesContent":["import React, { useEffect, useMemo, useRef } from 'react';\n\nexport interface UseLongPressOptions {\n /** Time in milliseconds to trigger the long press, default is 400ms */\n threshold?: number;\n\n /** Callback triggered when the long press starts */\n onStart?: (event: React.MouseEvent | React.TouchEvent) => void;\n\n /** Callback triggered when the long press finishes */\n onFinish?: (event: React.MouseEvent | React.TouchEvent) => void;\n\n /** Callback triggered when the long press is canceled */\n onCancel?: (event: React.MouseEvent | React.TouchEvent) => void;\n}\n\nexport interface UseLongPressReturnValue {\n onMouseDown: (event: React.MouseEvent) => void;\n onMouseUp: (event: React.MouseEvent) => void;\n onMouseLeave: (event: React.MouseEvent) => void;\n onTouchStart: (event: React.TouchEvent) => void;\n onTouchEnd: (event: React.TouchEvent) => void;\n}\n\nexport function useLongPress(\n onLongPress: (event: React.MouseEvent | React.TouchEvent) => void,\n options: UseLongPressOptions = {}\n): UseLongPressReturnValue {\n const { threshold = 400, onStart, onFinish, onCancel } = options;\n const isLongPressActive = useRef(false);\n const isPressed = useRef(false);\n const timeout = useRef<number>(-1);\n\n useEffect(() => () => window.clearTimeout(timeout.current), []);\n\n return useMemo(() => {\n if (typeof onLongPress !== 'function') {\n return {} as UseLongPressReturnValue;\n }\n\n const start = (event: React.MouseEvent | React.TouchEvent) => {\n if (!isMouseEvent(event) && !isTouchEvent(event)) {\n return;\n }\n\n if (onStart) {\n onStart(event);\n }\n\n isPressed.current = true;\n timeout.current = window.setTimeout(() => {\n onLongPress(event);\n isLongPressActive.current = true;\n }, threshold);\n };\n\n const cancel = (event: React.MouseEvent | React.TouchEvent) => {\n if (!isMouseEvent(event) && !isTouchEvent(event)) {\n return;\n }\n\n if (isLongPressActive.current) {\n if (onFinish) {\n onFinish(event);\n }\n } else if (isPressed.current) {\n if (onCancel) {\n onCancel(event);\n }\n }\n\n isLongPressActive.current = false;\n isPressed.current = false;\n\n if (timeout.current) {\n window.clearTimeout(timeout.current);\n }\n };\n\n return {\n onMouseDown: start,\n onMouseUp: cancel,\n onMouseLeave: cancel,\n onTouchStart: start,\n onTouchEnd: cancel,\n };\n }, [onLongPress, threshold, onCancel, onFinish, onStart]);\n}\n\nfunction isTouchEvent(event: React.MouseEvent | React.TouchEvent): event is React.TouchEvent {\n return window.TouchEvent\n ? event.nativeEvent instanceof TouchEvent\n : 'touches' in event.nativeEvent;\n}\n\nfunction isMouseEvent(event: React.MouseEvent | React.TouchEvent): event is React.MouseEvent {\n return event.nativeEvent instanceof MouseEvent;\n}\n\nexport namespace useLongPress {\n export type Options = UseLongPressOptions;\n export type ReturnValue = UseLongPressReturnValue;\n}\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"use-long-press.mjs","names":[],"sources":["../../src/use-long-press/use-long-press.ts"],"sourcesContent":["import React, { useEffect, useMemo, useRef } from 'react';\n\nexport interface UseLongPressOptions {\n /** Time in milliseconds to trigger the long press, default is 400ms */\n threshold?: number;\n\n /** Callback triggered when the long press starts */\n onStart?: (event: React.MouseEvent | React.TouchEvent) => void;\n\n /** Callback triggered when the long press finishes */\n onFinish?: (event: React.MouseEvent | React.TouchEvent) => void;\n\n /** Callback triggered when the long press is canceled */\n onCancel?: (event: React.MouseEvent | React.TouchEvent) => void;\n}\n\nexport interface UseLongPressReturnValue {\n onMouseDown: (event: React.MouseEvent) => void;\n onMouseUp: (event: React.MouseEvent) => void;\n onMouseLeave: (event: React.MouseEvent) => void;\n onTouchStart: (event: React.TouchEvent) => void;\n onTouchEnd: (event: React.TouchEvent) => void;\n onTouchCancel: (event: React.TouchEvent) => void;\n}\n\nexport function useLongPress(\n onLongPress: (event: React.MouseEvent | React.TouchEvent) => void,\n options: UseLongPressOptions = {}\n): UseLongPressReturnValue {\n const { threshold = 400, onStart, onFinish, onCancel } = options;\n const isLongPressActive = useRef(false);\n const isPressed = useRef(false);\n const timeout = useRef<number>(-1);\n\n useEffect(() => () => window.clearTimeout(timeout.current), []);\n\n return useMemo(() => {\n if (typeof onLongPress !== 'function') {\n return {} as UseLongPressReturnValue;\n }\n\n const start = (event: React.MouseEvent | React.TouchEvent) => {\n if (!isMouseEvent(event) && !isTouchEvent(event)) {\n return;\n }\n\n if (onStart) {\n onStart(event);\n }\n\n isPressed.current = true;\n timeout.current = window.setTimeout(() => {\n onLongPress(event);\n isLongPressActive.current = true;\n }, threshold);\n };\n\n const cancel = (event: React.MouseEvent | React.TouchEvent) => {\n if (!isMouseEvent(event) && !isTouchEvent(event)) {\n return;\n }\n\n if (isLongPressActive.current) {\n if (onFinish) {\n onFinish(event);\n }\n } else if (isPressed.current) {\n if (onCancel) {\n onCancel(event);\n }\n }\n\n isLongPressActive.current = false;\n isPressed.current = false;\n\n if (timeout.current !== -1) {\n window.clearTimeout(timeout.current);\n timeout.current = -1;\n }\n };\n\n return {\n onMouseDown: start,\n onMouseUp: cancel,\n onMouseLeave: cancel,\n onTouchStart: start,\n onTouchEnd: cancel,\n onTouchCancel: cancel,\n };\n }, [onLongPress, threshold, onCancel, onFinish, onStart]);\n}\n\nfunction isTouchEvent(event: React.MouseEvent | React.TouchEvent): event is React.TouchEvent {\n return window.TouchEvent\n ? event.nativeEvent instanceof TouchEvent\n : 'touches' in event.nativeEvent;\n}\n\nfunction isMouseEvent(event: React.MouseEvent | React.TouchEvent): event is React.MouseEvent {\n return event.nativeEvent instanceof MouseEvent;\n}\n\nexport namespace useLongPress {\n export type Options = UseLongPressOptions;\n export type ReturnValue = UseLongPressReturnValue;\n}\n"],"mappings":";;;AAyBA,SAAgB,aACd,aACA,UAA+B,EAAE,EACR;CACzB,MAAM,EAAE,YAAY,KAAK,SAAS,UAAU,aAAa;CACzD,MAAM,oBAAoB,OAAO,MAAM;CACvC,MAAM,YAAY,OAAO,MAAM;CAC/B,MAAM,UAAU,OAAe,GAAG;AAElC,uBAAsB,OAAO,aAAa,QAAQ,QAAQ,EAAE,EAAE,CAAC;AAE/D,QAAO,cAAc;AACnB,MAAI,OAAO,gBAAgB,WACzB,QAAO,EAAE;EAGX,MAAM,SAAS,UAA+C;AAC5D,OAAI,CAAC,aAAa,MAAM,IAAI,CAAC,aAAa,MAAM,CAC9C;AAGF,OAAI,QACF,SAAQ,MAAM;AAGhB,aAAU,UAAU;AACpB,WAAQ,UAAU,OAAO,iBAAiB;AACxC,gBAAY,MAAM;AAClB,sBAAkB,UAAU;MAC3B,UAAU;;EAGf,MAAM,UAAU,UAA+C;AAC7D,OAAI,CAAC,aAAa,MAAM,IAAI,CAAC,aAAa,MAAM,CAC9C;AAGF,OAAI,kBAAkB;QAChB,SACF,UAAS,MAAM;cAER,UAAU;QACf,SACF,UAAS,MAAM;;AAInB,qBAAkB,UAAU;AAC5B,aAAU,UAAU;AAEpB,OAAI,QAAQ,YAAY,IAAI;AAC1B,WAAO,aAAa,QAAQ,QAAQ;AACpC,YAAQ,UAAU;;;AAItB,SAAO;GACL,aAAa;GACb,WAAW;GACX,cAAc;GACd,cAAc;GACd,YAAY;GACZ,eAAe;GAChB;IACA;EAAC;EAAa;EAAW;EAAU;EAAU;EAAQ,CAAC;;AAG3D,SAAS,aAAa,OAAuE;AAC3F,QAAO,OAAO,aACV,MAAM,uBAAuB,aAC7B,aAAa,MAAM;;AAGzB,SAAS,aAAa,OAAuE;AAC3F,QAAO,MAAM,uBAAuB"}
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
//#region packages/@mantine/hooks/src/use-mask/use-mask.ts
|
|
4
|
+
const DEFAULT_TOKENS = {
|
|
5
|
+
"9": /[0-9]/,
|
|
6
|
+
a: /[A-Za-z]/,
|
|
7
|
+
A: /[A-Z]/,
|
|
8
|
+
"*": /[A-Za-z0-9]/,
|
|
9
|
+
"#": /[-+0-9]/
|
|
10
|
+
};
|
|
11
|
+
function parseMask(mask, tokens) {
|
|
12
|
+
if (Array.isArray(mask)) return mask.map((item) => {
|
|
13
|
+
if (item instanceof RegExp) return {
|
|
14
|
+
type: "token",
|
|
15
|
+
char: "_",
|
|
16
|
+
pattern: item
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
type: "literal",
|
|
20
|
+
char: item
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
const slots = [];
|
|
24
|
+
let optional = false;
|
|
25
|
+
for (let i = 0; i < mask.length; i++) {
|
|
26
|
+
const char = mask[i];
|
|
27
|
+
if (char === "\\" && i + 1 < mask.length) {
|
|
28
|
+
i++;
|
|
29
|
+
slots.push({
|
|
30
|
+
type: "literal",
|
|
31
|
+
char: mask[i]
|
|
32
|
+
});
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (char === "?") {
|
|
36
|
+
optional = true;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (tokens[char]) slots.push({
|
|
40
|
+
type: "token",
|
|
41
|
+
char,
|
|
42
|
+
pattern: tokens[char],
|
|
43
|
+
optional
|
|
44
|
+
});
|
|
45
|
+
else slots.push({
|
|
46
|
+
type: "literal",
|
|
47
|
+
char,
|
|
48
|
+
optional
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return slots;
|
|
52
|
+
}
|
|
53
|
+
function getSlotChar(slotCharOption, index) {
|
|
54
|
+
if (slotCharOption === null || slotCharOption === "" || slotCharOption === void 0) return "";
|
|
55
|
+
if (slotCharOption.length > 1) return slotCharOption[index] || "_";
|
|
56
|
+
return slotCharOption;
|
|
57
|
+
}
|
|
58
|
+
function applyMaskToRaw(raw, slots, _slotCharOption, transform) {
|
|
59
|
+
let result = "";
|
|
60
|
+
let rawIndex = 0;
|
|
61
|
+
let slotIndex = 0;
|
|
62
|
+
for (slotIndex = 0; slotIndex < slots.length; slotIndex++) {
|
|
63
|
+
const slot = slots[slotIndex];
|
|
64
|
+
if (slot.type === "literal") result += slot.char;
|
|
65
|
+
else if (rawIndex < raw.length) {
|
|
66
|
+
const ch = transform ? transform(raw[rawIndex]) : raw[rawIndex];
|
|
67
|
+
if (slot.pattern && slot.pattern.test(ch)) {
|
|
68
|
+
result += ch;
|
|
69
|
+
rawIndex++;
|
|
70
|
+
} else {
|
|
71
|
+
rawIndex++;
|
|
72
|
+
slotIndex--;
|
|
73
|
+
}
|
|
74
|
+
} else break;
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
function buildDisplayValue(value, slots, slotCharOption, showSlots) {
|
|
79
|
+
if (!showSlots) return value;
|
|
80
|
+
let display = value;
|
|
81
|
+
for (let i = value.length; i < slots.length; i++) {
|
|
82
|
+
const slot = slots[i];
|
|
83
|
+
if (slot.type === "literal") display += slot.char;
|
|
84
|
+
else {
|
|
85
|
+
const sc = getSlotChar(slotCharOption, i);
|
|
86
|
+
if (!sc) break;
|
|
87
|
+
display += sc;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return display;
|
|
91
|
+
}
|
|
92
|
+
function extractRaw(masked, slots) {
|
|
93
|
+
let raw = "";
|
|
94
|
+
for (let i = 0; i < masked.length && i < slots.length; i++) if (slots[i].type === "token") raw += masked[i];
|
|
95
|
+
return raw;
|
|
96
|
+
}
|
|
97
|
+
function checkComplete(masked, slots) {
|
|
98
|
+
for (let i = 0; i < slots.length; i++) if (slots[i].type === "token" && !slots[i].optional) {
|
|
99
|
+
if (i >= masked.length) return false;
|
|
100
|
+
if (!slots[i].pattern.test(masked[i])) return false;
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
function findNextTokenIndex(slots, from) {
|
|
105
|
+
for (let i = from; i < slots.length; i++) if (slots[i].type === "token") return i;
|
|
106
|
+
return slots.length;
|
|
107
|
+
}
|
|
108
|
+
function findPrevTokenIndex(slots, from) {
|
|
109
|
+
for (let i = from; i >= 0; i--) if (slots[i].type === "token") return i;
|
|
110
|
+
return -1;
|
|
111
|
+
}
|
|
112
|
+
function processInput(inputValue, slots, _slotCharOption) {
|
|
113
|
+
let result = "";
|
|
114
|
+
let inputIndex = 0;
|
|
115
|
+
for (let slotIndex = 0; slotIndex < slots.length && inputIndex <= inputValue.length; slotIndex++) {
|
|
116
|
+
const slot = slots[slotIndex];
|
|
117
|
+
if (slot.type === "literal") {
|
|
118
|
+
result += slot.char;
|
|
119
|
+
if (inputIndex < inputValue.length && inputValue[inputIndex] === slot.char) inputIndex++;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (inputIndex >= inputValue.length) break;
|
|
123
|
+
while (inputIndex < inputValue.length) {
|
|
124
|
+
const ch = inputValue[inputIndex];
|
|
125
|
+
inputIndex++;
|
|
126
|
+
if (slot.pattern.test(ch)) {
|
|
127
|
+
result += ch;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (result.length <= slotIndex) break;
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
function getResolvedOptions(options, rawValue) {
|
|
136
|
+
const tokens = {
|
|
137
|
+
...DEFAULT_TOKENS,
|
|
138
|
+
...options.tokens
|
|
139
|
+
};
|
|
140
|
+
let mask = options.mask;
|
|
141
|
+
let slotChar = options.slotChar === void 0 ? "_" : options.slotChar;
|
|
142
|
+
let separate = options.separate ?? false;
|
|
143
|
+
if (options.modify) {
|
|
144
|
+
const overrides = options.modify(rawValue);
|
|
145
|
+
if (overrides) {
|
|
146
|
+
if (overrides.mask !== void 0) mask = overrides.mask;
|
|
147
|
+
if (overrides.tokens !== void 0) Object.assign(tokens, overrides.tokens);
|
|
148
|
+
if (overrides.slotChar !== void 0) slotChar = overrides.slotChar;
|
|
149
|
+
if (overrides.separate !== void 0) separate = overrides.separate;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
slots: parseMask(mask, tokens),
|
|
154
|
+
slotChar,
|
|
155
|
+
separate,
|
|
156
|
+
tokens,
|
|
157
|
+
transform: options.transform
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function formatMask(raw, options) {
|
|
161
|
+
const { slots, slotChar, transform } = getResolvedOptions(options, raw);
|
|
162
|
+
return applyMaskToRaw(raw, slots, slotChar, transform);
|
|
163
|
+
}
|
|
164
|
+
function unformatMask(masked, options) {
|
|
165
|
+
const { slots } = getResolvedOptions(options, "");
|
|
166
|
+
return extractRaw(masked, slots);
|
|
167
|
+
}
|
|
168
|
+
function isMaskComplete(masked, options) {
|
|
169
|
+
const { slots } = getResolvedOptions(options, "");
|
|
170
|
+
return checkComplete(masked, slots);
|
|
171
|
+
}
|
|
172
|
+
function generatePattern(mode, options) {
|
|
173
|
+
const { slots } = getResolvedOptions(options, "");
|
|
174
|
+
let pattern = "";
|
|
175
|
+
for (const slot of slots) if (slot.type === "literal") pattern += slot.char.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
176
|
+
else {
|
|
177
|
+
const src = slot.pattern.source;
|
|
178
|
+
if (mode === "full-inexact") pattern += slot.optional ? `${src}?` : src;
|
|
179
|
+
else pattern += slot.optional ? `(${src})?` : `(${src})`;
|
|
180
|
+
}
|
|
181
|
+
return pattern;
|
|
182
|
+
}
|
|
183
|
+
function useMask(options) {
|
|
184
|
+
const optionsRef = useRef(options);
|
|
185
|
+
optionsRef.current = options;
|
|
186
|
+
const inputRef = useRef(null);
|
|
187
|
+
const [maskedValue, setMaskedValue] = useState("");
|
|
188
|
+
const [rawValue, setRawValue] = useState("");
|
|
189
|
+
const processedRef = useRef("");
|
|
190
|
+
const wasCompleteRef = useRef(false);
|
|
191
|
+
const isFocusedRef = useRef(false);
|
|
192
|
+
const getOptions = useCallback(() => {
|
|
193
|
+
const opts = optionsRef.current;
|
|
194
|
+
return getResolvedOptions(opts, rawValue);
|
|
195
|
+
}, [rawValue]);
|
|
196
|
+
const updateValue = useCallback((newMasked, cursorPos) => {
|
|
197
|
+
const opts = optionsRef.current;
|
|
198
|
+
const { slots } = getResolvedOptions(opts, extractRaw(newMasked, getResolvedOptions(opts, "").slots));
|
|
199
|
+
const { slots: resolvedSlots, slotChar } = getResolvedOptions(opts, extractRaw(newMasked, slots));
|
|
200
|
+
const reprocessed = processInput(newMasked, resolvedSlots, slotChar);
|
|
201
|
+
const newRaw = extractRaw(reprocessed, resolvedSlots);
|
|
202
|
+
const showSlots = opts.alwaysShowMask || isFocusedRef.current;
|
|
203
|
+
const showOnFocus = opts.showMaskOnFocus !== false;
|
|
204
|
+
const displayValue = buildDisplayValue(reprocessed, resolvedSlots, slotChar, showSlots && (showOnFocus || reprocessed.length > 0));
|
|
205
|
+
processedRef.current = reprocessed;
|
|
206
|
+
setMaskedValue(displayValue);
|
|
207
|
+
setRawValue(newRaw);
|
|
208
|
+
if (inputRef.current) {
|
|
209
|
+
inputRef.current.value = displayValue;
|
|
210
|
+
if (cursorPos !== void 0 && document.activeElement === inputRef.current) {
|
|
211
|
+
const pos = Math.min(cursorPos, reprocessed.length);
|
|
212
|
+
inputRef.current.setSelectionRange(pos, pos);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (opts.onChangeRaw) opts.onChangeRaw(newRaw, displayValue);
|
|
216
|
+
const complete = checkComplete(reprocessed, resolvedSlots);
|
|
217
|
+
if (complete && !wasCompleteRef.current && opts.onComplete) opts.onComplete(displayValue, newRaw);
|
|
218
|
+
wasCompleteRef.current = complete;
|
|
219
|
+
return {
|
|
220
|
+
displayValue,
|
|
221
|
+
newRaw,
|
|
222
|
+
reprocessed,
|
|
223
|
+
resolvedSlots
|
|
224
|
+
};
|
|
225
|
+
}, [getOptions]);
|
|
226
|
+
const handleInput = useCallback((e) => {
|
|
227
|
+
const input = e.target;
|
|
228
|
+
const opts = optionsRef.current;
|
|
229
|
+
const { slots: resolvedSlots, slotChar, transform } = getResolvedOptions(opts, "");
|
|
230
|
+
const reformatted = applyMaskToRaw(extractRaw(input.value, resolvedSlots), resolvedSlots, slotChar, transform);
|
|
231
|
+
updateValue(reformatted, reformatted.length);
|
|
232
|
+
}, [updateValue]);
|
|
233
|
+
const clampCursorToProcessed = useCallback((input) => {
|
|
234
|
+
const opts = optionsRef.current;
|
|
235
|
+
const { slots } = getResolvedOptions(opts, "");
|
|
236
|
+
const processed = processedRef.current;
|
|
237
|
+
const cursorPos = input.selectionStart ?? 0;
|
|
238
|
+
const maxPos = processed.length > 0 ? findNextEditablePosition(processed.length, slots, processed) : findNextTokenIndex(slots, 0);
|
|
239
|
+
if (cursorPos > maxPos) input.setSelectionRange(maxPos, maxPos);
|
|
240
|
+
}, []);
|
|
241
|
+
const handleFocus = useCallback(() => {
|
|
242
|
+
isFocusedRef.current = true;
|
|
243
|
+
const opts = optionsRef.current;
|
|
244
|
+
const input = inputRef.current;
|
|
245
|
+
if (!input) return;
|
|
246
|
+
const { slots, slotChar } = getResolvedOptions(opts, "");
|
|
247
|
+
const showOnFocus = opts.showMaskOnFocus !== false;
|
|
248
|
+
const processed = processedRef.current;
|
|
249
|
+
if (showOnFocus || opts.alwaysShowMask) {
|
|
250
|
+
const display = buildDisplayValue(processed, slots, slotChar, true);
|
|
251
|
+
input.value = display;
|
|
252
|
+
setMaskedValue(display);
|
|
253
|
+
}
|
|
254
|
+
requestAnimationFrame(() => {
|
|
255
|
+
if (input === document.activeElement) clampCursorToProcessed(input);
|
|
256
|
+
});
|
|
257
|
+
}, [clampCursorToProcessed]);
|
|
258
|
+
const handleMouseUp = useCallback(() => {
|
|
259
|
+
const input = inputRef.current;
|
|
260
|
+
if (!input || input !== document.activeElement) return;
|
|
261
|
+
clampCursorToProcessed(input);
|
|
262
|
+
}, [clampCursorToProcessed]);
|
|
263
|
+
const handleBlur = useCallback(() => {
|
|
264
|
+
isFocusedRef.current = false;
|
|
265
|
+
const opts = optionsRef.current;
|
|
266
|
+
const input = inputRef.current;
|
|
267
|
+
if (!input) return;
|
|
268
|
+
const { slots, slotChar } = getResolvedOptions(opts, rawValue);
|
|
269
|
+
const processed = processInput(input.value, slots, slotChar);
|
|
270
|
+
const complete = checkComplete(processed, slots);
|
|
271
|
+
if (opts.autoClear && !complete && processed.length > 0) {
|
|
272
|
+
input.value = "";
|
|
273
|
+
processedRef.current = "";
|
|
274
|
+
setMaskedValue("");
|
|
275
|
+
setRawValue("");
|
|
276
|
+
wasCompleteRef.current = false;
|
|
277
|
+
if (opts.onChangeRaw) opts.onChangeRaw("", "");
|
|
278
|
+
if (opts.alwaysShowMask) {
|
|
279
|
+
const emptyDisplay = buildDisplayValue("", slots, slotChar, true);
|
|
280
|
+
input.value = emptyDisplay;
|
|
281
|
+
setMaskedValue(emptyDisplay);
|
|
282
|
+
}
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (!opts.alwaysShowMask && !complete) {
|
|
286
|
+
const display = buildDisplayValue(processed, slots, slotChar, false);
|
|
287
|
+
input.value = display;
|
|
288
|
+
setMaskedValue(display);
|
|
289
|
+
}
|
|
290
|
+
}, [rawValue]);
|
|
291
|
+
const handleKeyDown = useCallback((e) => {
|
|
292
|
+
const input = e.target;
|
|
293
|
+
const opts = optionsRef.current;
|
|
294
|
+
const { slots, slotChar, transform } = getResolvedOptions(opts, rawValue);
|
|
295
|
+
const start = input.selectionStart ?? 0;
|
|
296
|
+
const end = input.selectionEnd ?? 0;
|
|
297
|
+
const processed = processedRef.current;
|
|
298
|
+
if (e.key === "Backspace") {
|
|
299
|
+
e.preventDefault();
|
|
300
|
+
if (e.metaKey || e.ctrlKey && !e.altKey) {
|
|
301
|
+
const clampedStart = Math.min(start, processed.length);
|
|
302
|
+
updateValue(applyMaskToRaw(extractRaw(processed.slice(clampedStart), slots.slice(clampedStart)), slots, slotChar, transform), 0);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (start !== end) {
|
|
306
|
+
const clampedEnd = Math.min(end, processed.length);
|
|
307
|
+
const before = processed.slice(0, start);
|
|
308
|
+
const afterRaw = extractRaw(processed.slice(clampedEnd), slots.slice(clampedEnd));
|
|
309
|
+
updateValue(applyMaskToRaw(extractRaw(before, slots) + afterRaw, slots, slotChar, transform), start);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (start === 0) return;
|
|
313
|
+
let deletePos = start - 1;
|
|
314
|
+
while (deletePos >= 0 && slots[deletePos] && slots[deletePos].type === "literal") deletePos--;
|
|
315
|
+
if (deletePos < 0) return;
|
|
316
|
+
updateValue(applyMaskToRaw(extractRaw(processed.slice(0, deletePos), slots.slice(0, deletePos)) + extractRaw(processed.slice(deletePos + 1), slots.slice(deletePos + 1)), slots, slotChar, transform), deletePos);
|
|
317
|
+
} else if (e.key === "Delete") {
|
|
318
|
+
e.preventDefault();
|
|
319
|
+
if (start !== end) {
|
|
320
|
+
const clampedEnd = Math.min(end, processed.length);
|
|
321
|
+
const before = processed.slice(0, start);
|
|
322
|
+
const afterRaw = extractRaw(processed.slice(clampedEnd), slots.slice(clampedEnd));
|
|
323
|
+
updateValue(applyMaskToRaw(extractRaw(before, slots) + afterRaw, slots, slotChar, transform), start);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
let deletePos = start;
|
|
327
|
+
while (deletePos < slots.length && slots[deletePos] && slots[deletePos].type === "literal") deletePos++;
|
|
328
|
+
if (deletePos >= processed.length) return;
|
|
329
|
+
updateValue(applyMaskToRaw(extractRaw(processed.slice(0, start), slots.slice(0, start)) + extractRaw(processed.slice(deletePos + 1), slots.slice(deletePos + 1)), slots, slotChar, transform), start);
|
|
330
|
+
} else if (e.key === "ArrowRight" && !e.shiftKey) {
|
|
331
|
+
const nextPos = findNextEditablePosition(start + 1, slots, input.value);
|
|
332
|
+
if (nextPos !== start + 1) {
|
|
333
|
+
e.preventDefault();
|
|
334
|
+
input.setSelectionRange(nextPos, nextPos);
|
|
335
|
+
}
|
|
336
|
+
} else if (e.key === "ArrowLeft" && !e.shiftKey) {
|
|
337
|
+
if (start > 0) {
|
|
338
|
+
const prevToken = findPrevTokenIndex(slots, start - 1);
|
|
339
|
+
if (prevToken >= 0 && prevToken !== start - 1) {
|
|
340
|
+
e.preventDefault();
|
|
341
|
+
input.setSelectionRange(prevToken + 1, prevToken + 1);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
} else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
345
|
+
e.preventDefault();
|
|
346
|
+
let insertPos = Math.min(start, processed.length);
|
|
347
|
+
while (insertPos < slots.length && slots[insertPos] && slots[insertPos].type === "literal") insertPos++;
|
|
348
|
+
if (insertPos >= slots.length) return;
|
|
349
|
+
const slot = slots[insertPos];
|
|
350
|
+
const ch = transform ? transform(e.key) : e.key;
|
|
351
|
+
if (!slot.pattern.test(ch)) return;
|
|
352
|
+
const beforeRaw = extractRaw(processed.slice(0, insertPos), slots.slice(0, insertPos));
|
|
353
|
+
const afterRaw = start < end ? extractRaw(processed.slice(Math.min(end, processed.length)), slots.slice(Math.min(end, processed.length))) : extractRaw(processed.slice(insertPos), slots.slice(insertPos));
|
|
354
|
+
const newValue = applyMaskToRaw(beforeRaw + ch + afterRaw, slots, slotChar, transform);
|
|
355
|
+
updateValue(newValue, findNextEditablePosition(insertPos + 1, slots, newValue));
|
|
356
|
+
}
|
|
357
|
+
}, [rawValue, updateValue]);
|
|
358
|
+
const handlePaste = useCallback((e) => {
|
|
359
|
+
e.preventDefault();
|
|
360
|
+
const input = e.target;
|
|
361
|
+
const opts = optionsRef.current;
|
|
362
|
+
const pastedText = e.clipboardData?.getData("text") ?? "";
|
|
363
|
+
const start = input.selectionStart ?? 0;
|
|
364
|
+
const end = input.selectionEnd ?? 0;
|
|
365
|
+
const processed = processedRef.current;
|
|
366
|
+
const { slots, slotChar, transform } = getResolvedOptions(opts, "");
|
|
367
|
+
const clampedEnd = Math.min(end, processed.length);
|
|
368
|
+
const beforeRaw = extractRaw(processed.slice(0, start), slots.slice(0, start));
|
|
369
|
+
const afterRaw = extractRaw(processed.slice(clampedEnd), slots.slice(clampedEnd));
|
|
370
|
+
const newValue = applyMaskToRaw(beforeRaw + pastedText + afterRaw, slots, slotChar, transform);
|
|
371
|
+
const { reprocessed } = updateValue(newValue);
|
|
372
|
+
const pasteEndPos = Math.min((reprocessed || newValue).length, slots.length);
|
|
373
|
+
if (input === document.activeElement) input.setSelectionRange(pasteEndPos, pasteEndPos);
|
|
374
|
+
}, [updateValue]);
|
|
375
|
+
const setAriaAttributes = useCallback((input) => {
|
|
376
|
+
if (optionsRef.current.invalid) input.setAttribute("aria-invalid", "true");
|
|
377
|
+
else input.removeAttribute("aria-invalid");
|
|
378
|
+
}, []);
|
|
379
|
+
const refCallback = useCallback((node) => {
|
|
380
|
+
const prevInput = inputRef.current;
|
|
381
|
+
if (prevInput) {
|
|
382
|
+
prevInput.removeEventListener("input", handleInput);
|
|
383
|
+
prevInput.removeEventListener("focus", handleFocus);
|
|
384
|
+
prevInput.removeEventListener("blur", handleBlur);
|
|
385
|
+
prevInput.removeEventListener("mouseup", handleMouseUp);
|
|
386
|
+
prevInput.removeEventListener("keydown", handleKeyDown);
|
|
387
|
+
prevInput.removeEventListener("paste", handlePaste);
|
|
388
|
+
}
|
|
389
|
+
inputRef.current = node;
|
|
390
|
+
if (node) {
|
|
391
|
+
node.addEventListener("input", handleInput);
|
|
392
|
+
node.addEventListener("focus", handleFocus);
|
|
393
|
+
node.addEventListener("blur", handleBlur);
|
|
394
|
+
node.addEventListener("mouseup", handleMouseUp);
|
|
395
|
+
node.addEventListener("keydown", handleKeyDown);
|
|
396
|
+
node.addEventListener("paste", handlePaste);
|
|
397
|
+
setAriaAttributes(node);
|
|
398
|
+
if (options.alwaysShowMask && !node.value) {
|
|
399
|
+
const { slots, slotChar } = getResolvedOptions(options, "");
|
|
400
|
+
const display = buildDisplayValue("", slots, slotChar, true);
|
|
401
|
+
node.value = display;
|
|
402
|
+
setMaskedValue(display);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}, [
|
|
406
|
+
handleInput,
|
|
407
|
+
handleFocus,
|
|
408
|
+
handleBlur,
|
|
409
|
+
handleMouseUp,
|
|
410
|
+
handleKeyDown,
|
|
411
|
+
handlePaste,
|
|
412
|
+
setAriaAttributes,
|
|
413
|
+
options
|
|
414
|
+
]);
|
|
415
|
+
useEffect(() => {
|
|
416
|
+
const input = inputRef.current;
|
|
417
|
+
if (!input) return;
|
|
418
|
+
setAriaAttributes(input);
|
|
419
|
+
}, [options.invalid, setAriaAttributes]);
|
|
420
|
+
return {
|
|
421
|
+
ref: refCallback,
|
|
422
|
+
value: maskedValue,
|
|
423
|
+
rawValue,
|
|
424
|
+
isComplete: (() => {
|
|
425
|
+
const { slots } = getOptions();
|
|
426
|
+
return checkComplete(processedRef.current, slots);
|
|
427
|
+
})(),
|
|
428
|
+
reset: useCallback(() => {
|
|
429
|
+
const opts = optionsRef.current;
|
|
430
|
+
const input = inputRef.current;
|
|
431
|
+
processedRef.current = "";
|
|
432
|
+
setMaskedValue("");
|
|
433
|
+
setRawValue("");
|
|
434
|
+
wasCompleteRef.current = false;
|
|
435
|
+
if (input) if (opts.alwaysShowMask) {
|
|
436
|
+
const { slots, slotChar } = getResolvedOptions(opts, "");
|
|
437
|
+
const display = buildDisplayValue("", slots, slotChar, true);
|
|
438
|
+
input.value = display;
|
|
439
|
+
setMaskedValue(display);
|
|
440
|
+
} else input.value = "";
|
|
441
|
+
if (opts.onChangeRaw) opts.onChangeRaw("", "");
|
|
442
|
+
}, [])
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
function findNextEditablePosition(from, slots, value) {
|
|
446
|
+
let pos = from;
|
|
447
|
+
while (pos < slots.length && pos < value.length && slots[pos] && slots[pos].type === "literal") pos++;
|
|
448
|
+
return pos;
|
|
449
|
+
}
|
|
450
|
+
//#endregion
|
|
451
|
+
export { formatMask, generatePattern, isMaskComplete, unformatMask, useMask };
|
|
452
|
+
|
|
453
|
+
//# sourceMappingURL=use-mask.mjs.map
|