@mantine/hooks 9.0.1 → 9.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/cjs/index.cjs +8 -0
  2. package/cjs/use-click-outside/use-click-outside.cjs +11 -8
  3. package/cjs/use-click-outside/use-click-outside.cjs.map +1 -1
  4. package/cjs/use-clipboard/use-clipboard.cjs +13 -6
  5. package/cjs/use-clipboard/use-clipboard.cjs.map +1 -1
  6. package/cjs/use-collapse/use-collapse.cjs +1 -1
  7. package/cjs/use-collapse/use-collapse.cjs.map +1 -1
  8. package/cjs/use-collapse/use-horizontal-collapse.cjs +4 -3
  9. package/cjs/use-collapse/use-horizontal-collapse.cjs.map +1 -1
  10. package/cjs/use-counter/use-counter.cjs +12 -3
  11. package/cjs/use-counter/use-counter.cjs.map +1 -1
  12. package/cjs/use-debounced-callback/use-debounced-callback.cjs +28 -4
  13. package/cjs/use-debounced-callback/use-debounced-callback.cjs.map +1 -1
  14. package/cjs/use-debounced-state/use-debounced-state.cjs +1 -1
  15. package/cjs/use-debounced-state/use-debounced-state.cjs.map +1 -1
  16. package/cjs/use-debounced-value/use-debounced-value.cjs +22 -2
  17. package/cjs/use-debounced-value/use-debounced-value.cjs.map +1 -1
  18. package/cjs/use-event-listener/use-event-listener.cjs.map +1 -1
  19. package/cjs/use-favicon/use-favicon.cjs +3 -1
  20. package/cjs/use-favicon/use-favicon.cjs.map +1 -1
  21. package/cjs/use-fetch/use-fetch.cjs +7 -4
  22. package/cjs/use-fetch/use-fetch.cjs.map +1 -1
  23. package/cjs/use-file-dialog/use-file-dialog.cjs +1 -0
  24. package/cjs/use-file-dialog/use-file-dialog.cjs.map +1 -1
  25. package/cjs/use-floating-window/use-floating-window.cjs +33 -36
  26. package/cjs/use-floating-window/use-floating-window.cjs.map +1 -1
  27. package/cjs/use-focus-trap/use-focus-trap.cjs +12 -9
  28. package/cjs/use-focus-trap/use-focus-trap.cjs.map +1 -1
  29. package/cjs/use-fullscreen/use-fullscreen.cjs +3 -1
  30. package/cjs/use-fullscreen/use-fullscreen.cjs.map +1 -1
  31. package/cjs/use-headroom/use-headroom.cjs +4 -1
  32. package/cjs/use-headroom/use-headroom.cjs.map +1 -1
  33. package/cjs/use-hotkeys/parse-hotkey.cjs +1 -2
  34. package/cjs/use-hotkeys/parse-hotkey.cjs.map +1 -1
  35. package/cjs/use-hover/use-hover.cjs +1 -0
  36. package/cjs/use-hover/use-hover.cjs.map +1 -1
  37. package/cjs/use-in-viewport/use-in-viewport.cjs +11 -7
  38. package/cjs/use-in-viewport/use-in-viewport.cjs.map +1 -1
  39. package/cjs/use-interval/use-interval.cjs +15 -6
  40. package/cjs/use-interval/use-interval.cjs.map +1 -1
  41. package/cjs/use-local-storage/create-storage.cjs +1 -1
  42. package/cjs/use-local-storage/create-storage.cjs.map +1 -1
  43. package/cjs/use-long-press/use-long-press.cjs +6 -2
  44. package/cjs/use-long-press/use-long-press.cjs.map +1 -1
  45. package/cjs/use-mask/use-mask.cjs +457 -0
  46. package/cjs/use-mask/use-mask.cjs.map +1 -0
  47. package/cjs/use-mouse/use-mouse.cjs +2 -2
  48. package/cjs/use-mouse/use-mouse.cjs.map +1 -1
  49. package/cjs/use-move/use-move.cjs +8 -0
  50. package/cjs/use-move/use-move.cjs.map +1 -1
  51. package/cjs/use-orientation/use-orientation.cjs +8 -2
  52. package/cjs/use-orientation/use-orientation.cjs.map +1 -1
  53. package/cjs/use-os/use-os.cjs +1 -1
  54. package/cjs/use-os/use-os.cjs.map +1 -1
  55. package/cjs/use-radial-move/use-radial-move.cjs +4 -4
  56. package/cjs/use-radial-move/use-radial-move.cjs.map +1 -1
  57. package/cjs/use-roving-index/use-roving-index.cjs +200 -0
  58. package/cjs/use-roving-index/use-roving-index.cjs.map +1 -0
  59. package/cjs/use-scroll-direction/use-scroll-direction.cjs +7 -7
  60. package/cjs/use-scroll-direction/use-scroll-direction.cjs.map +1 -1
  61. package/cjs/use-scroll-into-view/use-scroll-into-view.cjs +14 -4
  62. package/cjs/use-scroll-into-view/use-scroll-into-view.cjs.map +1 -1
  63. package/cjs/use-scroll-spy/use-scroll-spy.cjs +7 -3
  64. package/cjs/use-scroll-spy/use-scroll-spy.cjs.map +1 -1
  65. package/cjs/use-scroller/use-scroller.cjs +3 -3
  66. package/cjs/use-scroller/use-scroller.cjs.map +1 -1
  67. package/cjs/use-set/use-set.cjs +6 -1
  68. package/cjs/use-set/use-set.cjs.map +1 -1
  69. package/cjs/use-throttled-callback/use-throttled-callback.cjs +3 -1
  70. package/cjs/use-throttled-callback/use-throttled-callback.cjs.map +1 -1
  71. package/cjs/use-timeout/use-timeout.cjs +3 -1
  72. package/cjs/use-timeout/use-timeout.cjs.map +1 -1
  73. package/cjs/use-viewport-size/use-viewport-size.cjs +3 -3
  74. package/cjs/use-viewport-size/use-viewport-size.cjs.map +1 -1
  75. package/cjs/use-window-scroll/use-window-scroll.cjs +2 -2
  76. package/cjs/use-window-scroll/use-window-scroll.cjs.map +1 -1
  77. package/esm/index.mjs +3 -1
  78. package/esm/use-click-outside/use-click-outside.mjs +11 -8
  79. package/esm/use-click-outside/use-click-outside.mjs.map +1 -1
  80. package/esm/use-clipboard/use-clipboard.mjs +14 -7
  81. package/esm/use-clipboard/use-clipboard.mjs.map +1 -1
  82. package/esm/use-collapse/use-collapse.mjs +1 -1
  83. package/esm/use-collapse/use-collapse.mjs.map +1 -1
  84. package/esm/use-collapse/use-horizontal-collapse.mjs +5 -4
  85. package/esm/use-collapse/use-horizontal-collapse.mjs.map +1 -1
  86. package/esm/use-counter/use-counter.mjs +12 -3
  87. package/esm/use-counter/use-counter.mjs.map +1 -1
  88. package/esm/use-debounced-callback/use-debounced-callback.mjs +28 -4
  89. package/esm/use-debounced-callback/use-debounced-callback.mjs.map +1 -1
  90. package/esm/use-debounced-state/use-debounced-state.mjs +1 -1
  91. package/esm/use-debounced-state/use-debounced-state.mjs.map +1 -1
  92. package/esm/use-debounced-value/use-debounced-value.mjs +22 -2
  93. package/esm/use-debounced-value/use-debounced-value.mjs.map +1 -1
  94. package/esm/use-event-listener/use-event-listener.mjs.map +1 -1
  95. package/esm/use-favicon/use-favicon.mjs +3 -1
  96. package/esm/use-favicon/use-favicon.mjs.map +1 -1
  97. package/esm/use-fetch/use-fetch.mjs +7 -4
  98. package/esm/use-fetch/use-fetch.mjs.map +1 -1
  99. package/esm/use-file-dialog/use-file-dialog.mjs +1 -0
  100. package/esm/use-file-dialog/use-file-dialog.mjs.map +1 -1
  101. package/esm/use-floating-window/use-floating-window.mjs +33 -36
  102. package/esm/use-floating-window/use-floating-window.mjs.map +1 -1
  103. package/esm/use-focus-trap/use-focus-trap.mjs +12 -9
  104. package/esm/use-focus-trap/use-focus-trap.mjs.map +1 -1
  105. package/esm/use-fullscreen/use-fullscreen.mjs +3 -1
  106. package/esm/use-fullscreen/use-fullscreen.mjs.map +1 -1
  107. package/esm/use-headroom/use-headroom.mjs +4 -1
  108. package/esm/use-headroom/use-headroom.mjs.map +1 -1
  109. package/esm/use-hotkeys/parse-hotkey.mjs +1 -2
  110. package/esm/use-hotkeys/parse-hotkey.mjs.map +1 -1
  111. package/esm/use-hover/use-hover.mjs +1 -0
  112. package/esm/use-hover/use-hover.mjs.map +1 -1
  113. package/esm/use-in-viewport/use-in-viewport.mjs +11 -7
  114. package/esm/use-in-viewport/use-in-viewport.mjs.map +1 -1
  115. package/esm/use-interval/use-interval.mjs +15 -6
  116. package/esm/use-interval/use-interval.mjs.map +1 -1
  117. package/esm/use-local-storage/create-storage.mjs +1 -1
  118. package/esm/use-local-storage/create-storage.mjs.map +1 -1
  119. package/esm/use-long-press/use-long-press.mjs +6 -2
  120. package/esm/use-long-press/use-long-press.mjs.map +1 -1
  121. package/esm/use-mask/use-mask.mjs +453 -0
  122. package/esm/use-mask/use-mask.mjs.map +1 -0
  123. package/esm/use-mouse/use-mouse.mjs +2 -2
  124. package/esm/use-mouse/use-mouse.mjs.map +1 -1
  125. package/esm/use-move/use-move.mjs +8 -0
  126. package/esm/use-move/use-move.mjs.map +1 -1
  127. package/esm/use-orientation/use-orientation.mjs +8 -2
  128. package/esm/use-orientation/use-orientation.mjs.map +1 -1
  129. package/esm/use-os/use-os.mjs +1 -1
  130. package/esm/use-os/use-os.mjs.map +1 -1
  131. package/esm/use-radial-move/use-radial-move.mjs +5 -5
  132. package/esm/use-radial-move/use-radial-move.mjs.map +1 -1
  133. package/esm/use-roving-index/use-roving-index.mjs +200 -0
  134. package/esm/use-roving-index/use-roving-index.mjs.map +1 -0
  135. package/esm/use-scroll-direction/use-scroll-direction.mjs +7 -7
  136. package/esm/use-scroll-direction/use-scroll-direction.mjs.map +1 -1
  137. package/esm/use-scroll-into-view/use-scroll-into-view.mjs +15 -5
  138. package/esm/use-scroll-into-view/use-scroll-into-view.mjs.map +1 -1
  139. package/esm/use-scroll-spy/use-scroll-spy.mjs +8 -4
  140. package/esm/use-scroll-spy/use-scroll-spy.mjs.map +1 -1
  141. package/esm/use-scroller/use-scroller.mjs +3 -3
  142. package/esm/use-scroller/use-scroller.mjs.map +1 -1
  143. package/esm/use-set/use-set.mjs +6 -1
  144. package/esm/use-set/use-set.mjs.map +1 -1
  145. package/esm/use-throttled-callback/use-throttled-callback.mjs +3 -1
  146. package/esm/use-throttled-callback/use-throttled-callback.mjs.map +1 -1
  147. package/esm/use-timeout/use-timeout.mjs +3 -1
  148. package/esm/use-timeout/use-timeout.mjs.map +1 -1
  149. package/esm/use-viewport-size/use-viewport-size.mjs +3 -3
  150. package/esm/use-viewport-size/use-viewport-size.mjs.map +1 -1
  151. package/esm/use-window-scroll/use-window-scroll.mjs +2 -2
  152. package/esm/use-window-scroll/use-window-scroll.mjs.map +1 -1
  153. package/lib/index.d.mts +5 -1
  154. package/lib/index.d.ts +5 -1
  155. package/lib/use-click-outside/use-click-outside.d.ts +1 -1
  156. package/lib/use-collapse/use-horizontal-collapse.d.ts +5 -0
  157. package/lib/use-counter/use-counter.d.ts +1 -0
  158. package/lib/use-debounced-callback/use-debounced-callback.d.ts +4 -0
  159. package/lib/use-debounced-value/use-debounced-value.d.ts +6 -1
  160. package/lib/use-event-listener/use-event-listener.d.ts +1 -1
  161. package/lib/use-hotkeys/parse-hotkey.d.ts +4 -5
  162. package/lib/use-long-press/use-long-press.d.ts +1 -0
  163. package/lib/use-mask/use-mask.d.ts +60 -0
  164. package/lib/use-roving-index/use-roving-index.d.ts +49 -0
  165. package/lib/use-scroll-into-view/use-scroll-into-view.d.ts +4 -1
  166. package/package.json +1 -1
@@ -23,8 +23,14 @@ function useOrientation({ defaultAngle = 0, defaultType = "landscape-primary", g
23
23
  });
24
24
  };
25
25
  require_use_isomorphic_effect.useIsomorphicEffect(() => {
26
- window.screen.orientation?.addEventListener("change", handleOrientationChange);
27
- return () => window.screen.orientation?.removeEventListener("change", handleOrientationChange);
26
+ if (window.screen.orientation) {
27
+ setOrientation({
28
+ angle: window.screen.orientation.angle,
29
+ type: window.screen.orientation.type
30
+ });
31
+ window.screen.orientation.addEventListener("change", handleOrientationChange);
32
+ return () => window.screen.orientation?.removeEventListener("change", handleOrientationChange);
33
+ }
28
34
  }, []);
29
35
  return orientation;
