@ringcentral/spring-base 1.7.0 → 1.9.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 (75) hide show
  1. package/chunks/twMerge.js +0 -133
  2. package/config.js +0 -24
  3. package/contexts.js +0 -1
  4. package/hooks.js +61 -642
  5. package/index.js +3 -2
  6. package/package.json +1 -1
  7. package/src/config.d.ts +1 -26
  8. package/src/contexts/ResponsiveContext/breakpointList.d.ts +0 -1
  9. package/src/contexts/ResponsiveContext/types.d.ts +0 -11
  10. package/src/hooks/index.d.ts +2 -0
  11. package/src/hooks/useA11yKeyEvent/useA11yKeyEvent.d.ts +0 -17
  12. package/src/hooks/useActive/useActive.d.ts +4 -10
  13. package/src/hooks/useAnnouncer/useAnnouncer.d.ts +0 -10
  14. package/src/hooks/useAriaHideOutside/useAriaHideOutside.d.ts +0 -4
  15. package/src/hooks/useAudio/useAudio.d.ts +0 -20
  16. package/src/hooks/useBodyScrollLock/useBodyScrollLock.d.ts +0 -19
  17. package/src/hooks/useButton/index.d.ts +1 -0
  18. package/src/hooks/useButton/useButton.d.ts +2 -0
  19. package/src/hooks/useChange/useChange.d.ts +0 -10
  20. package/src/hooks/useClickAway/useClickAway.d.ts +0 -10
  21. package/src/hooks/useDebounce/useDebounce.d.ts +0 -6
  22. package/src/hooks/useEventListener/useEventListener.d.ts +0 -20
  23. package/src/hooks/useEver/useEver.d.ts +0 -12
  24. package/src/hooks/useFocusVisible/useFocusVisible.d.ts +0 -6
  25. package/src/hooks/useFontLoaded/useFontLoaded.d.ts +0 -12
  26. package/src/hooks/useForceUpdate/useForceUpdate.d.ts +0 -4
  27. package/src/hooks/useForkRef/useForkRef.d.ts +0 -17
  28. package/src/hooks/useGlobalListener/createGlobalListener.d.ts +0 -37
  29. package/src/hooks/useGlobalListener/useGlobalListener.d.ts +0 -5
  30. package/src/hooks/useHiddenTabindex/useHiddenTabindex.d.ts +0 -4
  31. package/src/hooks/useHover/useHover.d.ts +0 -3
  32. package/src/hooks/useId/useId.d.ts +0 -14
  33. package/src/hooks/useImgLoaded/useImgLoaded.d.ts +0 -14
  34. package/src/hooks/useInterval/useInterval.d.ts +0 -37
  35. package/src/hooks/useKeyDownOnce/useKeyDownOnce.d.ts +0 -6
  36. package/src/hooks/useKeyboardMoveFocus/moveIndexInTwoDimension.d.ts +0 -11
  37. package/src/hooks/useKeyboardMoveFocus/useKeyboardMoveFocus.d.ts +0 -33
  38. package/src/hooks/useKeyboardMoveFocus/useOnlyOneFocusable.d.ts +0 -6
  39. package/src/hooks/useLongPress/useLongPress.d.ts +0 -16
  40. package/src/hooks/useMountState/useMountState.d.ts +0 -14
  41. package/src/hooks/useOnLayoutReRender/useOnLayoutReRender.d.ts +0 -3
  42. package/src/hooks/useOnReRender/useOnReRender.d.ts +0 -3
  43. package/src/hooks/useOverflow/useOverflow.d.ts +0 -3
  44. package/src/hooks/usePrevious/usePrevious.d.ts +0 -6
  45. package/src/hooks/useRefState/useRefState.d.ts +0 -19
  46. package/src/hooks/useResizeObserver/useResizeObserver.d.ts +0 -15
  47. package/src/hooks/useResponsiveMatch/useResponsiveMatch.d.ts +0 -4
  48. package/src/hooks/useResultRef/useResultRef.d.ts +0 -8
  49. package/src/hooks/useRetry/useRetry.d.ts +2 -25
  50. package/src/hooks/useSleep/useSleep.d.ts +0 -17
  51. package/src/hooks/useTabOut/useTabOut.d.ts +0 -3
  52. package/src/hooks/useTestRunnerDetection.d.ts +0 -6
  53. package/src/hooks/useThrottle/useThrottle.d.ts +0 -6
  54. package/src/hooks/useTouchMouseEvent/useTouchMouseEvent.d.ts +2 -41
  55. package/src/hooks/useVirtuosoExternalWindow/index.d.ts +1 -0
  56. package/src/hooks/useVirtuosoExternalWindow/useVirtuosoExternalWindow.d.ts +5 -0
  57. package/src/typings/BaseColor.d.ts +0 -1
  58. package/src/typings/ComponentTypes.d.ts +4 -30
  59. package/src/typings/OverridableTypes.d.ts +0 -29
  60. package/src/typings/UnitMap.d.ts +0 -15
  61. package/src/utils/ariaHideOutside.d.ts +0 -8
  62. package/src/utils/checkDefaultPrevented.d.ts +0 -1
  63. package/src/utils/classes/useClasses.d.ts +0 -29
  64. package/src/utils/combineProps.d.ts +0 -88
  65. package/src/utils/getRefElement.d.ts +0 -3
  66. package/src/utils/isWebKit154.d.ts +0 -8
  67. package/src/utils/omit.d.ts +0 -1
  68. package/src/utils/outerSize.d.ts +0 -4
  69. package/src/utils/pick.d.ts +0 -1
  70. package/src/utils/preloadImg.d.ts +0 -1
  71. package/src/utils/removeClassName.d.ts +0 -28
  72. package/src/utils/runKeyHandler.d.ts +0 -8
  73. package/src/utils/selectionHandler.d.ts +0 -8
  74. package/src/utils/twMerge.d.ts +0 -3
  75. package/utils.js +0 -99
