@musecat/functionkit 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/.agents/references/components/ScrolltoTop.md +36 -0
  2. package/.agents/references/components/SwitchCase.md +47 -0
  3. package/.agents/references/components/ViewportPortal.md +34 -0
  4. package/.agents/references/cookie/cookie.md +47 -0
  5. package/.agents/references/datetime/dateTime.client.md +42 -0
  6. package/.agents/references/datetime/dateTime.server.md +42 -0
  7. package/.agents/references/datetime/dateTime.shared.md +86 -0
  8. package/.agents/references/hooks/useAvoidKeyboard.md +29 -0
  9. package/.agents/references/hooks/useCheckInvisible.md +36 -0
  10. package/.agents/references/hooks/useCheckScroll.md +36 -0
  11. package/.agents/references/hooks/useClientDateTime.md +39 -0
  12. package/.agents/references/hooks/useDebounce.md +46 -0
  13. package/.agents/references/hooks/useDebouncedCallback.md +34 -0
  14. package/.agents/references/hooks/useDoubleClick.md +40 -0
  15. package/.agents/references/hooks/useGeolocation.md +37 -0
  16. package/.agents/references/hooks/useHasMounted.md +27 -0
  17. package/.agents/references/hooks/useIntersectionObserver.md +41 -0
  18. package/.agents/references/hooks/useInterval.md +44 -0
  19. package/.agents/references/hooks/useKeyboardHeight.md +33 -0
  20. package/.agents/references/hooks/useKeyboardListNavigation.md +58 -0
  21. package/.agents/references/hooks/useLongPress.md +48 -0
  22. package/.agents/references/hooks/usePreservedCallback.md +37 -0
  23. package/.agents/references/hooks/usePreservedReference.md +34 -0
  24. package/.agents/references/hooks/useRefEffect.md +35 -0
  25. package/.agents/references/hooks/useRelativeDateTime.md +35 -0
  26. package/.agents/references/hooks/useTimeout.md +42 -0
  27. package/.agents/references/hooks/useToggleState.md +39 -0
  28. package/.agents/references/hooks/useViewportHeight.md +26 -0
  29. package/.agents/references/hooks/useViewportMatch.md +32 -0
  30. package/.agents/references/utils/browserStorage.md +41 -0
  31. package/.agents/references/utils/buildContext.md +47 -0
  32. package/.agents/references/utils/clipboardShare.md +59 -0
  33. package/.agents/references/utils/floatingMotion.md +67 -0
  34. package/.agents/references/utils/getDeviceInfo.md +43 -0
  35. package/.agents/references/utils/isEditableKeyboardTarget.md +35 -0
  36. package/.agents/references/utils/mergeRefs.md +29 -0
  37. package/.agents/references/utils/seen.md +37 -0
  38. package/.agents/references/utils/subscribeKeyboardHeight.md +35 -0
  39. package/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
  40. package/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
  41. package/.github/PULL_REQUEST_TEMPLATE.md +22 -0
  42. package/.github/workflows/ci.yml +20 -0
  43. package/.local/state/gh/device-id +1 -0
  44. package/.prettierrc +8 -0
  45. package/AGENTS.md +117 -396
  46. package/CODE_OF_CONDUCT.md +55 -0
  47. package/CONTRIBUTING.md +39 -0
  48. package/README.md +18 -5
  49. package/biome.json +25 -0
  50. package/index.ts +63 -68
  51. package/package.json +46 -13
  52. package/packages/components/ScrolltoTop.tsx +3 -3
  53. package/packages/components/SwitchCase.tsx +7 -7
  54. package/packages/components/ViewportPortal.tsx +22 -23
  55. package/packages/cookie/cookie.shared.ts +80 -110
  56. package/packages/cookie/cookieNames.shared.ts +9 -0
  57. package/packages/datetime/dateTime.client.ts +84 -86
  58. package/packages/datetime/dateTime.server.ts +84 -86
  59. package/packages/datetime/dateTime.shared.ts +249 -287
  60. package/packages/hooks/useAvoidKeyboard.ts +15 -15
  61. package/packages/hooks/useCheckInvisible.ts +16 -16
  62. package/packages/hooks/useCheckScroll.ts +10 -10
  63. package/packages/hooks/useClientDateTime.ts +24 -37
  64. package/packages/hooks/useDebounce.ts +90 -95
  65. package/packages/hooks/useDebouncedCallback.ts +45 -45
  66. package/packages/hooks/useDoubleClick.ts +43 -43
  67. package/packages/hooks/useGeolocation.ts +126 -130
  68. package/packages/hooks/useHasMounted.ts +5 -5
  69. package/packages/hooks/useIntersectionObserver.ts +20 -20
  70. package/packages/hooks/useInterval.ts +45 -45
  71. package/packages/hooks/useKeyboardHeight.ts +9 -9
  72. package/packages/hooks/useKeyboardListNavigation.ts +130 -158
  73. package/packages/hooks/useLongPress.ts +87 -98
  74. package/packages/hooks/usePreservedCallback.ts +12 -12
  75. package/packages/hooks/usePreservedReference.ts +10 -10
  76. package/packages/hooks/useRefEffect.ts +19 -19
  77. package/packages/hooks/useRelativeDateTime.ts +36 -39
  78. package/packages/hooks/useTimeout.ts +41 -41
  79. package/packages/hooks/useToggleState.ts +5 -5
  80. package/packages/hooks/useViewportHeight.ts +12 -13
  81. package/packages/hooks/useViewportMatch.ts +13 -23
  82. package/packages/utils/browserStorage.ts +26 -26
  83. package/packages/utils/buildContext.tsx +10 -11
  84. package/packages/utils/checkDevice.ts +64 -64
  85. package/packages/utils/clipboardShare.tsx +31 -31
  86. package/packages/utils/clipboardShare.types.ts +7 -7
  87. package/packages/utils/floatingMotion.ts +71 -77
  88. package/packages/utils/keyboardTarget.ts +9 -9
  89. package/packages/utils/mergeRefs.ts +9 -9
  90. package/packages/utils/seen.ts +15 -15
  91. package/packages/utils/subscribeKeyboardHeight.ts +49 -46
  92. package/tests/components/ScrolltoTop.test.tsx +15 -0
  93. package/tests/components/SwitchCase.test.tsx +33 -0
  94. package/tests/components/ViewportPortal.test.tsx +46 -0
  95. package/tests/cookie/cookie.test.ts +192 -0
  96. package/tests/datetime/datetime.test.ts +461 -0
  97. package/tests/hooks/useAvoidKeyboard.test.ts +43 -0
  98. package/tests/hooks/useCheckInvisible.test.ts +59 -0
  99. package/tests/hooks/useCheckScroll.test.ts +23 -0
  100. package/tests/hooks/useClientDateTime.test.ts +12 -0
  101. package/tests/hooks/useDebounce.test.ts +96 -0
  102. package/tests/hooks/useDebouncedCallback.test.ts +63 -0
  103. package/tests/hooks/useDoubleClick.test.ts +82 -0
  104. package/tests/hooks/useGeolocation.test.ts +201 -0
  105. package/tests/hooks/useHasMounted.test.ts +10 -0
  106. package/tests/hooks/useIntersectionObserver.test.ts +78 -0
  107. package/tests/hooks/useInterval.test.ts +56 -0
  108. package/tests/hooks/useKeyboardHeight.test.ts +10 -0
  109. package/tests/hooks/useKeyboardListNavigation.test.ts +404 -0
  110. package/tests/hooks/useLongPress.test.ts +214 -0
  111. package/tests/hooks/usePreservedCallback.test.ts +33 -0
  112. package/tests/hooks/usePreservedReference.test.ts +42 -0
  113. package/tests/hooks/useRefEffect.test.ts +67 -0
  114. package/tests/hooks/useRelativeDateTime.test.ts +21 -0
  115. package/tests/hooks/useTimeout.test.ts +72 -0
  116. package/tests/hooks/useToggleState.test.ts +43 -0
  117. package/tests/hooks/useViewportHeight.test.ts +32 -0
  118. package/tests/hooks/useViewportMatch.test.ts +35 -0
  119. package/tests/setup.ts +9 -0
  120. package/tests/utils/browserStorage.test.ts +114 -0
  121. package/tests/utils/buildContext.test.tsx +26 -0
  122. package/tests/utils/checkDevice.test.ts +140 -0
  123. package/tests/utils/clipboardShare.test.ts +152 -0
  124. package/tests/utils/floatingMotion.test.ts +135 -0
  125. package/tests/utils/keyboardTarget.test.ts +85 -0
  126. package/tests/utils/mergeRefs.test.ts +50 -0
  127. package/tests/utils/seen.test.ts +40 -0
  128. package/tests/utils/subscribeKeyboardHeight.test.ts +172 -0
  129. package/tsconfig.json +16 -16
  130. package/vitest.config.ts +22 -0