30
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-orientation.cjs","names":[],"sources":["../../src/use-orientation/use-orientation.ts"],"sourcesContent":["import { useState } from 'react';\nimport { useIsomorphicEffect } from '../use-isomorphic-effect/use-isomorphic-effect';\n\nexport interface UseOrientationOptions {\n /** Default angle value, used until the real can be retrieved\n * (during server side rendering and before js executes on the page)\n * If not provided, the default value is `0`\n * */\n defaultAngle?: number;\n\n /** Default angle value, used until the real can be retrieved\n * (during server side rendering and before js executes on the page)\n * If not provided, the default value is `'landscape-primary'`\n * */\n defaultType?: OrientationType;\n\n /** If true, the initial value will be resolved in useEffect (ssr safe)\n * If false, the initial value will be resolved in useLayoutEffect (ssr unsafe)\n * True by default.\n */\n getInitialValueInEffect?: boolean;\n}\n\nexport interface UseOrientationReturnType {\n angle: number;\n type: OrientationType;\n}\n\nfunction getInitialValue(\n initialValue: UseOrientationReturnType,\n getInitialValueInEffect: boolean\n): UseOrientationReturnType {\n if (getInitialValueInEffect) {\n return initialValue;\n }\n\n if (typeof window !== 'undefined' && 'screen' in window) {\n return {\n angle: window.screen.orientation?.angle ?? initialValue.angle,\n type: window.screen.orientation?.type ?? initialValue.type,\n };\n }\n\n return initialValue;\n}\n\nexport function useOrientation({\n defaultAngle = 0,\n defaultType = 'landscape-primary',\n getInitialValueInEffect = true,\n}: UseOrientationOptions = {}): UseOrientationReturnType {\n const [orientation, setOrientation] = useState<UseOrientationReturnType>(\n getInitialValue(\n {\n angle: defaultAngle,\n type: defaultType,\n },\n getInitialValueInEffect\n )\n );\n\n const handleOrientationChange = (event: Event) => {\n const target = event.currentTarget as ScreenOrientation;\n setOrientation({ angle: target?.angle || 0, type: target?.type || 'landscape-primary' });\n };\n\n useIsomorphicEffect(() => {\n window.screen.orientation?.addEventListener('change', handleOrientationChange);\n return () => window.screen.orientation?.removeEventListener('change', handleOrientationChange);\n }, []);\n\n return orientation;\n}\n\nexport namespace useOrientation {\n export type Options = UseOrientationOptions;\n export type ReturnType = UseOrientationReturnType;\n}\n"],"mappings":";;;;AA4BA,SAAS,gBACP,cACA,yBAC0B;AAC1B,KAAI,wBACF,QAAO;AAGT,KAAI,OAAO,WAAW,eAAe,YAAY,OAC/C,QAAO;EACL,OAAO,OAAO,OAAO,aAAa,SAAS,aAAa;EACxD,MAAM,OAAO,OAAO,aAAa,QAAQ,aAAa;EACvD;AAGH,QAAO;;AAGT,SAAgB,eAAe,EAC7B,eAAe,GACf,cAAc,qBACd,0BAA0B,SACD,EAAE,EAA4B;CACvD,MAAM,CAAC,aAAa,mBAAA,GAAA,MAAA,UAClB,gBACE;EACE,OAAO;EACP,MAAM;EACP,EACD,wBACD,CACF;CAED,MAAM,2BAA2B,UAAiB;EAChD,MAAM,SAAS,MAAM;AACrB,iBAAe;GAAE,OAAO,QAAQ,SAAS;GAAG,MAAM,QAAQ,QAAQ;GAAqB,CAAC;;AAG1F,+BAAA,0BAA0B;AACxB,SAAO,OAAO,aAAa,iBAAiB,UAAU,wBAAwB;AAC9E,eAAa,OAAO,OAAO,aAAa,oBAAoB,UAAU,wBAAwB;IAC7F,EAAE,CAAC;AAEN,QAAO"}
1
+ {"version":3,"file":"use-orientation.cjs","names":[],"sources":["../../src/use-orientation/use-orientation.ts"],"sourcesContent":["import { useState } from 'react';\nimport { useIsomorphicEffect } from '../use-isomorphic-effect/use-isomorphic-effect';\n\nexport interface UseOrientationOptions {\n /** Default angle value, used until the real can be retrieved\n * (during server side rendering and before js executes on the page)\n * If not provided, the default value is `0`\n * */\n defaultAngle?: number;\n\n /** Default angle value, used until the real can be retrieved\n * (during server side rendering and before js executes on the page)\n * If not provided, the default value is `'landscape-primary'`\n * */\n defaultType?: OrientationType;\n\n /** If true, the initial value will be resolved in useEffect (ssr safe)\n * If false, the initial value will be resolved in useLayoutEffect (ssr unsafe)\n * True by default.\n */\n getInitialValueInEffect?: boolean;\n}\n\nexport interface UseOrientationReturnType {\n angle: number;\n type: OrientationType;\n}\n\nfunction getInitialValue(\n initialValue: UseOrientationReturnType,\n getInitialValueInEffect: boolean\n): UseOrientationReturnType {\n if (getInitialValueInEffect) {\n return initialValue;\n }\n\n if (typeof window !== 'undefined' && 'screen' in window) {\n return {\n angle: window.screen.orientation?.angle ?? initialValue.angle,\n type: window.screen.orientation?.type ?? initialValue.type,\n };\n }\n\n return initialValue;\n}\n\nexport function useOrientation({\n defaultAngle = 0,\n defaultType = 'landscape-primary',\n getInitialValueInEffect = true,\n}: UseOrientationOptions = {}): UseOrientationReturnType {\n const [orientation, setOrientation] = useState<UseOrientationReturnType>(\n getInitialValue(\n {\n angle: defaultAngle,\n type: defaultType,\n },\n getInitialValueInEffect\n )\n );\n\n const handleOrientationChange = (event: Event) => {\n const target = event.currentTarget as ScreenOrientation;\n setOrientation({ angle: target?.angle || 0, type: target?.type || 'landscape-primary' });\n };\n\n useIsomorphicEffect(() => {\n if (window.screen.orientation) {\n setOrientation({\n angle: window.screen.orientation.angle,\n type: window.screen.orientation.type,\n });\n window.screen.orientation.addEventListener('change', handleOrientationChange);\n return () =>\n window.screen.orientation?.removeEventListener('change', handleOrientationChange);\n }\n\n return undefined;\n }, []);\n\n return orientation;\n}\n\nexport namespace useOrientation {\n export type Options = UseOrientationOptions;\n export type ReturnType = UseOrientationReturnType;\n}\n"],"mappings":";;;;AA4BA,SAAS,gBACP,cACA,yBAC0B;AAC1B,KAAI,wBACF,QAAO;AAGT,KAAI,OAAO,WAAW,eAAe,YAAY,OAC/C,QAAO;EACL,OAAO,OAAO,OAAO,aAAa,SAAS,aAAa;EACxD,MAAM,OAAO,OAAO,aAAa,QAAQ,aAAa;EACvD;AAGH,QAAO;;AAGT,SAAgB,eAAe,EAC7B,eAAe,GACf,cAAc,qBACd,0BAA0B,SACD,EAAE,EAA4B;CACvD,MAAM,CAAC,aAAa,mBAAA,GAAA,MAAA,UAClB,gBACE;EACE,OAAO;EACP,MAAM;EACP,EACD,wBACD,CACF;CAED,MAAM,2BAA2B,UAAiB;EAChD,MAAM,SAAS,MAAM;AACrB,iBAAe;GAAE,OAAO,QAAQ,SAAS;GAAG,MAAM,QAAQ,QAAQ;GAAqB,CAAC;;AAG1F,+BAAA,0BAA0B;AACxB,MAAI,OAAO,OAAO,aAAa;AAC7B,kBAAe;IACb,OAAO,OAAO,OAAO,YAAY;IACjC,MAAM,OAAO,OAAO,YAAY;IACjC,CAAC;AACF,UAAO,OAAO,YAAY,iBAAiB,UAAU,wBAAwB;AAC7E,gBACE,OAAO,OAAO,aAAa,oBAAoB,UAAU,wBAAwB;;IAIpF,EAAE,CAAC;AAEN,QAAO"}
@@ -27,8 +27,8 @@ function getOS() {
27
27
  if (isMacOS(userAgent)) return "macos";
28
28
  if (isWindows(userAgent)) return "windows";
29
29
  if (isAndroid(userAgent)) return "android";
30
- if (isLinux(userAgent)) return "linux";
31
30
  if (isChromeOS(userAgent)) return "chromeos";
31
+ if (isLinux(userAgent)) return "linux";
32
32
  return "undetermined";
33
33
  }
