@zentauri-ui/zentauri-components 1.9.2 → 2.0.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/CHANGELOG.md +17 -0
- package/README.md +32 -5
- package/cli/registry.json +14 -0
- package/dist/{chunk-L4PDJ6IB.mjs → chunk-44NX3DAZ.mjs} +3 -3
- package/dist/{chunk-L4PDJ6IB.mjs.map → chunk-44NX3DAZ.mjs.map} +1 -1
- package/dist/chunk-FAMHSJTK.js +19 -0
- package/dist/{chunk-AQHY4S33.js.map → chunk-FAMHSJTK.js.map} +1 -1
- package/dist/chunk-I42UYWYA.mjs +128 -0
- package/dist/chunk-I42UYWYA.mjs.map +1 -0
- package/dist/{chunk-5J6QMTES.js → chunk-IKXO5SJ4.js} +21 -5
- package/dist/chunk-IKXO5SJ4.js.map +1 -0
- package/dist/{chunk-OPUO55TO.mjs → chunk-JXSM2EHC.mjs} +3 -3
- package/dist/{chunk-OPUO55TO.mjs.map → chunk-JXSM2EHC.mjs.map} +1 -1
- package/dist/{chunk-LQPKZ5ZD.js → chunk-LS4GY2ZQ.js} +6 -6
- package/dist/{chunk-LQPKZ5ZD.js.map → chunk-LS4GY2ZQ.js.map} +1 -1
- package/dist/{chunk-VIKQGO4W.mjs → chunk-VQQHVKEU.mjs} +21 -5
- package/dist/{chunk-VIKQGO4W.mjs.map → chunk-VQQHVKEU.mjs.map} +1 -1
- package/dist/chunk-ZVRGLG35.js +144 -0
- package/dist/chunk-ZVRGLG35.js.map +1 -0
- package/dist/design-system/combobox.d.ts +124 -0
- package/dist/design-system/combobox.d.ts.map +1 -0
- package/dist/design-system/facade.js +6 -5
- package/dist/design-system/facade.js.map +1 -1
- package/dist/design-system/facade.mjs +5 -4
- package/dist/design-system/facade.mjs.map +1 -1
- package/dist/design-system/index.d.ts +1 -0
- package/dist/design-system/index.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +13 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/useCookie/index.d.ts +2 -0
- package/dist/hooks/useCookie/index.d.ts.map +1 -0
- package/dist/hooks/useCookie/useCookie.d.ts +36 -0
- package/dist/hooks/useCookie/useCookie.d.ts.map +1 -0
- package/dist/hooks/useCookie.js +82 -0
- package/dist/hooks/useCookie.js.map +1 -0
- package/dist/hooks/useCookie.mjs +80 -0
- package/dist/hooks/useCookie.mjs.map +1 -0
- package/dist/hooks/useCountdown/index.d.ts +2 -0
- package/dist/hooks/useCountdown/index.d.ts.map +1 -0
- package/dist/hooks/useCountdown/useCountdown.d.ts +40 -0
- package/dist/hooks/useCountdown/useCountdown.d.ts.map +1 -0
- package/dist/hooks/useCountdown.js +60 -0
- package/dist/hooks/useCountdown.js.map +1 -0
- package/dist/hooks/useCountdown.mjs +58 -0
- package/dist/hooks/useCountdown.mjs.map +1 -0
- package/dist/hooks/useEventListener/index.d.ts +2 -0
- package/dist/hooks/useEventListener/index.d.ts.map +1 -0
- package/dist/hooks/useEventListener/useEventListener.d.ts +22 -0
- package/dist/hooks/useEventListener/useEventListener.d.ts.map +1 -0
- package/dist/hooks/useEventListener.js +45 -0
- package/dist/hooks/useEventListener.js.map +1 -0
- package/dist/hooks/useEventListener.mjs +43 -0
- package/dist/hooks/useEventListener.mjs.map +1 -0
- package/dist/hooks/useGeolocation/index.d.ts +2 -0
- package/dist/hooks/useGeolocation/index.d.ts.map +1 -0
- package/dist/hooks/useGeolocation/useGeolocation.d.ts +48 -0
- package/dist/hooks/useGeolocation/useGeolocation.d.ts.map +1 -0
- package/dist/hooks/useGeolocation.js +111 -0
- package/dist/hooks/useGeolocation.js.map +1 -0
- package/dist/hooks/useGeolocation.mjs +109 -0
- package/dist/hooks/useGeolocation.mjs.map +1 -0
- package/dist/hooks/useHotkeys/index.d.ts +2 -0
- package/dist/hooks/useHotkeys/index.d.ts.map +1 -0
- package/dist/hooks/useHotkeys/useHotkeys.d.ts +24 -0
- package/dist/hooks/useHotkeys/useHotkeys.d.ts.map +1 -0
- package/dist/hooks/useHotkeys.js +86 -0
- package/dist/hooks/useHotkeys.js.map +1 -0
- package/dist/hooks/useHotkeys.mjs +84 -0
- package/dist/hooks/useHotkeys.mjs.map +1 -0
- package/dist/hooks/useIdleTimeout/index.d.ts +2 -0
- package/dist/hooks/useIdleTimeout/index.d.ts.map +1 -0
- package/dist/hooks/useIdleTimeout/useIdleTimeout.d.ts +31 -0
- package/dist/hooks/useIdleTimeout/useIdleTimeout.d.ts.map +1 -0
- package/dist/hooks/useIdleTimeout.js +77 -0
- package/dist/hooks/useIdleTimeout.js.map +1 -0
- package/dist/hooks/useIdleTimeout.mjs +75 -0
- package/dist/hooks/useIdleTimeout.mjs.map +1 -0
- package/dist/hooks/useInterval/index.d.ts +2 -0
- package/dist/hooks/useInterval/index.d.ts.map +1 -0
- package/dist/hooks/useInterval/useInterval.d.ts +12 -0
- package/dist/hooks/useInterval/useInterval.d.ts.map +1 -0
- package/dist/hooks/useInterval.js +27 -0
- package/dist/hooks/useInterval.js.map +1 -0
- package/dist/hooks/useInterval.mjs +25 -0
- package/dist/hooks/useInterval.mjs.map +1 -0
- package/dist/hooks/useKeyPress/index.d.ts +2 -0
- package/dist/hooks/useKeyPress/index.d.ts.map +1 -0
- package/dist/hooks/useKeyPress/useKeyPress.d.ts +15 -0
- package/dist/hooks/useKeyPress/useKeyPress.d.ts.map +1 -0
- package/dist/hooks/useKeyPress.js +47 -0
- package/dist/hooks/useKeyPress.js.map +1 -0
- package/dist/hooks/useKeyPress.mjs +45 -0
- package/dist/hooks/useKeyPress.mjs.map +1 -0
- package/dist/hooks/useLongPress/index.d.ts +2 -0
- package/dist/hooks/useLongPress/index.d.ts.map +1 -0
- package/dist/hooks/useLongPress/useLongPress.d.ts +46 -0
- package/dist/hooks/useLongPress/useLongPress.d.ts.map +1 -0
- package/dist/hooks/useLongPress.js +116 -0
- package/dist/hooks/useLongPress.js.map +1 -0
- package/dist/hooks/useLongPress.mjs +114 -0
- package/dist/hooks/useLongPress.mjs.map +1 -0
- package/dist/hooks/usePrevious/index.d.ts +2 -0
- package/dist/hooks/usePrevious/index.d.ts.map +1 -0
- package/dist/hooks/usePrevious/usePrevious.d.ts +13 -0
- package/dist/hooks/usePrevious/usePrevious.d.ts.map +1 -0
- package/dist/hooks/usePrevious.js +17 -0
- package/dist/hooks/usePrevious.js.map +1 -0
- package/dist/hooks/usePrevious.mjs +15 -0
- package/dist/hooks/usePrevious.mjs.map +1 -0
- package/dist/hooks/useScrollPosition/index.d.ts +2 -0
- package/dist/hooks/useScrollPosition/index.d.ts.map +1 -0
- package/dist/hooks/useScrollPosition/useScrollPosition.d.ts +37 -0
- package/dist/hooks/useScrollPosition/useScrollPosition.d.ts.map +1 -0
- package/dist/hooks/useScrollPosition.js +41 -0
- package/dist/hooks/useScrollPosition.js.map +1 -0
- package/dist/hooks/useScrollPosition.mjs +39 -0
- package/dist/hooks/useScrollPosition.mjs.map +1 -0
- package/dist/hooks/useTimeout/index.d.ts +2 -0
- package/dist/hooks/useTimeout/index.d.ts.map +1 -0
- package/dist/hooks/useTimeout/useTimeout.d.ts +19 -0
- package/dist/hooks/useTimeout/useTimeout.d.ts.map +1 -0
- package/dist/hooks/useTimeout.js +38 -0
- package/dist/hooks/useTimeout.js.map +1 -0
- package/dist/hooks/useTimeout.mjs +36 -0
- package/dist/hooks/useTimeout.mjs.map +1 -0
- package/dist/hooks/useVirtualList/index.d.ts +2 -0
- package/dist/hooks/useVirtualList/index.d.ts.map +1 -0
- package/dist/hooks/useVirtualList/useVirtualList.d.ts +47 -0
- package/dist/hooks/useVirtualList/useVirtualList.d.ts.map +1 -0
- package/dist/hooks/useVirtualList.js +87 -0
- package/dist/hooks/useVirtualList.js.map +1 -0
- package/dist/hooks/useVirtualList.mjs +85 -0
- package/dist/hooks/useVirtualList.mjs.map +1 -0
- package/dist/lib/facade.d.ts.map +1 -1
- package/dist/ui/buttons/animated.js +8 -7
- package/dist/ui/buttons/animated.js.map +1 -1
- package/dist/ui/buttons/animated.mjs +6 -5
- package/dist/ui/buttons/animated.mjs.map +1 -1
- package/dist/ui/buttons.js +9 -8
- package/dist/ui/buttons.mjs +7 -6
- package/dist/ui/combobox/combobox-base.d.ts +37 -0
- package/dist/ui/combobox/combobox-base.d.ts.map +1 -0
- package/dist/ui/combobox/combobox.d.ts +6 -0
- package/dist/ui/combobox/combobox.d.ts.map +1 -0
- package/dist/ui/combobox/index.d.ts +4 -0
- package/dist/ui/combobox/index.d.ts.map +1 -0
- package/dist/ui/combobox/types.d.ts +70 -0
- package/dist/ui/combobox/types.d.ts.map +1 -0
- package/dist/ui/combobox/variants.d.ts +17 -0
- package/dist/ui/combobox/variants.d.ts.map +1 -0
- package/dist/ui/combobox.js +510 -0
- package/dist/ui/combobox.js.map +1 -0
- package/dist/ui/combobox.mjs +495 -0
- package/dist/ui/combobox.mjs.map +1 -0
- package/dist/ui/dynamic-stepper.js +18 -17
- package/dist/ui/dynamic-stepper.js.map +1 -1
- package/dist/ui/dynamic-stepper.mjs +7 -6
- package/dist/ui/dynamic-stepper.mjs.map +1 -1
- package/dist/ui/pagination.js +14 -13
- package/dist/ui/pagination.js.map +1 -1
- package/dist/ui/pagination.mjs +6 -5
- package/dist/ui/pagination.mjs.map +1 -1
- package/package.json +1 -1
- package/src/design-system/combobox.ts +204 -0
- package/src/design-system/index.ts +1 -0
- package/src/hooks/index.ts +50 -0
- package/src/hooks/useCookie/index.ts +5 -0
- package/src/hooks/useCookie/useCookie.test.ts +57 -0
- package/src/hooks/useCookie/useCookie.ts +133 -0
- package/src/hooks/useCountdown/index.ts +5 -0
- package/src/hooks/useCountdown/useCountdown.test.ts +113 -0
- package/src/hooks/useCountdown/useCountdown.ts +106 -0
- package/src/hooks/useEventListener/index.ts +4 -0
- package/src/hooks/useEventListener/useEventListener.test.ts +60 -0
- package/src/hooks/useEventListener/useEventListener.ts +98 -0
- package/src/hooks/useGeolocation/index.ts +6 -0
- package/src/hooks/useGeolocation/useGeolocation.test.ts +108 -0
- package/src/hooks/useGeolocation/useGeolocation.ts +173 -0
- package/src/hooks/useHotkeys/index.ts +5 -0
- package/src/hooks/useHotkeys/useHotkeys.test.ts +82 -0
- package/src/hooks/useHotkeys/useHotkeys.ts +130 -0
- package/src/hooks/useIdleTimeout/index.ts +5 -0
- package/src/hooks/useIdleTimeout/useIdleTimeout.test.ts +97 -0
- package/src/hooks/useIdleTimeout/useIdleTimeout.ts +111 -0
- package/src/hooks/useInterval/index.ts +1 -0
- package/src/hooks/useInterval/useInterval.test.ts +56 -0
- package/src/hooks/useInterval/useInterval.ts +36 -0
- package/src/hooks/useKeyPress/index.ts +1 -0
- package/src/hooks/useKeyPress/useKeyPress.test.ts +67 -0
- package/src/hooks/useKeyPress/useKeyPress.ts +65 -0
- package/src/hooks/useLongPress/index.ts +5 -0
- package/src/hooks/useLongPress/useLongPress.test.ts +180 -0
- package/src/hooks/useLongPress/useLongPress.ts +177 -0
- package/src/hooks/usePrevious/index.ts +1 -0
- package/src/hooks/usePrevious/usePrevious.test.ts +33 -0
- package/src/hooks/usePrevious/usePrevious.ts +24 -0
- package/src/hooks/useScrollPosition/index.ts +5 -0
- package/src/hooks/useScrollPosition/useScrollPosition.test.ts +69 -0
- package/src/hooks/useScrollPosition/useScrollPosition.ts +88 -0
- package/src/hooks/useTimeout/index.ts +1 -0
- package/src/hooks/useTimeout/useTimeout.test.ts +63 -0
- package/src/hooks/useTimeout/useTimeout.ts +58 -0
- package/src/hooks/useVirtualList/index.ts +6 -0
- package/src/hooks/useVirtualList/useVirtualList.test.ts +102 -0
- package/src/hooks/useVirtualList/useVirtualList.ts +144 -0
- package/src/lib/facade.test.ts +7 -7
- package/src/lib/facade.ts +6 -2
- package/src/ui/combobox/combobox-base.tsx +552 -0
- package/src/ui/combobox/combobox.test.tsx +292 -0
- package/src/ui/combobox/combobox.tsx +8 -0
- package/src/ui/combobox/index.ts +33 -0
- package/src/ui/combobox/types.ts +91 -0
- package/src/ui/combobox/variants.ts +58 -0
- package/dist/chunk-5J6QMTES.js.map +0 -1
- package/dist/chunk-AQHY4S33.js +0 -19
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import '../chunk-J5LGTIGS.mjs';
|
|
3
|
+
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
4
|
+
|
|
5
|
+
var DEFAULT_ACTIVITY_EVENTS = [
|
|
6
|
+
"pointerdown",
|
|
7
|
+
"pointermove",
|
|
8
|
+
"keydown",
|
|
9
|
+
"wheel",
|
|
10
|
+
"touchstart",
|
|
11
|
+
"scroll"
|
|
12
|
+
];
|
|
13
|
+
function useIdleTimeout({
|
|
14
|
+
timeoutMs,
|
|
15
|
+
events,
|
|
16
|
+
initiallyIdle = false,
|
|
17
|
+
onIdle,
|
|
18
|
+
onActive
|
|
19
|
+
}) {
|
|
20
|
+
const [isIdle, setIsIdle] = useState(initiallyIdle);
|
|
21
|
+
const onIdleRef = useRef(onIdle);
|
|
22
|
+
const onActiveRef = useRef(onActive);
|
|
23
|
+
const restartRef = useRef(() => {
|
|
24
|
+
});
|
|
25
|
+
const firstRunRef = useRef(true);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
onIdleRef.current = onIdle;
|
|
28
|
+
onActiveRef.current = onActive;
|
|
29
|
+
}, [onActive, onIdle]);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (firstRunRef.current) {
|
|
32
|
+
firstRunRef.current = false;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (isIdle) {
|
|
36
|
+
onIdleRef.current?.();
|
|
37
|
+
} else {
|
|
38
|
+
onActiveRef.current?.();
|
|
39
|
+
}
|
|
40
|
+
}, [isIdle]);
|
|
41
|
+
const eventsKey = (events ?? DEFAULT_ACTIVITY_EVENTS).join(" ");
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const eventNames = eventsKey.split(" ").filter(Boolean);
|
|
44
|
+
let timeoutId;
|
|
45
|
+
const startTimer = () => {
|
|
46
|
+
window.clearTimeout(timeoutId);
|
|
47
|
+
timeoutId = window.setTimeout(() => {
|
|
48
|
+
setIsIdle(true);
|
|
49
|
+
}, timeoutMs);
|
|
50
|
+
};
|
|
51
|
+
const onActivity = () => {
|
|
52
|
+
setIsIdle(false);
|
|
53
|
+
startTimer();
|
|
54
|
+
};
|
|
55
|
+
restartRef.current = onActivity;
|
|
56
|
+
startTimer();
|
|
57
|
+
for (const name of eventNames) {
|
|
58
|
+
window.addEventListener(name, onActivity, { passive: true });
|
|
59
|
+
}
|
|
60
|
+
return () => {
|
|
61
|
+
window.clearTimeout(timeoutId);
|
|
62
|
+
for (const name of eventNames) {
|
|
63
|
+
window.removeEventListener(name, onActivity);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}, [eventsKey, timeoutMs]);
|
|
67
|
+
const reset = useCallback(() => {
|
|
68
|
+
restartRef.current();
|
|
69
|
+
}, []);
|
|
70
|
+
return { isIdle, reset };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { useIdleTimeout };
|
|
74
|
+
//# sourceMappingURL=useIdleTimeout.mjs.map
|
|
75
|
+
//# sourceMappingURL=useIdleTimeout.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useIdleTimeout/useIdleTimeout.ts"],"names":[],"mappings":";;;AAIA,IAAM,uBAAA,GAA0B;AAAA,EAC9B,aAAA;AAAA,EACA,aAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA,YAAA;AAAA,EACA;AACF,CAAA;AAiCO,SAAS,cAAA,CAAe;AAAA,EAC7B,SAAA;AAAA,EACA,MAAA;AAAA,EACA,aAAA,GAAgB,KAAA;AAAA,EAChB,MAAA;AAAA,EACA;AACF,CAAA,EAA+C;AAC7C,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAS,aAAa,CAAA;AAClD,EAAA,MAAM,SAAA,GAAY,OAAO,MAAM,CAAA;AAC/B,EAAA,MAAM,WAAA,GAAc,OAAO,QAAQ,CAAA;AACnC,EAAA,MAAM,UAAA,GAAa,OAAmB,MAAM;AAAA,EAAC,CAAC,CAAA;AAC9C,EAAA,MAAM,WAAA,GAAc,OAAO,IAAI,CAAA;AAE/B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,SAAA,CAAU,OAAA,GAAU,MAAA;AACpB,IAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AAAA,EACxB,CAAA,EAAG,CAAC,QAAA,EAAU,MAAM,CAAC,CAAA;AAErB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,YAAY,OAAA,EAAS;AACvB,MAAA,WAAA,CAAY,OAAA,GAAU,KAAA;AACtB,MAAA;AAAA,IACF;AACA,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,SAAA,CAAU,OAAA,IAAU;AAAA,IACtB,CAAA,MAAO;AACL,MAAA,WAAA,CAAY,OAAA,IAAU;AAAA,IACxB;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,EAAA,MAAM,SAAA,GAAA,CAAa,MAAA,IAAU,uBAAA,EAAyB,IAAA,CAAK,GAAG,CAAA;AAE9D,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,aAAa,SAAA,CAAU,KAAA,CAAM,GAAG,CAAA,CAAE,OAAO,OAAO,CAAA;AACtD,IAAA,IAAI,SAAA;AAEJ,IAAA,MAAM,aAAa,MAAM;AACvB,MAAA,MAAA,CAAO,aAAa,SAAS,CAAA;AAC7B,MAAA,SAAA,GAAY,MAAA,CAAO,WAAW,MAAM;AAClC,QAAA,SAAA,CAAU,IAAI,CAAA;AAAA,MAChB,GAAG,SAAS,CAAA;AAAA,IACd,CAAA;AAEA,IAAA,MAAM,aAAa,MAAM;AACvB,MAAA,SAAA,CAAU,KAAK,CAAA;AACf,MAAA,UAAA,EAAW;AAAA,IACb,CAAA;AAEA,IAAA,UAAA,CAAW,OAAA,GAAU,UAAA;AACrB,IAAA,UAAA,EAAW;AACX,IAAA,KAAA,MAAW,QAAQ,UAAA,EAAY;AAC7B,MAAA,MAAA,CAAO,iBAAiB,IAAA,EAAM,UAAA,EAAY,EAAE,OAAA,EAAS,MAAM,CAAA;AAAA,IAC7D;AACA,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,aAAa,SAAS,CAAA;AAC7B,MAAA,KAAA,MAAW,QAAQ,UAAA,EAAY;AAC7B,QAAA,MAAA,CAAO,mBAAA,CAAoB,MAAM,UAAU,CAAA;AAAA,MAC7C;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,SAAA,EAAW,SAAS,CAAC,CAAA;AAEzB,EAAA,MAAM,KAAA,GAAQ,YAAY,MAAM;AAC9B,IAAA,UAAA,CAAW,OAAA,EAAQ;AAAA,EACrB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,EAAE,QAAQ,KAAA,EAAM;AACzB","file":"useIdleTimeout.mjs","sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nconst DEFAULT_ACTIVITY_EVENTS = [\n \"pointerdown\",\n \"pointermove\",\n \"keydown\",\n \"wheel\",\n \"touchstart\",\n \"scroll\",\n] as const;\n\nexport type UseIdleTimeoutParams = {\n /** Inactivity duration in milliseconds before the user counts as idle. */\n timeoutMs: number;\n /** Window events treated as activity (default: pointer, key, wheel, touch, scroll). */\n events?: readonly string[];\n /** Start in the idle state (default `false`). */\n initiallyIdle?: boolean;\n /** Called when the user becomes idle. */\n onIdle?: () => void;\n /** Called when activity resumes after being idle. */\n onActive?: () => void;\n};\n\nexport type UseIdleTimeoutResult = {\n /** Whether the user is currently idle. */\n isIdle: boolean;\n /** Mark the user active and restart the inactivity timer (e.g. after programmatic activity). */\n reset: () => void;\n};\n\n/**\n * Detects user inactivity: `isIdle` flips to `true` after `timeoutMs` without any of the\n * activity events on `window`, and back to `false` on the next activity.\n *\n * - `onIdle` / `onActive` fire on transitions only (not on mount), and are read from refs\n * so inline callbacks stay fresh.\n * - Useful for session expiry warnings, pausing media or polling, and presence indicators.\n *\n * @param params - {@link UseIdleTimeoutParams}\n * @returns {@link UseIdleTimeoutResult}\n */\nexport function useIdleTimeout({\n timeoutMs,\n events,\n initiallyIdle = false,\n onIdle,\n onActive,\n}: UseIdleTimeoutParams): UseIdleTimeoutResult {\n const [isIdle, setIsIdle] = useState(initiallyIdle);\n const onIdleRef = useRef(onIdle);\n const onActiveRef = useRef(onActive);\n const restartRef = useRef<() => void>(() => {});\n const firstRunRef = useRef(true);\n\n useEffect(() => {\n onIdleRef.current = onIdle;\n onActiveRef.current = onActive;\n }, [onActive, onIdle]);\n\n useEffect(() => {\n if (firstRunRef.current) {\n firstRunRef.current = false;\n return;\n }\n if (isIdle) {\n onIdleRef.current?.();\n } else {\n onActiveRef.current?.();\n }\n }, [isIdle]);\n\n const eventsKey = (events ?? DEFAULT_ACTIVITY_EVENTS).join(\" \");\n\n useEffect(() => {\n const eventNames = eventsKey.split(\" \").filter(Boolean);\n let timeoutId: number | undefined;\n\n const startTimer = () => {\n window.clearTimeout(timeoutId);\n timeoutId = window.setTimeout(() => {\n setIsIdle(true);\n }, timeoutMs);\n };\n\n const onActivity = () => {\n setIsIdle(false);\n startTimer();\n };\n\n restartRef.current = onActivity;\n startTimer();\n for (const name of eventNames) {\n window.addEventListener(name, onActivity, { passive: true });\n }\n return () => {\n window.clearTimeout(timeoutId);\n for (const name of eventNames) {\n window.removeEventListener(name, onActivity);\n }\n };\n }, [eventsKey, timeoutMs]);\n\n const reset = useCallback(() => {\n restartRef.current();\n }, []);\n\n return { isIdle, reset };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/useInterval/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declarative `setInterval`: runs `callback` every `delayMs` milliseconds with automatic cleanup.
|
|
3
|
+
*
|
|
4
|
+
* - The latest callback is kept in a ref, so a new inline function each render does not restart the timer.
|
|
5
|
+
* - Pass `null` as the delay to pause the interval; pass a number again to resume.
|
|
6
|
+
* - Changing `delayMs` clears the previous interval and starts a fresh one.
|
|
7
|
+
*
|
|
8
|
+
* @param callback - Function invoked on every tick.
|
|
9
|
+
* @param delayMs - Interval in milliseconds, or `null` to pause.
|
|
10
|
+
*/
|
|
11
|
+
export declare function useInterval(callback: () => void, delayMs: number | null): void;
|
|
12
|
+
//# sourceMappingURL=useInterval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useInterval.d.ts","sourceRoot":"","sources":["../../../src/hooks/useInterval/useInterval.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,IAAI,EACpB,OAAO,EAAE,MAAM,GAAG,IAAI,GACrB,IAAI,CAkBN"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
require('../chunk-PZ5AY32C.js');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
|
|
7
|
+
function useInterval(callback, delayMs) {
|
|
8
|
+
const callbackRef = react.useRef(callback);
|
|
9
|
+
react.useEffect(() => {
|
|
10
|
+
callbackRef.current = callback;
|
|
11
|
+
}, [callback]);
|
|
12
|
+
react.useEffect(() => {
|
|
13
|
+
if (delayMs == null) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const id = window.setInterval(() => {
|
|
17
|
+
callbackRef.current();
|
|
18
|
+
}, delayMs);
|
|
19
|
+
return () => {
|
|
20
|
+
window.clearInterval(id);
|
|
21
|
+
};
|
|
22
|
+
}, [delayMs]);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
exports.useInterval = useInterval;
|
|
26
|
+
//# sourceMappingURL=useInterval.js.map
|
|
27
|
+
//# sourceMappingURL=useInterval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useInterval/useInterval.ts"],"names":["useRef","useEffect"],"mappings":";;;;;AAcO,SAAS,WAAA,CACd,UACA,OAAA,EACM;AACN,EAAA,MAAM,WAAA,GAAcA,aAAO,QAAQ,CAAA;AAEnC,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AAAA,EACxB,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,WAAW,IAAA,EAAM;AACnB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,EAAA,GAAK,MAAA,CAAO,WAAA,CAAY,MAAM;AAClC,MAAA,WAAA,CAAY,OAAA,EAAQ;AAAA,IACtB,GAAG,OAAO,CAAA;AACV,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,cAAc,EAAE,CAAA;AAAA,IACzB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AACd","file":"useInterval.js","sourcesContent":["\"use client\";\n\nimport { useEffect, useRef } from \"react\";\n\n/**\n * Declarative `setInterval`: runs `callback` every `delayMs` milliseconds with automatic cleanup.\n *\n * - The latest callback is kept in a ref, so a new inline function each render does not restart the timer.\n * - Pass `null` as the delay to pause the interval; pass a number again to resume.\n * - Changing `delayMs` clears the previous interval and starts a fresh one.\n *\n * @param callback - Function invoked on every tick.\n * @param delayMs - Interval in milliseconds, or `null` to pause.\n */\nexport function useInterval(\n callback: () => void,\n delayMs: number | null,\n): void {\n const callbackRef = useRef(callback);\n\n useEffect(() => {\n callbackRef.current = callback;\n }, [callback]);\n\n useEffect(() => {\n if (delayMs == null) {\n return;\n }\n const id = window.setInterval(() => {\n callbackRef.current();\n }, delayMs);\n return () => {\n window.clearInterval(id);\n };\n }, [delayMs]);\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import '../chunk-J5LGTIGS.mjs';
|
|
3
|
+
import { useRef, useEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
function useInterval(callback, delayMs) {
|
|
6
|
+
const callbackRef = useRef(callback);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
callbackRef.current = callback;
|
|
9
|
+
}, [callback]);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (delayMs == null) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const id = window.setInterval(() => {
|
|
15
|
+
callbackRef.current();
|
|
16
|
+
}, delayMs);
|
|
17
|
+
return () => {
|
|
18
|
+
window.clearInterval(id);
|
|
19
|
+
};
|
|
20
|
+
}, [delayMs]);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { useInterval };
|
|
24
|
+
//# sourceMappingURL=useInterval.mjs.map
|
|
25
|
+
//# sourceMappingURL=useInterval.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useInterval/useInterval.ts"],"names":[],"mappings":";;;AAcO,SAAS,WAAA,CACd,UACA,OAAA,EACM;AACN,EAAA,MAAM,WAAA,GAAc,OAAO,QAAQ,CAAA;AAEnC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AAAA,EACxB,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,WAAW,IAAA,EAAM;AACnB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,EAAA,GAAK,MAAA,CAAO,WAAA,CAAY,MAAM;AAClC,MAAA,WAAA,CAAY,OAAA,EAAQ;AAAA,IACtB,GAAG,OAAO,CAAA;AACV,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,cAAc,EAAE,CAAA;AAAA,IACzB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AACd","file":"useInterval.mjs","sourcesContent":["\"use client\";\n\nimport { useEffect, useRef } from \"react\";\n\n/**\n * Declarative `setInterval`: runs `callback` every `delayMs` milliseconds with automatic cleanup.\n *\n * - The latest callback is kept in a ref, so a new inline function each render does not restart the timer.\n * - Pass `null` as the delay to pause the interval; pass a number again to resume.\n * - Changing `delayMs` clears the previous interval and starts a fresh one.\n *\n * @param callback - Function invoked on every tick.\n * @param delayMs - Interval in milliseconds, or `null` to pause.\n */\nexport function useInterval(\n callback: () => void,\n delayMs: number | null,\n): void {\n const callbackRef = useRef(callback);\n\n useEffect(() => {\n callbackRef.current = callback;\n }, [callback]);\n\n useEffect(() => {\n if (delayMs == null) {\n return;\n }\n const id = window.setInterval(() => {\n callbackRef.current();\n }, delayMs);\n return () => {\n window.clearInterval(id);\n };\n }, [delayMs]);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/useKeyPress/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks whether a keyboard key (or any of several keys) is currently held down.
|
|
3
|
+
*
|
|
4
|
+
* - Matches against `event.key`, case-insensitively (`"k"`, `"Escape"`, `"ArrowUp"`, …).
|
|
5
|
+
* - Listens on `window` for `keydown` / `keyup`, and clears on window `blur` so the
|
|
6
|
+
* state cannot get stuck when focus leaves the page mid-press.
|
|
7
|
+
* - When an array of keys is watched, `pressed` remains `true` as long as ANY of the
|
|
8
|
+
* target keys are still held — releasing one watched key while another is held does
|
|
9
|
+
* not reset the state.
|
|
10
|
+
*
|
|
11
|
+
* @param targetKey - A key name or array of key names to watch.
|
|
12
|
+
* @returns `true` while one of the target keys is pressed.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useKeyPress(targetKey: string | string[]): boolean;
|
|
15
|
+
//# sourceMappingURL=useKeyPress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useKeyPress.d.ts","sourceRoot":"","sources":["../../../src/hooks/useKeyPress/useKeyPress.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CA+CjE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
require('../chunk-PZ5AY32C.js');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
|
|
7
|
+
function useKeyPress(targetKey) {
|
|
8
|
+
const [pressed, setPressed] = react.useState(false);
|
|
9
|
+
const normalizedKey = (Array.isArray(targetKey) ? targetKey : [targetKey]).map((k) => k.toLowerCase()).sort().join("\0");
|
|
10
|
+
react.useEffect(() => {
|
|
11
|
+
const keys = normalizedKey.split("\0");
|
|
12
|
+
const pressedKeys = /* @__PURE__ */ new Set();
|
|
13
|
+
const onKeyDown = (event) => {
|
|
14
|
+
const key = event.key.toLowerCase();
|
|
15
|
+
if (keys.includes(key)) {
|
|
16
|
+
pressedKeys.add(key);
|
|
17
|
+
setPressed(true);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const onKeyUp = (event) => {
|
|
21
|
+
const key = event.key.toLowerCase();
|
|
22
|
+
if (keys.includes(key)) {
|
|
23
|
+
pressedKeys.delete(key);
|
|
24
|
+
setPressed(pressedKeys.size > 0);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const onBlur = () => {
|
|
28
|
+
pressedKeys.clear();
|
|
29
|
+
setPressed(false);
|
|
30
|
+
};
|
|
31
|
+
window.addEventListener("keydown", onKeyDown);
|
|
32
|
+
window.addEventListener("keyup", onKeyUp);
|
|
33
|
+
window.addEventListener("blur", onBlur);
|
|
34
|
+
return () => {
|
|
35
|
+
pressedKeys.clear();
|
|
36
|
+
setPressed(false);
|
|
37
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
38
|
+
window.removeEventListener("keyup", onKeyUp);
|
|
39
|
+
window.removeEventListener("blur", onBlur);
|
|
40
|
+
};
|
|
41
|
+
}, [normalizedKey]);
|
|
42
|
+
return pressed;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
exports.useKeyPress = useKeyPress;
|
|
46
|
+
//# sourceMappingURL=useKeyPress.js.map
|
|
47
|
+
//# sourceMappingURL=useKeyPress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useKeyPress/useKeyPress.ts"],"names":["useState","useEffect"],"mappings":";;;;;AAiBO,SAAS,YAAY,SAAA,EAAuC;AACjE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIA,eAAS,KAAK,CAAA;AAI5C,EAAA,MAAM,iBAAiB,KAAA,CAAM,OAAA,CAAQ,SAAS,CAAA,GAAI,SAAA,GAAY,CAAC,SAAS,CAAA,EACrE,IAAI,CAAC,CAAA,KAAM,EAAE,WAAA,EAAa,EAC1B,IAAA,EAAK,CACL,KAAK,IAAM,CAAA;AAEd,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,IAAA,GAAO,aAAA,CAAc,KAAA,CAAM,IAAM,CAAA;AAGvC,IAAA,MAAM,WAAA,uBAAkB,GAAA,EAAY;AAEpC,IAAA,MAAM,SAAA,GAAY,CAAC,KAAA,KAAyB;AAC1C,MAAA,MAAM,GAAA,GAAM,KAAA,CAAM,GAAA,CAAI,WAAA,EAAY;AAClC,MAAA,IAAI,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,EAAG;AACtB,QAAA,WAAA,CAAY,IAAI,GAAG,CAAA;AACnB,QAAA,UAAA,CAAW,IAAI,CAAA;AAAA,MACjB;AAAA,IACF,CAAA;AACA,IAAA,MAAM,OAAA,GAAU,CAAC,KAAA,KAAyB;AACxC,MAAA,MAAM,GAAA,GAAM,KAAA,CAAM,GAAA,CAAI,WAAA,EAAY;AAClC,MAAA,IAAI,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,EAAG;AACtB,QAAA,WAAA,CAAY,OAAO,GAAG,CAAA;AACtB,QAAA,UAAA,CAAW,WAAA,CAAY,OAAO,CAAC,CAAA;AAAA,MACjC;AAAA,IACF,CAAA;AACA,IAAA,MAAM,SAAS,MAAM;AACnB,MAAA,WAAA,CAAY,KAAA,EAAM;AAClB,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB,CAAA;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,SAAS,CAAA;AAC5C,IAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,OAAO,CAAA;AACxC,IAAA,MAAA,CAAO,gBAAA,CAAiB,QAAQ,MAAM,CAAA;AACtC,IAAA,OAAO,MAAM;AACX,MAAA,WAAA,CAAY,KAAA,EAAM;AAClB,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,SAAS,CAAA;AAC/C,MAAA,MAAA,CAAO,mBAAA,CAAoB,SAAS,OAAO,CAAA;AAC3C,MAAA,MAAA,CAAO,mBAAA,CAAoB,QAAQ,MAAM,CAAA;AAAA,IAC3C,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA;AAElB,EAAA,OAAO,OAAA;AACT","file":"useKeyPress.js","sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\n/**\n * Tracks whether a keyboard key (or any of several keys) is currently held down.\n *\n * - Matches against `event.key`, case-insensitively (`\"k\"`, `\"Escape\"`, `\"ArrowUp\"`, …).\n * - Listens on `window` for `keydown` / `keyup`, and clears on window `blur` so the\n * state cannot get stuck when focus leaves the page mid-press.\n * - When an array of keys is watched, `pressed` remains `true` as long as ANY of the\n * target keys are still held — releasing one watched key while another is held does\n * not reset the state.\n *\n * @param targetKey - A key name or array of key names to watch.\n * @returns `true` while one of the target keys is pressed.\n */\nexport function useKeyPress(targetKey: string | string[]): boolean {\n const [pressed, setPressed] = useState(false);\n\n // Build a stable string key from the sorted target array.\n // Use \\x00 as separator (not \"|\") so the literal \"|\" key is handled correctly.\n const normalizedKey = (Array.isArray(targetKey) ? targetKey : [targetKey])\n .map((k) => k.toLowerCase())\n .sort()\n .join(\"\\x00\");\n\n useEffect(() => {\n const keys = normalizedKey.split(\"\\x00\");\n // Local Set tracks which target keys are currently pressed so that releasing\n // one key while another is still held does not incorrectly clear `pressed`.\n const pressedKeys = new Set<string>();\n\n const onKeyDown = (event: KeyboardEvent) => {\n const key = event.key.toLowerCase();\n if (keys.includes(key)) {\n pressedKeys.add(key);\n setPressed(true);\n }\n };\n const onKeyUp = (event: KeyboardEvent) => {\n const key = event.key.toLowerCase();\n if (keys.includes(key)) {\n pressedKeys.delete(key);\n setPressed(pressedKeys.size > 0);\n }\n };\n const onBlur = () => {\n pressedKeys.clear();\n setPressed(false);\n };\n window.addEventListener(\"keydown\", onKeyDown);\n window.addEventListener(\"keyup\", onKeyUp);\n window.addEventListener(\"blur\", onBlur);\n return () => {\n pressedKeys.clear();\n setPressed(false);\n window.removeEventListener(\"keydown\", onKeyDown);\n window.removeEventListener(\"keyup\", onKeyUp);\n window.removeEventListener(\"blur\", onBlur);\n };\n }, [normalizedKey]);\n\n return pressed;\n}\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import '../chunk-J5LGTIGS.mjs';
|
|
3
|
+
import { useState, useEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
function useKeyPress(targetKey) {
|
|
6
|
+
const [pressed, setPressed] = useState(false);
|
|
7
|
+
const normalizedKey = (Array.isArray(targetKey) ? targetKey : [targetKey]).map((k) => k.toLowerCase()).sort().join("\0");
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const keys = normalizedKey.split("\0");
|
|
10
|
+
const pressedKeys = /* @__PURE__ */ new Set();
|
|
11
|
+
const onKeyDown = (event) => {
|
|
12
|
+
const key = event.key.toLowerCase();
|
|
13
|
+
if (keys.includes(key)) {
|
|
14
|
+
pressedKeys.add(key);
|
|
15
|
+
setPressed(true);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const onKeyUp = (event) => {
|
|
19
|
+
const key = event.key.toLowerCase();
|
|
20
|
+
if (keys.includes(key)) {
|
|
21
|
+
pressedKeys.delete(key);
|
|
22
|
+
setPressed(pressedKeys.size > 0);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const onBlur = () => {
|
|
26
|
+
pressedKeys.clear();
|
|
27
|
+
setPressed(false);
|
|
28
|
+
};
|
|
29
|
+
window.addEventListener("keydown", onKeyDown);
|
|
30
|
+
window.addEventListener("keyup", onKeyUp);
|
|
31
|
+
window.addEventListener("blur", onBlur);
|
|
32
|
+
return () => {
|
|
33
|
+
pressedKeys.clear();
|
|
34
|
+
setPressed(false);
|
|
35
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
36
|
+
window.removeEventListener("keyup", onKeyUp);
|
|
37
|
+
window.removeEventListener("blur", onBlur);
|
|
38
|
+
};
|
|
39
|
+
}, [normalizedKey]);
|
|
40
|
+
return pressed;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { useKeyPress };
|
|
44
|
+
//# sourceMappingURL=useKeyPress.mjs.map
|
|
45
|
+
//# sourceMappingURL=useKeyPress.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useKeyPress/useKeyPress.ts"],"names":[],"mappings":";;;AAiBO,SAAS,YAAY,SAAA,EAAuC;AACjE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAI5C,EAAA,MAAM,iBAAiB,KAAA,CAAM,OAAA,CAAQ,SAAS,CAAA,GAAI,SAAA,GAAY,CAAC,SAAS,CAAA,EACrE,IAAI,CAAC,CAAA,KAAM,EAAE,WAAA,EAAa,EAC1B,IAAA,EAAK,CACL,KAAK,IAAM,CAAA;AAEd,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,IAAA,GAAO,aAAA,CAAc,KAAA,CAAM,IAAM,CAAA;AAGvC,IAAA,MAAM,WAAA,uBAAkB,GAAA,EAAY;AAEpC,IAAA,MAAM,SAAA,GAAY,CAAC,KAAA,KAAyB;AAC1C,MAAA,MAAM,GAAA,GAAM,KAAA,CAAM,GAAA,CAAI,WAAA,EAAY;AAClC,MAAA,IAAI,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,EAAG;AACtB,QAAA,WAAA,CAAY,IAAI,GAAG,CAAA;AACnB,QAAA,UAAA,CAAW,IAAI,CAAA;AAAA,MACjB;AAAA,IACF,CAAA;AACA,IAAA,MAAM,OAAA,GAAU,CAAC,KAAA,KAAyB;AACxC,MAAA,MAAM,GAAA,GAAM,KAAA,CAAM,GAAA,CAAI,WAAA,EAAY;AAClC,MAAA,IAAI,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,EAAG;AACtB,QAAA,WAAA,CAAY,OAAO,GAAG,CAAA;AACtB,QAAA,UAAA,CAAW,WAAA,CAAY,OAAO,CAAC,CAAA;AAAA,MACjC;AAAA,IACF,CAAA;AACA,IAAA,MAAM,SAAS,MAAM;AACnB,MAAA,WAAA,CAAY,KAAA,EAAM;AAClB,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB,CAAA;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,SAAS,CAAA;AAC5C,IAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,OAAO,CAAA;AACxC,IAAA,MAAA,CAAO,gBAAA,CAAiB,QAAQ,MAAM,CAAA;AACtC,IAAA,OAAO,MAAM;AACX,MAAA,WAAA,CAAY,KAAA,EAAM;AAClB,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,SAAS,CAAA;AAC/C,MAAA,MAAA,CAAO,mBAAA,CAAoB,SAAS,OAAO,CAAA;AAC3C,MAAA,MAAA,CAAO,mBAAA,CAAoB,QAAQ,MAAM,CAAA;AAAA,IAC3C,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA;AAElB,EAAA,OAAO,OAAA;AACT","file":"useKeyPress.mjs","sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\n/**\n * Tracks whether a keyboard key (or any of several keys) is currently held down.\n *\n * - Matches against `event.key`, case-insensitively (`\"k\"`, `\"Escape\"`, `\"ArrowUp\"`, …).\n * - Listens on `window` for `keydown` / `keyup`, and clears on window `blur` so the\n * state cannot get stuck when focus leaves the page mid-press.\n * - When an array of keys is watched, `pressed` remains `true` as long as ANY of the\n * target keys are still held — releasing one watched key while another is held does\n * not reset the state.\n *\n * @param targetKey - A key name or array of key names to watch.\n * @returns `true` while one of the target keys is pressed.\n */\nexport function useKeyPress(targetKey: string | string[]): boolean {\n const [pressed, setPressed] = useState(false);\n\n // Build a stable string key from the sorted target array.\n // Use \\x00 as separator (not \"|\") so the literal \"|\" key is handled correctly.\n const normalizedKey = (Array.isArray(targetKey) ? targetKey : [targetKey])\n .map((k) => k.toLowerCase())\n .sort()\n .join(\"\\x00\");\n\n useEffect(() => {\n const keys = normalizedKey.split(\"\\x00\");\n // Local Set tracks which target keys are currently pressed so that releasing\n // one key while another is still held does not incorrectly clear `pressed`.\n const pressedKeys = new Set<string>();\n\n const onKeyDown = (event: KeyboardEvent) => {\n const key = event.key.toLowerCase();\n if (keys.includes(key)) {\n pressedKeys.add(key);\n setPressed(true);\n }\n };\n const onKeyUp = (event: KeyboardEvent) => {\n const key = event.key.toLowerCase();\n if (keys.includes(key)) {\n pressedKeys.delete(key);\n setPressed(pressedKeys.size > 0);\n }\n };\n const onBlur = () => {\n pressedKeys.clear();\n setPressed(false);\n };\n window.addEventListener(\"keydown\", onKeyDown);\n window.addEventListener(\"keyup\", onKeyUp);\n window.addEventListener(\"blur\", onBlur);\n return () => {\n pressedKeys.clear();\n setPressed(false);\n window.removeEventListener(\"keydown\", onKeyDown);\n window.removeEventListener(\"keyup\", onKeyUp);\n window.removeEventListener(\"blur\", onBlur);\n };\n }, [normalizedKey]);\n\n return pressed;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/useLongPress/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,GACzB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { MouseEvent as ReactMouseEvent, PointerEvent as ReactPointerEvent } from "react";
|
|
2
|
+
export type UseLongPressOptions = {
|
|
3
|
+
/** Hold duration in milliseconds before the press counts as "long" (default `500`). */
|
|
4
|
+
thresholdMs?: number;
|
|
5
|
+
/** Pointer travel in pixels that cancels the press (default `10`). */
|
|
6
|
+
moveTolerancePx?: number;
|
|
7
|
+
/** Called when the pointer goes down (press attempt starts). */
|
|
8
|
+
onStart?: (event: ReactPointerEvent) => void;
|
|
9
|
+
/** Called on release after a long press fired. */
|
|
10
|
+
onFinish?: (event: ReactPointerEvent) => void;
|
|
11
|
+
/** Called when the press is released or cancelled before the threshold. */
|
|
12
|
+
onCancel?: (event: ReactPointerEvent) => void;
|
|
13
|
+
};
|
|
14
|
+
export type UseLongPressHandlers = {
|
|
15
|
+
onPointerDown: (event: ReactPointerEvent) => void;
|
|
16
|
+
onPointerMove: (event: ReactPointerEvent) => void;
|
|
17
|
+
onPointerUp: (event: ReactPointerEvent) => void;
|
|
18
|
+
onPointerLeave: (event: ReactPointerEvent) => void;
|
|
19
|
+
/** Handles browser-level pointer cancellation (e.g. system interruption, scroll takeover). */
|
|
20
|
+
onPointerCancel: (event: ReactPointerEvent) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Suppresses the synthetic `click` event that fires after a successful long press.
|
|
23
|
+
* Spread alongside the other handlers to prevent unintended navigation or button actions.
|
|
24
|
+
*/
|
|
25
|
+
onClick: (event: ReactMouseEvent) => void;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Long-press gesture detection built on pointer events, so it works for mouse, touch, and pen.
|
|
29
|
+
*
|
|
30
|
+
* Spread the returned handlers onto the target element. After the pointer is held
|
|
31
|
+
* `thresholdMs` without travelling more than `moveTolerancePx`, `callback` fires once;
|
|
32
|
+
* releasing afterwards calls `onFinish`, while early release / movement / leaving the
|
|
33
|
+
* element calls `onCancel`.
|
|
34
|
+
*
|
|
35
|
+
* Only the primary button (button 0 / left click / first touch contact) starts a long press;
|
|
36
|
+
* right and middle mouse buttons are ignored.
|
|
37
|
+
*
|
|
38
|
+
* Pair with `touch-action` / `select-none` CSS on touch targets to suppress native
|
|
39
|
+
* scrolling or text selection during the hold where needed.
|
|
40
|
+
*
|
|
41
|
+
* @param callback - Invoked once when the press crosses the threshold.
|
|
42
|
+
* @param options - {@link UseLongPressOptions}
|
|
43
|
+
* @returns Spreadable pointer handlers ({@link UseLongPressHandlers}).
|
|
44
|
+
*/
|
|
45
|
+
export declare function useLongPress(callback: (event: ReactPointerEvent) => void, options?: UseLongPressOptions): UseLongPressHandlers;
|
|
46
|
+
//# sourceMappingURL=useLongPress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLongPress.d.ts","sourceRoot":"","sources":["../../../src/hooks/useLongPress/useLongPress.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,IAAI,eAAe,EAC7B,YAAY,IAAI,iBAAiB,EAClC,MAAM,OAAO,CAAC;AAGf,MAAM,MAAM,mBAAmB,GAAG;IAChC,uFAAuF;IACvF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC7C,kDAAkD;IAClD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC9C,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAClD,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAChD,cAAc,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACnD,8FAA8F;IAC9F,eAAe,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACpD;;;OAGG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;CAC3C,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,EAC5C,OAAO,GAAE,mBAAwB,GAChC,oBAAoB,CAwHtB"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
require('../chunk-PZ5AY32C.js');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
|
|
7
|
+
function useLongPress(callback, options = {}) {
|
|
8
|
+
const {
|
|
9
|
+
thresholdMs = 500,
|
|
10
|
+
moveTolerancePx = 10,
|
|
11
|
+
onStart,
|
|
12
|
+
onFinish,
|
|
13
|
+
onCancel
|
|
14
|
+
} = options;
|
|
15
|
+
const callbackRef = react.useRef(callback);
|
|
16
|
+
const onStartRef = react.useRef(onStart);
|
|
17
|
+
const onFinishRef = react.useRef(onFinish);
|
|
18
|
+
const onCancelRef = react.useRef(onCancel);
|
|
19
|
+
react.useEffect(() => {
|
|
20
|
+
callbackRef.current = callback;
|
|
21
|
+
onStartRef.current = onStart;
|
|
22
|
+
onFinishRef.current = onFinish;
|
|
23
|
+
onCancelRef.current = onCancel;
|
|
24
|
+
}, [callback, onCancel, onFinish, onStart]);
|
|
25
|
+
const timeoutRef = react.useRef(void 0);
|
|
26
|
+
const triggeredRef = react.useRef(false);
|
|
27
|
+
const pressingRef = react.useRef(false);
|
|
28
|
+
const originRef = react.useRef({ x: 0, y: 0 });
|
|
29
|
+
const stopTimer = react.useCallback(() => {
|
|
30
|
+
if (timeoutRef.current !== void 0) {
|
|
31
|
+
window.clearTimeout(timeoutRef.current);
|
|
32
|
+
timeoutRef.current = void 0;
|
|
33
|
+
}
|
|
34
|
+
}, []);
|
|
35
|
+
react.useEffect(() => stopTimer, [stopTimer]);
|
|
36
|
+
const cancel = react.useCallback(
|
|
37
|
+
(event) => {
|
|
38
|
+
if (!pressingRef.current) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
pressingRef.current = false;
|
|
42
|
+
stopTimer();
|
|
43
|
+
if (!triggeredRef.current) {
|
|
44
|
+
onCancelRef.current?.(event);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
[stopTimer]
|
|
48
|
+
);
|
|
49
|
+
const onPointerDown = react.useCallback(
|
|
50
|
+
(event) => {
|
|
51
|
+
if (event.button > 0) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
pressingRef.current = true;
|
|
55
|
+
triggeredRef.current = false;
|
|
56
|
+
originRef.current = { x: event.clientX, y: event.clientY };
|
|
57
|
+
onStartRef.current?.(event);
|
|
58
|
+
stopTimer();
|
|
59
|
+
timeoutRef.current = window.setTimeout(() => {
|
|
60
|
+
timeoutRef.current = void 0;
|
|
61
|
+
if (pressingRef.current) {
|
|
62
|
+
triggeredRef.current = true;
|
|
63
|
+
callbackRef.current(event);
|
|
64
|
+
}
|
|
65
|
+
}, thresholdMs);
|
|
66
|
+
},
|
|
67
|
+
[stopTimer, thresholdMs]
|
|
68
|
+
);
|
|
69
|
+
const onPointerMove = react.useCallback(
|
|
70
|
+
(event) => {
|
|
71
|
+
if (!pressingRef.current || triggeredRef.current) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const dx = event.clientX - originRef.current.x;
|
|
75
|
+
const dy = event.clientY - originRef.current.y;
|
|
76
|
+
if (Math.hypot(dx, dy) > moveTolerancePx) {
|
|
77
|
+
cancel(event);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
[cancel, moveTolerancePx]
|
|
81
|
+
);
|
|
82
|
+
const onPointerUp = react.useCallback(
|
|
83
|
+
(event) => {
|
|
84
|
+
if (!pressingRef.current) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const triggered = triggeredRef.current;
|
|
88
|
+
pressingRef.current = false;
|
|
89
|
+
stopTimer();
|
|
90
|
+
if (triggered) {
|
|
91
|
+
onFinishRef.current?.(event);
|
|
92
|
+
} else {
|
|
93
|
+
onCancelRef.current?.(event);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
[stopTimer]
|
|
97
|
+
);
|
|
98
|
+
const onClick = react.useCallback((event) => {
|
|
99
|
+
if (triggeredRef.current) {
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
triggeredRef.current = false;
|
|
102
|
+
}
|
|
103
|
+
}, []);
|
|
104
|
+
return {
|
|
105
|
+
onPointerDown,
|
|
106
|
+
onPointerMove,
|
|
107
|
+
onPointerUp,
|
|
108
|
+
onPointerLeave: cancel,
|
|
109
|
+
onPointerCancel: cancel,
|
|
110
|
+
onClick
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
exports.useLongPress = useLongPress;
|
|
115
|
+
//# sourceMappingURL=useLongPress.js.map
|
|
116
|
+
//# sourceMappingURL=useLongPress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useLongPress/useLongPress.ts"],"names":["useRef","useEffect","useCallback"],"mappings":"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","file":"useLongPress.js","sourcesContent":["\"use client\";\n\nimport type {\n MouseEvent as ReactMouseEvent,\n PointerEvent as ReactPointerEvent,\n} from \"react\";\nimport { useCallback, useEffect, useRef } from \"react\";\n\nexport type UseLongPressOptions = {\n /** Hold duration in milliseconds before the press counts as \"long\" (default `500`). */\n thresholdMs?: number;\n /** Pointer travel in pixels that cancels the press (default `10`). */\n moveTolerancePx?: number;\n /** Called when the pointer goes down (press attempt starts). */\n onStart?: (event: ReactPointerEvent) => void;\n /** Called on release after a long press fired. */\n onFinish?: (event: ReactPointerEvent) => void;\n /** Called when the press is released or cancelled before the threshold. */\n onCancel?: (event: ReactPointerEvent) => void;\n};\n\nexport type UseLongPressHandlers = {\n onPointerDown: (event: ReactPointerEvent) => void;\n onPointerMove: (event: ReactPointerEvent) => void;\n onPointerUp: (event: ReactPointerEvent) => void;\n onPointerLeave: (event: ReactPointerEvent) => void;\n /** Handles browser-level pointer cancellation (e.g. system interruption, scroll takeover). */\n onPointerCancel: (event: ReactPointerEvent) => void;\n /**\n * Suppresses the synthetic `click` event that fires after a successful long press.\n * Spread alongside the other handlers to prevent unintended navigation or button actions.\n */\n onClick: (event: ReactMouseEvent) => void;\n};\n\n/**\n * Long-press gesture detection built on pointer events, so it works for mouse, touch, and pen.\n *\n * Spread the returned handlers onto the target element. After the pointer is held\n * `thresholdMs` without travelling more than `moveTolerancePx`, `callback` fires once;\n * releasing afterwards calls `onFinish`, while early release / movement / leaving the\n * element calls `onCancel`.\n *\n * Only the primary button (button 0 / left click / first touch contact) starts a long press;\n * right and middle mouse buttons are ignored.\n *\n * Pair with `touch-action` / `select-none` CSS on touch targets to suppress native\n * scrolling or text selection during the hold where needed.\n *\n * @param callback - Invoked once when the press crosses the threshold.\n * @param options - {@link UseLongPressOptions}\n * @returns Spreadable pointer handlers ({@link UseLongPressHandlers}).\n */\nexport function useLongPress(\n callback: (event: ReactPointerEvent) => void,\n options: UseLongPressOptions = {},\n): UseLongPressHandlers {\n const {\n thresholdMs = 500,\n moveTolerancePx = 10,\n onStart,\n onFinish,\n onCancel,\n } = options;\n\n const callbackRef = useRef(callback);\n const onStartRef = useRef(onStart);\n const onFinishRef = useRef(onFinish);\n const onCancelRef = useRef(onCancel);\n\n useEffect(() => {\n callbackRef.current = callback;\n onStartRef.current = onStart;\n onFinishRef.current = onFinish;\n onCancelRef.current = onCancel;\n }, [callback, onCancel, onFinish, onStart]);\n\n const timeoutRef = useRef<number | undefined>(undefined);\n const triggeredRef = useRef(false);\n const pressingRef = useRef(false);\n const originRef = useRef({ x: 0, y: 0 });\n\n const stopTimer = useCallback(() => {\n if (timeoutRef.current !== undefined) {\n window.clearTimeout(timeoutRef.current);\n timeoutRef.current = undefined;\n }\n }, []);\n\n useEffect(() => stopTimer, [stopTimer]);\n\n const cancel = useCallback(\n (event: ReactPointerEvent) => {\n if (!pressingRef.current) {\n return;\n }\n pressingRef.current = false;\n stopTimer();\n if (!triggeredRef.current) {\n onCancelRef.current?.(event);\n }\n },\n [stopTimer],\n );\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent) => {\n // Only respond to the primary button (left click / first touch contact).\n // event.button > 0 catches right (2) and middle (1) mouse buttons.\n if (event.button > 0) {\n return;\n }\n pressingRef.current = true;\n triggeredRef.current = false;\n originRef.current = { x: event.clientX, y: event.clientY };\n onStartRef.current?.(event);\n stopTimer();\n timeoutRef.current = window.setTimeout(() => {\n timeoutRef.current = undefined;\n if (pressingRef.current) {\n triggeredRef.current = true;\n callbackRef.current(event);\n }\n }, thresholdMs);\n },\n [stopTimer, thresholdMs],\n );\n\n const onPointerMove = useCallback(\n (event: ReactPointerEvent) => {\n if (!pressingRef.current || triggeredRef.current) {\n return;\n }\n const dx = event.clientX - originRef.current.x;\n const dy = event.clientY - originRef.current.y;\n if (Math.hypot(dx, dy) > moveTolerancePx) {\n cancel(event);\n }\n },\n [cancel, moveTolerancePx],\n );\n\n const onPointerUp = useCallback(\n (event: ReactPointerEvent) => {\n if (!pressingRef.current) {\n return;\n }\n const triggered = triggeredRef.current;\n pressingRef.current = false;\n stopTimer();\n if (triggered) {\n onFinishRef.current?.(event);\n } else {\n onCancelRef.current?.(event);\n }\n },\n [stopTimer],\n );\n\n // After a successful long press the browser fires a synthetic `click` event on pointer up.\n // This handler suppresses it so navigation and button actions aren't triggered unexpectedly.\n const onClick = useCallback((event: ReactMouseEvent) => {\n if (triggeredRef.current) {\n event.preventDefault();\n triggeredRef.current = false;\n }\n }, []);\n\n return {\n onPointerDown,\n onPointerMove,\n onPointerUp,\n onPointerLeave: cancel,\n onPointerCancel: cancel,\n onClick,\n };\n}\n"]}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import '../chunk-J5LGTIGS.mjs';
|
|
3
|
+
import { useRef, useEffect, useCallback } from 'react';
|
|
4
|
+
|
|
5
|
+
function useLongPress(callback, options = {}) {
|
|
6
|
+
const {
|
|
7
|
+
thresholdMs = 500,
|
|
8
|
+
moveTolerancePx = 10,
|
|
9
|
+
onStart,
|
|
10
|
+
onFinish,
|
|
11
|
+
onCancel
|
|
12
|
+
} = options;
|
|
13
|
+
const callbackRef = useRef(callback);
|
|
14
|
+
const onStartRef = useRef(onStart);
|
|
15
|
+
const onFinishRef = useRef(onFinish);
|
|
16
|
+
const onCancelRef = useRef(onCancel);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
callbackRef.current = callback;
|
|
19
|
+
onStartRef.current = onStart;
|
|
20
|
+
onFinishRef.current = onFinish;
|
|
21
|
+
onCancelRef.current = onCancel;
|
|
22
|
+
}, [callback, onCancel, onFinish, onStart]);
|
|
23
|
+
const timeoutRef = useRef(void 0);
|
|
24
|
+
const triggeredRef = useRef(false);
|
|
25
|
+
const pressingRef = useRef(false);
|
|
26
|
+
const originRef = useRef({ x: 0, y: 0 });
|
|
27
|
+
const stopTimer = useCallback(() => {
|
|
28
|
+
if (timeoutRef.current !== void 0) {
|
|
29
|
+
window.clearTimeout(timeoutRef.current);
|
|
30
|
+
timeoutRef.current = void 0;
|
|
31
|
+
}
|
|
32
|
+
}, []);
|
|
33
|
+
useEffect(() => stopTimer, [stopTimer]);
|
|
34
|
+
const cancel = useCallback(
|
|
35
|
+
(event) => {
|
|
36
|
+
if (!pressingRef.current) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
pressingRef.current = false;
|
|
40
|
+
stopTimer();
|
|
41
|
+
if (!triggeredRef.current) {
|
|
42
|
+
onCancelRef.current?.(event);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
[stopTimer]
|
|
46
|
+
);
|
|
47
|
+
const onPointerDown = useCallback(
|
|
48
|
+
(event) => {
|
|
49
|
+
if (event.button > 0) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
pressingRef.current = true;
|
|
53
|
+
triggeredRef.current = false;
|
|
54
|
+
originRef.current = { x: event.clientX, y: event.clientY };
|
|
55
|
+
onStartRef.current?.(event);
|
|
56
|
+
stopTimer();
|
|
57
|
+
timeoutRef.current = window.setTimeout(() => {
|
|
58
|
+
timeoutRef.current = void 0;
|
|
59
|
+
if (pressingRef.current) {
|
|
60
|
+
triggeredRef.current = true;
|
|
61
|
+
callbackRef.current(event);
|
|
62
|
+
}
|
|
63
|
+
}, thresholdMs);
|
|
64
|
+
},
|
|
65
|
+
[stopTimer, thresholdMs]
|
|
66
|
+
);
|
|
67
|
+
const onPointerMove = useCallback(
|
|
68
|
+
(event) => {
|
|
69
|
+
if (!pressingRef.current || triggeredRef.current) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const dx = event.clientX - originRef.current.x;
|
|
73
|
+
const dy = event.clientY - originRef.current.y;
|
|
74
|
+
if (Math.hypot(dx, dy) > moveTolerancePx) {
|
|
75
|
+
cancel(event);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
[cancel, moveTolerancePx]
|
|
79
|
+
);
|
|
80
|
+
const onPointerUp = useCallback(
|
|
81
|
+
(event) => {
|
|
82
|
+
if (!pressingRef.current) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const triggered = triggeredRef.current;
|
|
86
|
+
pressingRef.current = false;
|
|
87
|
+
stopTimer();
|
|
88
|
+
if (triggered) {
|
|
89
|
+
onFinishRef.current?.(event);
|
|
90
|
+
} else {
|
|
91
|
+
onCancelRef.current?.(event);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
[stopTimer]
|
|
95
|
+
);
|
|
96
|
+
const onClick = useCallback((event) => {
|
|
97
|
+
if (triggeredRef.current) {
|
|
98
|
+
event.preventDefault();
|
|
99
|
+
triggeredRef.current = false;
|
|
100
|
+
}
|
|
101
|
+
}, []);
|
|
102
|
+
return {
|
|
103
|
+
onPointerDown,
|
|
104
|
+
onPointerMove,
|
|
105
|
+
onPointerUp,
|
|
106
|
+
onPointerLeave: cancel,
|
|
107
|
+
onPointerCancel: cancel,
|
|
108
|
+
onClick
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export { useLongPress };
|
|
113
|
+
//# sourceMappingURL=useLongPress.mjs.map
|
|
114
|
+
//# sourceMappingURL=useLongPress.mjs.map
|