@mantine/hooks 9.0.2 → 9.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/index.cjs +8 -0
- package/cjs/use-click-outside/use-click-outside.cjs +11 -8
- package/cjs/use-click-outside/use-click-outside.cjs.map +1 -1
- package/cjs/use-clipboard/use-clipboard.cjs +13 -6
- package/cjs/use-clipboard/use-clipboard.cjs.map +1 -1
- package/cjs/use-collapse/use-collapse.cjs +1 -1
- package/cjs/use-collapse/use-collapse.cjs.map +1 -1
- package/cjs/use-collapse/use-horizontal-collapse.cjs +4 -3
- package/cjs/use-collapse/use-horizontal-collapse.cjs.map +1 -1
- package/cjs/use-counter/use-counter.cjs +12 -3
- package/cjs/use-counter/use-counter.cjs.map +1 -1
- package/cjs/use-debounced-callback/use-debounced-callback.cjs +28 -4
- package/cjs/use-debounced-callback/use-debounced-callback.cjs.map +1 -1
- package/cjs/use-debounced-state/use-debounced-state.cjs +1 -1
- package/cjs/use-debounced-state/use-debounced-state.cjs.map +1 -1
- package/cjs/use-debounced-value/use-debounced-value.cjs +22 -2
- package/cjs/use-debounced-value/use-debounced-value.cjs.map +1 -1
- package/cjs/use-event-listener/use-event-listener.cjs.map +1 -1
- package/cjs/use-favicon/use-favicon.cjs +3 -1
- package/cjs/use-favicon/use-favicon.cjs.map +1 -1
- package/cjs/use-fetch/use-fetch.cjs +7 -4
- package/cjs/use-fetch/use-fetch.cjs.map +1 -1
- package/cjs/use-file-dialog/use-file-dialog.cjs +1 -0
- package/cjs/use-file-dialog/use-file-dialog.cjs.map +1 -1
- package/cjs/use-floating-window/use-floating-window.cjs +33 -36
- package/cjs/use-floating-window/use-floating-window.cjs.map +1 -1
- package/cjs/use-focus-return/use-focus-return.cjs +7 -1
- package/cjs/use-focus-return/use-focus-return.cjs.map +1 -1
- package/cjs/use-focus-trap/use-focus-trap.cjs +12 -9
- package/cjs/use-focus-trap/use-focus-trap.cjs.map +1 -1
- package/cjs/use-fullscreen/use-fullscreen.cjs +3 -1
- package/cjs/use-fullscreen/use-fullscreen.cjs.map +1 -1
- package/cjs/use-headroom/use-headroom.cjs +4 -1
- package/cjs/use-headroom/use-headroom.cjs.map +1 -1
- package/cjs/use-hotkeys/parse-hotkey.cjs +1 -2
- package/cjs/use-hotkeys/parse-hotkey.cjs.map +1 -1
- package/cjs/use-hover/use-hover.cjs +1 -0
- package/cjs/use-hover/use-hover.cjs.map +1 -1
- package/cjs/use-in-viewport/use-in-viewport.cjs +11 -7
- package/cjs/use-in-viewport/use-in-viewport.cjs.map +1 -1
- package/cjs/use-interval/use-interval.cjs +15 -6
- package/cjs/use-interval/use-interval.cjs.map +1 -1
- package/cjs/use-local-storage/create-storage.cjs +1 -1
- package/cjs/use-local-storage/create-storage.cjs.map +1 -1
- package/cjs/use-long-press/use-long-press.cjs +6 -2
- package/cjs/use-long-press/use-long-press.cjs.map +1 -1
- package/cjs/use-mask/use-mask.cjs +486 -0
- package/cjs/use-mask/use-mask.cjs.map +1 -0
- package/cjs/use-mouse/use-mouse.cjs +2 -2
- package/cjs/use-mouse/use-mouse.cjs.map +1 -1
- package/cjs/use-move/use-move.cjs +8 -0
- package/cjs/use-move/use-move.cjs.map +1 -1
- package/cjs/use-orientation/use-orientation.cjs +8 -2
- package/cjs/use-orientation/use-orientation.cjs.map +1 -1
- package/cjs/use-os/use-os.cjs +1 -1
- package/cjs/use-os/use-os.cjs.map +1 -1
- package/cjs/use-radial-move/use-radial-move.cjs +4 -4
- package/cjs/use-radial-move/use-radial-move.cjs.map +1 -1
- package/cjs/use-roving-index/use-roving-index.cjs +200 -0
- package/cjs/use-roving-index/use-roving-index.cjs.map +1 -0
- package/cjs/use-scroll-direction/use-scroll-direction.cjs +7 -7
- package/cjs/use-scroll-direction/use-scroll-direction.cjs.map +1 -1
- package/cjs/use-scroll-into-view/use-scroll-into-view.cjs +14 -4
- package/cjs/use-scroll-into-view/use-scroll-into-view.cjs.map +1 -1
- package/cjs/use-scroll-spy/use-scroll-spy.cjs +7 -3
- package/cjs/use-scroll-spy/use-scroll-spy.cjs.map +1 -1
- package/cjs/use-scroller/use-scroller.cjs +3 -3
- package/cjs/use-scroller/use-scroller.cjs.map +1 -1
- package/cjs/use-set/use-set.cjs +6 -1
- package/cjs/use-set/use-set.cjs.map +1 -1
- package/cjs/use-throttled-callback/use-throttled-callback.cjs +3 -1
- package/cjs/use-throttled-callback/use-throttled-callback.cjs.map +1 -1
- package/cjs/use-timeout/use-timeout.cjs +3 -1
- package/cjs/use-timeout/use-timeout.cjs.map +1 -1
- package/cjs/use-viewport-size/use-viewport-size.cjs +3 -3
- package/cjs/use-viewport-size/use-viewport-size.cjs.map +1 -1
- package/cjs/use-window-scroll/use-window-scroll.cjs +2 -2
- package/cjs/use-window-scroll/use-window-scroll.cjs.map +1 -1
- package/esm/index.mjs +3 -1
- package/esm/use-click-outside/use-click-outside.mjs +11 -8
- package/esm/use-click-outside/use-click-outside.mjs.map +1 -1
- package/esm/use-clipboard/use-clipboard.mjs +14 -7
- package/esm/use-clipboard/use-clipboard.mjs.map +1 -1
- package/esm/use-collapse/use-collapse.mjs +1 -1
- package/esm/use-collapse/use-collapse.mjs.map +1 -1
- package/esm/use-collapse/use-horizontal-collapse.mjs +5 -4
- package/esm/use-collapse/use-horizontal-collapse.mjs.map +1 -1
- package/esm/use-counter/use-counter.mjs +12 -3
- package/esm/use-counter/use-counter.mjs.map +1 -1
- package/esm/use-debounced-callback/use-debounced-callback.mjs +28 -4
- package/esm/use-debounced-callback/use-debounced-callback.mjs.map +1 -1
- package/esm/use-debounced-state/use-debounced-state.mjs +1 -1
- package/esm/use-debounced-state/use-debounced-state.mjs.map +1 -1
- package/esm/use-debounced-value/use-debounced-value.mjs +22 -2
- package/esm/use-debounced-value/use-debounced-value.mjs.map +1 -1
- package/esm/use-event-listener/use-event-listener.mjs.map +1 -1
- package/esm/use-favicon/use-favicon.mjs +3 -1
- package/esm/use-favicon/use-favicon.mjs.map +1 -1
- package/esm/use-fetch/use-fetch.mjs +7 -4
- package/esm/use-fetch/use-fetch.mjs.map +1 -1
- package/esm/use-file-dialog/use-file-dialog.mjs +1 -0
- package/esm/use-file-dialog/use-file-dialog.mjs.map +1 -1
- package/esm/use-floating-window/use-floating-window.mjs +33 -36
- package/esm/use-floating-window/use-floating-window.mjs.map +1 -1
- package/esm/use-focus-return/use-focus-return.mjs +7 -1
- package/esm/use-focus-return/use-focus-return.mjs.map +1 -1
- package/esm/use-focus-trap/use-focus-trap.mjs +12 -9
- package/esm/use-focus-trap/use-focus-trap.mjs.map +1 -1
- package/esm/use-fullscreen/use-fullscreen.mjs +3 -1
- package/esm/use-fullscreen/use-fullscreen.mjs.map +1 -1
- package/esm/use-headroom/use-headroom.mjs +4 -1
- package/esm/use-headroom/use-headroom.mjs.map +1 -1
- package/esm/use-hotkeys/parse-hotkey.mjs +1 -2
- package/esm/use-hotkeys/parse-hotkey.mjs.map +1 -1
- package/esm/use-hover/use-hover.mjs +1 -0
- package/esm/use-hover/use-hover.mjs.map +1 -1
- package/esm/use-in-viewport/use-in-viewport.mjs +11 -7
- package/esm/use-in-viewport/use-in-viewport.mjs.map +1 -1
- package/esm/use-interval/use-interval.mjs +15 -6
- package/esm/use-interval/use-interval.mjs.map +1 -1
- package/esm/use-local-storage/create-storage.mjs +1 -1
- package/esm/use-local-storage/create-storage.mjs.map +1 -1
- package/esm/use-long-press/use-long-press.mjs +6 -2
- package/esm/use-long-press/use-long-press.mjs.map +1 -1
- package/esm/use-mask/use-mask.mjs +482 -0
- package/esm/use-mask/use-mask.mjs.map +1 -0
- package/esm/use-mouse/use-mouse.mjs +2 -2
- package/esm/use-mouse/use-mouse.mjs.map +1 -1
- package/esm/use-move/use-move.mjs +8 -0
- package/esm/use-move/use-move.mjs.map +1 -1
- package/esm/use-orientation/use-orientation.mjs +8 -2
- package/esm/use-orientation/use-orientation.mjs.map +1 -1
- package/esm/use-os/use-os.mjs +1 -1
- package/esm/use-os/use-os.mjs.map +1 -1
- package/esm/use-radial-move/use-radial-move.mjs +5 -5
- package/esm/use-radial-move/use-radial-move.mjs.map +1 -1
- package/esm/use-roving-index/use-roving-index.mjs +200 -0
- package/esm/use-roving-index/use-roving-index.mjs.map +1 -0
- package/esm/use-scroll-direction/use-scroll-direction.mjs +7 -7
- package/esm/use-scroll-direction/use-scroll-direction.mjs.map +1 -1
- package/esm/use-scroll-into-view/use-scroll-into-view.mjs +15 -5
- package/esm/use-scroll-into-view/use-scroll-into-view.mjs.map +1 -1
- package/esm/use-scroll-spy/use-scroll-spy.mjs +8 -4
- package/esm/use-scroll-spy/use-scroll-spy.mjs.map +1 -1
- package/esm/use-scroller/use-scroller.mjs +3 -3
- package/esm/use-scroller/use-scroller.mjs.map +1 -1
- package/esm/use-set/use-set.mjs +6 -1
- package/esm/use-set/use-set.mjs.map +1 -1
- package/esm/use-throttled-callback/use-throttled-callback.mjs +3 -1
- package/esm/use-throttled-callback/use-throttled-callback.mjs.map +1 -1
- package/esm/use-timeout/use-timeout.mjs +3 -1
- package/esm/use-timeout/use-timeout.mjs.map +1 -1
- package/esm/use-viewport-size/use-viewport-size.mjs +3 -3
- package/esm/use-viewport-size/use-viewport-size.mjs.map +1 -1
- package/esm/use-window-scroll/use-window-scroll.mjs +2 -2
- package/esm/use-window-scroll/use-window-scroll.mjs.map +1 -1
- package/lib/index.d.mts +5 -1
- package/lib/index.d.ts +5 -1
- package/lib/use-click-outside/use-click-outside.d.ts +1 -1
- package/lib/use-collapse/use-horizontal-collapse.d.ts +5 -0
- package/lib/use-counter/use-counter.d.ts +1 -0
- package/lib/use-debounced-callback/use-debounced-callback.d.ts +4 -0
- package/lib/use-debounced-value/use-debounced-value.d.ts +6 -1
- package/lib/use-event-listener/use-event-listener.d.ts +1 -1
- package/lib/use-hotkeys/parse-hotkey.d.ts +4 -5
- package/lib/use-long-press/use-long-press.d.ts +1 -0
- package/lib/use-mask/use-mask.d.ts +60 -0
- package/lib/use-roving-index/use-roving-index.d.ts +49 -0
- package/lib/use-scroll-into-view/use-scroll-into-view.d.ts +4 -1
- package/package.json +1 -1
|
@@ -23,8 +23,14 @@ function useOrientation({ defaultAngle = 0, defaultType = "landscape-primary", g
|
|
|
23
23
|
});
|
|
24
24
|
};
|
|
25
25
|
useIsomorphicEffect(() => {
|
|
26
|
-
window.screen.orientation
|
|
27
|
-
|
|
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.mjs","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
|
|
1
|
+
{"version":3,"file":"use-orientation.mjs","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,kBAAkB,SACpC,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,2BAA0B;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"}
|
package/esm/use-os/use-os.mjs
CHANGED
|
@@ -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.mjs","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 (
|
|
1
|
+
{"version":3,"file":"use-os.mjs","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,YAAY,SACxB,QAAQ,mBAAmB,iBAAiB,OAAO,CACpD;AAED,2BAA0B;AACxB,MAAI,QAAQ,iBACV,UAAS,MAAM;IAEhB,EAAE,CAAC;AAEN,QAAO"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { clamp } from "../utils/clamp/clamp.mjs";
|
|
3
|
-
import { useCallback,
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
4
|
//#region packages/@mantine/hooks/src/use-radial-move/use-radial-move.ts
|
|
5
5
|
function radiansToDegrees(radians) {
|
|
6
6
|
return radians * (180 / Math.PI);
|
|
@@ -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 = useRef(false);
|
|
32
31
|
const [active, setActive] = useState(false);
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
mounted.current = true;
|
|
35
|
-
}, []);
|
|
36
32
|
return {
|
|
37
33
|
ref: 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.mjs","names":[],"sources":["../../src/use-radial-move/use-radial-move.ts"],"sourcesContent":["import { useCallback,
|
|
1
|
+
{"version":3,"file":"use-radial-move.mjs","names":[],"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":"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"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useUncontrolled } from "../use-uncontrolled/use-uncontrolled.mjs";
|
|
3
|
+
import { useCallback, useEffect, useRef } from "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 = useRef(/* @__PURE__ */ new Map());
|
|
31
|
+
const isGrid = typeof columns === "number" && columns > 0;
|
|
32
|
+
const [activeIndex, setActiveIndex] = useUncontrolled({
|
|
33
|
+
value: focusedIndex,
|
|
34
|
+
defaultValue: initialIndex !== void 0 ? initialIndex : findFirstEnabled(total, isItemDisabled),
|
|
35
|
+
finalValue: 0,
|
|
36
|
+
onChange: onFocusChange
|
|
37
|
+
});
|
|
38
|
+
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 = 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 = 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 = 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: 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
|
+
export { useRovingIndex };
|
|
199
|
+
|
|
200
|
+
//# sourceMappingURL=use-roving-index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-roving-index.mjs","names":[],"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
|
import { useEffect, useEffectEvent, useRef, useState } from "react";
|
|
3
3
|
//#region packages/@mantine/hooks/src/use-scroll-direction/use-scroll-direction.ts
|
|
4
4
|
function useScrollDirection() {
|
|
5
|
-
const
|
|
5
|
+
const lastScrollTopRef = useRef(0);
|
|
6
6
|
const [scrollDirection, setScrollDirection] = useState("unknown");
|
|
7
|
-
const
|
|
7
|
+
const isResizingRef = useRef(false);
|
|
8
8
|
const resizeTimerRef = useRef(void 0);
|
|
9
9
|
const handleScroll = useEffectEvent(() => {
|
|
10
|
-
if (
|
|
10
|
+
if (isResizingRef.current) return;
|
|
11
11
|
const currentScrollTop = window.scrollY || document.documentElement.scrollTop;
|
|
12
|
-
setScrollDirection(currentScrollTop <
|
|
13
|
-
|
|
12
|
+
setScrollDirection(currentScrollTop < lastScrollTopRef.current ? "up" : "down");
|
|
13
|
+
lastScrollTopRef.current = currentScrollTop;
|
|
14
14
|
});
|
|
15
15
|
useEffect(() => {
|
|
16
16
|
const handleResize = () => {
|
|
17
|
-
|
|
17
|
+
isResizingRef.current = true;
|
|
18
18
|
window.clearTimeout(resizeTimerRef.current);
|
|
19
19
|
resizeTimerRef.current = window.setTimeout(() => {
|
|
20
|
-
|
|
20
|
+
isResizingRef.current = false;
|
|
21
21
|
}, 300);
|
|
22
22
|
};
|
|
23
23
|
window.addEventListener("scroll", handleScroll);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-scroll-direction.mjs","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
|
|
1
|
+
{"version":3,"file":"use-scroll-direction.mjs","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,mBAAmB,OAAO,EAAE;CAClC,MAAM,CAAC,iBAAiB,sBAAsB,SAA0B,UAAU;CAClF,MAAM,gBAAgB,OAAO,MAAM;CACnC,MAAM,iBAAiB,OAA2B,KAAA,EAAU;CAE5D,MAAM,eAAe,qBAAqB;AACxC,MAAI,cAAc,QAChB;EAGF,MAAM,mBAAmB,OAAO,WAAW,SAAS,gBAAgB;AACpE,qBAAmB,mBAAmB,iBAAiB,UAAU,OAAO,OAAO;AAC/E,mBAAiB,UAAU;GAC3B;AAEF,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"}
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useWindowEvent } from "../use-window-event/use-window-event.mjs";
|
|
3
3
|
import { useReducedMotion } from "../use-reduced-motion/use-reduced-motion.mjs";
|
|
4
|
-
import { useCallback, useEffect, useRef } from "react";
|
|
4
|
+
import { useCallback, useEffect, useRef, useState } from "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 = useRef(0);
|
|
8
8
|
const startTime = useRef(0);
|
|
9
9
|
const shouldStop = useRef(false);
|
|
10
|
+
const [scrolling, setScrolling] = useState(false);
|
|
10
11
|
const scrollableRef = useRef(null);
|
|
11
12
|
const targetRef = useRef(null);
|
|
12
13
|
const reducedMotion = useReducedMotion();
|
|
13
14
|
const cancel = () => {
|
|
14
|
-
if (frameID.current)
|
|
15
|
+
if (frameID.current) {
|
|
16
|
+
cancelAnimationFrame(frameID.current);
|
|
17
|
+
frameID.current = 0;
|
|
18
|
+
setScrolling(false);
|
|
19
|
+
}
|
|
15
20
|
};
|
|
16
21
|
const scrollIntoView = 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
|
|
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.mjs","names":[],"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.mjs","names":[],"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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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { randomId } from "../utils/random-id/random-id.mjs";
|
|
3
|
-
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { useEffect, useEffectEvent, useRef, useState } from "react";
|
|
4
4
|
//#region packages/@mantine/hooks/src/use-scroll-spy/use-scroll-spy.ts
|
|
5
5
|
function getHeadingsData(headings, getDepth, getValue) {
|
|
6
6
|
const result = [];
|
|
@@ -39,9 +39,9 @@ function useScrollSpy({ selector = "h1, h2, h3, h4, h5, h6", getDepth = getDefau
|
|
|
39
39
|
const [initialized, setInitialized] = useState(false);
|
|
40
40
|
const [data, setData] = useState([]);
|
|
41
41
|
const headingsRef = useRef([]);
|
|
42
|
-
const handleScroll = () => {
|
|
42
|
+
const handleScroll = 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
|
-
}, [
|
|
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.mjs","names":[],"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,MAAM,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,aAAa,SAAS,GAAG;CACxC,MAAM,CAAC,aAAa,kBAAkB,SAAS,MAAM;CACrD,MAAM,CAAC,MAAM,WAAW,SAAoC,EAAE,CAAC;CAC/D,MAAM,cAAc,OAAkC,EAAE,CAAC;CAEzD,MAAM,qBAAqB;
|
|
1
|
+
{"version":3,"file":"use-scroll-spy.mjs","names":[],"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,MAAM,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,aAAa,SAAS,GAAG;CACxC,MAAM,CAAC,aAAa,kBAAkB,SAAS,MAAM;CACrD,MAAM,CAAC,MAAM,WAAW,SAAoC,EAAE,CAAC;CAC/D,MAAM,cAAc,OAAkC,EAAE,CAAC;CAEzD,MAAM,eAAe,qBAAqB;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,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 <
|
|
25
|
-
newCanScrollEnd = scrollLeft > -(scrollWidth - clientWidth);
|
|
24
|
+
newCanScrollStart = scrollLeft < -1;
|
|
25
|
+
newCanScrollEnd = scrollLeft > -(scrollWidth - clientWidth) + 1;
|
|
26
26
|
} else {
|
|
27
|
-
newCanScrollStart = scrollLeft >
|
|
27
|
+
newCanScrollStart = scrollLeft > 1;
|
|
28
28
|
newCanScrollEnd = scrollLeft < scrollWidth - clientWidth - 1;
|
|
29
29
|
}
|
|
30
30
|
setCanScrollStart(newCanScrollStart);
|