34
34
  function useOs(options = { getValueInEffect: true }) {
@@ -1 +1 @@
1
- {"version":3,"file":"use-os.cjs","names":[],"sources":["../../src/use-os/use-os.ts"],"sourcesContent":["import { useState } from 'react';\nimport { useIsomorphicEffect } from '../use-isomorphic-effect/use-isomorphic-effect';\n\nexport type UseOSReturnValue =\n | 'undetermined'\n | 'macos'\n | 'ios'\n | 'windows'\n | 'android'\n | 'linux'\n | 'chromeos';\n\nfunction isMacOS(userAgent: string): boolean {\n const macosPattern = /(Macintosh)|(MacIntel)|(MacPPC)|(Mac68K)/i;\n\n return macosPattern.test(userAgent);\n}\n\nfunction isIOS(userAgent: string): boolean {\n const iosPattern = /(iPhone)|(iPad)|(iPod)/i;\n\n return iosPattern.test(userAgent);\n}\n\nfunction isWindows(userAgent: string): boolean {\n const windowsPattern = /(Win32)|(Win64)|(Windows)|(WinCE)/i;\n\n return windowsPattern.test(userAgent);\n}\n\nfunction isAndroid(userAgent: string): boolean {\n const androidPattern = /Android/i;\n\n return androidPattern.test(userAgent);\n}\n\nfunction isLinux(userAgent: string): boolean {\n const linuxPattern = /Linux/i;\n\n return linuxPattern.test(userAgent);\n}\n\nfunction isChromeOS(userAgent: string): boolean {\n const chromePattern = /CrOS/i;\n return chromePattern.test(userAgent);\n}\n\nfunction getOS(): UseOSReturnValue {\n if (typeof window === 'undefined') {\n return 'undetermined';\n }\n\n const { userAgent } = window.navigator;\n\n if (isIOS(userAgent) || (isMacOS(userAgent) && 'ontouchend' in document)) {\n return 'ios';\n }\n if (isMacOS(userAgent)) {\n return 'macos';\n }\n if (isWindows(userAgent)) {\n return 'windows';\n }\n if (isAndroid(userAgent)) {\n return 'android';\n }\n if (isLinux(userAgent)) {\n return 'linux';\n }\n if (isChromeOS(userAgent)) {\n return 'chromeos';\n }\n\n return 'undetermined';\n}\n\nexport interface UseOsOptions {\n getValueInEffect: boolean;\n}\n\nexport function useOs(options: UseOsOptions = { getValueInEffect: true }): UseOSReturnValue {\n const [value, setValue] = useState<UseOSReturnValue>(\n options.getValueInEffect ? 'undetermined' : getOS()\n );\n\n useIsomorphicEffect(() => {\n if (options.getValueInEffect) {\n setValue(getOS);\n }\n }, []);\n\n return value;\n}\n\nexport namespace useOs {\n export type Options = UseOsOptions;\n export type ReturnValue = UseOSReturnValue;\n}\n"],"mappings":";;;;AAYA,SAAS,QAAQ,WAA4B;AAG3C,QAFqB,4CAED,KAAK,UAAU;;AAGrC,SAAS,MAAM,WAA4B;AAGzC,QAFmB,0BAED,KAAK,UAAU;;AAGnC,SAAS,UAAU,WAA4B;AAG7C,QAFuB,qCAED,KAAK,UAAU;;AAGvC,SAAS,UAAU,WAA4B;AAG7C,QAFuB,WAED,KAAK,UAAU;;AAGvC,SAAS,QAAQ,WAA4B;AAG3C,QAFqB,SAED,KAAK,UAAU;;AAGrC,SAAS,WAAW,WAA4B;AAE9C,QADsB,QACD,KAAK,UAAU;;AAGtC,SAAS,QAA0B;AACjC,KAAI,OAAO,WAAW,YACpB,QAAO;CAGT,MAAM,EAAE,cAAc,OAAO;AAE7B,KAAI,MAAM,UAAU,IAAK,QAAQ,UAAU,IAAI,gBAAgB,SAC7D,QAAO;AAET,KAAI,QAAQ,UAAU,CACpB,QAAO;AAET,KAAI,UAAU,UAAU,CACtB,QAAO;AAET,KAAI,UAAU,UAAU,CACtB,QAAO;AAET,KAAI,QAAQ,UAAU,CACpB,QAAO;AAET,KAAI,WAAW,UAAU,CACvB,QAAO;AAGT,QAAO;;AAOT,SAAgB,MAAM,UAAwB,EAAE,kBAAkB,MAAM,EAAoB;CAC1F,MAAM,CAAC,OAAO,aAAA,GAAA,MAAA,UACZ,QAAQ,mBAAmB,iBAAiB,OAAO,CACpD;AAED,+BAAA,0BAA0B;AACxB,MAAI,QAAQ,iBACV,UAAS,MAAM;IAEhB,EAAE,CAAC;AAEN,QAAO"}
1
+ {"version":3,"file":"use-os.cjs","names":[],"sources":["../../src/use-os/use-os.ts"],"sourcesContent":["import { useState } from 'react';\nimport { useIsomorphicEffect } from '../use-isomorphic-effect/use-isomorphic-effect';\n\nexport type UseOSReturnValue =\n | 'undetermined'\n | 'macos'\n | 'ios'\n | 'windows'\n | 'android'\n | 'linux'\n | 'chromeos';\n\nfunction isMacOS(userAgent: string): boolean {\n const macosPattern = /(Macintosh)|(MacIntel)|(MacPPC)|(Mac68K)/i;\n\n return macosPattern.test(userAgent);\n}\n\nfunction isIOS(userAgent: string): boolean {\n const iosPattern = /(iPhone)|(iPad)|(iPod)/i;\n\n return iosPattern.test(userAgent);\n}\n\nfunction isWindows(userAgent: string): boolean {\n const windowsPattern = /(Win32)|(Win64)|(Windows)|(WinCE)/i;\n\n return windowsPattern.test(userAgent);\n}\n\nfunction isAndroid(userAgent: string): boolean {\n const androidPattern = /Android/i;\n\n return androidPattern.test(userAgent);\n}\n\nfunction isLinux(userAgent: string): boolean {\n const linuxPattern = /Linux/i;\n\n return linuxPattern.test(userAgent);\n}\n\nfunction isChromeOS(userAgent: string): boolean {\n const chromePattern = /CrOS/i;\n return chromePattern.test(userAgent);\n}\n\nfunction getOS(): UseOSReturnValue {\n if (typeof window === 'undefined') {\n return 'undetermined';\n }\n\n const { userAgent } = window.navigator;\n\n if (isIOS(userAgent) || (isMacOS(userAgent) && 'ontouchend' in document)) {\n return 'ios';\n }\n if (isMacOS(userAgent)) {\n return 'macos';\n }\n if (isWindows(userAgent)) {\n return 'windows';\n }\n if (isAndroid(userAgent)) {\n return 'android';\n }\n if (isChromeOS(userAgent)) {\n return 'chromeos';\n }\n if (isLinux(userAgent)) {\n return 'linux';\n }\n\n return 'undetermined';\n}\n\nexport interface UseOsOptions {\n getValueInEffect: boolean;\n}\n\nexport function useOs(options: UseOsOptions = { getValueInEffect: true }): UseOSReturnValue {\n const [value, setValue] = useState<UseOSReturnValue>(\n options.getValueInEffect ? 'undetermined' : getOS()\n );\n\n useIsomorphicEffect(() => {\n if (options.getValueInEffect) {\n setValue(getOS);\n }\n }, []);\n\n return value;\n}\n\nexport namespace useOs {\n export type Options = UseOsOptions;\n export type ReturnValue = UseOSReturnValue;\n}\n"],"mappings":";;;;AAYA,SAAS,QAAQ,WAA4B;AAG3C,QAFqB,4CAED,KAAK,UAAU;;AAGrC,SAAS,MAAM,WAA4B;AAGzC,QAFmB,0BAED,KAAK,UAAU;;AAGnC,SAAS,UAAU,WAA4B;AAG7C,QAFuB,qCAED,KAAK,UAAU;;AAGvC,SAAS,UAAU,WAA4B;AAG7C,QAFuB,WAED,KAAK,UAAU;;AAGvC,SAAS,QAAQ,WAA4B;AAG3C,QAFqB,SAED,KAAK,UAAU;;AAGrC,SAAS,WAAW,WAA4B;AAE9C,QADsB,QACD,KAAK,UAAU;;AAGtC,SAAS,QAA0B;AACjC,KAAI,OAAO,WAAW,YACpB,QAAO;CAGT,MAAM,EAAE,cAAc,OAAO;AAE7B,KAAI,MAAM,UAAU,IAAK,QAAQ,UAAU,IAAI,gBAAgB,SAC7D,QAAO;AAET,KAAI,QAAQ,UAAU,CACpB,QAAO;AAET,KAAI,UAAU,UAAU,CACtB,QAAO;AAET,KAAI,UAAU,UAAU,CACtB,QAAO;AAET,KAAI,WAAW,UAAU,CACvB,QAAO;AAET,KAAI,QAAQ,UAAU,CACpB,QAAO;AAGT,QAAO;;AAOT,SAAgB,MAAM,UAAwB,EAAE,kBAAkB,MAAM,EAAoB;CAC1F,MAAM,CAAC,OAAO,aAAA,GAAA,MAAA,UACZ,QAAQ,mBAAmB,iBAAiB,OAAO,CACpD;AAED,+BAAA,0BAA0B;AACxB,MAAI,QAAQ,iBACV,UAAS,MAAM;IAEhB,EAAE,CAAC;AAEN,QAAO"}
@@ -28,11 +28,7 @@ function normalizeRadialValue(degree, step) {
28
28
  return toFixed(high >= clamped / step ? high * step === 360 ? 0 : high * step : low * step, getDigitsAfterDot(step));
29
29
  }
30
30
  function useRadialMove(onChange, { step = .01, onChangeEnd, onScrubStart, onScrubEnd } = {}) {
31
- const mounted = (0, react.useRef)(false);
32
31
  const [active, setActive] = (0, react.useState)(false);
33
- (0, react.useEffect)(() => {
34
- mounted.current = true;
35
- }, []);
36
32
  return {
37
33
  ref: (0, react.useCallback)((node) => {
38
34
  const update = (event, done = false) => {
@@ -90,6 +86,10 @@ function useRadialMove(onChange, { step = .01, onChangeEnd, onScrubStart, onScru
90
86
  node.removeEventListener("mousedown", onMouseDown);
91
87
  node.removeEventListener("touchstart", handleTouchStart);
92
88
  }
89
+ document.removeEventListener("mousemove", handleMouseMove, false);
90
+ document.removeEventListener("mouseup", handleMouseUp, false);
91
+ document.removeEventListener("touchmove", handleTouchMove, false);
92
+ document.removeEventListener("touchend", handleTouchEnd, false);
93
93
  };
94
94
  }, [onChange]),
95
95
  active
@@ -1 +1 @@
1
- {"version":3,"file":"use-radial-move.cjs","names":["clamp"],"sources":["../../src/use-radial-move/use-radial-move.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { clamp } from '../utils';\n\nfunction radiansToDegrees(radians: number) {\n return radians * (180 / Math.PI);\n}\n\nfunction getElementCenter(element: HTMLElement) {\n const rect = element.getBoundingClientRect();\n return [rect.left + rect.width / 2, rect.top + rect.height / 2];\n}\n\nfunction getAngle(coordinates: [number, number], element: HTMLElement) {\n const center = getElementCenter(element);\n const x = coordinates[0] - center[0];\n const y = coordinates[1] - center[1];\n const deg = radiansToDegrees(Math.atan2(x, y)) + 180;\n return 360 - deg;\n}\n\nfunction toFixed(value: number, digits: number) {\n return parseFloat(value.toFixed(digits));\n}\n\nfunction getDigitsAfterDot(value: number) {\n return value.toString().split('.')[1]?.length || 0;\n}\n\nexport function normalizeRadialValue(degree: number, step: number) {\n const clamped = clamp(degree, 0, 360);\n const high = Math.ceil(clamped / step);\n const low = Math.round(clamped / step);\n return toFixed(\n high >= clamped / step ? (high * step === 360 ? 0 : high * step) : low * step,\n getDigitsAfterDot(step)\n );\n}\n\nexport interface UseRadialMoveOptions {\n /** Number by which value is incremented/decremented with mouse and touch events, `0.01` by default */\n step?: number;\n\n /** Called in `onMouseUp` and `onTouchEnd` events with the current value */\n onChangeEnd?: (value: number) => void;\n\n /** Called in `onMouseDown` and `onTouchStart` events */\n onScrubStart?: () => void;\n\n /** Called in `onMouseUp` and `onTouchEnd` events */\n onScrubEnd?: () => void;\n}\n\nexport interface UseRadialMoveReturnValue<T extends HTMLElement = any> {\n /** Ref to be passed to the element that should be used for radial move */\n ref: React.RefCallback<T | null>;\n\n /** Indicates whether the radial move is active */\n active: boolean;\n}\n\nexport function useRadialMove<T extends HTMLElement = any>(\n onChange: (value: number) => void,\n { step = 0.01, onChangeEnd, onScrubStart, onScrubEnd }: UseRadialMoveOptions = {}\n): UseRadialMoveReturnValue<T> {\n const mounted = useRef<boolean>(false);\n const [active, setActive] = useState(false);\n\n useEffect(() => {\n mounted.current = true;\n }, []);\n\n const refCallback: React.RefCallback<T | null> = useCallback(\n (node) => {\n const update = (event: MouseEvent, done = false) => {\n if (node) {\n node.style.userSelect = 'none';\n const deg = getAngle([event.clientX, event.clientY], node);\n const newValue = normalizeRadialValue(deg, step || 1);\n\n onChange(newValue);\n done && onChangeEnd?.(newValue);\n }\n };\n\n const beginTracking = () => {\n onScrubStart?.();\n setActive(true);\n document.addEventListener('mousemove', handleMouseMove, false);\n document.addEventListener('mouseup', handleMouseUp, false);\n document.addEventListener('touchmove', handleTouchMove, { passive: false });\n document.addEventListener('touchend', handleTouchEnd, false);\n };\n\n const endTracking = () => {\n onScrubEnd?.();\n setActive(false);\n document.removeEventListener('mousemove', handleMouseMove, false);\n document.removeEventListener('mouseup', handleMouseUp, false);\n document.removeEventListener('touchmove', handleTouchMove, false);\n document.removeEventListener('touchend', handleTouchEnd, false);\n };\n\n const onMouseDown = (event: MouseEvent) => {\n beginTracking();\n update(event);\n };\n\n const handleMouseMove = (event: MouseEvent) => {\n update(event);\n };\n\n const handleMouseUp = (event: MouseEvent) => {\n update(event, true);\n endTracking();\n };\n\n const handleTouchMove = (event: TouchEvent) => {\n event.preventDefault();\n update(event.touches[0] as any);\n };\n\n const handleTouchEnd = (event: TouchEvent) => {\n update(event.changedTouches[0] as any, true);\n endTracking();\n };\n\n const handleTouchStart = (event: TouchEvent) => {\n event.preventDefault();\n beginTracking();\n update(event.touches[0] as any);\n };\n\n node?.addEventListener('mousedown', onMouseDown);\n node?.addEventListener('touchstart', handleTouchStart, { passive: false });\n\n return () => {\n if (node) {\n node.removeEventListener('mousedown', onMouseDown);\n node.removeEventListener('touchstart', handleTouchStart);\n }\n };\n },\n [onChange]\n );\n\n return { ref: refCallback, active };\n}\n\nexport namespace useRadialMove {\n export type Options = UseRadialMoveOptions;\n export type ReturnValue<T extends HTMLElement> = UseRadialMoveReturnValue<T>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-radial-move.cjs","names":["clamp"],"sources":["../../src/use-radial-move/use-radial-move.ts"],"sourcesContent":["import { useCallback, useState } from 'react';\nimport { clamp } from '../utils';\n\nfunction radiansToDegrees(radians: number) {\n return radians * (180 / Math.PI);\n}\n\nfunction getElementCenter(element: HTMLElement) {\n const rect = element.getBoundingClientRect();\n return [rect.left + rect.width / 2, rect.top + rect.height / 2];\n}\n\nfunction getAngle(coordinates: [number, number], element: HTMLElement) {\n const center = getElementCenter(element);\n const x = coordinates[0] - center[0];\n const y = coordinates[1] - center[1];\n const deg = radiansToDegrees(Math.atan2(x, y)) + 180;\n return 360 - deg;\n}\n\nfunction toFixed(value: number, digits: number) {\n return parseFloat(value.toFixed(digits));\n}\n\nfunction getDigitsAfterDot(value: number) {\n return value.toString().split('.')[1]?.length || 0;\n}\n\nexport function normalizeRadialValue(degree: number, step: number) {\n const clamped = clamp(degree, 0, 360);\n const high = Math.ceil(clamped / step);\n const low = Math.round(clamped / step);\n return toFixed(\n high >= clamped / step ? (high * step === 360 ? 0 : high * step) : low * step,\n getDigitsAfterDot(step)\n );\n}\n\nexport interface UseRadialMoveOptions {\n /** Number by which value is incremented/decremented with mouse and touch events, `0.01` by default */\n step?: number;\n\n /** Called in `onMouseUp` and `onTouchEnd` events with the current value */\n onChangeEnd?: (value: number) => void;\n\n /** Called in `onMouseDown` and `onTouchStart` events */\n onScrubStart?: () => void;\n\n /** Called in `onMouseUp` and `onTouchEnd` events */\n onScrubEnd?: () => void;\n}\n\nexport interface UseRadialMoveReturnValue<T extends HTMLElement = any> {\n /** Ref to be passed to the element that should be used for radial move */\n ref: React.RefCallback<T | null>;\n\n /** Indicates whether the radial move is active */\n active: boolean;\n}\n\nexport function useRadialMove<T extends HTMLElement = any>(\n onChange: (value: number) => void,\n { step = 0.01, onChangeEnd, onScrubStart, onScrubEnd }: UseRadialMoveOptions = {}\n): UseRadialMoveReturnValue<T> {\n const [active, setActive] = useState(false);\n\n const refCallback: React.RefCallback<T | null> = useCallback(\n (node) => {\n const update = (event: MouseEvent, done = false) => {\n if (node) {\n node.style.userSelect = 'none';\n const deg = getAngle([event.clientX, event.clientY], node);\n const newValue = normalizeRadialValue(deg, step || 1);\n\n onChange(newValue);\n done && onChangeEnd?.(newValue);\n }\n };\n\n const beginTracking = () => {\n onScrubStart?.();\n setActive(true);\n document.addEventListener('mousemove', handleMouseMove, false);\n document.addEventListener('mouseup', handleMouseUp, false);\n document.addEventListener('touchmove', handleTouchMove, { passive: false });\n document.addEventListener('touchend', handleTouchEnd, false);\n };\n\n const endTracking = () => {\n onScrubEnd?.();\n setActive(false);\n document.removeEventListener('mousemove', handleMouseMove, false);\n document.removeEventListener('mouseup', handleMouseUp, false);\n document.removeEventListener('touchmove', handleTouchMove, false);\n document.removeEventListener('touchend', handleTouchEnd, false);\n };\n\n const onMouseDown = (event: MouseEvent) => {\n beginTracking();\n update(event);\n };\n\n const handleMouseMove = (event: MouseEvent) => {\n update(event);\n };\n\n const handleMouseUp = (event: MouseEvent) => {\n update(event, true);\n endTracking();\n };\n\n const handleTouchMove = (event: TouchEvent) => {\n event.preventDefault();\n update(event.touches[0] as any);\n };\n\n const handleTouchEnd = (event: TouchEvent) => {\n update(event.changedTouches[0] as any, true);\n endTracking();\n };\n\n const handleTouchStart = (event: TouchEvent) => {\n event.preventDefault();\n beginTracking();\n update(event.touches[0] as any);\n };\n\n node?.addEventListener('mousedown', onMouseDown);\n node?.addEventListener('touchstart', handleTouchStart, { passive: false });\n\n return () => {\n if (node) {\n node.removeEventListener('mousedown', onMouseDown);\n node.removeEventListener('touchstart', handleTouchStart);\n }\n document.removeEventListener('mousemove', handleMouseMove, false);\n document.removeEventListener('mouseup', handleMouseUp, false);\n document.removeEventListener('touchmove', handleTouchMove, false);\n document.removeEventListener('touchend', handleTouchEnd, false);\n };\n },\n [onChange]\n );\n\n return { ref: refCallback, active };\n}\n\nexport namespace useRadialMove {\n export type Options = UseRadialMoveOptions;\n export type ReturnValue<T extends HTMLElement> = UseRadialMoveReturnValue<T>;\n}\n"],"mappings":";;;;AAGA,SAAS,iBAAiB,SAAiB;AACzC,QAAO,WAAW,MAAM,KAAK;;AAG/B,SAAS,iBAAiB,SAAsB;CAC9C,MAAM,OAAO,QAAQ,uBAAuB;AAC5C,QAAO,CAAC,KAAK,OAAO,KAAK,QAAQ,GAAG,KAAK,MAAM,KAAK,SAAS,EAAE;;AAGjE,SAAS,SAAS,aAA+B,SAAsB;CACrE,MAAM,SAAS,iBAAiB,QAAQ;CACxC,MAAM,IAAI,YAAY,KAAK,OAAO;CAClC,MAAM,IAAI,YAAY,KAAK,OAAO;AAElC,QAAO,OADK,iBAAiB,KAAK,MAAM,GAAG,EAAE,CAAC,GAAG;;AAInD,SAAS,QAAQ,OAAe,QAAgB;AAC9C,QAAO,WAAW,MAAM,QAAQ,OAAO,CAAC;;AAG1C,SAAS,kBAAkB,OAAe;AACxC,QAAO,MAAM,UAAU,CAAC,MAAM,IAAI,CAAC,IAAI,UAAU;;AAGnD,SAAgB,qBAAqB,QAAgB,MAAc;CACjE,MAAM,UAAUA,cAAAA,MAAM,QAAQ,GAAG,IAAI;CACrC,MAAM,OAAO,KAAK,KAAK,UAAU,KAAK;CACtC,MAAM,MAAM,KAAK,MAAM,UAAU,KAAK;AACtC,QAAO,QACL,QAAQ,UAAU,OAAQ,OAAO,SAAS,MAAM,IAAI,OAAO,OAAQ,MAAM,MACzE,kBAAkB,KAAK,CACxB;;AAyBH,SAAgB,cACd,UACA,EAAE,OAAO,KAAM,aAAa,cAAc,eAAqC,EAAE,EACpD;CAC7B,MAAM,CAAC,QAAQ,cAAA,GAAA,MAAA,UAAsB,MAAM;AAgF3C,QAAO;EAAE,MAAA,GAAA,MAAA,cA7EN,SAAS;GACR,MAAM,UAAU,OAAmB,OAAO,UAAU;AAClD,QAAI,MAAM;AACR,UAAK,MAAM,aAAa;KAExB,MAAM,WAAW,qBADL,SAAS,CAAC,MAAM,SAAS,MAAM,QAAQ,EAAE,KAAK,EACf,QAAQ,EAAE;AAErD,cAAS,SAAS;AAClB,aAAQ,cAAc,SAAS;;;GAInC,MAAM,sBAAsB;AAC1B,oBAAgB;AAChB,cAAU,KAAK;AACf,aAAS,iBAAiB,aAAa,iBAAiB,MAAM;AAC9D,aAAS,iBAAiB,WAAW,eAAe,MAAM;AAC1D,aAAS,iBAAiB,aAAa,iBAAiB,EAAE,SAAS,OAAO,CAAC;AAC3E,aAAS,iBAAiB,YAAY,gBAAgB,MAAM;;GAG9D,MAAM,oBAAoB;AACxB,kBAAc;AACd,cAAU,MAAM;AAChB,aAAS,oBAAoB,aAAa,iBAAiB,MAAM;AACjE,aAAS,oBAAoB,WAAW,eAAe,MAAM;AAC7D,aAAS,oBAAoB,aAAa,iBAAiB,MAAM;AACjE,aAAS,oBAAoB,YAAY,gBAAgB,MAAM;;GAGjE,MAAM,eAAe,UAAsB;AACzC,mBAAe;AACf,WAAO,MAAM;;GAGf,MAAM,mBAAmB,UAAsB;AAC7C,WAAO,MAAM;;GAGf,MAAM,iBAAiB,UAAsB;AAC3C,WAAO,OAAO,KAAK;AACnB,iBAAa;;GAGf,MAAM,mBAAmB,UAAsB;AAC7C,UAAM,gBAAgB;AACtB,WAAO,MAAM,QAAQ,GAAU;;GAGjC,MAAM,kBAAkB,UAAsB;AAC5C,WAAO,MAAM,eAAe,IAAW,KAAK;AAC5C,iBAAa;;GAGf,MAAM,oBAAoB,UAAsB;AAC9C,UAAM,gBAAgB;AACtB,mBAAe;AACf,WAAO,MAAM,QAAQ,GAAU;;AAGjC,SAAM,iBAAiB,aAAa,YAAY;AAChD,SAAM,iBAAiB,cAAc,kBAAkB,EAAE,SAAS,OAAO,CAAC;AAE1E,gBAAa;AACX,QAAI,MAAM;AACR,UAAK,oBAAoB,aAAa,YAAY;AAClD,UAAK,oBAAoB,cAAc,iBAAiB;;AAE1D,aAAS,oBAAoB,aAAa,iBAAiB,MAAM;AACjE,aAAS,oBAAoB,WAAW,eAAe,MAAM;AAC7D,aAAS,oBAAoB,aAAa,iBAAiB,MAAM;AACjE,aAAS,oBAAoB,YAAY,gBAAgB,MAAM;;KAGnE,CAAC,SAAS,CACX;EAE0B;EAAQ"}
@@ -0,0 +1,200 @@
1
+ "use client";
2
+ const require_use_uncontrolled = require("../use-uncontrolled/use-uncontrolled.cjs");
3
+ let react = require("react");
4
+ //#region packages/@mantine/hooks/src/use-roving-index/use-roving-index.ts
5
+ function findNextEnabled(current, total, isItemDisabled, loop) {
6
+ for (let i = current + 1; i < total; i += 1) if (!isItemDisabled(i)) return i;
7
+ if (loop) {
8
+ for (let i = 0; i < current; i += 1) if (!isItemDisabled(i)) return i;
9
+ }
10
+ return current;
11
+ }
12
+ function findPreviousEnabled(current, total, isItemDisabled, loop) {
13
+ for (let i = current - 1; i >= 0; i -= 1) if (!isItemDisabled(i)) return i;
14
+ if (loop) {
15
+ for (let i = total - 1; i > current; i -= 1) if (!isItemDisabled(i)) return i;
16
+ }
17
+ return current;
18
+ }
19
+ function findFirstEnabled(total, isItemDisabled) {
20
+ for (let i = 0; i < total; i += 1) if (!isItemDisabled(i)) return i;
21
+ return 0;
22
+ }
23
+ function findLastEnabled(total, isItemDisabled) {
24
+ for (let i = total - 1; i >= 0; i -= 1) if (!isItemDisabled(i)) return i;
25
+ return 0;
26
+ }
27
+ const defaultIsItemDisabled = () => false;
28
+ function useRovingIndex(input) {
29
+ const { total, orientation = "horizontal", loop = true, dir = "ltr", activateOnFocus = false, columns, focusedIndex, initialIndex, onFocusChange, isItemDisabled = defaultIsItemDisabled } = input;
30
+ const itemRefs = (0, react.useRef)(/* @__PURE__ */ new Map());
31
+ const isGrid = typeof columns === "number" && columns > 0;
32
+ const [activeIndex, setActiveIndex] = require_use_uncontrolled.useUncontrolled({
33
+ value: focusedIndex,
34
+ defaultValue: initialIndex !== void 0 ? initialIndex : findFirstEnabled(total, isItemDisabled),
35
+ finalValue: 0,
36
+ onChange: onFocusChange
37
+ });
38
+ (0, react.useEffect)(() => {
39
+ if (total === 0) return;
40
+ if (activeIndex >= total) setActiveIndex(findLastEnabled(total, isItemDisabled));
41
+ else if (isItemDisabled(activeIndex)) setActiveIndex(findFirstEnabled(total, isItemDisabled));
42
+ }, [
43
+ total,
44
+ activeIndex,
45
+ isItemDisabled
46
+ ]);
47
+ const focusItem = (0, react.useCallback)((index) => {
48
+ setActiveIndex(index);
49
+ const element = itemRefs.current.get(index);
50
+ if (element) {
51
+ element.focus();
52
+ if (activateOnFocus) element.click();
53
+ }
54
+ }, [activateOnFocus, setActiveIndex]);
55
+ const handleGridKeyDown = (0, react.useCallback)((event, currentIndex) => {
56
+ const row = Math.floor(currentIndex / columns);
57
+ const col = currentIndex % columns;
58
+ const totalRows = Math.ceil(total / columns);
59
+ let nextIndex = null;
60
+ const isRtl = dir === "rtl";
61
+ switch (event.key) {
62
+ case "ArrowRight": {
63
+ const targetCol = isRtl ? col - 1 : col + 1;
64
+ if (targetCol >= 0 && targetCol < columns && row * columns + targetCol < total) {
65
+ const candidate = row * columns + targetCol;
66
+ if (!isItemDisabled(candidate)) nextIndex = candidate;
67
+ }
68
+ break;
69
+ }
70
+ case "ArrowLeft": {
71
+ const targetCol = isRtl ? col + 1 : col - 1;
72
+ if (targetCol >= 0 && targetCol < columns && row * columns + targetCol < total) {
73
+ const candidate = row * columns + targetCol;
74
+ if (!isItemDisabled(candidate)) nextIndex = candidate;
75
+ }
76
+ break;
77
+ }
78
+ case "ArrowDown":
79
+ for (let r = row + 1; r < totalRows; r += 1) {
80
+ const candidate = r * columns + col;
81
+ if (candidate < total && !isItemDisabled(candidate)) {
82
+ nextIndex = candidate;
83
+ break;
84
+ }
85
+ }
86
+ break;
87
+ case "ArrowUp":
88
+ for (let r = row - 1; r >= 0; r -= 1) {
89
+ const candidate = r * columns + col;
90
+ if (candidate < total && !isItemDisabled(candidate)) {
91
+ nextIndex = candidate;
92
+ break;
93
+ }
94
+ }
95
+ break;
96
+ case "Home":
97
+ if (event.ctrlKey) nextIndex = findFirstEnabled(total, isItemDisabled);
98
+ else {
99
+ const rowStart = row * columns;
100
+ for (let i = rowStart; i < rowStart + columns && i < total; i += 1) if (!isItemDisabled(i)) {
101
+ nextIndex = i;
102
+ break;
103
+ }
104
+ }
105
+ break;
106
+ case "End":
107
+ if (event.ctrlKey) nextIndex = findLastEnabled(total, isItemDisabled);
108
+ else {
109
+ const rowStart = row * columns;
110
+ const rowEnd = Math.min(rowStart + columns, total) - 1;
111
+ for (let i = rowEnd; i >= rowStart; i -= 1) if (!isItemDisabled(i)) {
112
+ nextIndex = i;
113
+ break;
114
+ }
115
+ }
116
+ break;
117
+ }
118
+ if (nextIndex !== null && nextIndex !== currentIndex) {
119
+ event.preventDefault();
120
+ event.stopPropagation();
121
+ focusItem(nextIndex);
122
+ }
123
+ }, [
124
+ total,
125
+ columns,
126
+ dir,
127
+ isItemDisabled,
128
+ focusItem
129
+ ]);
130
+ const handleListKeyDown = (0, react.useCallback)((event, currentIndex) => {
131
+ const isRtl = dir === "rtl";
132
+ let nextIndex = null;
133
+ switch (event.key) {
134
+ case "ArrowRight":
135
+ if (orientation === "horizontal" || orientation === "both") nextIndex = isRtl ? findPreviousEnabled(currentIndex, total, isItemDisabled, loop) : findNextEnabled(currentIndex, total, isItemDisabled, loop);
136
+ break;
137
+ case "ArrowLeft":
138
+ if (orientation === "horizontal" || orientation === "both") nextIndex = isRtl ? findNextEnabled(currentIndex, total, isItemDisabled, loop) : findPreviousEnabled(currentIndex, total, isItemDisabled, loop);
139
+ break;
140
+ case "ArrowDown":
141
+ if (orientation === "vertical" || orientation === "both") nextIndex = findNextEnabled(currentIndex, total, isItemDisabled, loop);
142
+ break;
143
+ case "ArrowUp":
144
+ if (orientation === "vertical" || orientation === "both") nextIndex = findPreviousEnabled(currentIndex, total, isItemDisabled, loop);
145
+ break;
146
+ case "Home":
147
+ nextIndex = findFirstEnabled(total, isItemDisabled);
148
+ break;
149
+ case "End":
150
+ nextIndex = findLastEnabled(total, isItemDisabled);
151
+ break;
152
+ }
153
+ if (nextIndex !== null && nextIndex !== currentIndex) {
154
+ event.preventDefault();
155
+ event.stopPropagation();
156
+ focusItem(nextIndex);
157
+ }
158
+ }, [
159
+ total,
160
+ orientation,
161
+ loop,
162
+ dir,
163
+ isItemDisabled,
164
+ focusItem
165
+ ]);
166
+ return {
167
+ getItemProps: (0, react.useCallback)((options) => {
168
+ const { index, onClick, onKeyDown } = options;
169
+ return {
170
+ tabIndex: index === activeIndex ? 0 : -1,
171
+ ref: (node) => {
172
+ if (node) itemRefs.current.set(index, node);
173
+ else itemRefs.current.delete(index);
174
+ },
175
+ onKeyDown: (event) => {
176
+ onKeyDown?.(event);
177
+ if (event.defaultPrevented) return;
178
+ if (isGrid) handleGridKeyDown(event, index);
179
+ else handleListKeyDown(event, index);
180
+ },
181
+ onClick: (event) => {
182
+ onClick?.(event);
183
+ setActiveIndex(index);
184
+ }
185
+ };
186
+ }, [
187
+ activeIndex,
188
+ isGrid,
189
+ handleGridKeyDown,
190
+ handleListKeyDown,
191
+ setActiveIndex
192
+ ]),
193
+ focusedIndex: activeIndex,
194
+ setFocusedIndex: setActiveIndex
195
+ };
196
+ }
197
+ //#endregion
198
+ exports.useRovingIndex = useRovingIndex;
199
+
200
+ //# sourceMappingURL=use-roving-index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-roving-index.cjs","names":["useUncontrolled"],"sources":["../../src/use-roving-index/use-roving-index.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { useUncontrolled } from '../use-uncontrolled/use-uncontrolled';\n\nexport interface UseRovingIndexInput {\n /** Total number of items in the group */\n total: number;\n\n /** Which arrow keys navigate, `'horizontal'` by default */\n orientation?: 'horizontal' | 'vertical' | 'both';\n\n /** Whether navigation wraps at boundaries, `true` by default */\n loop?: boolean;\n\n /** Text direction, `'ltr'` by default */\n dir?: 'rtl' | 'ltr';\n\n /** Whether to click element when it receives focus via keyboard, `false` by default */\n activateOnFocus?: boolean;\n\n /** Number of columns for grid (2D) navigation. When set, enables grid mode */\n columns?: number;\n\n /** Controlled focused index */\n focusedIndex?: number;\n\n /** Initial focused index for uncontrolled mode, first non-disabled item by default */\n initialIndex?: number;\n\n /** Called when focused index changes */\n onFocusChange?: (index: number) => void;\n\n /** Function to check if item at given index is disabled, `() => false` by default */\n isItemDisabled?: (index: number) => boolean;\n}\n\nexport interface UseRovingIndexGetItemPropsInput {\n /** Index of the item in the group */\n index: number;\n\n /** Called when item is clicked */\n onClick?: React.MouseEventHandler;\n\n /** Called when keydown event fires on item */\n onKeyDown?: React.KeyboardEventHandler;\n}\n\nexport interface UseRovingIndexReturnValue {\n /** Get props to spread on each navigable item */\n getItemProps: (options: UseRovingIndexGetItemPropsInput) => {\n tabIndex: 0 | -1;\n onKeyDown: React.KeyboardEventHandler;\n onClick: React.MouseEventHandler;\n ref: React.RefCallback<HTMLElement>;\n };\n\n /** Currently focused index */\n focusedIndex: number;\n\n /** Programmatically set focused index */\n setFocusedIndex: (index: number) => void;\n}\n\nfunction findNextEnabled(\n current: number,\n total: number,\n isItemDisabled: (index: number) => boolean,\n loop: boolean\n): number {\n for (let i = current + 1; i < total; i += 1) {\n if (!isItemDisabled(i)) {\n return i;\n }\n }\n\n if (loop) {\n for (let i = 0; i < current; i += 1) {\n if (!isItemDisabled(i)) {\n return i;\n }\n }\n }\n\n return current;\n}\n\nfunction findPreviousEnabled(\n current: number,\n total: number,\n isItemDisabled: (index: number) => boolean,\n loop: boolean\n): number {\n for (let i = current - 1; i >= 0; i -= 1) {\n if (!isItemDisabled(i)) {\n return i;\n }\n }\n\n if (loop) {\n for (let i = total - 1; i > current; i -= 1) {\n if (!isItemDisabled(i)) {\n return i;\n }\n }\n }\n\n return current;\n}\n\nfunction findFirstEnabled(total: number, isItemDisabled: (index: number) => boolean): number {\n for (let i = 0; i < total; i += 1) {\n if (!isItemDisabled(i)) {\n return i;\n }\n }\n\n return 0;\n}\n\nfunction findLastEnabled(total: number, isItemDisabled: (index: number) => boolean): number {\n for (let i = total - 1; i >= 0; i -= 1) {\n if (!isItemDisabled(i)) {\n return i;\n }\n }\n\n return 0;\n}\n\nconst defaultIsItemDisabled = () => false;\n\nexport function useRovingIndex(input: UseRovingIndexInput): UseRovingIndexReturnValue {\n const {\n total,\n orientation = 'horizontal',\n loop = true,\n dir = 'ltr',\n activateOnFocus = false,\n columns,\n focusedIndex,\n initialIndex,\n onFocusChange,\n isItemDisabled = defaultIsItemDisabled,\n } = input;\n\n const itemRefs = useRef<Map<number, HTMLElement>>(new Map());\n const isGrid = typeof columns === 'number' && columns > 0;\n\n const resolvedInitialIndex =\n initialIndex !== undefined ? initialIndex : findFirstEnabled(total, isItemDisabled);\n\n const [activeIndex, setActiveIndex] = useUncontrolled({\n value: focusedIndex,\n defaultValue: resolvedInitialIndex,\n finalValue: 0,\n onChange: onFocusChange,\n });\n\n useEffect(() => {\n if (total === 0) {\n return;\n }\n\n if (activeIndex >= total) {\n setActiveIndex(findLastEnabled(total, isItemDisabled));\n } else if (isItemDisabled(activeIndex)) {\n setActiveIndex(findFirstEnabled(total, isItemDisabled));\n }\n }, [total, activeIndex, isItemDisabled]);\n\n const focusItem = useCallback(\n (index: number) => {\n setActiveIndex(index);\n const element = itemRefs.current.get(index);\n if (element) {\n element.focus();\n if (activateOnFocus) {\n element.click();\n }\n }\n },\n [activateOnFocus, setActiveIndex]\n );\n\n const handleGridKeyDown = useCallback(\n (event: React.KeyboardEvent, currentIndex: number) => {\n const row = Math.floor(currentIndex / columns!);\n const col = currentIndex % columns!;\n const totalRows = Math.ceil(total / columns!);\n let nextIndex: number | null = null;\n\n const isRtl = dir === 'rtl';\n\n switch (event.key) {\n case 'ArrowRight': {\n const targetCol = isRtl ? col - 1 : col + 1;\n if (targetCol >= 0 && targetCol < columns! && row * columns! + targetCol < total) {\n const candidate = row * columns! + targetCol;\n if (!isItemDisabled(candidate)) {\n nextIndex = candidate;\n }\n }\n break;\n }\n\n case 'ArrowLeft': {\n const targetCol = isRtl ? col + 1 : col - 1;\n if (targetCol >= 0 && targetCol < columns! && row * columns! + targetCol < total) {\n const candidate = row * columns! + targetCol;\n if (!isItemDisabled(candidate)) {\n nextIndex = candidate;\n }\n }\n break;\n }\n\n case 'ArrowDown': {\n for (let r = row + 1; r < totalRows; r += 1) {\n const candidate = r * columns! + col;\n if (candidate < total && !isItemDisabled(candidate)) {\n nextIndex = candidate;\n break;\n }\n }\n break;\n }\n\n case 'ArrowUp': {\n for (let r = row - 1; r >= 0; r -= 1) {\n const candidate = r * columns! + col;\n if (candidate < total && !isItemDisabled(candidate)) {\n nextIndex = candidate;\n break;\n }\n }\n break;\n }\n\n case 'Home': {\n if (event.ctrlKey) {\n nextIndex = findFirstEnabled(total, isItemDisabled);\n } else {\n const rowStart = row * columns!;\n for (let i = rowStart; i < rowStart + columns! && i < total; i += 1) {\n if (!isItemDisabled(i)) {\n nextIndex = i;\n break;\n }\n }\n }\n break;\n }\n\n case 'End': {\n if (event.ctrlKey) {\n nextIndex = findLastEnabled(total, isItemDisabled);\n } else {\n const rowStart = row * columns!;\n const rowEnd = Math.min(rowStart + columns!, total) - 1;\n for (let i = rowEnd; i >= rowStart; i -= 1) {\n if (!isItemDisabled(i)) {\n nextIndex = i;\n break;\n }\n }\n }\n break;\n }\n }\n\n if (nextIndex !== null && nextIndex !== currentIndex) {\n event.preventDefault();\n event.stopPropagation();\n focusItem(nextIndex);\n }\n },\n [total, columns, dir, isItemDisabled, focusItem]\n );\n\n const handleListKeyDown = useCallback(\n (event: React.KeyboardEvent, currentIndex: number) => {\n const isRtl = dir === 'rtl';\n let nextIndex: number | null = null;\n\n switch (event.key) {\n case 'ArrowRight': {\n if (orientation === 'horizontal' || orientation === 'both') {\n nextIndex = isRtl\n ? findPreviousEnabled(currentIndex, total, isItemDisabled, loop)\n : findNextEnabled(currentIndex, total, isItemDisabled, loop);\n }\n break;\n }\n\n case 'ArrowLeft': {\n if (orientation === 'horizontal' || orientation === 'both') {\n nextIndex = isRtl\n ? findNextEnabled(currentIndex, total, isItemDisabled, loop)\n : findPreviousEnabled(currentIndex, total, isItemDisabled, loop);\n }\n break;\n }\n\n case 'ArrowDown': {\n if (orientation === 'vertical' || orientation === 'both') {\n nextIndex = findNextEnabled(currentIndex, total, isItemDisabled, loop);\n }\n break;\n }\n\n case 'ArrowUp': {\n if (orientation === 'vertical' || orientation === 'both') {\n nextIndex = findPreviousEnabled(currentIndex, total, isItemDisabled, loop);\n }\n break;\n }\n\n case 'Home': {\n nextIndex = findFirstEnabled(total, isItemDisabled);\n break;\n }\n\n case 'End': {\n nextIndex = findLastEnabled(total, isItemDisabled);\n break;\n }\n }\n\n if (nextIndex !== null && nextIndex !== currentIndex) {\n event.preventDefault();\n event.stopPropagation();\n focusItem(nextIndex);\n }\n },\n [total, orientation, loop, dir, isItemDisabled, focusItem]\n );\n\n const getItemProps = useCallback(\n (options: UseRovingIndexGetItemPropsInput) => {\n const { index, onClick, onKeyDown } = options;\n\n return {\n tabIndex: (index === activeIndex ? 0 : -1) as 0 | -1,\n\n ref: (node: HTMLElement | null) => {\n if (node) {\n itemRefs.current.set(index, node);\n } else {\n itemRefs.current.delete(index);\n }\n },\n\n onKeyDown: (event: React.KeyboardEvent) => {\n onKeyDown?.(event);\n\n if (event.defaultPrevented) {\n return;\n }\n\n if (isGrid) {\n handleGridKeyDown(event, index);\n } else {\n handleListKeyDown(event, index);\n }\n },\n\n onClick: (event: React.MouseEvent) => {\n onClick?.(event);\n setActiveIndex(index);\n },\n };\n },\n [activeIndex, isGrid, handleGridKeyDown, handleListKeyDown, setActiveIndex]\n );\n\n return {\n getItemProps,\n focusedIndex: activeIndex,\n setFocusedIndex: setActiveIndex,\n };\n}\n\nexport namespace useRovingIndex {\n export type Input = UseRovingIndexInput;\n export type GetItemPropsInput = UseRovingIndexGetItemPropsInput;\n export type ReturnValue = UseRovingIndexReturnValue;\n}\n"],"mappings":"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"}
@@ -2,22 +2,22 @@
2
2
  let react = require("react");
3
3
  //#region packages/@mantine/hooks/src/use-scroll-direction/use-scroll-direction.ts
4
4
  function useScrollDirection() {
5
- const [lastScrollTop, setLastScrollTop] = (0, react.useState)(0);
5
+ const lastScrollTopRef = (0, react.useRef)(0);
6
6
  const [scrollDirection, setScrollDirection] = (0, react.useState)("unknown");
7
- const [isResizing, setIsResizing] = (0, react.useState)(false);
7
+ const isResizingRef = (0, react.useRef)(false);
8
8
  const resizeTimerRef = (0, react.useRef)(void 0);
9
9
  const handleScroll = (0, react.useEffectEvent)(() => {
10
- if (isResizing) return;
10
+ if (isResizingRef.current) return;
11
11
  const currentScrollTop = window.scrollY || document.documentElement.scrollTop;
12
- setScrollDirection(currentScrollTop < lastScrollTop ? "up" : "down");
13
- setLastScrollTop(currentScrollTop);
12
+ setScrollDirection(currentScrollTop < lastScrollTopRef.current ? "up" : "down");
13
+ lastScrollTopRef.current = currentScrollTop;
14
14
  });
15
15
  (0, react.useEffect)(() => {
16
16
  const handleResize = () => {
17
- setIsResizing(true);
17
+ isResizingRef.current = true;
18
18
  window.clearTimeout(resizeTimerRef.current);
19
19
  resizeTimerRef.current = window.setTimeout(() => {
20
- setIsResizing(false);
20
+ isResizingRef.current = false;
21
21
  }, 300);
22
22
  };
23
23
  window.addEventListener("scroll", handleScroll);
@@ -1 +1 @@
1
- {"version":3,"file":"use-scroll-direction.cjs","names":[],"sources":["../../src/use-scroll-direction/use-scroll-direction.ts"],"sourcesContent":["import { useEffect, useEffectEvent, useRef, useState } from 'react';\n\nexport type ScrollDirection = 'up' | 'down' | 'unknown';\n\nexport function useScrollDirection(): ScrollDirection {\n const [lastScrollTop, setLastScrollTop] = useState(0);\n const [scrollDirection, setScrollDirection] = useState<ScrollDirection>('unknown');\n const [isResizing, setIsResizing] = useState(false);\n const resizeTimerRef = useRef<number | undefined>(undefined);\n\n const handleScroll = useEffectEvent(() => {\n if (isResizing) {\n return;\n }\n\n const currentScrollTop = window.scrollY || document.documentElement.scrollTop;\n setScrollDirection(currentScrollTop < lastScrollTop ? 'up' : 'down');\n setLastScrollTop(currentScrollTop);\n });\n\n useEffect(() => {\n const handleResize = () => {\n setIsResizing(true);\n window.clearTimeout(resizeTimerRef.current);\n resizeTimerRef.current = window.setTimeout(() => {\n setIsResizing(false);\n }, 300);\n };\n\n window.addEventListener('scroll', handleScroll);\n window.addEventListener('resize', handleResize);\n\n return () => {\n window.removeEventListener('scroll', handleScroll);\n window.removeEventListener('resize', handleResize);\n clearTimeout(resizeTimerRef.current);\n };\n }, []);\n\n return scrollDirection;\n}\n"],"mappings":";;;AAIA,SAAgB,qBAAsC;CACpD,MAAM,CAAC,eAAe,qBAAA,GAAA,MAAA,UAA6B,EAAE;CACrD,MAAM,CAAC,iBAAiB,uBAAA,GAAA,MAAA,UAAgD,UAAU;CAClF,MAAM,CAAC,YAAY,kBAAA,GAAA,MAAA,UAA0B,MAAM;CACnD,MAAM,kBAAA,GAAA,MAAA,QAA4C,KAAA,EAAU;CAE5D,MAAM,gBAAA,GAAA,MAAA,sBAAoC;AACxC,MAAI,WACF;EAGF,MAAM,mBAAmB,OAAO,WAAW,SAAS,gBAAgB;AACpE,qBAAmB,mBAAmB,gBAAgB,OAAO,OAAO;AACpE,mBAAiB,iBAAiB;GAClC;AAEF,EAAA,GAAA,MAAA,iBAAgB;EACd,MAAM,qBAAqB;AACzB,iBAAc,KAAK;AACnB,UAAO,aAAa,eAAe,QAAQ;AAC3C,kBAAe,UAAU,OAAO,iBAAiB;AAC/C,kBAAc,MAAM;MACnB,IAAI;;AAGT,SAAO,iBAAiB,UAAU,aAAa;AAC/C,SAAO,iBAAiB,UAAU,aAAa;AAE/C,eAAa;AACX,UAAO,oBAAoB,UAAU,aAAa;AAClD,UAAO,oBAAoB,UAAU,aAAa;AAClD,gBAAa,eAAe,QAAQ;;IAErC,EAAE,CAAC;AAEN,QAAO"}
1
+ {"version":3,"file":"use-scroll-direction.cjs","names":[],"sources":["../../src/use-scroll-direction/use-scroll-direction.ts"],"sourcesContent":["import { useEffect, useEffectEvent, useRef, useState } from 'react';\n\nexport type ScrollDirection = 'up' | 'down' | 'unknown';\n\nexport function useScrollDirection(): ScrollDirection {\n const lastScrollTopRef = useRef(0);\n const [scrollDirection, setScrollDirection] = useState<ScrollDirection>('unknown');\n const isResizingRef = useRef(false);\n const resizeTimerRef = useRef<number | undefined>(undefined);\n\n const handleScroll = useEffectEvent(() => {\n if (isResizingRef.current) {\n return;\n }\n\n const currentScrollTop = window.scrollY || document.documentElement.scrollTop;\n setScrollDirection(currentScrollTop < lastScrollTopRef.current ? 'up' : 'down');\n lastScrollTopRef.current = currentScrollTop;\n });\n\n useEffect(() => {\n const handleResize = () => {\n isResizingRef.current = true;\n window.clearTimeout(resizeTimerRef.current);\n resizeTimerRef.current = window.setTimeout(() => {\n isResizingRef.current = false;\n }, 300);\n };\n\n window.addEventListener('scroll', handleScroll);\n window.addEventListener('resize', handleResize);\n\n return () => {\n window.removeEventListener('scroll', handleScroll);\n window.removeEventListener('resize', handleResize);\n clearTimeout(resizeTimerRef.current);\n };\n }, []);\n\n return scrollDirection;\n}\n"],"mappings":";;;AAIA,SAAgB,qBAAsC;CACpD,MAAM,oBAAA,GAAA,MAAA,QAA0B,EAAE;CAClC,MAAM,CAAC,iBAAiB,uBAAA,GAAA,MAAA,UAAgD,UAAU;CAClF,MAAM,iBAAA,GAAA,MAAA,QAAuB,MAAM;CACnC,MAAM,kBAAA,GAAA,MAAA,QAA4C,KAAA,EAAU;CAE5D,MAAM,gBAAA,GAAA,MAAA,sBAAoC;AACxC,MAAI,cAAc,QAChB;EAGF,MAAM,mBAAmB,OAAO,WAAW,SAAS,gBAAgB;AACpE,qBAAmB,mBAAmB,iBAAiB,UAAU,OAAO,OAAO;AAC/E,mBAAiB,UAAU;GAC3B;AAEF,EAAA,GAAA,MAAA,iBAAgB;EACd,MAAM,qBAAqB;AACzB,iBAAc,UAAU;AACxB,UAAO,aAAa,eAAe,QAAQ;AAC3C,kBAAe,UAAU,OAAO,iBAAiB;AAC/C,kBAAc,UAAU;MACvB,IAAI;;AAGT,SAAO,iBAAiB,UAAU,aAAa;AAC/C,SAAO,iBAAiB,UAAU,aAAa;AAE/C,eAAa;AACX,UAAO,oBAAoB,UAAU,aAAa;AAClD,UAAO,oBAAoB,UAAU,aAAa;AAClD,gBAAa,eAAe,QAAQ;;IAErC,EAAE,CAAC;AAEN,QAAO"}
@@ -3,15 +3,20 @@ const require_use_window_event = require("../use-window-event/use-window-event.c
3
3
  const require_use_reduced_motion = require("../use-reduced-motion/use-reduced-motion.cjs");
4
4
  let react = require("react");
5
5
  //#region packages/@mantine/hooks/src/use-scroll-into-view/use-scroll-into-view.ts
6
- function useScrollIntoView({ duration = 1250, axis = "y", onScrollFinish, easing = easeInOutQuad, offset = 0, cancelable = true, isList = false } = {}) {
6
+ function useScrollIntoView({ duration = 1250, axis = "y", onScrollFinish, onScrollCancel, easing = easeInOutQuad, offset = 0, cancelable = true, isList = false } = {}) {
7
7
  const frameID = (0, react.useRef)(0);
8
8
  const startTime = (0, react.useRef)(0);
9
9
  const shouldStop = (0, react.useRef)(false);
10
+ const [scrolling, setScrolling] = (0, react.useState)(false);
10
11
  const scrollableRef = (0, react.useRef)(null);
11
12
  const targetRef = (0, react.useRef)(null);
12
13
  const reducedMotion = require_use_reduced_motion.useReducedMotion();
13
14
  const cancel = () => {
14
- if (frameID.current) cancelAnimationFrame(frameID.current);
15
+ if (frameID.current) {
16
+ cancelAnimationFrame(frameID.current);
17
+ frameID.current = 0;
18
+ setScrolling(false);
19
+ }
15
20
  };
16
21
  const scrollIntoView = (0, react.useCallback)(({ alignment = "start" } = {}) => {
17
22
  shouldStop.current = false;
@@ -28,6 +33,7 @@ function useScrollIntoView({ duration = 1250, axis = "y", onScrollFinish, easing
28
33
  offset,
29
34
  isList
30
35
  }) - (scrollableRef.current ? 0 : start);
36
+ setScrolling(true);
31
37
  function animateScroll() {
32
38
  if (startTime.current === 0) startTime.current = performance.now();
33
39
  const elapsed = performance.now() - startTime.current;
@@ -40,9 +46,11 @@ function useScrollIntoView({ duration = 1250, axis = "y", onScrollFinish, easing
40
46
  });
41
47
  if (!shouldStop.current && t < 1) frameID.current = requestAnimationFrame(animateScroll);
42
48
  else {
43
- typeof onScrollFinish === "function" && onScrollFinish();
49
+ if (shouldStop.current) typeof onScrollCancel === "function" && onScrollCancel();
50
+ else typeof onScrollFinish === "function" && onScrollFinish();
44
51
  startTime.current = 0;
45
52
  frameID.current = 0;
53
+ setScrolling(false);
46
54
  cancel();
47
55
  }
48
56
  }
@@ -54,6 +62,7 @@ function useScrollIntoView({ duration = 1250, axis = "y", onScrollFinish, easing
54
62
  isList,
55
63
  offset,
56
64
  onScrollFinish,
65
+ onScrollCancel,
57
66
  reducedMotion
58
67
  ]);
59
68
  const handleStop = () => {
@@ -71,7 +80,8 @@ function useScrollIntoView({ duration = 1250, axis = "y", onScrollFinish, easing
71
80
  scrollableRef,
72
81
  targetRef,
73
82
  scrollIntoView,
74
- cancel
83
+ cancel,
84
+ scrolling
75
85
  };
76
86
  }
77
87
  function easeInOutQuad(t) {
@@ -1 +1 @@
1
- {"version":3,"file":"use-scroll-into-view.cjs","names":["useReducedMotion"],"sources":["../../src/use-scroll-into-view/use-scroll-into-view.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { useReducedMotion } from '../use-reduced-motion/use-reduced-motion';\nimport { useWindowEvent } from '../use-window-event/use-window-event';\n\ninterface UseScrollIntoViewAnimation {\n /** Target element alignment relatively to parent based on current axis */\n alignment?: 'start' | 'end' | 'center';\n}\n\nexport interface UseScrollIntoViewOptions {\n /** Callback fired after scroll */\n onScrollFinish?: () => void;\n\n /** Duration of scroll in milliseconds */\n duration?: number;\n\n /** Axis of scroll */\n axis?: 'x' | 'y';\n\n /** Custom mathematical easing function */\n easing?: (t: number) => number;\n\n /** Additional distance between nearest edge and element */\n offset?: number;\n\n /** Indicator if animation may be interrupted by user scrolling */\n cancelable?: boolean;\n\n /** Prevents content jumping in scrolling lists with multiple targets */\n isList?: boolean;\n}\n\nexport interface UseScrollIntoViewReturnValue<\n Target extends HTMLElement = any,\n Parent extends HTMLElement | null = null,\n> {\n scrollableRef: React.RefObject<Parent | null>;\n targetRef: React.RefObject<Target | null>;\n scrollIntoView: (params?: UseScrollIntoViewAnimation) => void;\n cancel: () => void;\n}\n\nexport function useScrollIntoView<\n Target extends HTMLElement = any,\n Parent extends HTMLElement | null = null,\n>({\n duration = 1250,\n axis = 'y',\n onScrollFinish,\n easing = easeInOutQuad,\n offset = 0,\n cancelable = true,\n isList = false,\n}: UseScrollIntoViewOptions = {}): UseScrollIntoViewReturnValue<Target, Parent> {\n const frameID = useRef(0);\n const startTime = useRef(0);\n const shouldStop = useRef(false);\n\n const scrollableRef = useRef<Parent | null>(null);\n const targetRef = useRef<Target | null>(null);\n\n const reducedMotion = useReducedMotion();\n\n const cancel = (): void => {\n if (frameID.current) {\n cancelAnimationFrame(frameID.current);\n }\n };\n\n const scrollIntoView = useCallback(\n ({ alignment = 'start' }: UseScrollIntoViewAnimation = {}) => {\n shouldStop.current = false;\n\n if (frameID.current) {\n cancel();\n }\n\n const start = getScrollStart({ parent: scrollableRef.current, axis }) ?? 0;\n\n const change =\n getRelativePosition({\n parent: scrollableRef.current,\n target: targetRef.current,\n axis,\n alignment,\n offset,\n isList,\n }) - (scrollableRef.current ? 0 : start);\n\n function animateScroll() {\n if (startTime.current === 0) {\n startTime.current = performance.now();\n }\n\n const now = performance.now();\n const elapsed = now - startTime.current;\n\n // Easing timing progress\n const t = reducedMotion || duration === 0 ? 1 : elapsed / duration;\n\n const distance = start + change * easing(t);\n\n setScrollParam({\n parent: scrollableRef.current,\n axis,\n distance,\n });\n\n if (!shouldStop.current && t < 1) {\n frameID.current = requestAnimationFrame(animateScroll);\n } else {\n typeof onScrollFinish === 'function' && onScrollFinish();\n startTime.current = 0;\n frameID.current = 0;\n cancel();\n }\n }\n animateScroll();\n },\n [axis, duration, easing, isList, offset, onScrollFinish, reducedMotion]\n );\n\n const handleStop = () => {\n if (cancelable) {\n shouldStop.current = true;\n }\n };\n\n /**\n * Detection of one of these events stops scroll animation\n * wheel - mouse wheel / touch pad\n * touchmove - any touchable device\n */\n\n useWindowEvent('wheel', handleStop, {\n passive: true,\n });\n\n useWindowEvent('touchmove', handleStop, {\n passive: true,\n });\n\n // Cleanup requestAnimationFrame\n useEffect(() => cancel, []);\n\n return {\n scrollableRef,\n targetRef,\n scrollIntoView,\n cancel,\n };\n}\n\n// ---------------------------------------------------\n// Helpers\n// ---------------------------------------------------\n\nfunction easeInOutQuad(t: number) {\n return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;\n}\n\nfunction getRelativePosition({ axis, target, parent, alignment, offset, isList }: any): number {\n if (!target || (!parent && typeof document === 'undefined')) {\n return 0;\n }\n const isCustomParent = !!parent;\n const parentElement = parent || document.body;\n const parentPosition = parentElement.getBoundingClientRect();\n const targetPosition = target.getBoundingClientRect();\n\n const getDiff = (property: 'top' | 'left'): number =>\n targetPosition[property] - parentPosition[property];\n\n if (axis === 'y') {\n const diff = getDiff('top');\n\n if (diff === 0) {\n return 0;\n }\n\n if (alignment === 'start') {\n const distance = diff - offset;\n const shouldScroll = distance <= targetPosition.height * (isList ? 0 : 1) || !isList;\n\n return shouldScroll ? distance : 0;\n }\n\n const parentHeight = isCustomParent ? parentPosition.height : window.innerHeight;\n\n if (alignment === 'end') {\n const distance = diff + offset - parentHeight + targetPosition.height;\n const shouldScroll = distance >= -targetPosition.height * (isList ? 0 : 1) || !isList;\n\n return shouldScroll ? distance : 0;\n }\n\n if (alignment === 'center') {\n return diff - parentHeight / 2 + targetPosition.height / 2;\n }\n\n return 0;\n }\n\n if (axis === 'x') {\n const diff = getDiff('left');\n\n if (diff === 0) {\n return 0;\n }\n\n if (alignment === 'start') {\n const distance = diff - offset;\n const shouldScroll = distance <= targetPosition.width || !isList;\n\n return shouldScroll ? distance : 0;\n }\n\n const parentWidth = isCustomParent ? parentPosition.width : window.innerWidth;\n\n if (alignment === 'end') {\n const distance = diff + offset - parentWidth + targetPosition.width;\n const shouldScroll = distance >= -targetPosition.width || !isList;\n\n return shouldScroll ? distance : 0;\n }\n\n if (alignment === 'center') {\n return diff - parentWidth / 2 + targetPosition.width / 2;\n }\n\n return 0;\n }\n\n return 0;\n}\n\nfunction getScrollStart({ axis, parent }: any) {\n if (!parent && typeof document === 'undefined') {\n return 0;\n }\n\n const method = axis === 'y' ? 'scrollTop' : 'scrollLeft';\n\n if (parent) {\n return parent[method];\n }\n\n const { body, documentElement } = document;\n\n // While one of it has a value the second is equal 0\n return body[method] + documentElement[method];\n}\n\nfunction setScrollParam({ axis, parent, distance }: any) {\n if (!parent && typeof document === 'undefined') {\n return;\n }\n\n const method = axis === 'y' ? 'scrollTop' : 'scrollLeft';\n\n if (parent) {\n parent[method] = distance;\n } else {\n const { body, documentElement } = document;\n body[method] = distance;\n documentElement[method] = distance;\n }\n}\n\nexport namespace useScrollIntoView {\n export type Options = UseScrollIntoViewOptions;\n export type ReturnValue<\n Target extends HTMLElement,\n Parent extends HTMLElement | null,\n > = UseScrollIntoViewReturnValue<Target, Parent>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-scroll-into-view.cjs","names":["useReducedMotion"],"sources":["../../src/use-scroll-into-view/use-scroll-into-view.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { useReducedMotion } from '../use-reduced-motion/use-reduced-motion';\nimport { useWindowEvent } from '../use-window-event/use-window-event';\n\ninterface UseScrollIntoViewAnimation {\n /** Target element alignment relatively to parent based on current axis */\n alignment?: 'start' | 'end' | 'center';\n}\n\nexport interface UseScrollIntoViewOptions {\n /** Callback fired after scroll */\n onScrollFinish?: () => void;\n\n /** Callback fired when scroll animation is canceled by user interaction */\n onScrollCancel?: () => void;\n\n /** Duration of scroll in milliseconds */\n duration?: number;\n\n /** Axis of scroll */\n axis?: 'x' | 'y';\n\n /** Custom mathematical easing function */\n easing?: (t: number) => number;\n\n /** Additional distance between nearest edge and element */\n offset?: number;\n\n /** Indicator if animation may be interrupted by user scrolling */\n cancelable?: boolean;\n\n /** Prevents content jumping in scrolling lists with multiple targets */\n isList?: boolean;\n}\n\nexport interface UseScrollIntoViewReturnValue<\n Target extends HTMLElement = any,\n Parent extends HTMLElement | null = null,\n> {\n scrollableRef: React.RefObject<Parent | null>;\n targetRef: React.RefObject<Target | null>;\n scrollIntoView: (params?: UseScrollIntoViewAnimation) => void;\n cancel: () => void;\n scrolling: boolean;\n}\n\nexport function useScrollIntoView<\n Target extends HTMLElement = any,\n Parent extends HTMLElement | null = null,\n>({\n duration = 1250,\n axis = 'y',\n onScrollFinish,\n onScrollCancel,\n easing = easeInOutQuad,\n offset = 0,\n cancelable = true,\n isList = false,\n}: UseScrollIntoViewOptions = {}): UseScrollIntoViewReturnValue<Target, Parent> {\n const frameID = useRef(0);\n const startTime = useRef(0);\n const shouldStop = useRef(false);\n const [scrolling, setScrolling] = useState(false);\n\n const scrollableRef = useRef<Parent | null>(null);\n const targetRef = useRef<Target | null>(null);\n\n const reducedMotion = useReducedMotion();\n\n const cancel = (): void => {\n if (frameID.current) {\n cancelAnimationFrame(frameID.current);\n frameID.current = 0;\n setScrolling(false);\n }\n };\n\n const scrollIntoView = useCallback(\n ({ alignment = 'start' }: UseScrollIntoViewAnimation = {}) => {\n shouldStop.current = false;\n\n if (frameID.current) {\n cancel();\n }\n\n const start = getScrollStart({ parent: scrollableRef.current, axis }) ?? 0;\n\n const change =\n getRelativePosition({\n parent: scrollableRef.current,\n target: targetRef.current,\n axis,\n alignment,\n offset,\n isList,\n }) - (scrollableRef.current ? 0 : start);\n\n setScrolling(true);\n\n function animateScroll() {\n if (startTime.current === 0) {\n startTime.current = performance.now();\n }\n\n const now = performance.now();\n const elapsed = now - startTime.current;\n\n // Easing timing progress\n const t = reducedMotion || duration === 0 ? 1 : elapsed / duration;\n\n const distance = start + change * easing(t);\n\n setScrollParam({\n parent: scrollableRef.current,\n axis,\n distance,\n });\n\n if (!shouldStop.current && t < 1) {\n frameID.current = requestAnimationFrame(animateScroll);\n } else {\n if (shouldStop.current) {\n typeof onScrollCancel === 'function' && onScrollCancel();\n } else {\n typeof onScrollFinish === 'function' && onScrollFinish();\n }\n startTime.current = 0;\n frameID.current = 0;\n setScrolling(false);\n cancel();\n }\n }\n animateScroll();\n },\n [axis, duration, easing, isList, offset, onScrollFinish, onScrollCancel, reducedMotion]\n );\n\n const handleStop = () => {\n if (cancelable) {\n shouldStop.current = true;\n }\n };\n\n /**\n * Detection of one of these events stops scroll animation\n * wheel - mouse wheel / touch pad\n * touchmove - any touchable device\n */\n\n useWindowEvent('wheel', handleStop, {\n passive: true,\n });\n\n useWindowEvent('touchmove', handleStop, {\n passive: true,\n });\n\n // Cleanup requestAnimationFrame\n useEffect(() => cancel, []);\n\n return {\n scrollableRef,\n targetRef,\n scrollIntoView,\n cancel,\n scrolling,\n };\n}\n\n// ---------------------------------------------------\n// Helpers\n// ---------------------------------------------------\n\nfunction easeInOutQuad(t: number) {\n return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;\n}\n\nfunction getRelativePosition({ axis, target, parent, alignment, offset, isList }: any): number {\n if (!target || (!parent && typeof document === 'undefined')) {\n return 0;\n }\n const isCustomParent = !!parent;\n const parentElement = parent || document.body;\n const parentPosition = parentElement.getBoundingClientRect();\n const targetPosition = target.getBoundingClientRect();\n\n const getDiff = (property: 'top' | 'left'): number =>\n targetPosition[property] - parentPosition[property];\n\n if (axis === 'y') {\n const diff = getDiff('top');\n\n if (diff === 0) {\n return 0;\n }\n\n if (alignment === 'start') {\n const distance = diff - offset;\n const shouldScroll = distance <= targetPosition.height * (isList ? 0 : 1) || !isList;\n\n return shouldScroll ? distance : 0;\n }\n\n const parentHeight = isCustomParent ? parentPosition.height : window.innerHeight;\n\n if (alignment === 'end') {\n const distance = diff + offset - parentHeight + targetPosition.height;\n const shouldScroll = distance >= -targetPosition.height * (isList ? 0 : 1) || !isList;\n\n return shouldScroll ? distance : 0;\n }\n\n if (alignment === 'center') {\n return diff - parentHeight / 2 + targetPosition.height / 2;\n }\n\n return 0;\n }\n\n if (axis === 'x') {\n const diff = getDiff('left');\n\n if (diff === 0) {\n return 0;\n }\n\n if (alignment === 'start') {\n const distance = diff - offset;\n const shouldScroll = distance <= targetPosition.width || !isList;\n\n return shouldScroll ? distance : 0;\n }\n\n const parentWidth = isCustomParent ? parentPosition.width : window.innerWidth;\n\n if (alignment === 'end') {\n const distance = diff + offset - parentWidth + targetPosition.width;\n const shouldScroll = distance >= -targetPosition.width || !isList;\n\n return shouldScroll ? distance : 0;\n }\n\n if (alignment === 'center') {\n return diff - parentWidth / 2 + targetPosition.width / 2;\n }\n\n return 0;\n }\n\n return 0;\n}\n\nfunction getScrollStart({ axis, parent }: any) {\n if (!parent && typeof document === 'undefined') {\n return 0;\n }\n\n const method = axis === 'y' ? 'scrollTop' : 'scrollLeft';\n\n if (parent) {\n return parent[method];\n }\n\n const { body, documentElement } = document;\n\n // While one of it has a value the second is equal 0\n return body[method] + documentElement[method];\n}\n\nfunction setScrollParam({ axis, parent, distance }: any) {\n if (!parent && typeof document === 'undefined') {\n return;\n }\n\n const method = axis === 'y' ? 'scrollTop' : 'scrollLeft';\n\n if (parent) {\n parent[method] = distance;\n } else {\n const { body, documentElement } = document;\n body[method] = distance;\n documentElement[method] = distance;\n }\n}\n\nexport namespace useScrollIntoView {\n export type Options = UseScrollIntoViewOptions;\n export type ReturnValue<\n Target extends HTMLElement,\n Parent extends HTMLElement | null,\n > = UseScrollIntoViewReturnValue<Target, Parent>;\n}\n"],"mappings":"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"}
@@ -39,9 +39,9 @@ function useScrollSpy({ selector = "h1, h2, h3, h4, h5, h6", getDepth = getDefau
39
39
  const [initialized, setInitialized] = (0, react.useState)(false);
40
40
  const [data, setData] = (0, react.useState)([]);
41
41
  const headingsRef = (0, react.useRef)([]);
42
- const handleScroll = () => {
42
+ const handleScroll = (0, react.useEffectEvent)(() => {
43
43
  setActive(getActiveElement(headingsRef.current.map((d) => d.getNode().getBoundingClientRect()), offset));
44
- };
44
+ });
45
45
  const initialize = () => {
46
46
  const headings = getHeadingsData(Array.from(document.querySelectorAll(selector)), getDepth, getValue);
47
47
  headingsRef.current = headings;
@@ -54,7 +54,11 @@ function useScrollSpy({ selector = "h1, h2, h3, h4, h5, h6", getDepth = getDefau
54
54
  const _scrollHost = scrollHost || window;
55
55
  _scrollHost.addEventListener("scroll", handleScroll);
56
56
  return () => _scrollHost.removeEventListener("scroll", handleScroll);
57
- }, [scrollHost]);
57
+ }, [
58
+ scrollHost,
59
+ selector,
60
+ offset
61
+ ]);
58
62
  return {
59
63
  reinitialize: initialize,
60
64
  active,
@@ -1 +1 @@
1
- {"version":3,"file":"use-scroll-spy.cjs","names":["randomId"],"sources":["../../src/use-scroll-spy/use-scroll-spy.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { randomId } from '../utils';\n\nfunction getHeadingsData(\n headings: HTMLElement[],\n getDepth: (element: HTMLElement) => number,\n getValue: (element: HTMLElement) => string\n): UseScrollSpyHeadingData[] {\n const result: UseScrollSpyHeadingData[] = [];\n\n for (let i = 0; i < headings.length; i += 1) {\n const heading = headings[i];\n result.push({\n depth: getDepth(heading),\n value: getValue(heading),\n id: heading.id || randomId(),\n getNode: () => (heading.id ? document.getElementById(heading.id)! : heading),\n });\n }\n\n return result;\n}\n\nfunction getActiveElement(rects: DOMRect[], offset: number = 0) {\n if (rects.length === 0) {\n return -1;\n }\n\n const closest = rects.reduce(\n (acc, item, index) => {\n if (Math.abs(acc.position - offset) < Math.abs(item.y - offset)) {\n return acc;\n }\n\n return {\n index,\n position: item.y,\n };\n },\n { index: 0, position: rects[0].y }\n );\n\n return closest.index;\n}\n\nfunction getDefaultDepth(element: HTMLElement) {\n return Number(element.tagName[1]);\n}\n\nfunction getDefaultValue(element: HTMLElement) {\n return element.textContent || '';\n}\n\nexport interface UseScrollSpyHeadingData {\n /** Heading depth, 1-6 */\n depth: number;\n\n /** Heading text content value */\n value: string;\n\n /** Heading id */\n id: string;\n\n /** Function to get heading node */\n getNode: () => HTMLElement;\n}\n\nexport interface UseScrollSpyOptions {\n /** Selector to get headings, `'h1, h2, h3, h4, h5, h6'` by default */\n selector?: string;\n\n /** A function to retrieve depth of heading, by default depth is calculated based on tag name */\n getDepth?: (element: HTMLElement) => number;\n\n /** A function to retrieve heading value, by default `element.textContent` is used */\n getValue?: (element: HTMLElement) => string;\n\n /** Host element to attach scroll event listener, if not provided, `window` is used */\n scrollHost?: HTMLElement;\n\n /** Offset from the top of the viewport to use when determining the active heading, `0` by default */\n offset?: number;\n}\n\nexport interface UseScrollSpyReturnValue {\n /** Index of the active heading in the `data` array */\n active: number;\n\n /** Headings data. If not initialize, data is represented by an empty array. */\n data: UseScrollSpyHeadingData[];\n\n /** True if headings value have been retrieved from the DOM. */\n initialized: boolean;\n\n /** Function to update headings values after the parent component has mounted. */\n reinitialize: () => void;\n}\n\nexport function useScrollSpy({\n selector = 'h1, h2, h3, h4, h5, h6',\n getDepth = getDefaultDepth,\n getValue = getDefaultValue,\n offset = 0,\n scrollHost,\n}: UseScrollSpyOptions = {}): UseScrollSpyReturnValue {\n const [active, setActive] = useState(-1);\n const [initialized, setInitialized] = useState(false);\n const [data, setData] = useState<UseScrollSpyHeadingData[]>([]);\n const headingsRef = useRef<UseScrollSpyHeadingData[]>([]);\n\n const handleScroll = () => {\n setActive(\n getActiveElement(\n headingsRef.current.map((d) => d.getNode().getBoundingClientRect()),\n offset\n )\n );\n };\n\n const initialize = () => {\n const headings = getHeadingsData(\n Array.from(document.querySelectorAll(selector)),\n getDepth,\n getValue\n );\n headingsRef.current = headings;\n setInitialized(true);\n setData(headings);\n setActive(\n getActiveElement(\n headings.map((d) => d.getNode().getBoundingClientRect()),\n offset\n )\n );\n };\n\n useEffect(() => {\n initialize();\n const _scrollHost = scrollHost || window;\n _scrollHost.addEventListener('scroll', handleScroll);\n return () => _scrollHost.removeEventListener('scroll', handleScroll);\n }, [scrollHost]);\n\n return {\n reinitialize: initialize,\n active,\n initialized,\n data,\n };\n}\n\nexport namespace useScrollSpy {\n export type Options = UseScrollSpyOptions;\n export type ReturnValue = UseScrollSpyReturnValue;\n}\n"],"mappings":";;;;AAGA,SAAS,gBACP,UACA,UACA,UAC2B;CAC3B,MAAM,SAAoC,EAAE;AAE5C,MAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;EAC3C,MAAM,UAAU,SAAS;AACzB,SAAO,KAAK;GACV,OAAO,SAAS,QAAQ;GACxB,OAAO,SAAS,QAAQ;GACxB,IAAI,QAAQ,MAAMA,kBAAAA,UAAU;GAC5B,eAAgB,QAAQ,KAAK,SAAS,eAAe,QAAQ,GAAG,GAAI;GACrE,CAAC;;AAGJ,QAAO;;AAGT,SAAS,iBAAiB,OAAkB,SAAiB,GAAG;AAC9D,KAAI,MAAM,WAAW,EACnB,QAAO;AAiBT,QAdgB,MAAM,QACnB,KAAK,MAAM,UAAU;AACpB,MAAI,KAAK,IAAI,IAAI,WAAW,OAAO,GAAG,KAAK,IAAI,KAAK,IAAI,OAAO,CAC7D,QAAO;AAGT,SAAO;GACL;GACA,UAAU,KAAK;GAChB;IAEH;EAAE,OAAO;EAAG,UAAU,MAAM,GAAG;EAAG,CACnC,CAEc;;AAGjB,SAAS,gBAAgB,SAAsB;AAC7C,QAAO,OAAO,QAAQ,QAAQ,GAAG;;AAGnC,SAAS,gBAAgB,SAAsB;AAC7C,QAAO,QAAQ,eAAe;;AAgDhC,SAAgB,aAAa,EAC3B,WAAW,0BACX,WAAW,iBACX,WAAW,iBACX,SAAS,GACT,eACuB,EAAE,EAA2B;CACpD,MAAM,CAAC,QAAQ,cAAA,GAAA,MAAA,UAAsB,GAAG;CACxC,MAAM,CAAC,aAAa,mBAAA,GAAA,MAAA,UAA2B,MAAM;CACrD,MAAM,CAAC,MAAM,YAAA,GAAA,MAAA,UAA+C,EAAE,CAAC;CAC/D,MAAM,eAAA,GAAA,MAAA,QAAgD,EAAE,CAAC;CAEzD,MAAM,qBAAqB;AACzB,YACE,iBACE,YAAY,QAAQ,KAAK,MAAM,EAAE,SAAS,CAAC,uBAAuB,CAAC,EACnE,OACD,CACF;;CAGH,MAAM,mBAAmB;EACvB,MAAM,WAAW,gBACf,MAAM,KAAK,SAAS,iBAAiB,SAAS,CAAC,EAC/C,UACA,SACD;AACD,cAAY,UAAU;AACtB,iBAAe,KAAK;AACpB,UAAQ,SAAS;AACjB,YACE,iBACE,SAAS,KAAK,MAAM,EAAE,SAAS,CAAC,uBAAuB,CAAC,EACxD,OACD,CACF;;AAGH,EAAA,GAAA,MAAA,iBAAgB;AACd,cAAY;EACZ,MAAM,cAAc,cAAc;AAClC,cAAY,iBAAiB,UAAU,aAAa;AACpD,eAAa,YAAY,oBAAoB,UAAU,aAAa;IACnE,CAAC,WAAW,CAAC;AAEhB,QAAO;EACL,cAAc;EACd;EACA;EACA;EACD"}
1
+ {"version":3,"file":"use-scroll-spy.cjs","names":["randomId"],"sources":["../../src/use-scroll-spy/use-scroll-spy.ts"],"sourcesContent":["import { useEffect, useEffectEvent, useRef, useState } from 'react';\nimport { randomId } from '../utils';\n\nfunction getHeadingsData(\n headings: HTMLElement[],\n getDepth: (element: HTMLElement) => number,\n getValue: (element: HTMLElement) => string\n): UseScrollSpyHeadingData[] {\n const result: UseScrollSpyHeadingData[] = [];\n\n for (let i = 0; i < headings.length; i += 1) {\n const heading = headings[i];\n result.push({\n depth: getDepth(heading),\n value: getValue(heading),\n id: heading.id || randomId(),\n getNode: () => (heading.id ? document.getElementById(heading.id)! : heading),\n });\n }\n\n return result;\n}\n\nfunction getActiveElement(rects: DOMRect[], offset: number = 0) {\n if (rects.length === 0) {\n return -1;\n }\n\n const closest = rects.reduce(\n (acc, item, index) => {\n if (Math.abs(acc.position - offset) < Math.abs(item.y - offset)) {\n return acc;\n }\n\n return {\n index,\n position: item.y,\n };\n },\n { index: 0, position: rects[0].y }\n );\n\n return closest.index;\n}\n\nfunction getDefaultDepth(element: HTMLElement) {\n return Number(element.tagName[1]);\n}\n\nfunction getDefaultValue(element: HTMLElement) {\n return element.textContent || '';\n}\n\nexport interface UseScrollSpyHeadingData {\n /** Heading depth, 1-6 */\n depth: number;\n\n /** Heading text content value */\n value: string;\n\n /** Heading id */\n id: string;\n\n /** Function to get heading node */\n getNode: () => HTMLElement;\n}\n\nexport interface UseScrollSpyOptions {\n /** Selector to get headings, `'h1, h2, h3, h4, h5, h6'` by default */\n selector?: string;\n\n /** A function to retrieve depth of heading, by default depth is calculated based on tag name */\n getDepth?: (element: HTMLElement) => number;\n\n /** A function to retrieve heading value, by default `element.textContent` is used */\n getValue?: (element: HTMLElement) => string;\n\n /** Host element to attach scroll event listener, if not provided, `window` is used */\n scrollHost?: HTMLElement;\n\n /** Offset from the top of the viewport to use when determining the active heading, `0` by default */\n offset?: number;\n}\n\nexport interface UseScrollSpyReturnValue {\n /** Index of the active heading in the `data` array */\n active: number;\n\n /** Headings data. If not initialize, data is represented by an empty array. */\n data: UseScrollSpyHeadingData[];\n\n /** True if headings value have been retrieved from the DOM. */\n initialized: boolean;\n\n /** Function to update headings values after the parent component has mounted. */\n reinitialize: () => void;\n}\n\nexport function useScrollSpy({\n selector = 'h1, h2, h3, h4, h5, h6',\n getDepth = getDefaultDepth,\n getValue = getDefaultValue,\n offset = 0,\n scrollHost,\n}: UseScrollSpyOptions = {}): UseScrollSpyReturnValue {\n const [active, setActive] = useState(-1);\n const [initialized, setInitialized] = useState(false);\n const [data, setData] = useState<UseScrollSpyHeadingData[]>([]);\n const headingsRef = useRef<UseScrollSpyHeadingData[]>([]);\n\n const handleScroll = useEffectEvent(() => {\n setActive(\n getActiveElement(\n headingsRef.current.map((d) => d.getNode().getBoundingClientRect()),\n offset\n )\n );\n });\n\n const initialize = () => {\n const headings = getHeadingsData(\n Array.from(document.querySelectorAll(selector)),\n getDepth,\n getValue\n );\n headingsRef.current = headings;\n setInitialized(true);\n setData(headings);\n setActive(\n getActiveElement(\n headings.map((d) => d.getNode().getBoundingClientRect()),\n offset\n )\n );\n };\n\n useEffect(() => {\n initialize();\n const _scrollHost = scrollHost || window;\n _scrollHost.addEventListener('scroll', handleScroll);\n return () => _scrollHost.removeEventListener('scroll', handleScroll);\n }, [scrollHost, selector, offset]);\n\n return {\n reinitialize: initialize,\n active,\n initialized,\n data,\n };\n}\n\nexport namespace useScrollSpy {\n export type Options = UseScrollSpyOptions;\n export type ReturnValue = UseScrollSpyReturnValue;\n}\n"],"mappings":";;;;AAGA,SAAS,gBACP,UACA,UACA,UAC2B;CAC3B,MAAM,SAAoC,EAAE;AAE5C,MAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;EAC3C,MAAM,UAAU,SAAS;AACzB,SAAO,KAAK;GACV,OAAO,SAAS,QAAQ;GACxB,OAAO,SAAS,QAAQ;GACxB,IAAI,QAAQ,MAAMA,kBAAAA,UAAU;GAC5B,eAAgB,QAAQ,KAAK,SAAS,eAAe,QAAQ,GAAG,GAAI;GACrE,CAAC;;AAGJ,QAAO;;AAGT,SAAS,iBAAiB,OAAkB,SAAiB,GAAG;AAC9D,KAAI,MAAM,WAAW,EACnB,QAAO;AAiBT,QAdgB,MAAM,QACnB,KAAK,MAAM,UAAU;AACpB,MAAI,KAAK,IAAI,IAAI,WAAW,OAAO,GAAG,KAAK,IAAI,KAAK,IAAI,OAAO,CAC7D,QAAO;AAGT,SAAO;GACL;GACA,UAAU,KAAK;GAChB;IAEH;EAAE,OAAO;EAAG,UAAU,MAAM,GAAG;EAAG,CACnC,CAEc;;AAGjB,SAAS,gBAAgB,SAAsB;AAC7C,QAAO,OAAO,QAAQ,QAAQ,GAAG;;AAGnC,SAAS,gBAAgB,SAAsB;AAC7C,QAAO,QAAQ,eAAe;;AAgDhC,SAAgB,aAAa,EAC3B,WAAW,0BACX,WAAW,iBACX,WAAW,iBACX,SAAS,GACT,eACuB,EAAE,EAA2B;CACpD,MAAM,CAAC,QAAQ,cAAA,GAAA,MAAA,UAAsB,GAAG;CACxC,MAAM,CAAC,aAAa,mBAAA,GAAA,MAAA,UAA2B,MAAM;CACrD,MAAM,CAAC,MAAM,YAAA,GAAA,MAAA,UAA+C,EAAE,CAAC;CAC/D,MAAM,eAAA,GAAA,MAAA,QAAgD,EAAE,CAAC;CAEzD,MAAM,gBAAA,GAAA,MAAA,sBAAoC;AACxC,YACE,iBACE,YAAY,QAAQ,KAAK,MAAM,EAAE,SAAS,CAAC,uBAAuB,CAAC,EACnE,OACD,CACF;GACD;CAEF,MAAM,mBAAmB;EACvB,MAAM,WAAW,gBACf,MAAM,KAAK,SAAS,iBAAiB,SAAS,CAAC,EAC/C,UACA,SACD;AACD,cAAY,UAAU;AACtB,iBAAe,KAAK;AACpB,UAAQ,SAAS;AACjB,YACE,iBACE,SAAS,KAAK,MAAM,EAAE,SAAS,CAAC,uBAAuB,CAAC,EACxD,OACD,CACF;;AAGH,EAAA,GAAA,MAAA,iBAAgB;AACd,cAAY;EACZ,MAAM,cAAc,cAAc;AAClC,cAAY,iBAAiB,UAAU,aAAa;AACpD,eAAa,YAAY,oBAAoB,UAAU,aAAa;IACnE;EAAC;EAAY;EAAU;EAAO,CAAC;AAElC,QAAO;EACL,cAAc;EACd;EACA;EACA;EACD"}
@@ -21,10 +21,10 @@ function useScroller(options = {}) {
21
21
  let newCanScrollStart;
22
22
  let newCanScrollEnd;
23
23
  if (isRtl) {
24
- newCanScrollStart = scrollLeft < 0;
25
- newCanScrollEnd = scrollLeft > -(scrollWidth - clientWidth);
24
+ newCanScrollStart = scrollLeft < -1;
25
+ newCanScrollEnd = scrollLeft > -(scrollWidth - clientWidth) + 1;
26
26
  } else {
27
- newCanScrollStart = scrollLeft > 0;
27
+ newCanScrollStart = scrollLeft > 1;
28
28
  newCanScrollEnd = scrollLeft < scrollWidth - clientWidth - 1;
29
29
  }
30
30
  setCanScrollStart(newCanScrollStart);