package/hooks.js CHANGED
@@ -2,31 +2,25 @@ import { unstable_useEventCallback, unstable_ownerDocument, unstable_Timeout, un
2
2
  export { unstable_useControlled as useControlled } from '@mui/utils';
3
3
  import * as React from 'react';
4
4
  import { useState, useEffect, useRef, useCallback, useLayoutEffect, useMemo } from 'react';
5
- import uniqueId from 'lodash/uniqueId';
6
5
  import { useWindowContext, useResponsiveContext, getMatchDetail } from './contexts.js';
7
- import { a as ariaHideOutside, g as getRefElement, l as logInDev, c as combineProps, o as ownerWindow } from './chunks/twMerge.js';
8
- import 'lodash/isPlainObject';
6
+ import uniqueId from 'lodash/uniqueId';
7
+ import { useButton as useButton$1 } from '@mui/base/useButton';
8
+ import { c as combineProps, a as ariaHideOutside, g as getRefElement, l as logInDev, o as ownerWindow } from './chunks/twMerge.js';
9
9
  import './config.js';
10
10
  import 'lodash/omit';
11
11
  import 'lodash/pick';
12
+ import 'lodash/isPlainObject';
12
13
  import debounce from 'lodash/debounce';
13
14
  import noop from 'lodash/noop';
14
15
  import throttle from 'lodash/throttle';
15
16
  import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';
16
17
  import 'tailwind-merge';
17
18
 
18
- // this file template replace material-ui/core/utils method
19
- // when upgrade mui that will not need assign type anymore
20
19
  const useEventCallback = (func) => {
21
20
  return unstable_useEventCallback(func);
22
21
  };
23
22
 
24
23
  const defaultCheckKeys = ['Enter', ' '];
25
- /**
26
- * bind event with a11y keyboard, `space`, `enter`, only trigger when key includes in `checkKeys`
27
- *
28
- * that will auto preventDefault by default, if you don't want that, set `false` with `preventDefault`
29
- */
30
24
  const useA11yKeyEvent = (fn, { onlyOnFocus, preventDefault = true, checkKeys = defaultCheckKeys, } = {}) => {
31
25
  return useEventCallback((event) => {
32
26
  if (!fn || (onlyOnFocus && event.target !== event.currentTarget))
@@ -39,7 +33,6 @@ const useA11yKeyEvent = (fn, { onlyOnFocus, preventDefault = true, checkKeys = d
39
33
  });
40
34
  };
41
35
 
42
- // base https://github.com/mui/material-ui/blob/master/packages/mui-base/src/useButton/useButton.ts
43
36
  const isNativeButton = (el) => {
44
37
  const tag = el.tagName;
45
38
  return (tag === 'BUTTON' ||
@@ -47,23 +40,29 @@ const isNativeButton = (el) => {
47
40
  ['button', 'submit', 'reset'].includes(el?.type)) ||
48
41
  (tag === 'A' && el?.href));
49
42
  };
50
- /**
51
- * bind event with a11y keyboard, `space`. help not native button to implement `:active`
52
- *
53
- * that will auto preventDefault by default, if you don't want that, set `false` with `preventDefault`
54
- */
55
- const useActive = ({ onKeyDown, onKeyUp, onClick, disabled = false, } = {}) => {
43
+ const useActive = ({ onKeyDown, onKeyUp, onMouseDown, onClick, disabled = false, } = {}) => {
56
44
  const [active, setActive] = useState(false);
45
+ const { document: targetDocument = document } = useWindowContext();
57
46
  useEffect(() => {
58
47
  if (disabled)
59
48
  setActive(false);
60
49
  }, [disabled]);
61
50
  if (disabled) {
62
51
  return {
63
- activeHandlers: { onKeyDown, onKeyUp, onClick },
52
+ activeHandlers: { onKeyDown, onKeyUp, onMouseDown, onClick },
64
53
  active: false,
65
54
  };
66
55
  }
56
+ const handleMouseDown = (event) => {
57
+ onMouseDown?.(event);
58
+ if (event.defaultPrevented) {
59
+ return;
60
+ }
61
+ setActive(true);
62
+ targetDocument.addEventListener('mouseup', () => {
63
+ setActive(false);
64
+ }, { once: true });
65
+ };
67
66
  const handleKeyDown = (event) => {
68
67
  onKeyDown?.(event);
69
68
  if (event.defaultPrevented) {
@@ -77,7 +76,6 @@ const useActive = ({ onKeyDown, onKeyUp, onClick, disabled = false, } = {}) => {
77
76
  if (event.target === event.currentTarget && event.key === ' ') {
78
77
  setActive(true);
79
78
  }
80
- // Keyboard accessibility for non interactive elements
81
79
  if (event.target === event.currentTarget &&
82
80
  !isNativeButton(event.target) &&
83
81
  event.key === 'Enter') {
@@ -86,13 +84,10 @@ const useActive = ({ onKeyDown, onKeyUp, onClick, disabled = false, } = {}) => {
86
84
  }
87
85
  };
88
86
  const handleKeyUp = (event) => {
89
- // calling preventDefault in keyUp on a <button> will not dispatch a click event if Space is pressed
90
- // https://codesandbox.io/s/button-keyup-preventdefault-dn7f0
91
87
  if (event.target === event.currentTarget) {
92
88
  setActive(false);
93
89
  }
94
90
  onKeyUp?.(event);
95
- // Keyboard accessibility for non interactive elements
96
91
  if (event.target === event.currentTarget &&
97
92
  !isNativeButton(event.target) &&
98
93
  event.key === ' ' &&
@@ -101,19 +96,16 @@ const useActive = ({ onKeyDown, onKeyUp, onClick, disabled = false, } = {}) => {
101
96
  }
102
97
  };
103
98
  return {
104
- activeHandlers: { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp, onClick },
99
+ activeHandlers: {
100
+ onKeyDown: handleKeyDown,
101
+ onKeyUp: handleKeyUp,
102
+ onMouseDown: handleMouseDown,
103
+ onClick,
104
+ },
105
105
  active,
106
106
  };
107
107
  };
108
108
 
109
- /**
110
- * provide a ref for only calculate `cb` once
111
- * prevent `useRef(getValue())`, that `getValue` calculate every re-render
112
- *
113
- * @param cb that get method of get this ref init value, only calculate once
114
- * @returns same as `useRef` result
115
- *
116
- */
117
109
  function useResultRef(cb) {
118
110
  const renderRef = useRef(0);
119
111
  const ref = useRef();
@@ -125,20 +117,6 @@ function useResultRef(cb) {
125
117
  }
126
118
 
127
119
  const DEFAULT_PREFIX = 'spring-id-';
128
- /**
129
- * Generates a unique ID.
130
- *
131
- * @param id - Default component id or prefix id.
132
- * @param prefixMode Whether enable prefix the ID with.
133
- * @returns unique ID
134
- * @example
135
- * useId() => 'spring-id-xxxx'
136
- * useId('') => 'spring-id-xxxx'
137
- * useId(null) => 'spring-id-xxxx'
138
- * useId(undefined) => 'spring-id-xxxx'
139
- * useId('nameId') => 'nameId'
140
- * useId('nameId', true) => 'nameId-xxxx'
141
- */
142
120
  function useId(id, prefixMode) {
143
121
  const { current: resultId } = useResultRef(() => {
144
122
  if (id) {
@@ -161,16 +139,6 @@ const visuallyHiddenStyles = `
161
139
  white-space: nowrap;
162
140
  width: 1px;
163
141
  `;
164
- /**
165
- * read announce when need read message.
166
- *
167
- * @example
168
- * ```ts
169
- * const announce = useAnnouncer('simpleId');
170
- *
171
- * announce(`You click "Announce" button`);
172
- * ```
173
- */
174
142
  const useAnnouncer = (id) => {
175
143
  const _id = useId(id ? `rc-announcer-${id}` : 'rc-announcer', !id);
176
144
  const ref = useRef(null);
@@ -195,7 +163,6 @@ const useAnnouncer = (id) => {
195
163
  });
196
164
  };
197
165
  }, [_id]);
198
- /** announce method, call with message that want let reader speak */
199
166
  return useCallback((message) => {
200
167
  const el = ref.current;
201
168
  if (el && message) {
@@ -205,6 +172,23 @@ const useAnnouncer = (id) => {
205
172
  }, []);
206
173
  };
207
174
 
175
+ function useButton(parameters) {
176
+ const muiButtonResult = useButton$1(parameters);
177
+ const { active: customActive, activeHandlers } = useActive({
178
+ disabled: parameters.disabled,
179
+ });
180
+ const originalGetRootProps = muiButtonResult.getRootProps;
181
+ const getRootProps = (externalProps) => {
182
+ const mergedProps = combineProps(externalProps, activeHandlers);
183
+ return originalGetRootProps(mergedProps);
184
+ };
185
+ return {
186
+ ...muiButtonResult,
187
+ active: customActive,
188
+ getRootProps,
189
+ };
190
+ }
191
+
208
192
  const useAriaHideOutside = (props = {}) => {
209
193
  const { enable = true } = props;
210
194
  const { externalWindow, document } = useWindowContext();
@@ -217,34 +201,10 @@ const useAriaHideOutside = (props = {}) => {
217
201
  }, [enable, externalWindow, document?.body]);
218
202
  return {
219
203
  ariaHideOutsideRef: ariaHideOutsideRef,
220
- /**
221
- * Whether the `aria-hidden` attribute is being applied to outside elements.
222
- * When true, elements outside the ref are hidden from screen readers.
223
- */
224
204
  ariaHideEnabled: enable,
225
205
  };
226
206
  };
227
207
 
228
- /**
229
- * get Browser `Audio` instance, and auto destroy when component be destroyed.
230
- *
231
- *
232
- * @param init a callback when you init that audio
233
- *
234
- * @example
235
- *
236
- * ```ts
237
- * const audio = useAudio((instance) => {
238
- * instance.src = 'https://your.audio'
239
- * });
240
- *
241
- * const click = () => audio.play();
242
- *
243
- * click();
244
- * ```
245
- * @see stories {@link https://develop-rcui-int.fiji.gliprc.com/?path=/story/%F0%9F%94%A7-foundation-hooks-useaudio--simple-announcer 🔧-foundation-hooks-useaudio--simple-announcer}
246
- * @see {@link https://chromium-review.googlesource.com/c/chromium/src/+/2816118}
247
- */
248
208
  const useAudio = (init) => {
249
209
  const audioRef = useResultRef(() => {
250
210
  const audio = new Audio();
@@ -254,20 +214,12 @@ const useAudio = (init) => {
254
214
  useEffect(() => {
255
215
  return () => {
256
216
  audioRef.current.srcObject = null;
257
- // just for remove ref, confirm browser GC work correctly
258
217
  audioRef.current = null;
259
218
  };
260
- // eslint-disable-next-line react-hooks/exhaustive-deps
261
219
  }, []);
262
220
  return audioRef.current;
263
221
  };
264
222
 
265
- /**
266
- * get previous render value
267
- * @param cb that get method of target value
268
- * @param isDefault if set `true`, the first time will get value from `cb`
269
- * @returns previous value get from `cb`
270
- */
271
223
  function usePrevious(cb, isDefault) {
272
224
  const ref = useRef(isDefault ? cb() : undefined);
273
225
  useEffect(() => {
@@ -276,11 +228,6 @@ function usePrevious(cb, isDefault) {
276
228
  return ref.current;
277
229
  }
278
230
 
279
- /**
280
- * get value when prev listen value change
281
- * @param cb that method you want calculate to get value
282
- * @param listenCb prev compare listener value
283
- */
284
231
  const useChange = (cb, listenCb) => {
285
232
  const countRef = useRef(0);
286
233
  const valueRef = useRef();
@@ -292,11 +239,6 @@ const useChange = (cb, listenCb) => {
292
239
  }
293
240
  return valueRef.current;
294
241
  };
295
- /**
296
- * get value when prev deps one of array value change
297
- * @param cb that method you want calculate to get value
298
- * @param deps prev compare value array
299
- */
300
242
  const useDepsChange = (cb, deps) => {
301
243
  const countRef = useRef(0);
302
244
  const valueRef = useRef();
@@ -310,24 +252,16 @@ const useDepsChange = (cb, deps) => {
310
252
  return valueRef.current;
311
253
  };
312
254
 
313
- // base https://github.com/mui/material-ui/blob/master/packages/mui-base/src/ClickAwayListener/ClickAwayListener.tsx
314
255
  function clickedRootScrollbar(event, doc) {
315
256
  return (doc.documentElement.clientWidth < event.clientX ||
316
257
  doc.documentElement.clientHeight < event.clientY);
317
258
  }
318
- /**
319
- * @deprecated Use `ClickAwayListener` from `@mui/base` instead.
320
- *
321
- * @param onClickAway Callback fired when a "click away" event is detected.
322
- */
323
259
  const useClickAway = (onClickAway, props = {}) => {
324
260
  const { disableReactTree = false, isEnabled = true } = props;
325
261
  const movedRef = useRef(false);
326
262
  const activatedRef = useRef(false);
327
263
  const syntheticEventRef = useRef(false);
328
264
  const [target, setTarget] = useState();
329
- // This hook is usually triggered after target appears
330
- // so we need to trigger re-render component in ref callback to make sure useEffect can get target correctly
331
265
  const clickAwayRef = useEventCallback((e) => {
332
266
  setTarget(e);
333
267
  });
@@ -336,8 +270,6 @@ const useClickAway = (onClickAway, props = {}) => {
336
270
  if (!isEnabled)
337
271
  return;
338
272
  if (shouldActive) {
339
- // Ensure that this component is not "activated" synchronously.
340
- // https://github.com/facebook/react/issues/20074
341
273
  const timer = setTimeout(() => {
342
274
  activatedRef.current = true;
343
275
  }, 0);
@@ -348,50 +280,32 @@ const useClickAway = (onClickAway, props = {}) => {
348
280
  }
349
281
  return undefined;
350
282
  }, [isEnabled, shouldActive]);
351
- // The handler doesn't take event.defaultPrevented into account:
352
- //
353
- // event.preventDefault() is meant to stop default behaviors like
354
- // clicking a checkbox to check it, hitting a button to submit a form,
355
- // and hitting left arrow to move the cursor in a text input etc.
356
- // Only special HTML elements have these default behaviors.
357
283
  const handleClickAway = useEventCallback((event) => {
358
- // Given developers can stop the propagation of the synthetic event,
359
- // we can only be confident with a positive value.
360
284
  const insideReactTree = syntheticEventRef.current;
361
285
  syntheticEventRef.current = false;
362
286
  const doc = unstable_ownerDocument(target);
363
- // 1. IE11 support, which trigger the handleClickAway even after the unbind
364
- // 2. The child might render null.
365
- // 3. Behave like a blur listener.
366
287
  if (!activatedRef.current ||
367
288
  !target ||
368
289
  ('clientX' in event && clickedRootScrollbar(event, doc))) {
369
290
  return;
370
291
  }
371
- // Do not act if user performed touchmove
372
292
  if (movedRef.current) {
373
293
  movedRef.current = false;
374
294
  return;
375
295
  }
376
296
  let insideDOM;
377
- // If not enough, can use https://github.com/DieterHolvoet/event-propagation-path/blob/master/propagationPath.js
378
297
  if (event.composedPath) {
379
298
  insideDOM = event.composedPath().indexOf(target) > -1;
380
299
  }
381
300
  else {
382
301
  insideDOM =
383
- !doc.documentElement.contains(
384
- // @ts-expect-error returns `false` as intended when not dispatched from a Node
385
- event.target) ||
386
- target.contains(
387
- // @ts-expect-error returns `false` as intended when not dispatched from a Node
388
- event.target);
302
+ !doc.documentElement.contains(event.target) ||
303
+ target.contains(event.target);
389
304
  }
390
305
  if (!insideDOM && (disableReactTree || !insideReactTree)) {
391
306
  onClickAway(event);
392
307
  }
393
308
  });
394
- // Keep track of mouse/touch events that bubbled up through the portal.
395
309
  const handleSynthetic = (e) => {
396
310
  syntheticEventRef.current = true;
397
311
  };
@@ -423,37 +337,13 @@ const useClickAway = (onClickAway, props = {}) => {
423
337
  };
424
338
  };
425
339
 
426
- /**
427
- * provide a debounce method for debounce method
428
- * @param fn method for debounce
429
- * @param debounceTime debounce timeout value, default is `200`ms
430
- * @param options debounce setting, default is `{trailing: true, leading: flase}`, detail see lodash
431
- */
432
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
433
340
  const useDebounce = (fn, debounceTime = 200, options) => {
434
341
  const memoFn = useEventCallback(fn);
435
- // eslint-disable-next-line react-hooks/exhaustive-deps
436
342
  const debounceFn = useCallback(debounce(memoFn, debounceTime, options), []);
437
- // eslint-disable-next-line react-hooks/exhaustive-deps
438
343
  useEffect(() => () => debounceFn.cancel(), []);
439
344
  return debounceFn;
440
345
  };
441
346
 
442
- /**
443
- * bind event when component mount and auto remove event when destroy.
444
- * can remove listener by call returned function or pass empty 'target'.
445
- *
446
- * @param target
447
- * listener will be removed when target is changed to null,
448
- * or rebinding to new element when `ref.current` change
449
- * @returns a method object
450
- * - listen() listen listener again, if that have listener, that will not listen again
451
- * - remove() remove listener
452
- * @example
453
- * ```ts
454
- * useEventListener(window, 'keyup', () => console.log('window key up'))
455
- * ```
456
- */
457
347
  function useEventListener(target, key, callback, options) {
458
348
  const { startImmediately = true, ...addEventListenerOptions } = options ?? {};
459
349
  const listener = useEventCallback(callback);
@@ -472,7 +362,6 @@ function useEventListener(target, key, callback, options) {
472
362
  bindRef.current();
473
363
  }
474
364
  return cancelRef.current;
475
- // eslint-disable-next-line react-hooks/exhaustive-deps
476
365
  }, [target]);
477
366
  return {
478
367
  listen: () => bindRef.current(),
@@ -481,27 +370,7 @@ function useEventListener(target, key, callback, options) {
481
370
  }
482
371
 
483
372
  const defaultEverTargetValue = (value) => !!value;
484
- /**
485
- * that value ever to be `you want value`
486
- * Return true when the value ever to be `you want value` once.
487
- *
488
- * @example
489
- * ```ts
490
- * // once that isOpen is true, that result will always be `true`
491
- * const isEverOpen = useEver(isOpen);
492
- *
493
- * const isEverToBe2 = useEver(count, (val) => val === 2);
494
- * ```
495
- */
496
- const useEver = (
497
- /** to be checked value */
498
- value,
499
- /**
500
- * value ever to be callback, default when value to be exited.
501
- *
502
- * @default (value) => !!value
503
- */
504
- targetValue = defaultEverTargetValue) => {
373
+ const useEver = (value, targetValue = defaultEverTargetValue) => {
505
374
  const result = useRef(false);
506
375
  if (!result.current) {
507
376
  result.current = targetValue(value);
@@ -509,8 +378,6 @@ targetValue = defaultEverTargetValue) => {
509
378
  return result.current;
510
379
  };
511
380
 
512
- /* eslint-disable sonarjs/no-collapsible-if */
513
- /* eslint-disable sonarjs/prefer-single-boolean-return */
514
381
  let hadKeyboardEvent = true;
515
382
  let hadFocusVisibleRecently = false;
516
383
  const hadFocusVisibleRecentlyTimeout = new unstable_Timeout();
@@ -529,13 +396,6 @@ const inputTypesWhitelist = {
529
396
  datetime: true,
530
397
  'datetime-local': true,
531
398
  };
532
- /**
533
- * Computes whether the given element should automatically trigger the
534
- * `focus-visible` class being added, i.e. whether it should always match
535
- * `:focus-visible` when focused.
536
- * @param {Element} node
537
- * @returns {boolean}
538
- */
539
399
  function focusTriggersKeyboardModality(node) {
540
400
  const { type, tagName } = node;
541
401
  if (tagName === 'INPUT' &&
@@ -551,35 +411,17 @@ function focusTriggersKeyboardModality(node) {
551
411
  }
552
412
  return false;
553
413
  }
554
- /**
555
- * Keep track of our keyboard modality state with `hadKeyboardEvent`.
556
- * If the most recent user interaction was via the keyboard;
557
- * and the key press did not include a meta, alt/option, or control key;
558
- * then the modality is keyboard. Otherwise, the modality is not keyboard.
559
- * @param {KeyboardEvent} event
560
- */
561
414
  function handleKeyDown(event) {
562
415
  if (event.metaKey || event.altKey || event.ctrlKey) {
563
416
  return;
564
417
  }
565
418
  hadKeyboardEvent = true;
566
419
  }
567
- /**
568
- * If at any point a user clicks with a pointing device, ensure that we change
569
- * the modality away from keyboard.
570
- * This avoids the situation where a user presses a key on an already focused
571
- * element, and then clicks on a different element, focusing it with a
572
- * pointing device, while we still think we're in keyboard modality.
573
- */
574
420
  function handlePointerDown() {
575
421
  hadKeyboardEvent = false;
576
422
  }
577
423
  function handleVisibilityChange() {
578
424
  if (this.visibilityState === 'hidden') {
579
- // If the tab becomes active again, the browser will handle calling focus
580
- // on the element (Safari actually calls it twice).
581
- // If this tab change caused a blur on an element with focus-visible,
582
- // re-apply the class when the user switches back to the tab.
583
425
  if (hadFocusVisibleRecently) {
584
426
  hadKeyboardEvent = true;
585
427
  }
@@ -605,13 +447,7 @@ function isFocusVisible(event) {
605
447
  return target.matches(':focus-visible');
606
448
  }
607
449
  catch (error) {
608
- // Browsers not implementing :focus-visible will throw a SyntaxError.
609
- // We use our own heuristic for those browsers.
610
- // Rethrow might be better if it's not the expected error but do we really
611
- // want to crash if focus-visible malfunctioned?
612
450
  }
613
- // No need for validFocusTarget check. The user does that by attaching it to
614
- // focusable events only.
615
451
  return hadKeyboardEvent || focusTriggersKeyboardModality(target);
616
452
  }
617
453
  function useIsFocusVisible() {
@@ -621,20 +457,8 @@ function useIsFocusVisible() {
621
457
  }
622
458
  }, []);
623
459
  const isFocusVisibleRef = React.useRef(false);
624
- /**
625
- * Should be called if a blur event is fired
626
- */
627
460
  function handleBlurVisible() {
628
- // checking against potential state variable does not suffice if we focus and blur synchronously.
629
- // React wouldn't have time to trigger a re-render so `focusVisible` would be stale.
630
- // Ideally we would adjust `isFocusVisible(event)` to look at `relatedTarget` for blur events.
631
- // This doesn't work in IE11 due to https://github.com/facebook/react/issues/3751
632
- // TODO: check again if React releases their internal changes to focus event handling (https://github.com/facebook/react/pull/19186).
633
461
  if (isFocusVisibleRef.current) {
634
- // To detect a tab/window switch, we look for a blur event followed
635
- // rapidly by a visibility change.
636
- // If we don't see a visibility change within 100ms, it's probably a
637
- // regular focus change.
638
462
  hadFocusVisibleRecently = true;
639
463
  hadFocusVisibleRecentlyTimeout.start(100, () => {
640
464
  hadFocusVisibleRecently = false;
@@ -644,9 +468,6 @@ function useIsFocusVisible() {
644
468
  }
645
469
  return false;
646
470
  }
647
- /**
648
- * Should be called if a blur event is fired
649
- */
650
471
  function handleFocusVisible(event) {
651
472
  if (isFocusVisible(event)) {
652
473
  isFocusVisibleRef.current = true;
@@ -662,11 +483,6 @@ function useIsFocusVisible() {
662
483
  };
663
484
  }
664
485
 
665
- /**
666
- * bind event with a11y keyboard, `space`, `enter`, only trigger when key includes in `checkKeys`
667
- *
668
- * that will auto preventDefault by default, if you don't want that, set `false` with `preventDefault`
669
- */
670
486
  const useFocusVisible = ({ onFocusVisible, onFocus, onBlur, within = false, } = {}) => {
671
487
  const { isFocusVisibleRef, onFocus: handleFocusVisible, onBlur: handleBlurVisible, ref: focusVisibleRef, } = useIsFocusVisible();
672
488
  const [focusVisible, setFocusVisible] = useState(false);
@@ -675,15 +491,6 @@ const useFocusVisible = ({ onFocusVisible, onFocus, onBlur, within = false, } =
675
491
  handleBlurVisible(event);
676
492
  if (isFocusVisibleRef.current === false) {
677
493
  setFocusVisible(false);
678
- /**
679
- * when focus element by press `tab`, will trigger focus event in next event loop
680
- *
681
- * if current target is containing related target and we don't check whether it is containing related target,
682
- * `FocusVisibleWithin` will be false before `focus` trigger,
683
- * then `FocusVisibleWithin` will be true after `focus` trigger
684
- *
685
- * And if current target is containing related target, `FocusVisibleWithin` will never be false
686
- */
687
494
  if (within &&
688
495
  !event.currentTarget.contains(event.relatedTarget)) {
689
496
  setFocusVisibleWithin(false);
@@ -726,17 +533,12 @@ const useFocusVisible = ({ onFocusVisible, onFocus, onBlur, within = false, } =
726
533
  onBlur: handleBlur,
727
534
  onFocus: handleFocus,
728
535
  },
729
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
730
536
  focusVisibleRef: focusVisibleRef,
731
537
  focusVisible,
732
538
  focusVisibleWithin,
733
539
  };
734
540
  };
735
541
 
736
- /**
737
- * a method for trigger force update
738
- * @returns force update method
739
- */
740
542
  function useForceUpdate() {
741
543
  const [, dispatch] = useState(Object.create(null));
742
544
  return useEventCallback(() => {
@@ -744,39 +546,9 @@ function useForceUpdate() {
744
546
  });
745
547
  }
746
548
 
747
- /**
748
- * That method will give you ability fork same value `ref object` or `method`
749
- * Example:
750
- * ```ts
751
- * const refA = useRef();
752
- * const refB = useRef();
753
- *
754
- * const ref = useForkRef(refA, refB)
755
- *
756
- * useEffect(() => {
757
- * console.log(refA); // get that current div dom
758
- * console.log(refB); // also get that current div dom
759
- * },[])
760
- *
761
- * return <div ref={ref} />
762
- * ```
763
- */
764
549
  const useForkRef = unstable_useForkRef;
765
550
 
766
551
  const globalListenerEventMap = new Map();
767
- /**
768
- * create globalListener handler for share one listener event
769
- *
770
- * @example
771
- * ```ts
772
- * const globalListener = createGlobalListener('focus', () => { console.log('focus') });
773
- * const globalListener2 = createGlobalListener('focus', () => { console.log('focus') });
774
- * globalListener.listen();
775
- * globalListener2.listen();
776
- *
777
- * => // that will only create an event listener on window, but every callback will get emit value when event triggered
778
- * ```
779
- */
780
552
  const createGlobalListener = (key, listener, options, { customKey = key, target: targetProp = window, } = {}) => {
781
553
  let listening = false;
782
554
  const getMapValue = () => globalListenerEventMap.get(customKey);
@@ -818,63 +590,18 @@ const createGlobalListener = (key, listener, options, { customKey = key, target:
818
590
  };
819
591
  return {
820
592
  listen: () => addListener(),
821
- /**
822
- * remove listener
823
- */
824
593
  remove: () => removeListener(),
825
- /**
826
- * current listener state
827
- */
828
594
  get state() {
829
595
  return {
830
- /**
831
- * current listener state count
832
- */
833
596
  get count() {
834
597
  return getMapValue()?.listeners.size || 0;
835
598
  },
836
- /**
837
- * is that be listening
838
- */
839
599
  listening,
840
600
  };
841
601
  },
842
602
  };
843
603
  };
844
604
 
845
- /**
846
- * bind global event, when you bind same key event,
847
- * that will reuse one event listener and trigger both callback once that listener be triggered.
848
- *
849
- * also you can control listener with method `listen` and `remove`
850
- * and get listener `state` for check listener count number and current listing state.
851
- *
852
- * config:
853
- * - `target`: custom binding event target, default is `window`
854
- * - `customKey`: custom key for determining different event group, default is same as `key`
855
- * - `startImmediately`: start listener immediately, default is `true`
856
- *
857
- * * `key`, `options` only work when set first time, never change after first render
858
- *
859
- * @example
860
- * ```ts
861
- * useGlobalListener('keyup', () => console.log('window key up1'))
862
- *
863
- * useGlobalListener('keyup', () => console.log('window key up2'))
864
- *
865
- * useGlobalListener('touchend', () => console.log('window key up3'), {
866
- * target: someWantHostElement,
867
- * customKey: 'hostTouchend',
868
- * })
869
- *
870
- * useGlobalListener('touchend', () => console.log('window key up3'), { passive: false }, {
871
- * target: someWantHostElement,
872
- * customKey: 'hostTouchend',
873
- * })
874
- *
875
- * => // that will only create an event listener on `window`, but every callback will get emit value when event triggered
876
- * ```
877
- */
878
605
  function useGlobalListener(key, listener, ...args) {
879
606
  const { options, config } = getListenerOverloadOption(args);
880
607
  const { customKey = key, target: targetProp = window, startImmediately = true, } = config || {};
@@ -884,7 +611,6 @@ function useGlobalListener(key, listener, ...args) {
884
611
  target: targetProp,
885
612
  }));
886
613
  if (process.env.NODE_ENV !== 'production') {
887
- // eslint-disable-next-line react-hooks/rules-of-hooks
888
614
  useEffect(() => {
889
615
  if (customKey === key) {
890
616
  if (Object.keys(options).length > 0) {
@@ -902,7 +628,6 @@ function useGlobalListener(key, listener, ...args) {
902
628
  });
903
629
  }
904
630
  }
905
- // eslint-disable-next-line react-hooks/exhaustive-deps
906
631
  }, []);
907
632
  }
908
633
  useEffect(() => {
@@ -910,7 +635,6 @@ function useGlobalListener(key, listener, ...args) {
910
635
  globalListener.listen();
911
636
  }
912
637
  return globalListener.remove;
913
- // eslint-disable-next-line react-hooks/exhaustive-deps
914
638
  }, []);
915
639
  return globalListener;
916
640
  }
@@ -936,10 +660,6 @@ function getListenerOverloadOption(args) {
936
660
  return { options, config };
937
661
  }
938
662
 
939
- /**
940
- * group hidden logic, when binding events element focus,
941
- * that hiddenRef will auto be `tabindex={-1}` *
942
- */
943
663
  const useHiddenTabindex = (target) => {
944
664
  const handleFocus = useEventCallback(() => {
945
665
  const hiddenElm = getRefElement(target);
@@ -1014,20 +734,6 @@ const useHoverDirty = ({ target, disabled = false, }) => {
1014
734
  };
1015
735
  };
1016
736
 
1017
- /**
1018
- * get load state of an image by passing src or srcSet
1019
- * - false: not load yet
1020
- * - 'loaded': successfully loaded
1021
- * - 'error': fail to load
1022
- *
1023
- * @example
1024
- * ```ts
1025
- * const loaded = useImgLoaded({
1026
- * src,
1027
- * srcSet,
1028
- * });
1029
- * ```
1030
- */
1031
737
  const useImgLoaded = ({ crossOrigin, referrerPolicy, src, srcSet, }) => {
1032
738
  const [loaded, setLoaded] = useState(false);
1033
739
  useEffect(() => {
@@ -1064,38 +770,7 @@ const useImgLoaded = ({ crossOrigin, referrerPolicy, src, srcSet, }) => {
1064
770
  return loaded;
1065
771
  };
1066
772
 
1067
- /**
1068
- * controllable interval, provide easy way to control interval
1069
- *
1070
- * - cancel: cancel current interval
1071
- * - pause: pause current interval
1072
- * - play: play current interval
1073
- * - getRunning: return current interval running state
1074
- *
1075
- * @example
1076
- * ```ts
1077
- * const { cancel, play, pause } = useInterval(() => {
1078
- * console.log('interval be triggered');
1079
- * }, 1000)
1080
- *
1081
- * // get current run times
1082
- * useInterval((times) => {
1083
- * console.log('current times start from 1', times);
1084
- * }, 2000)
1085
- *
1086
- * // not start interval immediately
1087
- * useInterval((times) => {
1088
- * console.log('current times start from 1', times);
1089
- * }, 2000, false)
1090
- * ```
1091
- */
1092
- const useInterval = (
1093
- /** callback with times, times start from 1 */
1094
- callback,
1095
- /** what delay time of that interval */
1096
- delay = 0,
1097
- /** is that start interval immediately */
1098
- startImmediately = true) => {
773
+ const useInterval = (callback, delay = 0, startImmediately = true) => {
1099
774
  const intervalRef = useRef();
1100
775
  const countRef = useRef(0);
1101
776
  const getRunning = useEventCallback(() => !!intervalRef.current);
@@ -1128,31 +803,13 @@ startImmediately = true) => {
1128
803
  return () => cancel();
1129
804
  }, [cancel, play, startImmediately]);
1130
805
  return {
1131
- /**
1132
- * cancel current interval
1133
- */
1134
806
  cancel,
1135
- /**
1136
- * pause current interval
1137
- */
1138
807
  pause,
1139
- /**
1140
- * play current interval
1141
- */
1142
808
  play,
1143
- /**
1144
- * return current interval running state
1145
- */
1146
809
  getRunning,
1147
810
  };
1148
811
  };
1149
812
 
1150
- /**
1151
- * Provide user trigger `onKeyDown` only one time when hold press on key.
1152
- * @param includeKeys that check include keys
1153
- * @param methods trigger when first keydown or keyup
1154
- * @returns
1155
- */
1156
813
  const useKeyDownOnce = (includeKeys, { onKeyDown, onKeyUp }) => {
1157
814
  const isKeyDownMapRef = useRef({});
1158
815
  const handleKeyDown = useEventCallback((e) => {
@@ -1182,13 +839,8 @@ const useKeyDownOnce = (includeKeys, { onKeyDown, onKeyUp }) => {
1182
839
  };
1183
840
  };
1184
841
 
1185
- /**
1186
- * that can move index in column array
1187
- */
1188
842
  function moveIndexInTwoDimension(direction, options) {
1189
843
  const { currIndex, columns, total, infinite } = options;
1190
- // start index never small than 0
1191
- // const index = Math.max(currIndex, 0);
1192
844
  const lastIndex = total - 1;
1193
845
  const isInOrder = infinite === 'order';
1194
846
  const fillTotal = Math.ceil(total / columns) * columns;
@@ -1237,8 +889,6 @@ function moveIndexInTwoDimension(direction, options) {
1237
889
  if (!infinite && resultIndex >= total) {
1238
890
  return currIndex;
1239
891
  }
1240
- // * when that bigger then sourceTotal, mean that touch end line
1241
- // * move to current full latest and move again.
1242
892
  if (resultIndex >= total) {
1243
893
  if (direction === 'ArrowRight') {
1244
894
  resultIndex = fillTotal - 1;
@@ -1251,9 +901,6 @@ function moveIndexInTwoDimension(direction, options) {
1251
901
  return resultIndex;
1252
902
  }
1253
903
 
1254
- /**
1255
- * use keyboard move index in two dimensional
1256
- */
1257
904
  const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocusedIndexChange, getOptionDisabled, getOptionSearchText, columns = 1, endSearchDebounceTime = 500, }) => {
1258
905
  const total = options.length;
1259
906
  const searchText = useRef('');
@@ -1280,7 +927,6 @@ const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocus
1280
927
  let findDirection;
1281
928
  if (total === 0)
1282
929
  return;
1283
- // eslint-disable-next-line no-constant-condition
1284
930
  while (true) {
1285
931
  const toIndex = findDirection
1286
932
  ? findDirection === 'next'
@@ -1302,22 +948,16 @@ const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocus
1302
948
  }
1303
949
  break;
1304
950
  }
1305
- // * mean that item is disabled, use same direction find next
1306
951
  else {
1307
- // * find toIndex is same as current, mean that already find all
1308
952
  if (focusedIndexRef.current !== -1 &&
1309
953
  toIndex === Math.max(focusedIndexRef.current, 0)) {
1310
954
  break;
1311
955
  }
1312
- // * if no item is focused currently and toIndex is already the last item checked,
1313
- // * it means no item in the list is focusable
1314
956
  if (focusedIndexRef.current === -1) {
1315
- // from bottom to top and the first item in the list is not focusable
1316
957
  if ((Math.abs(currIndex - toIndex) !== 1 || currIndex > toIndex) &&
1317
958
  toIndex === 0) {
1318
959
  break;
1319
960
  }
1320
- // from top to bottom and the last item in the list is not focusable
1321
961
  if (Math.abs(currIndex - toIndex) === 1 &&
1322
962
  currIndex < toIndex &&
1323
963
  toIndex === total - 1) {
@@ -1325,7 +965,6 @@ const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocus
1325
965
  }
1326
966
  }
1327
967
  currIndex = toIndex;
1328
- // * if that is Hom or End, find use increase or decrease
1329
968
  if (key === 'Home') {
1330
969
  findDirection = 'next';
1331
970
  }
@@ -1335,16 +974,13 @@ const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocus
1335
974
  }
1336
975
  }
1337
976
  };
1338
- const getPrevFocusableOption = useCallback((index = focusedIndexRef.current,
1339
- /** is that infinite search */
1340
- isInfinite = true) => {
977
+ const getPrevFocusableOption = useCallback((index = focusedIndexRef.current, isInfinite = true) => {
1341
978
  for (let i = index; i >= 0; i--) {
1342
979
  if (!getOptionDisabled?.(options[i])) {
1343
980
  return i;
1344
981
  }
1345
982
  }
1346
983
  if (isInfinite) {
1347
- // loop find
1348
984
  for (let i = total - 1; i > index; i--) {
1349
985
  if (!getOptionDisabled?.(options[i])) {
1350
986
  return i;
@@ -1353,16 +989,13 @@ const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocus
1353
989
  }
1354
990
  return -1;
1355
991
  }, [focusedIndexRef, getOptionDisabled, options, total]);
1356
- const getNextFocusableOption = useCallback((index = focusedIndexRef.current,
1357
- /** is that infinite search */
1358
- isInfinite = true) => {
992
+ const getNextFocusableOption = useCallback((index = focusedIndexRef.current, isInfinite = true) => {
1359
993
  for (let i = index; i < total; i++) {
1360
994
  if (!getOptionDisabled?.(options[i])) {
1361
995
  return i;
1362
996
  }
1363
997
  }
1364
998
  if (isInfinite) {
1365
- // loop find
1366
999
  for (let i = 0; i < index; i++) {
1367
1000
  if (!getOptionDisabled?.(options[i])) {
1368
1001
  return i;
@@ -1372,26 +1005,13 @@ const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocus
1372
1005
  return -1;
1373
1006
  }, [focusedIndexRef, getOptionDisabled, options, total]);
1374
1007
  return {
1375
- /**
1376
- * Get previous focusable option(include current index).
1377
- */
1378
1008
  getPrevFocusableOption,
1379
- /**
1380
- * Get next focusable option(include current index).
1381
- */
1382
1009
  getNextFocusableOption,
1383
- /**
1384
- * Use to handle keyboard event.
1385
- */
1386
1010
  onKeyFocusedIndexHandle,
1387
1011
  };
1388
1012
  };
1389
1013
 
1390
1014
  const MOVE_ATTR_KEY = 'data-move-focused';
1391
- /**
1392
- * when focus in container below element with binding `getItemProps`,
1393
- * that will only have one element can be focusable.
1394
- */
1395
1015
  const useOnlyOneFocusable = ({ focusedIndexRef, containerRef, }) => {
1396
1016
  const moveFocusedId = useId(MOVE_ATTR_KEY, true);
1397
1017
  const getIndexElm = (toIndex) => containerRef.current?.querySelector(`[${moveFocusedId}="${toIndex}"]`);
@@ -1424,28 +1044,6 @@ const useOnlyOneFocusable = ({ focusedIndexRef, containerRef, }) => {
1424
1044
  };
1425
1045
  };
1426
1046
 
1427
- /**
1428
- * Provide a utils for switch trigger touch event or mouse event between different device
1429
- *
1430
- * - Trigger `touch` event in `touch support` device
1431
- * - onTouchStart
1432
- * - onTouchEnd
1433
- * - Trigger `mouse` event in `non touch support` device
1434
- * - onMouseDown
1435
- * - onMouseUp
1436
- *
1437
- * ### Once that is touch support device, that mouse event will never can be trigger
1438
- * https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent
1439
- *
1440
- * Event order:
1441
- * - touchstart
1442
- * - Zero or more touchmove events, depending on movement of the finger(s)
1443
- * - touchend
1444
- * - mousemove
1445
- * - mousedown
1446
- * - mouseup
1447
- * - click
1448
- */
1449
1047
  const useTouchMouseEvent = ({ onMouseDown: onMouseDownArg, onMouseUp: onMouseUpArg, onTouchStart: onTouchStartArg, onTouchEnd: onTouchEndArg, }, { actionRef } = {}) => {
1450
1048
  const stopMouseEventRef = useRef(false);
1451
1049
  const onTouchStart = useEventCallback((e) => {
@@ -1491,9 +1089,6 @@ const useTouchMouseEvent = ({ onMouseDown: onMouseDownArg, onMouseUp: onMouseUpA
1491
1089
  onMouseUp,
1492
1090
  };
1493
1091
  };
1494
- /**
1495
- * check is element equal or contains target element
1496
- */
1497
1092
  const isElmEqualOrContain = (sourceTarget, containTarget) => {
1498
1093
  const elm = getRefElement(sourceTarget);
1499
1094
  const target = getRefElement(containTarget);
@@ -1503,28 +1098,18 @@ const isElmEqualOrContain = (sourceTarget, containTarget) => {
1503
1098
  const isTouchEvent = (ev) => {
1504
1099
  return 'touches' in ev;
1505
1100
  };
1506
- /**
1507
- * Provide longPress helper, both `click`/`tab`/`keydown` will trigger event
1508
- *
1509
- * - Trigger `onTap` when user action leave small than delay time.
1510
- * - Trigger `onPress` when action time is long than delay time.
1511
- *
1512
- * @default UseLongPressOptions { delay = 300 }
1513
- */
1514
1101
  const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTouchEnd: onTouchEndArg, onMouseDown, onKeyDown, onKeyUp, onMouseUp: onMouseUpArg, } = {}, { isPreventDefault = true, delay = 300, externalWindow = window, } = {}) => {
1515
1102
  const isEmittedRef = useRef(false);
1516
1103
  const isA11yDownRef = useRef(false);
1517
1104
  const actionRef = useRef(null);
1518
1105
  const reasonRef = useRef('click');
1519
1106
  const emitLongPress = useDebounce((e) => {
1520
- // * only when have onPress set have emit
1521
1107
  if (onPress) {
1522
1108
  onPress(e, reasonRef.current);
1523
1109
  isEmittedRef.current = true;
1524
1110
  }
1525
1111
  }, delay);
1526
1112
  const end = (e, isInside) => {
1527
- // ! mouse up only trigger when up on element, so we host that in document
1528
1113
  globalMouseUpListener.remove();
1529
1114
  if (!isEmittedRef.current && isInside) {
1530
1115
  onTap?.(e, reasonRef.current);
@@ -1534,7 +1119,6 @@ const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTou
1534
1119
  emitLongPress.cancel();
1535
1120
  };
1536
1121
  const start = (e) => {
1537
- // ! mouse up only trigger when up on element, so we host that in document
1538
1122
  globalMouseUpListener.listen();
1539
1123
  const isTouch = e.type === 'touchstart';
1540
1124
  if (isTouch) {
@@ -1544,7 +1128,6 @@ const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTou
1544
1128
  };
1545
1129
  const { onMouseUp, onTouchEnd, onTouchStart, ...events } = useTouchMouseEvent({
1546
1130
  onTouchStart: (e) => {
1547
- // prevent long touch leave element focus
1548
1131
  if (isPreventDefault) {
1549
1132
  e.preventDefault();
1550
1133
  }
@@ -1552,7 +1135,6 @@ const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTou
1552
1135
  start(e);
1553
1136
  },
1554
1137
  onTouchEnd: (e, isInside) => {
1555
- // prevent leave element focus
1556
1138
  if (isPreventDefault) {
1557
1139
  e.preventDefault();
1558
1140
  }
@@ -1567,7 +1149,6 @@ const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTou
1567
1149
  start(e);
1568
1150
  },
1569
1151
  onMouseUp: (e, isInside) => {
1570
- // prevent leave element focus
1571
1152
  if (isPreventDefault) {
1572
1153
  e.preventDefault();
1573
1154
  }
@@ -1575,11 +1156,9 @@ const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTou
1575
1156
  end(e, isInside);
1576
1157
  },
1577
1158
  }, { actionRef });
1578
- // use event listener for make event `passive` work in safari
1579
1159
  useEventListener(actionRef, 'touchstart', onTouchStart, {
1580
1160
  passive: false,
1581
1161
  });
1582
- // use event listener for make event `passive` work in safari
1583
1162
  useEventListener(actionRef, 'touchend', onTouchEnd, {
1584
1163
  passive: false,
1585
1164
  });
@@ -1589,7 +1168,6 @@ const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTou
1589
1168
  });
1590
1169
  const handleDeleteKeyDown = useA11yKeyEvent((e) => {
1591
1170
  reasonRef.current = 'keyboard';
1592
- // * keydown when hold will trigger many time, show when current is a11y down, not trigger again
1593
1171
  if (!isA11yDownRef.current) {
1594
1172
  emitLongPress(e);
1595
1173
  }
@@ -1613,20 +1191,6 @@ const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTou
1613
1191
  };
1614
1192
  };
1615
1193
 
1616
- /**
1617
- * get that component mounted state `ref`
1618
- *
1619
- * @example
1620
- *
1621
- * ```ts
1622
- const isMountedRef = useMountState();
1623
-
1624
- if (isMountedRef.current) {
1625
- console.log('component mounted');
1626
- }
1627
- *
1628
- * ```
1629
- */
1630
1194
  const useMountState = () => {
1631
1195
  const isMountedRef = useRef(false);
1632
1196
  useEffect(() => {
@@ -1638,9 +1202,6 @@ const useMountState = () => {
1638
1202
  return isMountedRef;
1639
1203
  };
1640
1204
 
1641
- /**
1642
- * only trigger when re-render (in useLayoutEffect), not trigger on component first render
1643
- */
1644
1205
  const useOnLayoutReRender = (cb, deps) => {
1645
1206
  const count = useRef(0);
1646
1207
  const method = useEventCallback(cb);
@@ -1648,17 +1209,12 @@ const useOnLayoutReRender = (cb, deps) => {
1648
1209
  if (count.current === 0) {
1649
1210
  count.current = 1;
1650
1211
  return () => {
1651
- //
1652
1212
  };
1653
1213
  }
1654
1214
  return method();
1655
- // eslint-disable-next-line react-hooks/exhaustive-deps
1656
1215
  }, deps);
1657
1216
  };
1658
1217
 
1659
- /**
1660
- * only trigger when re-render, not trigger on component first render
1661
- */
1662
1218
  const useOnReRender = (cb, deps) => {
1663
1219
  const count = useRef(0);
1664
1220
  const method = useEventCallback(cb);
@@ -1666,28 +1222,13 @@ const useOnReRender = (cb, deps) => {
1666
1222
  if (count.current === 0) {
1667
1223
  count.current = 1;
1668
1224
  return () => {
1669
- //
1670
1225
  };
1671
1226
  }
1672
1227
  return method();
1673
- // eslint-disable-next-line react-hooks/exhaustive-deps
1674
1228
  }, deps);
1675
1229
  };
1676
1230
 
1677
- /**
1678
- * get resize event with auto disconnect when layout destroy.
1679
- * bring with debounce for event not too many
1680
- * @param ref that ref for element
1681
- * @param callback callback when element resize
1682
- * @param performance options that improve performance
1683
- */
1684
- const useResizeObserver = (
1685
- /** target element ref */
1686
- target,
1687
- /** trigger when `ResizeObserver` emit */
1688
- callback,
1689
- /** performance options, `mode`, `time`, `ignoreFireWhenObserve` */
1690
- performance = {}) => {
1231
+ const useResizeObserver = (target, callback, performance = {}) => {
1691
1232
  const { mode = 'debounce', time: performanceTime = 200, ignoreFireWhenObserve, options, } = performance;
1692
1233
  const countRef = useRef(0);
1693
1234
  const cb = useEventCallback((...args) => {
@@ -1725,29 +1266,14 @@ performance = {}) => {
1725
1266
  return () => {
1726
1267
  observer.disconnect();
1727
1268
  };
1728
- // eslint-disable-next-line react-hooks/exhaustive-deps
1729
1269
  }, [el]);
1730
1270
  return observerRef.current;
1731
1271
  };
1732
1272
 
1733
- /**
1734
- * trigger when overflow state change
1735
- */
1736
- const useOverflow = (
1737
- /** target element ref */
1738
- target,
1739
- /** trigger when `ResizeObserver` emit */
1740
- cb,
1741
- /**
1742
- * performance options, `mode`, `time`, `ignoreFireWhenObserve`
1743
- *
1744
- * @default { mode: 'throttle' time: 200 }
1745
- */
1746
- performance = {
1273
+ const useOverflow = (target, cb, performance = {
1747
1274
  mode: 'throttle',
1748
1275
  time: 200,
1749
1276
  }) => {
1750
- // * not have default value for that can trigger change first time.
1751
1277
  const showRef = useRef();
1752
1278
  return useResizeObserver(target, () => {
1753
1279
  const element = getRefElement(target);
@@ -1757,7 +1283,6 @@ performance = {
1757
1283
  const show = scrollWidth > clientWidth || scrollHeight > clientHeight;
1758
1284
  if (showRef.current === show)
1759
1285
  return;
1760
- // only emit when state change
1761
1286
  showRef.current = show;
1762
1287
  cb(show, {
1763
1288
  scrollWidth,
@@ -1768,38 +1293,10 @@ performance = {
1768
1293
  }, performance);
1769
1294
  };
1770
1295
 
1771
- /**
1772
- * Returns a stateful like state with Ref, and a function to update it current value.
1773
- * can trigger update value without re-render
1774
- *
1775
- * @param value init value
1776
- * @param customUpdate you can custom you update method, or just set your `forceUpdate` to reuse same instance
1777
- * @returns an array with [ref, update]
1778
- *
1779
- * @example
1780
- * ```ts
1781
- * const [inputRef, updateInput] = useRefState(123);
1782
- *
1783
- * if(needReRender){
1784
- * updateInput(456); // trigger re-render
1785
- * } else {
1786
- * updateInput(456, false); // not trigger re-render
1787
- * }
1788
- * ```
1789
- */
1790
1296
  const useRefState = (value, customUpdate) => {
1791
- // eslint-disable-next-line react-hooks/rules-of-hooks
1792
1297
  const forceUpdate = customUpdate ?? useForceUpdate();
1793
1298
  const innerValueRef = useRef(value);
1794
- /**
1795
- * update ref value, set isUpdate to `false` will not trigger re-render
1796
- */
1797
- const setInnerValue = useEventCallback((toValue,
1798
- /**
1799
- * is re-render
1800
- * @default true
1801
- */
1802
- isUpdate = true) => {
1299
+ const setInnerValue = useEventCallback((toValue, isUpdate = true) => {
1803
1300
  innerValueRef.current = toValue;
1804
1301
  if (isUpdate) {
1805
1302
  forceUpdate();
@@ -1808,10 +1305,6 @@ const useRefState = (value, customUpdate) => {
1808
1305
  return [innerValueRef, setInnerValue];
1809
1306
  };
1810
1307
 
1811
- /**
1812
- * get all matched breakpoints
1813
- * @param defaultBreakpoint use defaultBreakpoint when Responsive init (will pass undefined)
1814
- */
1815
1308
  const useResponsiveMatch = (defaultBreakpoint) => {
1816
1309
  const _breakpoint = useResponsiveContext();
1817
1310
  const breakpoint = _breakpoint === undefined ? defaultBreakpoint : _breakpoint;
@@ -1821,13 +1314,6 @@ const useResponsiveMatch = (defaultBreakpoint) => {
1821
1314
  return matchDetail.matchMap;
1822
1315
  };
1823
1316
 
1824
- /**
1825
- * cancelable sleep method.
1826
- *
1827
- * - sleep: a promise sleep
1828
- * - cancel: cancel that sleep event
1829
- * - getSleeping: get current sleeping state
1830
- */
1831
1317
  const useSleep = () => {
1832
1318
  const timeoutIdRef = useRef();
1833
1319
  const rejectRef = useRef();
@@ -1857,36 +1343,13 @@ const useSleep = () => {
1857
1343
  };
1858
1344
  }, []);
1859
1345
  return {
1860
- /**
1861
- * sleep method, that will be cancelable with `cancel`,
1862
- * and when trigger many time that will always clear previous sleep
1863
- */
1864
1346
  sleep,
1865
- /**
1866
- * cancel current sleep timer
1867
- */
1868
1347
  cancel,
1869
- /**
1870
- * return is current is sleeping
1871
- */
1872
1348
  getSleeping: () => sleepingRef.current,
1873
1349
  };
1874
1350
  };
1875
1351
 
1876
- /* eslint-disable no-await-in-loop */
1877
- /**
1878
- * retry times when exec callback fail
1879
- *
1880
- * * `retry`: that will auto add `times` end of your call back
1881
- * * `cancel`: cancel that retry event, that only cancel inner interval promise, **if you need cancel your `cb` promise, you should handle by yourself.**
1882
- */
1883
- const useRetry = (
1884
- /**
1885
- * exec callback, when you want to end that retry, return `true` with your callback
1886
- */
1887
- cb,
1888
- /** retry options */
1889
- options) => {
1352
+ const useRetry = (cb, options) => {
1890
1353
  const { sleep, cancel: cancelSleep } = useSleep();
1891
1354
  const isCancelRef = useRef(true);
1892
1355
  const complete = () => {
@@ -1895,7 +1358,6 @@ options) => {
1895
1358
  };
1896
1359
  const cancel = () => {
1897
1360
  if (!isCancelRef.current) {
1898
- // * when outside cancel
1899
1361
  options?.onCancel?.();
1900
1362
  complete();
1901
1363
  }
@@ -1905,7 +1367,6 @@ options) => {
1905
1367
  cancel();
1906
1368
  isCancelRef.current = false;
1907
1369
  options?.onStart?.();
1908
- // * when exec success, just return directly.
1909
1370
  if (await cb(...args)) {
1910
1371
  if (!isCancelRef.current) {
1911
1372
  options?.onSuccess?.();
@@ -1937,21 +1398,11 @@ options) => {
1937
1398
  complete();
1938
1399
  };
1939
1400
  return {
1940
- /**
1941
- * retry method, that will be cancelable with `cancel`,
1942
- * and when trigger many time that will always clear previous retry
1943
- */
1944
1401
  retry,
1945
- /**
1946
- * cancel current retry
1947
- */
1948
1402
  cancel,
1949
1403
  };
1950
1404
  };
1951
1405
 
1952
- /**
1953
- * @param onTabOut Callback fired when a "tab out" event is detected.
1954
- */
1955
1406
  const useTabOut = (onTabOut) => {
1956
1407
  const lastKeyDownIsTabRef = useRef(false);
1957
1408
  const handleKeyDown = (e) => {
@@ -1977,28 +1428,13 @@ const useTabOut = (onTabOut) => {
1977
1428
  };
1978
1429
  };
1979
1430
 
1980
- /**
1981
- * provide a throttle method for throttle method
1982
- * @param fn method for throttle
1983
- * @param throttleTime throttle timeout value, default is `200`ms
1984
- * @param options throttle setting, default is `{trailing: true, leading: true}`, detail see lodash
1985
- */
1986
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1987
1431
  const useThrottle = (fn, throttleTime = 200, options) => {
1988
1432
  const memoFn = useEventCallback(fn);
1989
- // eslint-disable-next-line react-hooks/exhaustive-deps
1990
1433
  const throttleFn = useCallback(throttle(memoFn, throttleTime, options), []);
1991
- // eslint-disable-next-line react-hooks/exhaustive-deps
1992
1434
  useEffect(() => () => throttleFn.cancel(), []);
1993
1435
  return throttleFn;
1994
1436
  };
1995
1437
 
1996
- /**
1997
- * Internal hook to detect if the component is running in a test runner environment
1998
- * Used by Storybook stories to disable network requests and animations during testing
1999
- * @returns {boolean} true if running in test runner (headless browser), false otherwise
2000
- * @internal
2001
- */
2002
1438
  const useTestRunnerDetection = () => {
2003
1439
  return useMemo(() => {
2004
1440
  return (typeof window !== 'undefined' &&
@@ -2009,10 +1445,7 @@ const useTestRunnerDetection = () => {
2009
1445
  function useTimeout(callback, delay) {
2010
1446
  const savedCallback = useRef(callback);
2011
1447
  savedCallback.current = callback;
2012
- // Set up the timeout.
2013
1448
  useEffect(() => {
2014
- // Don't schedule if no delay is specified.
2015
- // Note: 0 is a valid value for delay.
2016
1449
  if (!delay && delay !== 0) {
2017
1450
  return;
2018
1451
  }
@@ -2021,28 +1454,12 @@ function useTimeout(callback, delay) {
2021
1454
  }, [delay]);
2022
1455
  }
2023
1456
 
2024
- /**
2025
- * Check if Font Loading API is available in the current environment
2026
- * @returns boolean indicating if document.fonts.ready is available
2027
- */
2028
1457
  const isFontLoadingAPIAvailable = () => {
2029
1458
  return (typeof document !== 'undefined' &&
2030
1459
  document.fonts &&
2031
1460
  typeof document.fonts.ready === 'object' &&
2032
1461
  typeof document.fonts.ready.then === 'function');
2033
1462
  };
2034
- /**
2035
- * Hook to detect when fonts are loaded
2036
- *
2037
- * This hook provides graceful degradation:
2038
- * - When Font Loading API is not available (older browsers, SSR, test environments),
2039
- * it assumes fonts are loaded to prevent blocking UI
2040
- * - When disabled, it immediately returns true without listening to font events
2041
- * - When enabled and API is available, it listens for font loading completion
2042
- *
2043
- * @param enabled - Whether to listen for font loading events (default: true)
2044
- * @returns boolean indicating whether fonts are loaded
2045
- */
2046
1463
  const useFontLoaded = (enabled = true) => {
2047
1464
  const [loaded, setLoaded] = useState(() => !enabled || !isFontLoadingAPIAvailable());
2048
1465
  useEffect(() => {
@@ -2062,14 +1479,6 @@ const useFontLoaded = (enabled = true) => {
2062
1479
  return loaded;
2063
1480
  };
2064
1481
 
2065
- /**
2066
- * Hook to lock body scroll while allowing scroll on target element
2067
- * Uses body-scroll-lock library for cross-platform compatibility
2068
- *
2069
- * @param isLocked - Whether scroll should be locked
2070
- * @param targetElement - Element that should remain scrollable (optional)
2071
- * @param options - Configuration options
2072
- */
2073
1482
  const useBodyScrollLock = (isLocked, targetElement, options = {}) => {
2074
1483
  const { disabled = false, reserveScrollBarGap = true, allowTouchMove, } = options;
2075
1484
  const { document } = useWindowContext();
@@ -2098,4 +1507,14 @@ const useBodyScrollLock = (isLocked, targetElement, options = {}) => {
2098
1507
  ]);
2099
1508
  };
2100
1509
 
2101
- export { createGlobalListener, globalListenerEventMap, isElmEqualOrContain, isTouchEvent, moveIndexInTwoDimension, teardown, useA11yKeyEvent, useActive, useAnnouncer, useAriaHideOutside, useAudio, useBodyScrollLock, useChange, useClickAway, useDebounce, useDepsChange, useEventCallback, useEventListener, useEver, useFocusVisible, useFontLoaded, useForceUpdate, useForkRef, useGlobalListener, useHiddenTabindex, useHover, useHoverDirty, useId, useImgLoaded, useInterval, useIsFocusVisible, useKeyDownOnce, useKeyboardMoveFocus, useLongPress, useMountState, useOnLayoutReRender, useOnReRender, useOnlyOneFocusable, useOverflow, usePrevious, useRefState, useResizeObserver, useResponsiveMatch, useResultRef, useRetry, useSleep, useTabOut, useTestRunnerDetection, useThrottle, useTimeout, useTouchMouseEvent, visuallyHiddenStyles };
1510
+ const useVirtuosoExternalWindow = () => {
1511
+ const { externalWindow } = useWindowContext();
1512
+ if (externalWindow && externalWindow !== globalThis.window) {
1513
+ return {
1514
+ externalWindow,
1515
+ };
1516
+ }
1517
+ return undefined;
1518
+ };
1519
+
1520
+ export { createGlobalListener, globalListenerEventMap, isElmEqualOrContain, isTouchEvent, moveIndexInTwoDimension, teardown, useA11yKeyEvent, useActive, useAnnouncer, useAriaHideOutside, useAudio, useBodyScrollLock, useButton, useChange, useClickAway, useDebounce, useDepsChange, useEventCallback, useEventListener, useEver, useFocusVisible, useFontLoaded, useForceUpdate, useForkRef, useGlobalListener, useHiddenTabindex, useHover, useHoverDirty, useId, useImgLoaded, useInterval, useIsFocusVisible, useKeyDownOnce, useKeyboardMoveFocus, useLongPress, useMountState, useOnLayoutReRender, useOnReRender, useOnlyOneFocusable, useOverflow, usePrevious, useRefState, useResizeObserver, useResponsiveMatch, useResultRef, useRetry, useSleep, useTabOut, useTestRunnerDetection, useThrottle, useTimeout, useTouchMouseEvent, useVirtuosoExternalWindow, visuallyHiddenStyles };