@ringcentral/spring-base 1.7.0 → 1.8.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 (69) 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 +10 -635
  5. package/package.json +1 -1
  6. package/src/config.d.ts +1 -26
  7. package/src/contexts/ResponsiveContext/breakpointList.d.ts +0 -1
  8. package/src/contexts/ResponsiveContext/types.d.ts +0 -11
  9. package/src/hooks/useA11yKeyEvent/useA11yKeyEvent.d.ts +0 -17
  10. package/src/hooks/useActive/useActive.d.ts +0 -8
  11. package/src/hooks/useAnnouncer/useAnnouncer.d.ts +0 -10
  12. package/src/hooks/useAriaHideOutside/useAriaHideOutside.d.ts +0 -4
  13. package/src/hooks/useAudio/useAudio.d.ts +0 -20
  14. package/src/hooks/useBodyScrollLock/useBodyScrollLock.d.ts +0 -19
  15. package/src/hooks/useChange/useChange.d.ts +0 -10
  16. package/src/hooks/useClickAway/useClickAway.d.ts +0 -10
  17. package/src/hooks/useDebounce/useDebounce.d.ts +0 -6
  18. package/src/hooks/useEventListener/useEventListener.d.ts +0 -20
  19. package/src/hooks/useEver/useEver.d.ts +0 -12
  20. package/src/hooks/useFocusVisible/useFocusVisible.d.ts +0 -6
  21. package/src/hooks/useFontLoaded/useFontLoaded.d.ts +0 -12
  22. package/src/hooks/useForceUpdate/useForceUpdate.d.ts +0 -4
  23. package/src/hooks/useForkRef/useForkRef.d.ts +0 -17
  24. package/src/hooks/useGlobalListener/createGlobalListener.d.ts +0 -37
  25. package/src/hooks/useGlobalListener/useGlobalListener.d.ts +0 -5
  26. package/src/hooks/useHiddenTabindex/useHiddenTabindex.d.ts +0 -4
  27. package/src/hooks/useHover/useHover.d.ts +0 -3
  28. package/src/hooks/useId/useId.d.ts +0 -14
  29. package/src/hooks/useImgLoaded/useImgLoaded.d.ts +0 -14
  30. package/src/hooks/useInterval/useInterval.d.ts +0 -37
  31. package/src/hooks/useKeyDownOnce/useKeyDownOnce.d.ts +0 -6
  32. package/src/hooks/useKeyboardMoveFocus/moveIndexInTwoDimension.d.ts +0 -11
  33. package/src/hooks/useKeyboardMoveFocus/useKeyboardMoveFocus.d.ts +0 -33
  34. package/src/hooks/useKeyboardMoveFocus/useOnlyOneFocusable.d.ts +0 -6
  35. package/src/hooks/useLongPress/useLongPress.d.ts +0 -16
  36. package/src/hooks/useMountState/useMountState.d.ts +0 -14
  37. package/src/hooks/useOnLayoutReRender/useOnLayoutReRender.d.ts +0 -3
  38. package/src/hooks/useOnReRender/useOnReRender.d.ts +0 -3
  39. package/src/hooks/useOverflow/useOverflow.d.ts +0 -3
  40. package/src/hooks/usePrevious/usePrevious.d.ts +0 -6
  41. package/src/hooks/useRefState/useRefState.d.ts +0 -19
  42. package/src/hooks/useResizeObserver/useResizeObserver.d.ts +0 -15
  43. package/src/hooks/useResponsiveMatch/useResponsiveMatch.d.ts +0 -4
  44. package/src/hooks/useResultRef/useResultRef.d.ts +0 -8
  45. package/src/hooks/useRetry/useRetry.d.ts +2 -25
  46. package/src/hooks/useSleep/useSleep.d.ts +0 -17
  47. package/src/hooks/useTabOut/useTabOut.d.ts +0 -3
  48. package/src/hooks/useTestRunnerDetection.d.ts +0 -6
  49. package/src/hooks/useThrottle/useThrottle.d.ts +0 -6
  50. package/src/hooks/useTouchMouseEvent/useTouchMouseEvent.d.ts +2 -41
  51. package/src/typings/BaseColor.d.ts +0 -1
  52. package/src/typings/ComponentTypes.d.ts +0 -26
  53. package/src/typings/OverridableTypes.d.ts +0 -29
  54. package/src/typings/UnitMap.d.ts +0 -15
  55. package/src/utils/ariaHideOutside.d.ts +0 -8
  56. package/src/utils/checkDefaultPrevented.d.ts +0 -1
  57. package/src/utils/classes/useClasses.d.ts +0 -29
  58. package/src/utils/combineProps.d.ts +0 -88
  59. package/src/utils/getRefElement.d.ts +0 -3
  60. package/src/utils/isWebKit154.d.ts +0 -8
  61. package/src/utils/omit.d.ts +0 -1
  62. package/src/utils/outerSize.d.ts +0 -4
  63. package/src/utils/pick.d.ts +0 -1
  64. package/src/utils/preloadImg.d.ts +0 -1
  65. package/src/utils/removeClassName.d.ts +0 -28
  66. package/src/utils/runKeyHandler.d.ts +0 -8
  67. package/src/utils/selectionHandler.d.ts +0 -8
  68. package/src/utils/twMerge.d.ts +0 -3
  69. package/utils.js +0 -99
package/hooks.js CHANGED
@@ -15,18 +15,11 @@ import throttle from 'lodash/throttle';
15
15
  import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';
16
16
  import 'tailwind-merge';