@@ -7,164 +7,136 @@ const FIRST_ITEM_KEYS = new Set(["ArrowDown", "ArrowRight", "Home"]);
7
7
  const LAST_ITEM_KEYS = new Set(["ArrowUp", "ArrowLeft", "End"]);
8
8
 
9
9
  type UseKeyboardListNavigationOptions = {
10
- itemCount: number;
10
+ itemCount: number;
11
11
  };
12
12
 
13
- export function useKeyboardListNavigation({
14
- itemCount,
15
- }: UseKeyboardListNavigationOptions) {
16
- const [activeIndex, setActiveIndex] = useState(-1);
17
- const itemRefs = useRef<(HTMLElement | null)[]>([]);
18
-
19
- const setItemRef = useCallback(
20
- (index: number, element: HTMLElement | null) => {
21
- itemRefs.current[index] = element;
22
- },
23
- [],
24
- );
25
-
26
- const focusItem = useCallback((index: number) => {
27
- const target = itemRefs.current[index];
28
- if (!target) {
29
- return false;
30
- }
31
-
32
- setActiveIndex(index);
33
- target.focus();
34
- target.scrollIntoView({
35
- block: "nearest",
36
- inline: "nearest",
37
- });
38
- return true;
39
- }, []);
40
-
41
- const focusBoundaryItem = useCallback(
42
- (key: string) => {
43
- if (itemCount === 0) {
44
- return false;
45
- }
46
-
47
- if (FIRST_ITEM_KEYS.has(key)) {
48
- return focusItem(0);
49
- }
50
-
51
- if (LAST_ITEM_KEYS.has(key)) {
52
- return focusItem(itemCount - 1);
53
- }
54
-
55
- return false;
56
- },
57
- [focusItem, itemCount],
58
- );
59
-
60
- const moveActiveItem = useCallback(
61
- (key: string) => {
62
- if (itemCount === 0) {
63
- return false;
64
- }
65
-
66
- if (key === "Home") {
67
- return focusItem(0);
68
- }
69
-
70
- if (key === "End") {
71
- return focusItem(itemCount - 1);
72
- }
73
-
74
- if (key === "ArrowDown" || key === "ArrowRight") {
75
- const nextIndex = activeIndex < 0 ? 0 : (activeIndex + 1) % itemCount;
76
- return focusItem(nextIndex);
77
- }
78
-
79
- if (key === "ArrowUp" || key === "ArrowLeft") {
80
- const nextIndex =
81
- activeIndex < 0
82
- ? itemCount - 1
83
- : (activeIndex - 1 + itemCount) % itemCount;
84
- return focusItem(nextIndex);
85
- }
86
-
87
- return false;
88
- },
89
- [activeIndex, focusItem, itemCount],
90
- );
91
-
92
- const clickActiveItem = useCallback(() => {
93
- if (activeIndex < 0) {
94
- return false;
95
- }
96
-
97
- itemRefs.current[activeIndex]?.click();
98
- return true;
99
- }, [activeIndex]);
100
-
101
- const handleListKeyDown = useCallback(
102
- (
103
- event: React.KeyboardEvent<HTMLElement>,
104
- options?: { container?: HTMLElement | null },
105
- ) => {
106
- if (itemCount === 0 || event.defaultPrevented) {
107
- return;
108
- }
109
-
110
- const target = event.target as HTMLElement | null;
111
-
112
- if (!target) {
113
- return;
114
- }
115
-
116
- if (isEditableKeyboardTarget(target)) {
117
- return;
118
- }
119
-
120
- if (options?.container && !options.container.contains(target)) {
121
- return;
122
- }
123
-
124
- if (event.key === "Escape") {
125
- setActiveIndex(-1);
126
- return;
127
- }
128
-
129
- if (
130
- event.key === "Enter" ||
131
- event.key === " " ||
132
- event.key === "ArrowDown" ||
133
- event.key === "ArrowUp" ||
134
- event.key === "ArrowLeft" ||
135
- event.key === "ArrowRight" ||
136
- event.key === "Home" ||
137
- event.key === "End"
138
- ) {
139
- event.preventDefault();
140
- }
141
-
142
- if (event.key === "Enter" || event.key === " ") {
143
- clickActiveItem();
144
- return;
145
- }
146
-
147
- if (moveActiveItem(event.key)) {
148
- return;
149
- }
150
-
151
- if (focusBoundaryItem(event.key)) {
152
- return;
153
- }
154
- },
155
- [clickActiveItem, moveActiveItem, focusBoundaryItem, itemCount],
156
- );
157
-
158
- useEffect(() => {
159
- itemRefs.current = itemRefs.current.slice(0, itemCount);
160
- }, [itemCount]);
161
-
162
- return {
163
- activeIndex,
164
- setActiveIndex,
165
- setItemRef,
166
- clickActiveItem,
167
- focusBoundaryItem,
168
- handleListKeyDown,
169
- };
13
+ export function useKeyboardListNavigation({ itemCount }: UseKeyboardListNavigationOptions) {
14
+ const [activeIndex, setActiveIndex] = useState(-1);
15
+ const itemRefs = useRef<(HTMLElement | null)[]>([]);
16
+
17
+ const setItemRef = useCallback((index: number, element: HTMLElement | null) => {
18
+ itemRefs.current[index] = element;
19
+ }, []);
20
+
21
+ const focusItem = useCallback((index: number) => {
22
+ const target = itemRefs.current[index];
23
+ if (!target) {
24
+ return false;
25
+ }
26
+
27
+ setActiveIndex(index);
28
+ target.focus();
29
+ target.scrollIntoView({
30
+ block: "nearest",
31
+ inline: "nearest",
32
+ });
33
+ return true;
34
+ }, []);
35
+
36
+ const focusBoundaryItem = useCallback(
37
+ (key: string) => {
38
+ if (itemCount === 0) {
39
+ return false;
40
+ }
41
+
42
+ if (FIRST_ITEM_KEYS.has(key)) {
43
+ return focusItem(0);
44
+ }
45
+
46
+ if (LAST_ITEM_KEYS.has(key)) {
47
+ return focusItem(itemCount - 1);
48
+ }
49
+
50
+ return false;
51
+ },
52
+ [focusItem, itemCount],
53
+ );
54
+
55
+ const moveActiveItem = useCallback(
56
+ (key: string) => {
57
+ if (key === "ArrowDown" || key === "ArrowRight") {
58
+ const nextIndex = activeIndex < 0 ? 0 : (activeIndex + 1) % itemCount;
59
+ return focusItem(nextIndex);
60
+ }
61
+
62
+ if (key === "ArrowUp" || key === "ArrowLeft") {
63
+ const nextIndex =
64
+ activeIndex < 0 ? itemCount - 1 : (activeIndex - 1 + itemCount) % itemCount;
65
+ return focusItem(nextIndex);
66
+ }
67
+
68
+ return focusBoundaryItem(key);
69
+ },
70
+ [activeIndex, focusItem, focusBoundaryItem, itemCount],
71
+ );
72
+
73
+ const clickActiveItem = useCallback(() => {
74
+ if (activeIndex < 0) {
75
+ return false;
76
+ }
77
+
78
+ itemRefs.current[activeIndex]?.click();
79
+ return true;
80
+ }, [activeIndex]);
81
+
82
+ const handleListKeyDown = useCallback(
83
+ (event: React.KeyboardEvent<HTMLElement>, options?: { container?: HTMLElement | null }) => {
84
+ if (itemCount === 0 || event.defaultPrevented) {
85
+ return;
86
+ }
87
+
88
+ const target = event.target as HTMLElement | null;
89
+
90
+ if (!target) {
91
+ return;
92
+ }
93
+
94
+ if (isEditableKeyboardTarget(target)) {
95
+ return;
96
+ }
97
+
98
+ if (options?.container && !options.container.contains(target)) {
99
+ return;
100
+ }
101
+
102
+ if (event.key === "Escape") {
103
+ setActiveIndex(-1);
104
+ return;
105
+ }
106
+
107
+ if (
108
+ event.key === "Enter" ||
109
+ event.key === " " ||
110
+ event.key === "ArrowDown" ||
111
+ event.key === "ArrowUp" ||
112
+ event.key === "ArrowLeft" ||
113
+ event.key === "ArrowRight" ||
114
+ event.key === "Home" ||
115
+ event.key === "End"
116
+ ) {
117
+ event.preventDefault();
118
+ }
119
+
120
+ if (event.key === "Enter" || event.key === " ") {
121
+ clickActiveItem();
122
+ return;
123
+ }
124
+
125
+ moveActiveItem(event.key);
126
+ },
127
+ [clickActiveItem, moveActiveItem, itemCount],
128
+ );
129
+
130
+ useEffect(() => {
131
+ itemRefs.current = itemRefs.current.slice(0, itemCount);
132
+ }, [itemCount]);
133
+
134
+ return {
135
+ activeIndex,
136
+ setActiveIndex,
137
+ setItemRef,
138
+ clickActiveItem,
139
+ focusBoundaryItem,
140
+ handleListKeyDown,
141
+ };
170
142
  }
@@ -3,118 +3,107 @@
3
3
  import { type MouseEvent, type TouchEvent, useCallback, useRef } from "react";
4
4
 
5
5
  export type UseLongPressOptions<E extends HTMLElement> = {
6
- delay?: number;
7
- moveThreshold?: {
8
- x?: number;
9
- y?: number;
10
- };
11
- onClick?: (event: MouseEvent<E> | TouchEvent<E>) => void;
12
- onLongPressEnd?: (event: MouseEvent<E> | TouchEvent<E>) => void;
6
+ delay?: number;
7
+ moveThreshold?: {
8
+ x?: number;
9
+ y?: number;
10
+ };
11
+ onClick?: (event: MouseEvent<E> | TouchEvent<E>) => void;
12
+ onLongPressEnd?: (event: MouseEvent<E> | TouchEvent<E>) => void;
13
13
  };
14
14
 
15
15
  export function useLongPress<E extends HTMLElement = HTMLElement>(
16
- onLongPress: (event: MouseEvent<E> | TouchEvent<E>) => void,
17
- {
18
- delay = 500,
19
- moveThreshold,
20
- onClick,
21
- onLongPressEnd,
22
- }: UseLongPressOptions<E> = {},
16
+ onLongPress: (event: MouseEvent<E> | TouchEvent<E>) => void,
17
+ { delay = 500, moveThreshold, onClick, onLongPressEnd }: UseLongPressOptions<E> = {},
23
18
  ) {
24
- const timeoutRef = useRef<number | null>(null);
25
- const isLongPressActiveRef = useRef(false);
26
- const initialPositionRef = useRef({ x: 0, y: 0 });
19
+ const timeoutRef = useRef<number | null>(null);
20
+ const isLongPressActiveRef = useRef(false);
21
+ const initialPositionRef = useRef({ x: 0, y: 0 });
27
22
 
28
- const savedOnLongPress = useRef(onLongPress);
29
- const savedOnClick = useRef(onClick);
30
- const savedOnLongPressEnd = useRef(onLongPressEnd);
31
- savedOnLongPress.current = onLongPress;
32
- savedOnClick.current = onClick;
33
- savedOnLongPressEnd.current = onLongPressEnd;
23
+ const savedOnLongPress = useRef(onLongPress);
24
+ const savedOnClick = useRef(onClick);
25
+ const savedOnLongPressEnd = useRef(onLongPressEnd);
26
+ savedOnLongPress.current = onLongPress;
27
+ savedOnClick.current = onClick;
28
+ savedOnLongPressEnd.current = onLongPressEnd;
34
29
 
35
- const hasThreshold =
36
- moveThreshold?.x !== undefined || moveThreshold?.y !== undefined;
30
+ const hasThreshold = moveThreshold?.x !== undefined || moveThreshold?.y !== undefined;
37
31
 
38
- const getClientPosition = useCallback(
39
- (event: MouseEvent<E> | TouchEvent<E>) => {
40
- if ("touches" in event.nativeEvent) {
41
- const touch = event.nativeEvent.touches[0];
42
- return { x: touch.clientX, y: touch.clientY };
43
- }
44
- return {
45
- x: event.nativeEvent.clientX,
46
- y: event.nativeEvent.clientY,
47
- };
48
- },
49
- [],
50
- );
32
+ const getClientPosition = useCallback((event: MouseEvent<E> | TouchEvent<E>) => {
33
+ if ("touches" in event.nativeEvent) {
34
+ const touch = event.nativeEvent.touches[0];
35
+ return { x: touch.clientX, y: touch.clientY };
36
+ }
37
+ return {
38
+ x: event.nativeEvent.clientX,
39
+ y: event.nativeEvent.clientY,
40
+ };
41
+ }, []);
51
42
 
52
- const isMovedBeyondThreshold = useCallback(
53
- (event: MouseEvent<E> | TouchEvent<E>) => {
54
- const { x, y } = getClientPosition(event);
55
- const deltaX = Math.abs(x - initialPositionRef.current.x);
56
- const deltaY = Math.abs(y - initialPositionRef.current.y);
43
+ const isMovedBeyondThreshold = useCallback(
44
+ (event: MouseEvent<E> | TouchEvent<E>) => {
45
+ const { x, y } = getClientPosition(event);
46
+ const deltaX = Math.abs(x - initialPositionRef.current.x);
47
+ const deltaY = Math.abs(y - initialPositionRef.current.y);
57
48
 
58
- return (
59
- (moveThreshold?.x !== undefined && deltaX > moveThreshold.x) ||
60
- (moveThreshold?.y !== undefined && deltaY > moveThreshold.y)
61
- );
62
- },
63
- [getClientPosition, moveThreshold],
64
- );
49
+ return (
50
+ (moveThreshold?.x !== undefined && deltaX > moveThreshold.x) ||
51
+ (moveThreshold?.y !== undefined && deltaY > moveThreshold.y)
52
+ );
53
+ },
54
+ [getClientPosition, moveThreshold],
55
+ );
65
56
 
66
- const cancelLongPress = useCallback(() => {
67
- if (timeoutRef.current !== null) {
68
- window.clearTimeout(timeoutRef.current);
69
- timeoutRef.current = null;
70
- }
71
- }, []);
57
+ const cancelLongPress = useCallback(() => {
58
+ if (timeoutRef.current !== null) {
59
+ window.clearTimeout(timeoutRef.current);
60
+ timeoutRef.current = null;
61
+ }
62
+ }, []);
72
63
 
73
- const handlePressStart = useCallback(
74
- (event: MouseEvent<E> | TouchEvent<E>) => {
75
- cancelLongPress();
76
- initialPositionRef.current = getClientPosition(event);
77
- isLongPressActiveRef.current = false;
64
+ const handlePressStart = useCallback(
65
+ (event: MouseEvent<E> | TouchEvent<E>) => {
66
+ cancelLongPress();
67
+ initialPositionRef.current = getClientPosition(event);
68
+ isLongPressActiveRef.current = false;
78
69
 
79
- timeoutRef.current = window.setTimeout(() => {
80
- isLongPressActiveRef.current = true;
81
- savedOnLongPress.current(event);
82
- }, delay);
83
- },
84
- [cancelLongPress, delay, getClientPosition],
85
- );
70
+ timeoutRef.current = window.setTimeout(() => {
71
+ isLongPressActiveRef.current = true;
72
+ savedOnLongPress.current(event);
73
+ }, delay);
74
+ },
75
+ [cancelLongPress, delay, getClientPosition],
76
+ );
86
77
 
87
- const handlePressEnd = useCallback(
88
- (event: MouseEvent<E> | TouchEvent<E>) => {
89
- if (isLongPressActiveRef.current) {
90
- savedOnLongPressEnd.current?.(event);
91
- } else if (timeoutRef.current !== null) {
92
- savedOnClick.current?.(event);
93
- }
78
+ const handlePressEnd = useCallback(
79
+ (event: MouseEvent<E> | TouchEvent<E>) => {
80
+ if (isLongPressActiveRef.current) {
81
+ savedOnLongPressEnd.current?.(event);
82
+ } else if (timeoutRef.current !== null) {
83
+ savedOnClick.current?.(event);
84
+ }
94
85
 
95
- cancelLongPress();
96
- isLongPressActiveRef.current = false;
97
- },
98
- [cancelLongPress],
99
- );
86
+ cancelLongPress();
87
+ isLongPressActiveRef.current = false;
88
+ },
89
+ [cancelLongPress],
90
+ );
100
91
 
101
- const handlePressMove = useCallback(
102
- (event: MouseEvent<E> | TouchEvent<E>) => {
103
- if (timeoutRef.current !== null && isMovedBeyondThreshold(event)) {
104
- cancelLongPress();
105
- }
106
- },
107
- [cancelLongPress, isMovedBeyondThreshold],
108
- );
92
+ const handlePressMove = useCallback(
93
+ (event: MouseEvent<E> | TouchEvent<E>) => {
94
+ if (timeoutRef.current !== null && isMovedBeyondThreshold(event)) {
95
+ cancelLongPress();
96
+ }
97
+ },
98
+ [cancelLongPress, isMovedBeyondThreshold],
99
+ );
109
100
 
110
- return {
111
- onMouseDown: handlePressStart,
112
- onMouseUp: handlePressEnd,
113
- onMouseLeave: cancelLongPress,
114
- onTouchStart: handlePressStart,
115
- onTouchEnd: handlePressEnd,
116
- ...(hasThreshold
117
- ? { onTouchMove: handlePressMove, onMouseMove: handlePressMove }
118
- : {}),
119
- };
101
+ return {
102
+ onMouseDown: handlePressStart,
103
+ onMouseUp: handlePressEnd,
104
+ onMouseLeave: cancelLongPress,
105
+ onTouchStart: handlePressStart,
106
+ onTouchEnd: handlePressEnd,
107
+ ...(hasThreshold ? { onTouchMove: handlePressMove, onMouseMove: handlePressMove } : {}),
108
+ };
120
109
  }
@@ -3,19 +3,19 @@
3
3
  import { useCallback, useEffect, useRef } from "react";
4
4
 
5
5
  export function usePreservedCallback<
6
- Arguments extends unknown[] = unknown[],
7
- ReturnValue = unknown,
6
+ Arguments extends unknown[] = unknown[],
7
+ ReturnValue = unknown,
8
8
  >(callback: (...args: Arguments) => ReturnValue) {
9
- const callbackRef = useRef(callback);
9
+ const callbackRef = useRef(callback);
10
10
 
11
- useEffect(
12
- function syncCallbackRef() {
13
- callbackRef.current = callback;
14
- },
15
- [callback],
16
- );
11
+ useEffect(
12
+ function syncCallbackRef() {
13
+ callbackRef.current = callback;
14
+ },
15
+ [callback],
16
+ );
17
17
 
18
- return useCallback((...args: Arguments) => {
19
- return callbackRef.current(...args);
20
- }, []);
18
+ return useCallback((...args: Arguments) => {
19
+ return callbackRef.current(...args);
20
+ }, []);
21
21
  }
@@ -3,19 +3,19 @@
3
3
  import { useMemo, useRef } from "react";
4
4
 
5
5
  function areDeeplyEqual<T>(a: T, b: T): boolean {
6
- return JSON.stringify(a) === JSON.stringify(b);
6
+ return JSON.stringify(a) === JSON.stringify(b);
7
7
  }
8
8
 
9
9
  export function usePreservedReference<T>(
10
- value: T,
11
- areValuesEqual: (a: T, b: T) => boolean = areDeeplyEqual,
10
+ value: T,
11
+ areValuesEqual: (a: T, b: T) => boolean = areDeeplyEqual,
12
12
  ): T {
13
- const ref = useRef(value);
13
+ const ref = useRef(value);
14
14
 
15
- return useMemo(() => {
16
- if (!areValuesEqual(ref.current, value)) {
17
- ref.current = value;
18
- }
19
- return ref.current;
20
- }, [areValuesEqual, value]);
15
+ return useMemo(() => {
16
+ if (!areValuesEqual(ref.current, value)) {
17
+ ref.current = value;
18
+ }
19
+ return ref.current;
20
+ }, [areValuesEqual, value]);
21
21
  }
@@ -7,29 +7,29 @@ import { usePreservedCallback } from "./usePreservedCallback";
7
7
  export type CleanupCallback = () => void;
8
8
 
9
9
  export function useRefEffect<RefElement extends HTMLElement = HTMLElement>(
10
- callback: (element: RefElement) => CleanupCallback | undefined,
11
- deps: DependencyList,
10
+ callback: (element: RefElement) => CleanupCallback | undefined,
11
+ deps: DependencyList,
12
12
  ): (element: RefElement | null) => void {
13
- const preservedCallback = usePreservedCallback(callback);
14
- const cleanupCallbackRef = useRef<CleanupCallback>(() => {});
13
+ const preservedCallback = usePreservedCallback(callback);
14
+ const cleanupCallbackRef = useRef<CleanupCallback>(() => {});
15
15
 
16
- const effect = useCallback(
17
- (element: RefElement | null) => {
18
- cleanupCallbackRef.current();
19
- cleanupCallbackRef.current = () => {};
16
+ const effect = useCallback(
17
+ (element: RefElement | null) => {
18
+ cleanupCallbackRef.current();
19
+ cleanupCallbackRef.current = () => {};
20
20
 
21
- if (element == null) {
22
- return;
23
- }
21
+ if (element == null) {
22
+ return;
23
+ }
24
24
 
25
- const cleanup = preservedCallback(element);
25
+ const cleanup = preservedCallback(element);
26
26
 
27
- if (typeof cleanup === "function") {
28
- cleanupCallbackRef.current = cleanup;
29
- }
30
- },
31
- [preservedCallback, ...deps],
32
- );
27
+ if (typeof cleanup === "function") {
28
+ cleanupCallbackRef.current = cleanup;
29
+ }
30
+ },
31
+ [preservedCallback, ...deps],
32
+ );
33
33
 
34
- return effect;
34
+ return effect;
35
35
  }