@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,111 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
require('../chunk-PZ5AY32C.js');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
|
|
7
|
+
function toSnapshot(position) {
|
|
8
|
+
const { coords } = position;
|
|
9
|
+
return {
|
|
10
|
+
latitude: coords.latitude,
|
|
11
|
+
longitude: coords.longitude,
|
|
12
|
+
accuracy: coords.accuracy,
|
|
13
|
+
altitude: coords.altitude,
|
|
14
|
+
altitudeAccuracy: coords.altitudeAccuracy,
|
|
15
|
+
heading: coords.heading,
|
|
16
|
+
speed: coords.speed,
|
|
17
|
+
timestamp: position.timestamp
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function useGeolocation(params = {}) {
|
|
21
|
+
const {
|
|
22
|
+
enabled = true,
|
|
23
|
+
watch = true,
|
|
24
|
+
enableHighAccuracy,
|
|
25
|
+
maximumAge,
|
|
26
|
+
timeout
|
|
27
|
+
} = params;
|
|
28
|
+
const isSupported = typeof navigator !== "undefined" && "geolocation" in navigator;
|
|
29
|
+
const [permission, setPermission] = react.useState(
|
|
30
|
+
"unknown"
|
|
31
|
+
);
|
|
32
|
+
const [position, setPosition] = react.useState(null);
|
|
33
|
+
const [error, setError] = react.useState(null);
|
|
34
|
+
const [settled, setSettled] = react.useState(false);
|
|
35
|
+
react.useEffect(() => {
|
|
36
|
+
if (typeof navigator === "undefined" || navigator.permissions?.query == null) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
let active = true;
|
|
40
|
+
let status;
|
|
41
|
+
const onChange = () => {
|
|
42
|
+
if (active && status) {
|
|
43
|
+
setPermission(status.state);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
navigator.permissions.query({ name: "geolocation" }).then((result) => {
|
|
47
|
+
if (!active) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
status = result;
|
|
51
|
+
setPermission(result.state);
|
|
52
|
+
result.addEventListener("change", onChange);
|
|
53
|
+
}).catch(() => {
|
|
54
|
+
});
|
|
55
|
+
return () => {
|
|
56
|
+
active = false;
|
|
57
|
+
status?.removeEventListener("change", onChange);
|
|
58
|
+
};
|
|
59
|
+
}, []);
|
|
60
|
+
react.useEffect(() => {
|
|
61
|
+
if (!enabled || !isSupported) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const geolocation = navigator.geolocation;
|
|
65
|
+
let active = true;
|
|
66
|
+
setSettled(false);
|
|
67
|
+
const onSuccess = (next) => {
|
|
68
|
+
if (!active) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
setPosition(toSnapshot(next));
|
|
72
|
+
setError(null);
|
|
73
|
+
setSettled(true);
|
|
74
|
+
};
|
|
75
|
+
const onError = (nextError) => {
|
|
76
|
+
if (!active) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
setError(nextError);
|
|
80
|
+
setSettled(true);
|
|
81
|
+
};
|
|
82
|
+
const options = {
|
|
83
|
+
enableHighAccuracy,
|
|
84
|
+
maximumAge,
|
|
85
|
+
timeout
|
|
86
|
+
};
|
|
87
|
+
let watchId;
|
|
88
|
+
if (watch) {
|
|
89
|
+
watchId = geolocation.watchPosition(onSuccess, onError, options);
|
|
90
|
+
} else {
|
|
91
|
+
geolocation.getCurrentPosition(onSuccess, onError, options);
|
|
92
|
+
}
|
|
93
|
+
return () => {
|
|
94
|
+
active = false;
|
|
95
|
+
if (watchId !== void 0) {
|
|
96
|
+
geolocation.clearWatch(watchId);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}, [enableHighAccuracy, enabled, isSupported, maximumAge, timeout, watch]);
|
|
100
|
+
return {
|
|
101
|
+
isSupported,
|
|
102
|
+
loading: enabled && isSupported && !settled,
|
|
103
|
+
permission,
|
|
104
|
+
position,
|
|
105
|
+
error
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
exports.useGeolocation = useGeolocation;
|
|
110
|
+
//# sourceMappingURL=useGeolocation.js.map
|
|
111
|
+
//# sourceMappingURL=useGeolocation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useGeolocation/useGeolocation.ts"],"names":["useState","useEffect"],"mappings":"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","file":"useGeolocation.js","sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\nexport type GeolocationCoordinatesSnapshot = {\n latitude: number;\n longitude: number;\n accuracy: number;\n altitude: number | null;\n altitudeAccuracy: number | null;\n heading: number | null;\n speed: number | null;\n timestamp: number;\n};\n\nexport type UseGeolocationParams = {\n /** Start requesting the position (default `true`). Set `false` to defer the permission prompt. */\n enabled?: boolean;\n /** Keep watching for position updates instead of a one-shot read (default `true`). */\n watch?: boolean;\n /** `PositionOptions.enableHighAccuracy`. */\n enableHighAccuracy?: boolean;\n /** `PositionOptions.maximumAge` in milliseconds. */\n maximumAge?: number;\n /** `PositionOptions.timeout` in milliseconds. */\n timeout?: number;\n};\n\nexport type UseGeolocationResult = {\n /** Whether `navigator.geolocation` exists in this browser. */\n isSupported: boolean;\n /** True while enabled and no position or error has arrived yet. */\n loading: boolean;\n /** Permission state from the Permissions API (`\"unknown\"` where unsupported or before resolution). */\n permission: PermissionState | \"unknown\";\n /** Latest position snapshot, or `null` before the first fix. */\n position: GeolocationCoordinatesSnapshot | null;\n /** Latest geolocation error, cleared when a new fix succeeds. */\n error: GeolocationPositionError | null;\n};\n\nfunction toSnapshot(position: GeolocationPosition): GeolocationCoordinatesSnapshot {\n const { coords } = position;\n return {\n latitude: coords.latitude,\n longitude: coords.longitude,\n accuracy: coords.accuracy,\n altitude: coords.altitude,\n altitudeAccuracy: coords.altitudeAccuracy,\n heading: coords.heading,\n speed: coords.speed,\n timestamp: position.timestamp,\n };\n}\n\n/**\n * Browser geolocation with loading, error, and Permissions API state.\n *\n * - Requesting the position triggers the browser permission prompt; pass `enabled: false`\n * and flip it from a user gesture to avoid prompting on mount.\n * - `watch: true` (default) uses `watchPosition` for live updates; `false` reads once.\n * - `permission` mirrors `navigator.permissions.query({ name: \"geolocation\" })` including\n * change events, independent of whether a request is active.\n *\n * @param params - {@link UseGeolocationParams}\n * @returns {@link UseGeolocationResult}\n */\nexport function useGeolocation(\n params: UseGeolocationParams = {},\n): UseGeolocationResult {\n const {\n enabled = true,\n watch = true,\n enableHighAccuracy,\n maximumAge,\n timeout,\n } = params;\n\n const isSupported =\n typeof navigator !== \"undefined\" && \"geolocation\" in navigator;\n\n const [permission, setPermission] = useState<PermissionState | \"unknown\">(\n \"unknown\",\n );\n const [position, setPosition] =\n useState<GeolocationCoordinatesSnapshot | null>(null);\n const [error, setError] = useState<GeolocationPositionError | null>(null);\n const [settled, setSettled] = useState(false);\n\n useEffect(() => {\n if (\n typeof navigator === \"undefined\" ||\n navigator.permissions?.query == null\n ) {\n return;\n }\n let active = true;\n let status: PermissionStatus | undefined;\n const onChange = () => {\n if (active && status) {\n setPermission(status.state);\n }\n };\n navigator.permissions\n .query({ name: \"geolocation\" })\n .then((result) => {\n // Guard against the component unmounting before this promise resolves,\n // which would otherwise attach a listener that can never be cleaned up.\n if (!active) {\n return;\n }\n status = result;\n setPermission(result.state);\n result.addEventListener(\"change\", onChange);\n })\n .catch(() => {\n /* permissions API may reject for geolocation in some browsers */\n });\n return () => {\n active = false;\n status?.removeEventListener(\"change\", onChange);\n };\n }, []);\n\n useEffect(() => {\n if (!enabled || !isSupported) {\n return;\n }\n const geolocation = navigator.geolocation;\n let active = true;\n setSettled(false);\n const onSuccess = (next: GeolocationPosition) => {\n if (!active) {\n return;\n }\n setPosition(toSnapshot(next));\n setError(null);\n setSettled(true);\n };\n const onError = (nextError: GeolocationPositionError) => {\n if (!active) {\n return;\n }\n setError(nextError);\n setSettled(true);\n };\n const options: PositionOptions = {\n enableHighAccuracy,\n maximumAge,\n timeout,\n };\n let watchId: number | undefined;\n if (watch) {\n watchId = geolocation.watchPosition(onSuccess, onError, options);\n } else {\n geolocation.getCurrentPosition(onSuccess, onError, options);\n }\n return () => {\n active = false;\n if (watchId !== undefined) {\n geolocation.clearWatch(watchId);\n }\n };\n }, [enableHighAccuracy, enabled, isSupported, maximumAge, timeout, watch]);\n\n return {\n isSupported,\n loading: enabled && isSupported && !settled,\n permission,\n position,\n error,\n };\n}\n"]}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import '../chunk-J5LGTIGS.mjs';
|
|
3
|
+
import { useState, useEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
function toSnapshot(position) {
|
|
6
|
+
const { coords } = position;
|
|
7
|
+
return {
|
|
8
|
+
latitude: coords.latitude,
|
|
9
|
+
longitude: coords.longitude,
|
|
10
|
+
accuracy: coords.accuracy,
|
|
11
|
+
altitude: coords.altitude,
|
|
12
|
+
altitudeAccuracy: coords.altitudeAccuracy,
|
|
13
|
+
heading: coords.heading,
|
|
14
|
+
speed: coords.speed,
|
|
15
|
+
timestamp: position.timestamp
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function useGeolocation(params = {}) {
|
|
19
|
+
const {
|
|
20
|
+
enabled = true,
|
|
21
|
+
watch = true,
|
|
22
|
+
enableHighAccuracy,
|
|
23
|
+
maximumAge,
|
|
24
|
+
timeout
|
|
25
|
+
} = params;
|
|
26
|
+
const isSupported = typeof navigator !== "undefined" && "geolocation" in navigator;
|
|
27
|
+
const [permission, setPermission] = useState(
|
|
28
|
+
"unknown"
|
|
29
|
+
);
|
|
30
|
+
const [position, setPosition] = useState(null);
|
|
31
|
+
const [error, setError] = useState(null);
|
|
32
|
+
const [settled, setSettled] = useState(false);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (typeof navigator === "undefined" || navigator.permissions?.query == null) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
let active = true;
|
|
38
|
+
let status;
|
|
39
|
+
const onChange = () => {
|
|
40
|
+
if (active && status) {
|
|
41
|
+
setPermission(status.state);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
navigator.permissions.query({ name: "geolocation" }).then((result) => {
|
|
45
|
+
if (!active) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
status = result;
|
|
49
|
+
setPermission(result.state);
|
|
50
|
+
result.addEventListener("change", onChange);
|
|
51
|
+
}).catch(() => {
|
|
52
|
+
});
|
|
53
|
+
return () => {
|
|
54
|
+
active = false;
|
|
55
|
+
status?.removeEventListener("change", onChange);
|
|
56
|
+
};
|
|
57
|
+
}, []);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (!enabled || !isSupported) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const geolocation = navigator.geolocation;
|
|
63
|
+
let active = true;
|
|
64
|
+
setSettled(false);
|
|
65
|
+
const onSuccess = (next) => {
|
|
66
|
+
if (!active) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
setPosition(toSnapshot(next));
|
|
70
|
+
setError(null);
|
|
71
|
+
setSettled(true);
|
|
72
|
+
};
|
|
73
|
+
const onError = (nextError) => {
|
|
74
|
+
if (!active) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
setError(nextError);
|
|
78
|
+
setSettled(true);
|
|
79
|
+
};
|
|
80
|
+
const options = {
|
|
81
|
+
enableHighAccuracy,
|
|
82
|
+
maximumAge,
|
|
83
|
+
timeout
|
|
84
|
+
};
|
|
85
|
+
let watchId;
|
|
86
|
+
if (watch) {
|
|
87
|
+
watchId = geolocation.watchPosition(onSuccess, onError, options);
|
|
88
|
+
} else {
|
|
89
|
+
geolocation.getCurrentPosition(onSuccess, onError, options);
|
|
90
|
+
}
|
|
91
|
+
return () => {
|
|
92
|
+
active = false;
|
|
93
|
+
if (watchId !== void 0) {
|
|
94
|
+
geolocation.clearWatch(watchId);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}, [enableHighAccuracy, enabled, isSupported, maximumAge, timeout, watch]);
|
|
98
|
+
return {
|
|
99
|
+
isSupported,
|
|
100
|
+
loading: enabled && isSupported && !settled,
|
|
101
|
+
permission,
|
|
102
|
+
position,
|
|
103
|
+
error
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export { useGeolocation };
|
|
108
|
+
//# sourceMappingURL=useGeolocation.mjs.map
|
|
109
|
+
//# sourceMappingURL=useGeolocation.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useGeolocation/useGeolocation.ts"],"names":[],"mappings":"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","file":"useGeolocation.mjs","sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\nexport type GeolocationCoordinatesSnapshot = {\n latitude: number;\n longitude: number;\n accuracy: number;\n altitude: number | null;\n altitudeAccuracy: number | null;\n heading: number | null;\n speed: number | null;\n timestamp: number;\n};\n\nexport type UseGeolocationParams = {\n /** Start requesting the position (default `true`). Set `false` to defer the permission prompt. */\n enabled?: boolean;\n /** Keep watching for position updates instead of a one-shot read (default `true`). */\n watch?: boolean;\n /** `PositionOptions.enableHighAccuracy`. */\n enableHighAccuracy?: boolean;\n /** `PositionOptions.maximumAge` in milliseconds. */\n maximumAge?: number;\n /** `PositionOptions.timeout` in milliseconds. */\n timeout?: number;\n};\n\nexport type UseGeolocationResult = {\n /** Whether `navigator.geolocation` exists in this browser. */\n isSupported: boolean;\n /** True while enabled and no position or error has arrived yet. */\n loading: boolean;\n /** Permission state from the Permissions API (`\"unknown\"` where unsupported or before resolution). */\n permission: PermissionState | \"unknown\";\n /** Latest position snapshot, or `null` before the first fix. */\n position: GeolocationCoordinatesSnapshot | null;\n /** Latest geolocation error, cleared when a new fix succeeds. */\n error: GeolocationPositionError | null;\n};\n\nfunction toSnapshot(position: GeolocationPosition): GeolocationCoordinatesSnapshot {\n const { coords } = position;\n return {\n latitude: coords.latitude,\n longitude: coords.longitude,\n accuracy: coords.accuracy,\n altitude: coords.altitude,\n altitudeAccuracy: coords.altitudeAccuracy,\n heading: coords.heading,\n speed: coords.speed,\n timestamp: position.timestamp,\n };\n}\n\n/**\n * Browser geolocation with loading, error, and Permissions API state.\n *\n * - Requesting the position triggers the browser permission prompt; pass `enabled: false`\n * and flip it from a user gesture to avoid prompting on mount.\n * - `watch: true` (default) uses `watchPosition` for live updates; `false` reads once.\n * - `permission` mirrors `navigator.permissions.query({ name: \"geolocation\" })` including\n * change events, independent of whether a request is active.\n *\n * @param params - {@link UseGeolocationParams}\n * @returns {@link UseGeolocationResult}\n */\nexport function useGeolocation(\n params: UseGeolocationParams = {},\n): UseGeolocationResult {\n const {\n enabled = true,\n watch = true,\n enableHighAccuracy,\n maximumAge,\n timeout,\n } = params;\n\n const isSupported =\n typeof navigator !== \"undefined\" && \"geolocation\" in navigator;\n\n const [permission, setPermission] = useState<PermissionState | \"unknown\">(\n \"unknown\",\n );\n const [position, setPosition] =\n useState<GeolocationCoordinatesSnapshot | null>(null);\n const [error, setError] = useState<GeolocationPositionError | null>(null);\n const [settled, setSettled] = useState(false);\n\n useEffect(() => {\n if (\n typeof navigator === \"undefined\" ||\n navigator.permissions?.query == null\n ) {\n return;\n }\n let active = true;\n let status: PermissionStatus | undefined;\n const onChange = () => {\n if (active && status) {\n setPermission(status.state);\n }\n };\n navigator.permissions\n .query({ name: \"geolocation\" })\n .then((result) => {\n // Guard against the component unmounting before this promise resolves,\n // which would otherwise attach a listener that can never be cleaned up.\n if (!active) {\n return;\n }\n status = result;\n setPermission(result.state);\n result.addEventListener(\"change\", onChange);\n })\n .catch(() => {\n /* permissions API may reject for geolocation in some browsers */\n });\n return () => {\n active = false;\n status?.removeEventListener(\"change\", onChange);\n };\n }, []);\n\n useEffect(() => {\n if (!enabled || !isSupported) {\n return;\n }\n const geolocation = navigator.geolocation;\n let active = true;\n setSettled(false);\n const onSuccess = (next: GeolocationPosition) => {\n if (!active) {\n return;\n }\n setPosition(toSnapshot(next));\n setError(null);\n setSettled(true);\n };\n const onError = (nextError: GeolocationPositionError) => {\n if (!active) {\n return;\n }\n setError(nextError);\n setSettled(true);\n };\n const options: PositionOptions = {\n enableHighAccuracy,\n maximumAge,\n timeout,\n };\n let watchId: number | undefined;\n if (watch) {\n watchId = geolocation.watchPosition(onSuccess, onError, options);\n } else {\n geolocation.getCurrentPosition(onSuccess, onError, options);\n }\n return () => {\n active = false;\n if (watchId !== undefined) {\n geolocation.clearWatch(watchId);\n }\n };\n }, [enableHighAccuracy, enabled, isSupported, maximumAge, timeout, watch]);\n\n return {\n isSupported,\n loading: enabled && isSupported && !settled,\n permission,\n position,\n error,\n };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/useHotkeys/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,KAAK,aAAa,EAClB,KAAK,iBAAiB,GACvB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type HotkeyHandler = (event: KeyboardEvent) => void;
|
|
2
|
+
export type UseHotkeysOptions = {
|
|
3
|
+
/** Disable all bindings without unmounting (default `true`). */
|
|
4
|
+
enabled?: boolean;
|
|
5
|
+
/** Call `event.preventDefault()` on a match (default `true`). */
|
|
6
|
+
preventDefault?: boolean;
|
|
7
|
+
/** Fire even when typing in inputs, textareas, selects, or contentEditable (default `false`). */
|
|
8
|
+
allowInInputs?: boolean;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Binds keyboard shortcut combos (e.g. `"mod+k"`, `"ctrl+shift+p"`, `"escape"`) to handlers on `window`.
|
|
12
|
+
*
|
|
13
|
+
* - Combo syntax: modifier tokens (`ctrl`/`control`, `meta`/`cmd`/`command`, `alt`/`option`, `shift`,
|
|
14
|
+
* `mod`) joined with `+` around a final key matched against `event.key` (use `space` for the spacebar).
|
|
15
|
+
* - `mod` matches Cmd on macOS *or* Ctrl elsewhere (it accepts either modifier).
|
|
16
|
+
* - Bindings are read from a ref on every keydown, so inline objects and closures stay fresh
|
|
17
|
+
* without re-subscribing.
|
|
18
|
+
* - Shortcuts are suppressed while typing in form fields or contentEditable unless `allowInInputs`.
|
|
19
|
+
*
|
|
20
|
+
* @param bindings - Map of combo string → handler. The first matching combo wins per event.
|
|
21
|
+
* @param options - {@link UseHotkeysOptions}
|
|
22
|
+
*/
|
|
23
|
+
export declare function useHotkeys(bindings: Record<string, HotkeyHandler>, options?: UseHotkeysOptions): void;
|
|
24
|
+
//# sourceMappingURL=useHotkeys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useHotkeys.d.ts","sourceRoot":"","sources":["../../../src/hooks/useHotkeys/useHotkeys.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;AAE3D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iEAAiE;IACjE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iGAAiG;IACjG,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAoEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EACvC,OAAO,GAAE,iBAAsB,GAC9B,IAAI,CAgCN"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
require('../chunk-PZ5AY32C.js');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
|
|
7
|
+
function parseHotkey(combo) {
|
|
8
|
+
const parsed = {
|
|
9
|
+
key: "",
|
|
10
|
+
ctrl: false,
|
|
11
|
+
meta: false,
|
|
12
|
+
alt: false,
|
|
13
|
+
shift: false,
|
|
14
|
+
mod: false
|
|
15
|
+
};
|
|
16
|
+
for (const raw of combo.split("+")) {
|
|
17
|
+
const token = raw.trim().toLowerCase();
|
|
18
|
+
if (token === "ctrl" || token === "control") {
|
|
19
|
+
parsed.ctrl = true;
|
|
20
|
+
} else if (token === "meta" || token === "cmd" || token === "command") {
|
|
21
|
+
parsed.meta = true;
|
|
22
|
+
} else if (token === "alt" || token === "option") {
|
|
23
|
+
parsed.alt = true;
|
|
24
|
+
} else if (token === "shift") {
|
|
25
|
+
parsed.shift = true;
|
|
26
|
+
} else if (token === "mod") {
|
|
27
|
+
parsed.mod = true;
|
|
28
|
+
} else {
|
|
29
|
+
parsed.key = token === "space" ? " " : token;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return parsed;
|
|
33
|
+
}
|
|
34
|
+
function matchesHotkey(event, hotkey) {
|
|
35
|
+
if (hotkey.key !== event.key.toLowerCase()) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
if (hotkey.mod) {
|
|
39
|
+
if (!event.ctrlKey && !event.metaKey) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
} else if (hotkey.ctrl !== event.ctrlKey || hotkey.meta !== event.metaKey) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
return hotkey.alt === event.altKey && hotkey.shift === event.shiftKey;
|
|
46
|
+
}
|
|
47
|
+
function isEditableTarget(target) {
|
|
48
|
+
if (!(target instanceof HTMLElement)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT" || target.isContentEditable;
|
|
52
|
+
}
|
|
53
|
+
function useHotkeys(bindings, options = {}) {
|
|
54
|
+
const { enabled = true, preventDefault = true, allowInInputs = false } = options;
|
|
55
|
+
const bindingsRef = react.useRef(bindings);
|
|
56
|
+
react.useEffect(() => {
|
|
57
|
+
bindingsRef.current = bindings;
|
|
58
|
+
}, [bindings]);
|
|
59
|
+
react.useEffect(() => {
|
|
60
|
+
if (!enabled) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const onKeyDown = (event) => {
|
|
64
|
+
if (!allowInInputs && isEditableTarget(event.target)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
for (const [combo, handler] of Object.entries(bindingsRef.current)) {
|
|
68
|
+
if (matchesHotkey(event, parseHotkey(combo))) {
|
|
69
|
+
if (preventDefault) {
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
}
|
|
72
|
+
handler(event);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
window.addEventListener("keydown", onKeyDown);
|
|
78
|
+
return () => {
|
|
79
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
80
|
+
};
|
|
81
|
+
}, [allowInInputs, enabled, preventDefault]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
exports.useHotkeys = useHotkeys;
|
|
85
|
+
//# sourceMappingURL=useHotkeys.js.map
|
|
86
|
+
//# sourceMappingURL=useHotkeys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useHotkeys/useHotkeys.ts"],"names":["useRef","useEffect"],"mappings":"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","file":"useHotkeys.js","sourcesContent":["\"use client\";\n\nimport { useEffect, useRef } from \"react\";\n\nexport type HotkeyHandler = (event: KeyboardEvent) => void;\n\nexport type UseHotkeysOptions = {\n /** Disable all bindings without unmounting (default `true`). */\n enabled?: boolean;\n /** Call `event.preventDefault()` on a match (default `true`). */\n preventDefault?: boolean;\n /** Fire even when typing in inputs, textareas, selects, or contentEditable (default `false`). */\n allowInInputs?: boolean;\n};\n\ntype ParsedHotkey = {\n key: string;\n ctrl: boolean;\n meta: boolean;\n alt: boolean;\n shift: boolean;\n mod: boolean;\n};\n\nfunction parseHotkey(combo: string): ParsedHotkey {\n const parsed: ParsedHotkey = {\n key: \"\",\n ctrl: false,\n meta: false,\n alt: false,\n shift: false,\n mod: false,\n };\n for (const raw of combo.split(\"+\")) {\n const token = raw.trim().toLowerCase();\n if (token === \"ctrl\" || token === \"control\") {\n parsed.ctrl = true;\n } else if (token === \"meta\" || token === \"cmd\" || token === \"command\") {\n parsed.meta = true;\n } else if (token === \"alt\" || token === \"option\") {\n parsed.alt = true;\n } else if (token === \"shift\") {\n parsed.shift = true;\n } else if (token === \"mod\") {\n parsed.mod = true;\n } else {\n parsed.key = token === \"space\" ? \" \" : token;\n }\n }\n return parsed;\n}\n\nfunction matchesHotkey(event: KeyboardEvent, hotkey: ParsedHotkey): boolean {\n if (hotkey.key !== event.key.toLowerCase()) {\n return false;\n }\n if (hotkey.mod) {\n if (!event.ctrlKey && !event.metaKey) {\n return false;\n }\n } else if (\n hotkey.ctrl !== event.ctrlKey ||\n hotkey.meta !== event.metaKey\n ) {\n return false;\n }\n return hotkey.alt === event.altKey && hotkey.shift === event.shiftKey;\n}\n\nfunction isEditableTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) {\n return false;\n }\n return (\n target.tagName === \"INPUT\" ||\n target.tagName === \"TEXTAREA\" ||\n target.tagName === \"SELECT\" ||\n target.isContentEditable\n );\n}\n\n/**\n * Binds keyboard shortcut combos (e.g. `\"mod+k\"`, `\"ctrl+shift+p\"`, `\"escape\"`) to handlers on `window`.\n *\n * - Combo syntax: modifier tokens (`ctrl`/`control`, `meta`/`cmd`/`command`, `alt`/`option`, `shift`,\n * `mod`) joined with `+` around a final key matched against `event.key` (use `space` for the spacebar).\n * - `mod` matches Cmd on macOS *or* Ctrl elsewhere (it accepts either modifier).\n * - Bindings are read from a ref on every keydown, so inline objects and closures stay fresh\n * without re-subscribing.\n * - Shortcuts are suppressed while typing in form fields or contentEditable unless `allowInInputs`.\n *\n * @param bindings - Map of combo string → handler. The first matching combo wins per event.\n * @param options - {@link UseHotkeysOptions}\n */\nexport function useHotkeys(\n bindings: Record<string, HotkeyHandler>,\n options: UseHotkeysOptions = {},\n): void {\n const { enabled = true, preventDefault = true, allowInInputs = false } =\n options;\n const bindingsRef = useRef(bindings);\n\n useEffect(() => {\n bindingsRef.current = bindings;\n }, [bindings]);\n\n useEffect(() => {\n if (!enabled) {\n return;\n }\n const onKeyDown = (event: KeyboardEvent) => {\n if (!allowInInputs && isEditableTarget(event.target)) {\n return;\n }\n for (const [combo, handler] of Object.entries(bindingsRef.current)) {\n if (matchesHotkey(event, parseHotkey(combo))) {\n if (preventDefault) {\n event.preventDefault();\n }\n handler(event);\n return;\n }\n }\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => {\n window.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [allowInInputs, enabled, preventDefault]);\n}\n"]}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import '../chunk-J5LGTIGS.mjs';
|
|
3
|
+
import { useRef, useEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
function parseHotkey(combo) {
|
|
6
|
+
const parsed = {
|
|
7
|
+
key: "",
|
|
8
|
+
ctrl: false,
|
|
9
|
+
meta: false,
|
|
10
|
+
alt: false,
|
|
11
|
+
shift: false,
|
|
12
|
+
mod: false
|
|
13
|
+
};
|
|
14
|
+
for (const raw of combo.split("+")) {
|
|
15
|
+
const token = raw.trim().toLowerCase();
|
|
16
|
+
if (token === "ctrl" || token === "control") {
|
|
17
|
+
parsed.ctrl = true;
|
|
18
|
+
} else if (token === "meta" || token === "cmd" || token === "command") {
|
|
19
|
+
parsed.meta = true;
|
|
20
|
+
} else if (token === "alt" || token === "option") {
|
|
21
|
+
parsed.alt = true;
|
|
22
|
+
} else if (token === "shift") {
|
|
23
|
+
parsed.shift = true;
|
|
24
|
+
} else if (token === "mod") {
|
|
25
|
+
parsed.mod = true;
|
|
26
|
+
} else {
|
|
27
|
+
parsed.key = token === "space" ? " " : token;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return parsed;
|
|
31
|
+
}
|
|
32
|
+
function matchesHotkey(event, hotkey) {
|
|
33
|
+
if (hotkey.key !== event.key.toLowerCase()) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (hotkey.mod) {
|
|
37
|
+
if (!event.ctrlKey && !event.metaKey) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
} else if (hotkey.ctrl !== event.ctrlKey || hotkey.meta !== event.metaKey) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return hotkey.alt === event.altKey && hotkey.shift === event.shiftKey;
|
|
44
|
+
}
|
|
45
|
+
function isEditableTarget(target) {
|
|
46
|
+
if (!(target instanceof HTMLElement)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
return target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT" || target.isContentEditable;
|
|
50
|
+
}
|
|
51
|
+
function useHotkeys(bindings, options = {}) {
|
|
52
|
+
const { enabled = true, preventDefault = true, allowInInputs = false } = options;
|
|
53
|
+
const bindingsRef = useRef(bindings);
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
bindingsRef.current = bindings;
|
|
56
|
+
}, [bindings]);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!enabled) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const onKeyDown = (event) => {
|
|
62
|
+
if (!allowInInputs && isEditableTarget(event.target)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
for (const [combo, handler] of Object.entries(bindingsRef.current)) {
|
|
66
|
+
if (matchesHotkey(event, parseHotkey(combo))) {
|
|
67
|
+
if (preventDefault) {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
}
|
|
70
|
+
handler(event);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
window.addEventListener("keydown", onKeyDown);
|
|
76
|
+
return () => {
|
|
77
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
78
|
+
};
|
|
79
|
+
}, [allowInInputs, enabled, preventDefault]);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export { useHotkeys };
|
|
83
|
+
//# sourceMappingURL=useHotkeys.mjs.map
|
|
84
|
+
//# sourceMappingURL=useHotkeys.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useHotkeys/useHotkeys.ts"],"names":[],"mappings":";;;AAwBA,SAAS,YAAY,KAAA,EAA6B;AAChD,EAAA,MAAM,MAAA,GAAuB;AAAA,IAC3B,GAAA,EAAK,EAAA;AAAA,IACL,IAAA,EAAM,KAAA;AAAA,IACN,IAAA,EAAM,KAAA;AAAA,IACN,GAAA,EAAK,KAAA;AAAA,IACL,KAAA,EAAO,KAAA;AAAA,IACP,GAAA,EAAK;AAAA,GACP;AACA,EAAA,KAAA,MAAW,GAAA,IAAO,KAAA,CAAM,KAAA,CAAM,GAAG,CAAA,EAAG;AAClC,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,IAAA,EAAK,CAAE,WAAA,EAAY;AACrC,IAAA,IAAI,KAAA,KAAU,MAAA,IAAU,KAAA,KAAU,SAAA,EAAW;AAC3C,MAAA,MAAA,CAAO,IAAA,GAAO,IAAA;AAAA,IAChB,WAAW,KAAA,KAAU,MAAA,IAAU,KAAA,KAAU,KAAA,IAAS,UAAU,SAAA,EAAW;AACrE,MAAA,MAAA,CAAO,IAAA,GAAO,IAAA;AAAA,IAChB,CAAA,MAAA,IAAW,KAAA,KAAU,KAAA,IAAS,KAAA,KAAU,QAAA,EAAU;AAChD,MAAA,MAAA,CAAO,GAAA,GAAM,IAAA;AAAA,IACf,CAAA,MAAA,IAAW,UAAU,OAAA,EAAS;AAC5B,MAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AAAA,IACjB,CAAA,MAAA,IAAW,UAAU,KAAA,EAAO;AAC1B,MAAA,MAAA,CAAO,GAAA,GAAM,IAAA;AAAA,IACf,CAAA,MAAO;AACL,MAAA,MAAA,CAAO,GAAA,GAAM,KAAA,KAAU,OAAA,GAAU,GAAA,GAAM,KAAA;AAAA,IACzC;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,aAAA,CAAc,OAAsB,MAAA,EAA+B;AAC1E,EAAA,IAAI,MAAA,CAAO,GAAA,KAAQ,KAAA,CAAM,GAAA,CAAI,aAAY,EAAG;AAC1C,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,IAAI,OAAO,GAAA,EAAK;AACd,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,IAAW,CAAC,MAAM,OAAA,EAAS;AACpC,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,EACF,CAAA,MAAA,IACE,OAAO,IAAA,KAAS,KAAA,CAAM,WACtB,MAAA,CAAO,IAAA,KAAS,MAAM,OAAA,EACtB;AACA,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,OAAO,OAAO,GAAA,KAAQ,KAAA,CAAM,MAAA,IAAU,MAAA,CAAO,UAAU,KAAA,CAAM,QAAA;AAC/D;AAEA,SAAS,iBAAiB,MAAA,EAAqC;AAC7D,EAAA,IAAI,EAAE,kBAAkB,WAAA,CAAA,EAAc;AACpC,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,OACE,MAAA,CAAO,YAAY,OAAA,IACnB,MAAA,CAAO,YAAY,UAAA,IACnB,MAAA,CAAO,OAAA,KAAY,QAAA,IACnB,MAAA,CAAO,iBAAA;AAEX;AAeO,SAAS,UAAA,CACd,QAAA,EACA,OAAA,GAA6B,EAAC,EACxB;AACN,EAAA,MAAM,EAAE,OAAA,GAAU,IAAA,EAAM,iBAAiB,IAAA,EAAM,aAAA,GAAgB,OAAM,GACnE,OAAA;AACF,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,CAAC,OAAA,EAAS;AACZ,MAAA;AAAA,IACF;AACA,IAAA,MAAM,SAAA,GAAY,CAAC,KAAA,KAAyB;AAC1C,MAAA,IAAI,CAAC,aAAA,IAAiB,gBAAA,CAAiB,KAAA,CAAM,MAAM,CAAA,EAAG;AACpD,QAAA;AAAA,MACF;AACA,MAAA,KAAA,MAAW,CAAC,OAAO,OAAO,CAAA,IAAK,OAAO,OAAA,CAAQ,WAAA,CAAY,OAAO,CAAA,EAAG;AAClE,QAAA,IAAI,aAAA,CAAc,KAAA,EAAO,WAAA,CAAY,KAAK,CAAC,CAAA,EAAG;AAC5C,UAAA,IAAI,cAAA,EAAgB;AAClB,YAAA,KAAA,CAAM,cAAA,EAAe;AAAA,UACvB;AACA,UAAA,OAAA,CAAQ,KAAK,CAAA;AACb,UAAA;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAA;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,SAAS,CAAA;AAC5C,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,SAAS,CAAA;AAAA,IACjD,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,aAAA,EAAe,OAAA,EAAS,cAAc,CAAC,CAAA;AAC7C","file":"useHotkeys.mjs","sourcesContent":["\"use client\";\n\nimport { useEffect, useRef } from \"react\";\n\nexport type HotkeyHandler = (event: KeyboardEvent) => void;\n\nexport type UseHotkeysOptions = {\n /** Disable all bindings without unmounting (default `true`). */\n enabled?: boolean;\n /** Call `event.preventDefault()` on a match (default `true`). */\n preventDefault?: boolean;\n /** Fire even when typing in inputs, textareas, selects, or contentEditable (default `false`). */\n allowInInputs?: boolean;\n};\n\ntype ParsedHotkey = {\n key: string;\n ctrl: boolean;\n meta: boolean;\n alt: boolean;\n shift: boolean;\n mod: boolean;\n};\n\nfunction parseHotkey(combo: string): ParsedHotkey {\n const parsed: ParsedHotkey = {\n key: \"\",\n ctrl: false,\n meta: false,\n alt: false,\n shift: false,\n mod: false,\n };\n for (const raw of combo.split(\"+\")) {\n const token = raw.trim().toLowerCase();\n if (token === \"ctrl\" || token === \"control\") {\n parsed.ctrl = true;\n } else if (token === \"meta\" || token === \"cmd\" || token === \"command\") {\n parsed.meta = true;\n } else if (token === \"alt\" || token === \"option\") {\n parsed.alt = true;\n } else if (token === \"shift\") {\n parsed.shift = true;\n } else if (token === \"mod\") {\n parsed.mod = true;\n } else {\n parsed.key = token === \"space\" ? \" \" : token;\n }\n }\n return parsed;\n}\n\nfunction matchesHotkey(event: KeyboardEvent, hotkey: ParsedHotkey): boolean {\n if (hotkey.key !== event.key.toLowerCase()) {\n return false;\n }\n if (hotkey.mod) {\n if (!event.ctrlKey && !event.metaKey) {\n return false;\n }\n } else if (\n hotkey.ctrl !== event.ctrlKey ||\n hotkey.meta !== event.metaKey\n ) {\n return false;\n }\n return hotkey.alt === event.altKey && hotkey.shift === event.shiftKey;\n}\n\nfunction isEditableTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) {\n return false;\n }\n return (\n target.tagName === \"INPUT\" ||\n target.tagName === \"TEXTAREA\" ||\n target.tagName === \"SELECT\" ||\n target.isContentEditable\n );\n}\n\n/**\n * Binds keyboard shortcut combos (e.g. `\"mod+k\"`, `\"ctrl+shift+p\"`, `\"escape\"`) to handlers on `window`.\n *\n * - Combo syntax: modifier tokens (`ctrl`/`control`, `meta`/`cmd`/`command`, `alt`/`option`, `shift`,\n * `mod`) joined with `+` around a final key matched against `event.key` (use `space` for the spacebar).\n * - `mod` matches Cmd on macOS *or* Ctrl elsewhere (it accepts either modifier).\n * - Bindings are read from a ref on every keydown, so inline objects and closures stay fresh\n * without re-subscribing.\n * - Shortcuts are suppressed while typing in form fields or contentEditable unless `allowInInputs`.\n *\n * @param bindings - Map of combo string → handler. The first matching combo wins per event.\n * @param options - {@link UseHotkeysOptions}\n */\nexport function useHotkeys(\n bindings: Record<string, HotkeyHandler>,\n options: UseHotkeysOptions = {},\n): void {\n const { enabled = true, preventDefault = true, allowInInputs = false } =\n options;\n const bindingsRef = useRef(bindings);\n\n useEffect(() => {\n bindingsRef.current = bindings;\n }, [bindings]);\n\n useEffect(() => {\n if (!enabled) {\n return;\n }\n const onKeyDown = (event: KeyboardEvent) => {\n if (!allowInInputs && isEditableTarget(event.target)) {\n return;\n }\n for (const [combo, handler] of Object.entries(bindingsRef.current)) {\n if (matchesHotkey(event, parseHotkey(combo))) {\n if (preventDefault) {\n event.preventDefault();\n }\n handler(event);\n return;\n }\n }\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => {\n window.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [allowInInputs, enabled, preventDefault]);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/useIdleTimeout/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,GAC1B,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type UseIdleTimeoutParams = {
|
|
2
|
+
/** Inactivity duration in milliseconds before the user counts as idle. */
|
|
3
|
+
timeoutMs: number;
|
|
4
|
+
/** Window events treated as activity (default: pointer, key, wheel, touch, scroll). */
|
|
5
|
+
events?: readonly string[];
|
|
6
|
+
/** Start in the idle state (default `false`). */
|
|
7
|
+
initiallyIdle?: boolean;
|
|
8
|
+
/** Called when the user becomes idle. */
|
|
9
|
+
onIdle?: () => void;
|
|
10
|
+
/** Called when activity resumes after being idle. */
|
|
11
|
+
onActive?: () => void;
|
|
12
|
+
};
|
|
13
|
+
export type UseIdleTimeoutResult = {
|
|
14
|
+
/** Whether the user is currently idle. */
|
|
15
|
+
isIdle: boolean;
|
|
16
|
+
/** Mark the user active and restart the inactivity timer (e.g. after programmatic activity). */
|
|
17
|
+
reset: () => void;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Detects user inactivity: `isIdle` flips to `true` after `timeoutMs` without any of the
|
|
21
|
+
* activity events on `window`, and back to `false` on the next activity.
|
|
22
|
+
*
|
|
23
|
+
* - `onIdle` / `onActive` fire on transitions only (not on mount), and are read from refs
|
|
24
|
+
* so inline callbacks stay fresh.
|
|
25
|
+
* - Useful for session expiry warnings, pausing media or polling, and presence indicators.
|
|
26
|
+
*
|
|
27
|
+
* @param params - {@link UseIdleTimeoutParams}
|
|
28
|
+
* @returns {@link UseIdleTimeoutResult}
|
|
29
|
+
*/
|
|
30
|
+
export declare function useIdleTimeout({ timeoutMs, events, initiallyIdle, onIdle, onActive, }: UseIdleTimeoutParams): UseIdleTimeoutResult;
|
|
31
|
+
//# sourceMappingURL=useIdleTimeout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIdleTimeout.d.ts","sourceRoot":"","sources":["../../../src/hooks/useIdleTimeout/useIdleTimeout.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,oBAAoB,GAAG;IACjC,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,iDAAiD;IACjD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,0CAA0C;IAC1C,MAAM,EAAE,OAAO,CAAC;IAChB,gGAAgG;IAChG,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,MAAM,EACN,aAAqB,EACrB,MAAM,EACN,QAAQ,GACT,EAAE,oBAAoB,GAAG,oBAAoB,CA4D7C"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
require('../chunk-PZ5AY32C.js');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
|
|
7
|
+
var DEFAULT_ACTIVITY_EVENTS = [
|
|
8
|
+
"pointerdown",
|
|
9
|
+
"pointermove",
|
|
10
|
+
"keydown",
|
|
11
|
+
"wheel",
|
|
12
|
+
"touchstart",
|
|
13
|
+
"scroll"
|
|
14
|
+
];
|
|
15
|
+
function useIdleTimeout({
|
|
16
|
+
timeoutMs,
|
|
17
|
+
events,
|
|
18
|
+
initiallyIdle = false,
|
|
19
|
+
onIdle,
|
|
20
|
+
onActive
|
|
21
|
+
}) {
|
|
22
|
+
const [isIdle, setIsIdle] = react.useState(initiallyIdle);
|
|
23
|
+
const onIdleRef = react.useRef(onIdle);
|
|
24
|
+
const onActiveRef = react.useRef(onActive);
|
|
25
|
+
const restartRef = react.useRef(() => {
|
|
26
|
+
});
|
|
27
|
+
const firstRunRef = react.useRef(true);
|
|
28
|
+
react.useEffect(() => {
|
|
29
|
+
onIdleRef.current = onIdle;
|
|
30
|
+
onActiveRef.current = onActive;
|
|
31
|
+
}, [onActive, onIdle]);
|
|
32
|
+
react.useEffect(() => {
|
|
33
|
+
if (firstRunRef.current) {
|
|
34
|
+
firstRunRef.current = false;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (isIdle) {
|
|
38
|
+
onIdleRef.current?.();
|
|
39
|
+
} else {
|
|
40
|
+
onActiveRef.current?.();
|
|
41
|
+
}
|
|
42
|
+
}, [isIdle]);
|
|
43
|
+
const eventsKey = (events ?? DEFAULT_ACTIVITY_EVENTS).join(" ");
|
|
44
|
+
react.useEffect(() => {
|
|
45
|
+
const eventNames = eventsKey.split(" ").filter(Boolean);
|
|
46
|
+
let timeoutId;
|
|
47
|
+
const startTimer = () => {
|
|
48
|
+
window.clearTimeout(timeoutId);
|
|
49
|
+
timeoutId = window.setTimeout(() => {
|
|
50
|
+
setIsIdle(true);
|
|
51
|
+
}, timeoutMs);
|
|
52
|
+
};
|
|
53
|
+
const onActivity = () => {
|
|
54
|
+
setIsIdle(false);
|
|
55
|
+
startTimer();
|
|
56
|
+
};
|
|
57
|
+
restartRef.current = onActivity;
|
|
58
|
+
startTimer();
|
|
59
|
+
for (const name of eventNames) {
|
|
60
|
+
window.addEventListener(name, onActivity, { passive: true });
|
|
61
|
+
}
|
|
62
|
+
return () => {
|
|
63
|
+
window.clearTimeout(timeoutId);
|
|
64
|
+
for (const name of eventNames) {
|
|
65
|
+
window.removeEventListener(name, onActivity);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}, [eventsKey, timeoutMs]);
|
|
69
|
+
const reset = react.useCallback(() => {
|
|
70
|
+
restartRef.current();
|
|
71
|
+
}, []);
|
|
72
|
+
return { isIdle, reset };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
exports.useIdleTimeout = useIdleTimeout;
|
|
76
|
+
//# sourceMappingURL=useIdleTimeout.js.map
|
|
77
|
+
//# sourceMappingURL=useIdleTimeout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useIdleTimeout/useIdleTimeout.ts"],"names":["useState","useRef","useEffect","useCallback"],"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,GAAIA,eAAS,aAAa,CAAA;AAClD,EAAA,MAAM,SAAA,GAAYC,aAAO,MAAM,CAAA;AAC/B,EAAA,MAAM,WAAA,GAAcA,aAAO,QAAQ,CAAA;AACnC,EAAA,MAAM,UAAA,GAAaA,aAAmB,MAAM;AAAA,EAAC,CAAC,CAAA;AAC9C,EAAA,MAAM,WAAA,GAAcA,aAAO,IAAI,CAAA;AAE/B,EAAAC,eAAA,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,EAAAA,eAAA,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,EAAAA,eAAA,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,GAAQC,kBAAY,MAAM;AAC9B,IAAA,UAAA,CAAW,OAAA,EAAQ;AAAA,EACrB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,EAAE,QAAQ,KAAA,EAAM;AACzB","file":"useIdleTimeout.js","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"]}
|