17
17
 
18
- // this file template replace material-ui/core/utils method
19
- // when upgrade mui that will not need assign type anymore
20
18
  const useEventCallback = (func) => {
21
19
  return unstable_useEventCallback(func);
22
20
  };
23
21
 
24
22
  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
23
  const useA11yKeyEvent = (fn, { onlyOnFocus, preventDefault = true, checkKeys = defaultCheckKeys, } = {}) => {
31
24
  return useEventCallback((event) => {
32
25
  if (!fn || (onlyOnFocus && event.target !== event.currentTarget))
@@ -39,7 +32,6 @@ const useA11yKeyEvent = (fn, { onlyOnFocus, preventDefault = true, checkKeys = d
39
32
  });
40
33
  };
41
34
 
42
- // base https://github.com/mui/material-ui/blob/master/packages/mui-base/src/useButton/useButton.ts
43
35
  const isNativeButton = (el) => {
44
36
  const tag = el.tagName;
45
37
  return (tag === 'BUTTON' ||
@@ -47,11 +39,6 @@ const isNativeButton = (el) => {
47
39
  ['button', 'submit', 'reset'].includes(el?.type)) ||
48
40
  (tag === 'A' && el?.href));
49
41
  };
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
42
  const useActive = ({ onKeyDown, onKeyUp, onClick, disabled = false, } = {}) => {
56
43
  const [active, setActive] = useState(false);
57
44
  useEffect(() => {
@@ -77,7 +64,6 @@ const useActive = ({ onKeyDown, onKeyUp, onClick, disabled = false, } = {}) => {
77
64
  if (event.target === event.currentTarget && event.key === ' ') {
78
65
  setActive(true);
79
66
  }
80
- // Keyboard accessibility for non interactive elements
81
67
  if (event.target === event.currentTarget &&
82
68
  !isNativeButton(event.target) &&
83
69
  event.key === 'Enter') {
@@ -86,13 +72,10 @@ const useActive = ({ onKeyDown, onKeyUp, onClick, disabled = false, } = {}) => {
86
72
  }
87
73
  };
88
74
  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
75
  if (event.target === event.currentTarget) {
92
76
  setActive(false);
93
77
  }
94
78
  onKeyUp?.(event);
95
- // Keyboard accessibility for non interactive elements
96
79
  if (event.target === event.currentTarget &&
97
80
  !isNativeButton(event.target) &&
98
81
  event.key === ' ' &&
@@ -106,14 +89,6 @@ const useActive = ({ onKeyDown, onKeyUp, onClick, disabled = false, } = {}) => {
106
89
  };
107
90
  };
108
91
 
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
92
  function useResultRef(cb) {
118
93
  const renderRef = useRef(0);
119
94
  const ref = useRef();
@@ -125,20 +100,6 @@ function useResultRef(cb) {
125
100
  }
126
101
 
127
102
  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
103
  function useId(id, prefixMode) {
143
104
  const { current: resultId } = useResultRef(() => {
144
105
  if (id) {
@@ -161,16 +122,6 @@ const visuallyHiddenStyles = `
161
122
  white-space: nowrap;
162
123
  width: 1px;
163
124
  `;
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
125
  const useAnnouncer = (id) => {
175
126
  const _id = useId(id ? `rc-announcer-${id}` : 'rc-announcer', !id);
176
127
  const ref = useRef(null);
@@ -195,7 +146,6 @@ const useAnnouncer = (id) => {
195
146
  });
196
147
  };
197
148
  }, [_id]);
198
- /** announce method, call with message that want let reader speak */
199
149
  return useCallback((message) => {
200
150
  const el = ref.current;
201
151
  if (el && message) {
@@ -217,34 +167,10 @@ const useAriaHideOutside = (props = {}) => {
217
167
  }, [enable, externalWindow, document?.body]);
218
168
  return {
219
169
  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
170
  ariaHideEnabled: enable,
225
171
  };
226
172
  };
227
173
 
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
174
  const useAudio = (init) => {
249
175
  const audioRef = useResultRef(() => {
250
176
  const audio = new Audio();
@@ -254,20 +180,12 @@ const useAudio = (init) => {
254
180
  useEffect(() => {
255
181
  return () => {
256
182
  audioRef.current.srcObject = null;
257
- // just for remove ref, confirm browser GC work correctly
258
183
  audioRef.current = null;
259
184
  };
260
- // eslint-disable-next-line react-hooks/exhaustive-deps
261
185
  }, []);
262
186
  return audioRef.current;
263
187
  };
264
188
 
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
189
  function usePrevious(cb, isDefault) {
272
190
  const ref = useRef(isDefault ? cb() : undefined);
273
191
  useEffect(() => {
@@ -276,11 +194,6 @@ function usePrevious(cb, isDefault) {
276
194
  return ref.current;
277
195
  }
278
196
 
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
197
  const useChange = (cb, listenCb) => {
285
198
  const countRef = useRef(0);
286
199
  const valueRef = useRef();
@@ -292,11 +205,6 @@ const useChange = (cb, listenCb) => {
292
205
  }
293
206
  return valueRef.current;
294
207
  };
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
208
  const useDepsChange = (cb, deps) => {
301
209
  const countRef = useRef(0);
302
210
  const valueRef = useRef();
@@ -310,24 +218,16 @@ const useDepsChange = (cb, deps) => {
310
218
  return valueRef.current;
311
219
  };
312
220
 
313
- // base https://github.com/mui/material-ui/blob/master/packages/mui-base/src/ClickAwayListener/ClickAwayListener.tsx
314
221
  function clickedRootScrollbar(event, doc) {
315
222
  return (doc.documentElement.clientWidth < event.clientX ||
316
223
  doc.documentElement.clientHeight < event.clientY);
317
224
  }
318
- /**
319
- * @deprecated Use `ClickAwayListener` from `@mui/base` instead.
320
- *
321
- * @param onClickAway Callback fired when a "click away" event is detected.
322
- */
323
225
  const useClickAway = (onClickAway, props = {}) => {
324
226
  const { disableReactTree = false, isEnabled = true } = props;
325
227
  const movedRef = useRef(false);
326
228
  const activatedRef = useRef(false);
327
229
  const syntheticEventRef = useRef(false);
328
230
  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
231
  const clickAwayRef = useEventCallback((e) => {
332
232
  setTarget(e);
333
233
  });
@@ -336,8 +236,6 @@ const useClickAway = (onClickAway, props = {}) => {
336
236
  if (!isEnabled)
337
237
  return;
338
238
  if (shouldActive) {
339
- // Ensure that this component is not "activated" synchronously.
340
- // https://github.com/facebook/react/issues/20074
341
239
  const timer = setTimeout(() => {
342
240
  activatedRef.current = true;
343
241
  }, 0);
@@ -348,50 +246,32 @@ const useClickAway = (onClickAway, props = {}) => {
348
246
  }
349
247
  return undefined;
350
248
  }, [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
249
  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
250
  const insideReactTree = syntheticEventRef.current;
361
251
  syntheticEventRef.current = false;
362
252
  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
253
  if (!activatedRef.current ||
367
254
  !target ||
368
255
  ('clientX' in event && clickedRootScrollbar(event, doc))) {
369
256
  return;
370
257
  }
371
- // Do not act if user performed touchmove
372
258
  if (movedRef.current) {
373
259
  movedRef.current = false;
374
260
  return;
375
261
  }
376
262
  let insideDOM;
377
- // If not enough, can use https://github.com/DieterHolvoet/event-propagation-path/blob/master/propagationPath.js
378
263
  if (event.composedPath) {
379
264
  insideDOM = event.composedPath().indexOf(target) > -1;
380
265
  }
381
266
  else {
382
267
  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);
268
+ !doc.documentElement.contains(event.target) ||
269
+ target.contains(event.target);
389
270
  }
390
271
  if (!insideDOM && (disableReactTree || !insideReactTree)) {
391
272
  onClickAway(event);
392
273
  }
393
274
  });
394
- // Keep track of mouse/touch events that bubbled up through the portal.
395
275
  const handleSynthetic = (e) => {
396
276
  syntheticEventRef.current = true;
397
277
  };
@@ -423,37 +303,13 @@ const useClickAway = (onClickAway, props = {}) => {
423
303
  };
424
304
  };
425
305
 
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
306
  const useDebounce = (fn, debounceTime = 200, options) => {
434
307
  const memoFn = useEventCallback(fn);
435
- // eslint-disable-next-line react-hooks/exhaustive-deps
436
308
  const debounceFn = useCallback(debounce(memoFn, debounceTime, options), []);
437
- // eslint-disable-next-line react-hooks/exhaustive-deps
438
309
  useEffect(() => () => debounceFn.cancel(), []);
439
310
  return debounceFn;
440
311
  };
441
312
 
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
313
  function useEventListener(target, key, callback, options) {
458
314
  const { startImmediately = true, ...addEventListenerOptions } = options ?? {};
459
315
  const listener = useEventCallback(callback);
@@ -472,7 +328,6 @@ function useEventListener(target, key, callback, options) {
472
328
  bindRef.current();
473
329
  }
474
330
  return cancelRef.current;
475
- // eslint-disable-next-line react-hooks/exhaustive-deps
476
331
  }, [target]);
477
332
  return {
478
333
  listen: () => bindRef.current(),
@@ -481,27 +336,7 @@ function useEventListener(target, key, callback, options) {
481
336
  }
482
337
 
483
338
  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) => {
339
+ const useEver = (value, targetValue = defaultEverTargetValue) => {
505
340
  const result = useRef(false);
506
341
  if (!result.current) {
507
342
  result.current = targetValue(value);
@@ -509,8 +344,6 @@ targetValue = defaultEverTargetValue) => {
509
344
  return result.current;
510
345
  };
511
346
 
512
- /* eslint-disable sonarjs/no-collapsible-if */
513
- /* eslint-disable sonarjs/prefer-single-boolean-return */
514
347
  let hadKeyboardEvent = true;
515
348
  let hadFocusVisibleRecently = false;
516
349
  const hadFocusVisibleRecentlyTimeout = new unstable_Timeout();
@@ -529,13 +362,6 @@ const inputTypesWhitelist = {
529
362
  datetime: true,
530
363
  'datetime-local': true,
531
364
  };
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
365
  function focusTriggersKeyboardModality(node) {
540
366
  const { type, tagName } = node;
541
367
  if (tagName === 'INPUT' &&
@@ -551,35 +377,17 @@ function focusTriggersKeyboardModality(node) {
551
377
  }
552
378
  return false;
553
379
  }
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
380
  function handleKeyDown(event) {
562
381
  if (event.metaKey || event.altKey || event.ctrlKey) {
563
382
  return;
564
383
  }
565
384
  hadKeyboardEvent = true;
566
385
  }
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
386
  function handlePointerDown() {
575
387
  hadKeyboardEvent = false;
576
388
  }
577
389
  function handleVisibilityChange() {
578
390
  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
391
  if (hadFocusVisibleRecently) {
584
392
  hadKeyboardEvent = true;
585
393
  }
@@ -605,13 +413,7 @@ function isFocusVisible(event) {
605
413
  return target.matches(':focus-visible');
606
414
  }
607
415
  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
416
  }
613
- // No need for validFocusTarget check. The user does that by attaching it to
614
- // focusable events only.
615
417
  return hadKeyboardEvent || focusTriggersKeyboardModality(target);
616
418
  }
617
419
  function useIsFocusVisible() {
@@ -621,20 +423,8 @@ function useIsFocusVisible() {
621
423
  }
622
424
  }, []);
623
425
  const isFocusVisibleRef = React.useRef(false);
624
- /**
625
- * Should be called if a blur event is fired
626
- */
627
426
  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
427
  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
428
  hadFocusVisibleRecently = true;
639
429
  hadFocusVisibleRecentlyTimeout.start(100, () => {
640
430
  hadFocusVisibleRecently = false;
@@ -644,9 +434,6 @@ function useIsFocusVisible() {
644
434
  }
645
435
  return false;
646
436
  }
647
- /**
648
- * Should be called if a blur event is fired
649
- */
650
437
  function handleFocusVisible(event) {
651
438
  if (isFocusVisible(event)) {
652
439
  isFocusVisibleRef.current = true;
@@ -662,11 +449,6 @@ function useIsFocusVisible() {
662
449
  };
663
450
  }
664
451
 
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
452
  const useFocusVisible = ({ onFocusVisible, onFocus, onBlur, within = false, } = {}) => {
671
453
  const { isFocusVisibleRef, onFocus: handleFocusVisible, onBlur: handleBlurVisible, ref: focusVisibleRef, } = useIsFocusVisible();
672
454
  const [focusVisible, setFocusVisible] = useState(false);
@@ -675,15 +457,6 @@ const useFocusVisible = ({ onFocusVisible, onFocus, onBlur, within = false, } =
675
457
  handleBlurVisible(event);
676
458
  if (isFocusVisibleRef.current === false) {
677
459
  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
460
  if (within &&
688
461
  !event.currentTarget.contains(event.relatedTarget)) {
689
462
  setFocusVisibleWithin(false);
@@ -726,17 +499,12 @@ const useFocusVisible = ({ onFocusVisible, onFocus, onBlur, within = false, } =
726
499
  onBlur: handleBlur,
727
500
  onFocus: handleFocus,
728
501
  },
729
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
730
502
  focusVisibleRef: focusVisibleRef,
731
503
  focusVisible,
732
504
  focusVisibleWithin,
733
505
  };
734
506
  };
735
507
 
736
- /**
737
- * a method for trigger force update
738
- * @returns force update method
739
- */
740
508
  function useForceUpdate() {
741
509
  const [, dispatch] = useState(Object.create(null));
742
510
  return useEventCallback(() => {
@@ -744,39 +512,9 @@ function useForceUpdate() {
744
512
  });
745
513
  }
746
514
 
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
515
  const useForkRef = unstable_useForkRef;
765
516
 
766
517
  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
518
  const createGlobalListener = (key, listener, options, { customKey = key, target: targetProp = window, } = {}) => {
781
519
  let listening = false;
782
520
  const getMapValue = () => globalListenerEventMap.get(customKey);
@@ -818,63 +556,18 @@ const createGlobalListener = (key, listener, options, { customKey = key, target:
818
556
  };
819
557
  return {
820
558
  listen: () => addListener(),
821
- /**
822
- * remove listener
823
- */
824
559
  remove: () => removeListener(),
825
- /**
826
- * current listener state
827
- */
828
560
  get state() {
829
561
  return {
830
- /**
831
- * current listener state count
832
- */
833
562
  get count() {
834
563
  return getMapValue()?.listeners.size || 0;
835
564
  },
836
- /**
837
- * is that be listening
838
- */
839
565
  listening,
840
566
  };
841
567
  },
842
568
  };
843
569
  };
844
570
 
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
571
  function useGlobalListener(key, listener, ...args) {
879
572
  const { options, config } = getListenerOverloadOption(args);
880
573
  const { customKey = key, target: targetProp = window, startImmediately = true, } = config || {};
@@ -884,7 +577,6 @@ function useGlobalListener(key, listener, ...args) {
884
577
  target: targetProp,
885
578
  }));
886
579
  if (process.env.NODE_ENV !== 'production') {
887
- // eslint-disable-next-line react-hooks/rules-of-hooks
888
580
  useEffect(() => {
889
581
  if (customKey === key) {
890
582
  if (Object.keys(options).length > 0) {
@@ -902,7 +594,6 @@ function useGlobalListener(key, listener, ...args) {
902
594
  });
903
595
  }
904
596
  }
905
- // eslint-disable-next-line react-hooks/exhaustive-deps
906
597
  }, []);
907
598
  }
908
599
  useEffect(() => {
@@ -910,7 +601,6 @@ function useGlobalListener(key, listener, ...args) {
910
601
  globalListener.listen();
911
602
  }
912
603
  return globalListener.remove;
913
- // eslint-disable-next-line react-hooks/exhaustive-deps
914
604
  }, []);
915
605
  return globalListener;
916
606
  }
@@ -936,10 +626,6 @@ function getListenerOverloadOption(args) {
936
626
  return { options, config };
937
627
  }
938
628
 
939
- /**
940
- * group hidden logic, when binding events element focus,
941
- * that hiddenRef will auto be `tabindex={-1}` *
942
- */
943
629
  const useHiddenTabindex = (target) => {
944
630
  const handleFocus = useEventCallback(() => {
945
631
  const hiddenElm = getRefElement(target);
@@ -1014,20 +700,6 @@ const useHoverDirty = ({ target, disabled = false, }) => {
1014
700
  };
1015
701
  };
1016
702
 
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
703
  const useImgLoaded = ({ crossOrigin, referrerPolicy, src, srcSet, }) => {
1032
704
  const [loaded, setLoaded] = useState(false);
1033
705
  useEffect(() => {
@@ -1064,38 +736,7 @@ const useImgLoaded = ({ crossOrigin, referrerPolicy, src, srcSet, }) => {
1064
736
  return loaded;
1065
737
  };
1066
738
 
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) => {
739
+ const useInterval = (callback, delay = 0, startImmediately = true) => {
1099
740
  const intervalRef = useRef();
1100
741
  const countRef = useRef(0);
1101
742
  const getRunning = useEventCallback(() => !!intervalRef.current);
@@ -1128,31 +769,13 @@ startImmediately = true) => {
1128
769
  return () => cancel();
1129
770
  }, [cancel, play, startImmediately]);
1130
771
  return {
1131
- /**
1132
- * cancel current interval
1133
- */
1134
772
  cancel,
1135
- /**
1136
- * pause current interval
1137
- */
1138
773
  pause,
1139
- /**
1140
- * play current interval
1141
- */
1142
774
  play,
1143
- /**
1144
- * return current interval running state
1145
- */
1146
775
  getRunning,
1147
776
  };
1148
777
  };
1149
778
 
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
779
  const useKeyDownOnce = (includeKeys, { onKeyDown, onKeyUp }) => {
1157
780
  const isKeyDownMapRef = useRef({});
1158
781
  const handleKeyDown = useEventCallback((e) => {
@@ -1182,13 +805,8 @@ const useKeyDownOnce = (includeKeys, { onKeyDown, onKeyUp }) => {
1182
805
  };
1183
806
  };
1184
807
 
1185
- /**
1186
- * that can move index in column array
1187
- */
1188
808
  function moveIndexInTwoDimension(direction, options) {
1189
809
  const { currIndex, columns, total, infinite } = options;
1190
- // start index never small than 0
1191
- // const index = Math.max(currIndex, 0);
1192
810
  const lastIndex = total - 1;
1193
811
  const isInOrder = infinite === 'order';
1194
812
  const fillTotal = Math.ceil(total / columns) * columns;
@@ -1237,8 +855,6 @@ function moveIndexInTwoDimension(direction, options) {
1237
855
  if (!infinite && resultIndex >= total) {
1238
856
  return currIndex;
1239
857
  }
1240
- // * when that bigger then sourceTotal, mean that touch end line
1241
- // * move to current full latest and move again.
1242
858
  if (resultIndex >= total) {
1243
859
  if (direction === 'ArrowRight') {
1244
860
  resultIndex = fillTotal - 1;
@@ -1251,9 +867,6 @@ function moveIndexInTwoDimension(direction, options) {
1251
867
  return resultIndex;
1252
868
  }
1253
869
 
1254
- /**
1255
- * use keyboard move index in two dimensional
1256
- */
1257
870
  const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocusedIndexChange, getOptionDisabled, getOptionSearchText, columns = 1, endSearchDebounceTime = 500, }) => {
1258
871
  const total = options.length;
1259
872
  const searchText = useRef('');
@@ -1280,7 +893,6 @@ const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocus
1280
893
  let findDirection;
1281
894
  if (total === 0)
1282
895
  return;
1283
- // eslint-disable-next-line no-constant-condition
1284
896
  while (true) {
1285
897
  const toIndex = findDirection
1286
898
  ? findDirection === 'next'
@@ -1302,22 +914,16 @@ const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocus
1302
914
  }
1303
915
  break;
1304
916
  }
1305
- // * mean that item is disabled, use same direction find next
1306
917
  else {
1307
- // * find toIndex is same as current, mean that already find all
1308
918
  if (focusedIndexRef.current !== -1 &&
1309
919
  toIndex === Math.max(focusedIndexRef.current, 0)) {
1310
920
  break;
1311
921
  }
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
922
  if (focusedIndexRef.current === -1) {
1315
- // from bottom to top and the first item in the list is not focusable
1316
923
  if ((Math.abs(currIndex - toIndex) !== 1 || currIndex > toIndex) &&
1317
924
  toIndex === 0) {
1318
925
  break;
1319
926
  }
1320
- // from top to bottom and the last item in the list is not focusable
1321
927
  if (Math.abs(currIndex - toIndex) === 1 &&
1322
928
  currIndex < toIndex &&
1323
929
  toIndex === total - 1) {
@@ -1325,7 +931,6 @@ const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocus
1325
931
  }
1326
932
  }
1327
933
  currIndex = toIndex;
1328
- // * if that is Hom or End, find use increase or decrease
1329
934
  if (key === 'Home') {
1330
935
  findDirection = 'next';
1331
936
  }
@@ -1335,16 +940,13 @@ const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocus
1335
940
  }
1336
941
  }
1337
942
  };
1338
- const getPrevFocusableOption = useCallback((index = focusedIndexRef.current,
1339
- /** is that infinite search */
1340
- isInfinite = true) => {
943
+ const getPrevFocusableOption = useCallback((index = focusedIndexRef.current, isInfinite = true) => {
1341
944
  for (let i = index; i >= 0; i--) {
1342
945
  if (!getOptionDisabled?.(options[i])) {
1343
946
  return i;
1344
947
  }
1345
948
  }
1346
949
  if (isInfinite) {
1347
- // loop find
1348
950
  for (let i = total - 1; i > index; i--) {
1349
951
  if (!getOptionDisabled?.(options[i])) {
1350
952
  return i;
@@ -1353,16 +955,13 @@ const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocus
1353
955
  }
1354
956
  return -1;
1355
957
  }, [focusedIndexRef, getOptionDisabled, options, total]);
1356
- const getNextFocusableOption = useCallback((index = focusedIndexRef.current,
1357
- /** is that infinite search */
1358
- isInfinite = true) => {
958
+ const getNextFocusableOption = useCallback((index = focusedIndexRef.current, isInfinite = true) => {
1359
959
  for (let i = index; i < total; i++) {
1360
960
  if (!getOptionDisabled?.(options[i])) {
1361
961
  return i;
1362
962
  }
1363
963
  }
1364
964
  if (isInfinite) {
1365
- // loop find
1366
965
  for (let i = 0; i < index; i++) {
1367
966
  if (!getOptionDisabled?.(options[i])) {
1368
967
  return i;
@@ -1372,26 +971,13 @@ const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocus
1372
971
  return -1;
1373
972
  }, [focusedIndexRef, getOptionDisabled, options, total]);
1374
973
  return {
1375
- /**
1376
- * Get previous focusable option(include current index).
1377
- */
1378
974
  getPrevFocusableOption,
1379
- /**
1380
- * Get next focusable option(include current index).
1381
- */
1382
975
  getNextFocusableOption,
1383
- /**
1384
- * Use to handle keyboard event.
1385
- */
1386
976
  onKeyFocusedIndexHandle,
1387
977
  };
1388
978
  };
1389
979
 
1390
980
  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
981
  const useOnlyOneFocusable = ({ focusedIndexRef, containerRef, }) => {
1396
982
  const moveFocusedId = useId(MOVE_ATTR_KEY, true);
1397
983
  const getIndexElm = (toIndex) => containerRef.current?.querySelector(`[${moveFocusedId}="${toIndex}"]`);
@@ -1424,28 +1010,6 @@ const useOnlyOneFocusable = ({ focusedIndexRef, containerRef, }) => {
1424
1010
  };
1425
1011
  };
1426
1012
 
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
1013
  const useTouchMouseEvent = ({ onMouseDown: onMouseDownArg, onMouseUp: onMouseUpArg, onTouchStart: onTouchStartArg, onTouchEnd: onTouchEndArg, }, { actionRef } = {}) => {
1450
1014
  const stopMouseEventRef = useRef(false);
1451
1015
  const onTouchStart = useEventCallback((e) => {
@@ -1491,9 +1055,6 @@ const useTouchMouseEvent = ({ onMouseDown: onMouseDownArg, onMouseUp: onMouseUpA
1491
1055
  onMouseUp,
1492
1056
  };
1493
1057
  };
1494
- /**
1495
- * check is element equal or contains target element
1496
- */
1497
1058
  const isElmEqualOrContain = (sourceTarget, containTarget) => {
1498
1059
  const elm = getRefElement(sourceTarget);
1499
1060
  const target = getRefElement(containTarget);
@@ -1503,28 +1064,18 @@ const isElmEqualOrContain = (sourceTarget, containTarget) => {
1503
1064
  const isTouchEvent = (ev) => {
1504
1065
  return 'touches' in ev;
1505
1066
  };
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
1067
  const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTouchEnd: onTouchEndArg, onMouseDown, onKeyDown, onKeyUp, onMouseUp: onMouseUpArg, } = {}, { isPreventDefault = true, delay = 300, externalWindow = window, } = {}) => {
1515
1068
  const isEmittedRef = useRef(false);
1516
1069
  const isA11yDownRef = useRef(false);
1517
1070
  const actionRef = useRef(null);
1518
1071
  const reasonRef = useRef('click');
1519
1072
  const emitLongPress = useDebounce((e) => {
1520
- // * only when have onPress set have emit
1521
1073
  if (onPress) {
1522
1074
  onPress(e, reasonRef.current);
1523
1075
  isEmittedRef.current = true;
1524
1076
  }
1525
1077
  }, delay);
1526
1078
  const end = (e, isInside) => {
1527
- // ! mouse up only trigger when up on element, so we host that in document
1528
1079
  globalMouseUpListener.remove();
1529
1080
  if (!isEmittedRef.current && isInside) {
1530
1081
  onTap?.(e, reasonRef.current);
@@ -1534,7 +1085,6 @@ const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTou
1534
1085
  emitLongPress.cancel();
1535
1086
  };
1536
1087
  const start = (e) => {
1537
- // ! mouse up only trigger when up on element, so we host that in document
1538
1088
  globalMouseUpListener.listen();
1539
1089
  const isTouch = e.type === 'touchstart';
1540
1090
  if (isTouch) {
@@ -1544,7 +1094,6 @@ const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTou
1544
1094
  };
1545
1095
  const { onMouseUp, onTouchEnd, onTouchStart, ...events } = useTouchMouseEvent({
1546
1096
  onTouchStart: (e) => {
1547
- // prevent long touch leave element focus
1548
1097
  if (isPreventDefault) {
1549
1098
  e.preventDefault();
1550
1099
  }
@@ -1552,7 +1101,6 @@ const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTou
1552
1101
  start(e);
1553
1102
  },
1554
1103
  onTouchEnd: (e, isInside) => {
1555
- // prevent leave element focus
1556
1104
  if (isPreventDefault) {
1557
1105
  e.preventDefault();
1558
1106
  }
@@ -1567,7 +1115,6 @@ const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTou
1567
1115
  start(e);
1568
1116
  },
1569
1117
  onMouseUp: (e, isInside) => {
1570
- // prevent leave element focus
1571
1118
  if (isPreventDefault) {
1572
1119
  e.preventDefault();
1573
1120
  }
@@ -1575,11 +1122,9 @@ const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTou
1575
1122
  end(e, isInside);
1576
1123
  },
1577
1124
  }, { actionRef });
1578
- // use event listener for make event `passive` work in safari
1579
1125
  useEventListener(actionRef, 'touchstart', onTouchStart, {
1580
1126
  passive: false,
1581
1127
  });
1582
- // use event listener for make event `passive` work in safari
1583
1128
  useEventListener(actionRef, 'touchend', onTouchEnd, {
1584
1129
  passive: false,
1585
1130
  });
@@ -1589,7 +1134,6 @@ const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTou
1589
1134
  });
1590
1135
  const handleDeleteKeyDown = useA11yKeyEvent((e) => {
1591
1136
  reasonRef.current = 'keyboard';
1592
- // * keydown when hold will trigger many time, show when current is a11y down, not trigger again
1593
1137
  if (!isA11yDownRef.current) {
1594
1138
  emitLongPress(e);
1595
1139
  }
@@ -1613,20 +1157,6 @@ const useLongPress = ({ onTap, onPress }, { onTouchStart: onTouchStartArg, onTou
1613
1157
  };
1614
1158
  };
1615
1159
 
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
1160
  const useMountState = () => {
1631
1161
  const isMountedRef = useRef(false);
1632
1162
  useEffect(() => {
@@ -1638,9 +1168,6 @@ const useMountState = () => {
1638
1168
  return isMountedRef;
1639
1169
  };
1640
1170
 
1641
- /**
1642
- * only trigger when re-render (in useLayoutEffect), not trigger on component first render
1643
- */
1644
1171
  const useOnLayoutReRender = (cb, deps) => {
1645
1172
  const count = useRef(0);
1646
1173
  const method = useEventCallback(cb);
@@ -1648,17 +1175,12 @@ const useOnLayoutReRender = (cb, deps) => {
1648
1175
  if (count.current === 0) {
1649
1176
  count.current = 1;
1650
1177
  return () => {
1651
- //
1652
1178
  };
1653
1179
  }
1654
1180
  return method();
1655
- // eslint-disable-next-line react-hooks/exhaustive-deps
1656
1181
  }, deps);
1657
1182
  };
1658
1183
 
1659
- /**
1660
- * only trigger when re-render, not trigger on component first render
1661
- */
1662
1184
  const useOnReRender = (cb, deps) => {
1663
1185
  const count = useRef(0);
1664
1186
  const method = useEventCallback(cb);
@@ -1666,28 +1188,13 @@ const useOnReRender = (cb, deps) => {
1666
1188
  if (count.current === 0) {
1667
1189
  count.current = 1;
1668
1190
  return () => {
1669
- //
1670
1191
  };
1671
1192
  }
1672
1193
  return method();
1673
- // eslint-disable-next-line react-hooks/exhaustive-deps
1674
1194
  }, deps);
1675
1195
  };
1676
1196
 
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 = {}) => {
1197
+ const useResizeObserver = (target, callback, performance = {}) => {
1691
1198
  const { mode = 'debounce', time: performanceTime = 200, ignoreFireWhenObserve, options, } = performance;
1692
1199
  const countRef = useRef(0);
1693
1200
  const cb = useEventCallback((...args) => {
@@ -1725,29 +1232,14 @@ performance = {}) => {
1725
1232
  return () => {
1726
1233
  observer.disconnect();
1727
1234
  };
1728
- // eslint-disable-next-line react-hooks/exhaustive-deps
1729
1235
  }, [el]);
1730
1236
  return observerRef.current;
1731
1237
  };
1732
1238
 
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 = {
1239
+ const useOverflow = (target, cb, performance = {
1747
1240
  mode: 'throttle',
1748
1241
  time: 200,
1749
1242
  }) => {
1750
- // * not have default value for that can trigger change first time.
1751
1243
  const showRef = useRef();
1752
1244
  return useResizeObserver(target, () => {
1753
1245
  const element = getRefElement(target);
@@ -1757,7 +1249,6 @@ performance = {
1757
1249
  const show = scrollWidth > clientWidth || scrollHeight > clientHeight;
1758
1250
  if (showRef.current === show)
1759
1251
  return;
1760
- // only emit when state change
1761
1252
  showRef.current = show;
1762
1253
  cb(show, {
1763
1254
  scrollWidth,
@@ -1768,38 +1259,10 @@ performance = {
1768
1259
  }, performance);
1769
1260
  };
1770
1261
 
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
1262
  const useRefState = (value, customUpdate) => {
1791
- // eslint-disable-next-line react-hooks/rules-of-hooks
1792
1263
  const forceUpdate = customUpdate ?? useForceUpdate();
1793
1264
  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) => {
1265
+ const setInnerValue = useEventCallback((toValue, isUpdate = true) => {
1803
1266
  innerValueRef.current = toValue;
1804
1267
  if (isUpdate) {
1805
1268
  forceUpdate();
@@ -1808,10 +1271,6 @@ const useRefState = (value, customUpdate) => {
1808
1271
  return [innerValueRef, setInnerValue];
1809
1272
  };
1810
1273
 
1811
- /**
1812
- * get all matched breakpoints
1813
- * @param defaultBreakpoint use defaultBreakpoint when Responsive init (will pass undefined)
1814
- */
1815
1274
  const useResponsiveMatch = (defaultBreakpoint) => {
1816
1275
  const _breakpoint = useResponsiveContext();
1817
1276
  const breakpoint = _breakpoint === undefined ? defaultBreakpoint : _breakpoint;
@@ -1821,13 +1280,6 @@ const useResponsiveMatch = (defaultBreakpoint) => {
1821
1280
  return matchDetail.matchMap;
1822
1281
  };
1823
1282
 
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
1283
  const useSleep = () => {
1832
1284
  const timeoutIdRef = useRef();
1833
1285
  const rejectRef = useRef();
@@ -1857,36 +1309,13 @@ const useSleep = () => {
1857
1309
  };
1858
1310
  }, []);
1859
1311
  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
1312
  sleep,
1865
- /**
1866
- * cancel current sleep timer
1867
- */
1868
1313
  cancel,
1869
- /**
1870
- * return is current is sleeping
1871
- */
1872
1314
  getSleeping: () => sleepingRef.current,
1873
1315
  };
1874
1316
  };
1875
1317
 
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) => {
1318
+ const useRetry = (cb, options) => {
1890
1319
  const { sleep, cancel: cancelSleep } = useSleep();
1891
1320
  const isCancelRef = useRef(true);
1892
1321
  const complete = () => {
@@ -1895,7 +1324,6 @@ options) => {
1895
1324
  };
1896
1325
  const cancel = () => {
1897
1326
  if (!isCancelRef.current) {
1898
- // * when outside cancel
1899
1327
  options?.onCancel?.();
1900
1328
  complete();
1901
1329
  }
@@ -1905,7 +1333,6 @@ options) => {
1905
1333
  cancel();
1906
1334
  isCancelRef.current = false;
1907
1335
  options?.onStart?.();
1908
- // * when exec success, just return directly.
1909
1336
  if (await cb(...args)) {
1910
1337
  if (!isCancelRef.current) {
1911
1338
  options?.onSuccess?.();
@@ -1937,21 +1364,11 @@ options) => {
1937
1364
  complete();
1938
1365
  };
1939
1366
  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
1367
  retry,
1945
- /**
1946
- * cancel current retry
1947
- */
1948
1368
  cancel,
1949
1369
  };
1950
1370
  };
1951
1371
 
1952
- /**
1953
- * @param onTabOut Callback fired when a "tab out" event is detected.
1954
- */
1955
1372
  const useTabOut = (onTabOut) => {
1956
1373
  const lastKeyDownIsTabRef = useRef(false);
1957
1374
  const handleKeyDown = (e) => {
@@ -1977,28 +1394,13 @@ const useTabOut = (onTabOut) => {
1977
1394
  };
1978
1395
  };
1979
1396
 
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
1397
  const useThrottle = (fn, throttleTime = 200, options) => {
1988
1398
  const memoFn = useEventCallback(fn);
1989
- // eslint-disable-next-line react-hooks/exhaustive-deps
1990
1399
  const throttleFn = useCallback(throttle(memoFn, throttleTime, options), []);
1991
- // eslint-disable-next-line react-hooks/exhaustive-deps
1992
1400
  useEffect(() => () => throttleFn.cancel(), []);
1993
1401
  return throttleFn;
1994
1402
  };
1995
1403
 
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
1404
  const useTestRunnerDetection = () => {
2003
1405
  return useMemo(() => {
2004
1406
  return (typeof window !== 'undefined' &&
@@ -2009,10 +1411,7 @@ const useTestRunnerDetection = () => {
2009
1411
  function useTimeout(callback, delay) {
2010
1412
  const savedCallback = useRef(callback);
2011
1413
  savedCallback.current = callback;
2012
- // Set up the timeout.
2013
1414
  useEffect(() => {
2014
- // Don't schedule if no delay is specified.
2015
- // Note: 0 is a valid value for delay.
2016
1415
  if (!delay && delay !== 0) {
2017
1416
  return;
2018
1417
  }
@@ -2021,28 +1420,12 @@ function useTimeout(callback, delay) {
2021
1420
  }, [delay]);
2022
1421
  }
2023
1422
 
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
1423
  const isFontLoadingAPIAvailable = () => {
2029
1424
  return (typeof document !== 'undefined' &&
2030
1425
  document.fonts &&
2031
1426
  typeof document.fonts.ready === 'object' &&
2032
1427
  typeof document.fonts.ready.then === 'function');
2033
1428
  };
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
1429
  const useFontLoaded = (enabled = true) => {
2047
1430
  const [loaded, setLoaded] = useState(() => !enabled || !isFontLoadingAPIAvailable());
2048
1431
  useEffect(() => {
@@ -2062,14 +1445,6 @@ const useFontLoaded = (enabled = true) => {
2062
1445
  return loaded;
2063
1446
  };
2064
1447
 
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
1448
  const useBodyScrollLock = (isLocked, targetElement, options = {}) => {
2074
1449
  const { disabled = false, reserveScrollBarGap = true, allowTouchMove, } = options;
2075
1450
  const { document } = useWindowContext();