├── .github └── workflows │ └── deploy.yaml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .prettierignore ├── .prettierrc.mjs ├── LICENSE ├── README.md ├── SECURITY.md ├── package.json ├── packages ├── cli │ ├── README.md │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── add.ts │ │ ├── bin.ts │ │ ├── cli.ts │ │ ├── init.ts │ │ ├── registry │ │ │ ├── index.ts │ │ │ └── utils │ │ │ │ ├── extractDependencies.ts │ │ │ │ └── index.ts │ │ └── utils │ │ │ ├── constants │ │ │ ├── appPath.ts │ │ │ ├── index.ts │ │ │ └── urls.ts │ │ │ ├── helpers │ │ │ ├── getConfig.ts │ │ │ ├── getPackageManager.ts │ │ │ └── index.ts │ │ │ └── types │ │ │ └── index.ts │ ├── tsconfig.json │ └── tsup.config.ts ├── core │ ├── README.md │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── bundle │ │ │ ├── helpers │ │ │ │ ├── createContext │ │ │ │ │ └── createContext.js │ │ │ │ ├── createContextSelector │ │ │ │ │ ├── createContextSelector.js │ │ │ │ │ └── createReactiveContext.js │ │ │ │ ├── createReactiveContext │ │ │ │ │ └── createReactiveContext.js │ │ │ │ ├── createStore │ │ │ │ │ └── createStore.js │ │ │ │ └── index.js │ │ │ ├── hooks │ │ │ │ ├── index.js │ │ │ │ ├── useActiveElement │ │ │ │ │ └── useActiveElement.js │ │ │ │ ├── useAsync │ │ │ │ │ └── useAsync.js │ │ │ │ ├── useBattery │ │ │ │ │ └── useBattery.js │ │ │ │ ├── useBluetooth │ │ │ │ │ └── useBluetooth.js │ │ │ │ ├── useBoolean │ │ │ │ │ └── useBoolean.js │ │ │ │ ├── useBreakpoints │ │ │ │ │ ├── constants │ │ │ │ │ │ └── breakpoints.js │ │ │ │ │ ├── helpers │ │ │ │ │ │ ├── breakpoints.js │ │ │ │ │ │ └── index.js │ │ │ │ │ └── useBreakpoints.js │ │ │ │ ├── useBrowserLanguage │ │ │ │ │ └── useBrowserLanguage.js │ │ │ │ ├── useClickOutside │ │ │ │ │ └── useClickOutside.js │ │ │ │ ├── useClipboard │ │ │ │ │ └── useClipboard.js │ │ │ │ ├── useConst │ │ │ │ │ └── useConst.js │ │ │ │ ├── useCookie │ │ │ │ │ ├── helpers │ │ │ │ │ │ ├── clearCookies.js │ │ │ │ │ │ ├── getCookies.js │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ ├── removeCookie.js │ │ │ │ │ │ └── setCookie.js │ │ │ │ │ └── useCookie.js │ │ │ │ ├── useCookies │ │ │ │ │ └── useCookies.js │ │ │ │ ├── useCopy │ │ │ │ │ └── useCopy.js │ │ │ │ ├── useCounter │ │ │ │ │ └── useCounter.js │ │ │ │ ├── useCssVar │ │ │ │ │ └── useCssVar.js │ │ │ │ ├── useDebounceCallback │ │ │ │ │ └── useDebounceCallback.js │ │ │ │ ├── useDebounceValue │ │ │ │ │ └── useDebounceValue.js │ │ │ │ ├── useDefault │ │ │ │ │ └── useDefault.js │ │ │ │ ├── useDeviceMotion │ │ │ │ │ └── useDeviceMotion.js │ │ │ │ ├── useDeviceOrientation │ │ │ │ │ └── useDeviceOrientation.js │ │ │ │ ├── useDevicePixelRatio │ │ │ │ │ └── useDevicePixelRatio.js │ │ │ │ ├── useDidUpdate │ │ │ │ │ └── useDidUpdate.js │ │ │ │ ├── useDisclosure │ │ │ │ │ └── useDisclosure.js │ │ │ │ ├── useDisplayMedia │ │ │ │ │ └── useDisplayMedia.js │ │ │ │ ├── useDocumentEvent │ │ │ │ │ └── useDocumentEvent.js │ │ │ │ ├── useDocumentTitle │ │ │ │ │ └── useDocumentTitle.js │ │ │ │ ├── useDocumentVisibility │ │ │ │ │ └── useDocumentVisibility.js │ │ │ │ ├── useDoubleClick │ │ │ │ │ └── useDoubleClick.js │ │ │ │ ├── useDropZone │ │ │ │ │ └── useDropZone.js │ │ │ │ ├── useElementSize │ │ │ │ │ └── useElementSize.js │ │ │ │ ├── useEvent │ │ │ │ │ └── useEvent.js │ │ │ │ ├── useEventListener │ │ │ │ │ └── useEventListener.js │ │ │ │ ├── useEventSource │ │ │ │ │ └── useEventSource.js │ │ │ │ ├── useEyeDropper │ │ │ │ │ └── useEyeDropper.js │ │ │ │ ├── useFavicon │ │ │ │ │ └── useFavicon.js │ │ │ │ ├── useField │ │ │ │ │ └── useField.js │ │ │ │ ├── useFileDialog │ │ │ │ │ └── useFileDialog.js │ │ │ │ ├── useFocus │ │ │ │ │ └── useFocus.js │ │ │ │ ├── useFps │ │ │ │ │ └── useFps.js │ │ │ │ ├── useFul │ │ │ │ │ └── useFul.js │ │ │ │ ├── useFullscreen │ │ │ │ │ └── useFullscreen.js │ │ │ │ ├── useGamepad │ │ │ │ │ ├── helpers │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── mapGamepadToXbox360Controller.js │ │ │ │ │ └── useGamepad.js │ │ │ │ ├── useGeolocation │ │ │ │ │ └── useGeolocation.js │ │ │ │ ├── useHash │ │ │ │ │ └── useHash.js │ │ │ │ ├── useHotkeys │ │ │ │ │ └── useHotkeys.js │ │ │ │ ├── useHover │ │ │ │ │ └── useHover.js │ │ │ │ ├── useIdle │ │ │ │ │ └── useIdle.js │ │ │ │ ├── useImage │ │ │ │ │ └── useImage.js │ │ │ │ ├── useInfiniteScroll │ │ │ │ │ └── useInfiniteScroll.js │ │ │ │ ├── useIntersectionObserver │ │ │ │ │ └── useIntersectionObserver.js │ │ │ │ ├── useInterval │ │ │ │ │ └── useInterval.js │ │ │ │ ├── useIsFirstRender │ │ │ │ │ └── useIsFirstRender.js │ │ │ │ ├── useIsomorphicLayoutEffect │ │ │ │ │ └── useIsomorphicLayoutEffect.js │ │ │ │ ├── useKeyPress │ │ │ │ │ └── useKeyPress.js │ │ │ │ ├── useKeyPressEvent │ │ │ │ │ └── useKeyPressEvent.js │ │ │ │ ├── useKeyboard │ │ │ │ │ └── useKeyboard.js │ │ │ │ ├── useKeysPressed │ │ │ │ │ └── useKeysPressed.js │ │ │ │ ├── useLastChanged │ │ │ │ │ └── useLastChanged.js │ │ │ │ ├── useLatest │ │ │ │ │ └── useLatest.js │ │ │ │ ├── useLess │ │ │ │ │ └── useLess.js │ │ │ │ ├── useList │ │ │ │ │ └── useList.js │ │ │ │ ├── useLocalStorage │ │ │ │ │ └── useLocalStorage.js │ │ │ │ ├── useLockCallback │ │ │ │ │ └── useLockCallback.js │ │ │ │ ├── useLogger │ │ │ │ │ └── useLogger.js │ │ │ │ ├── useLongPress │ │ │ │ │ └── useLongPress.js │ │ │ │ ├── useMap │ │ │ │ │ └── useMap.js │ │ │ │ ├── useMeasure │ │ │ │ │ └── useMeasure.js │ │ │ │ ├── useMediaQuery │ │ │ │ │ └── useMediaQuery.js │ │ │ │ ├── useMemory │ │ │ │ │ └── useMemory.js │ │ │ │ ├── useMount │ │ │ │ │ └── useMount.js │ │ │ │ ├── useMouse │ │ │ │ │ └── useMouse.js │ │ │ │ ├── useMutation │ │ │ │ │ └── useMutation.js │ │ │ │ ├── useMutationObserver │ │ │ │ │ └── useMutationObserver.js │ │ │ │ ├── useNetwork │ │ │ │ │ └── useNetwork.js │ │ │ │ ├── useOffsetPagination │ │ │ │ │ └── useOffsetPagination.js │ │ │ │ ├── useOnce │ │ │ │ │ └── useOnce.js │ │ │ │ ├── useOnline │ │ │ │ │ └── useOnline.js │ │ │ │ ├── useOperatingSystem │ │ │ │ │ └── useOperatingSystem.js │ │ │ │ ├── useOptimistic │ │ │ │ │ └── useOptimistic.js │ │ │ │ ├── useOrientation │ │ │ │ │ └── useOrientation.js │ │ │ │ ├── useOtpCredential │ │ │ │ │ └── useOtpCredential.js │ │ │ │ ├── usePageLeave │ │ │ │ │ └── usePageLeave.js │ │ │ │ ├── usePaint │ │ │ │ │ ├── helpers │ │ │ │ │ │ ├── Paint.js │ │ │ │ │ │ ├── Pointer.js │ │ │ │ │ │ └── index.js │ │ │ │ │ └── usePaint.js │ │ │ │ ├── useParallax │ │ │ │ │ └── useParallax.js │ │ │ │ ├── usePerformanceObserver │ │ │ │ │ └── usePerformanceObserver.js │ │ │ │ ├── usePermission │ │ │ │ │ └── usePermission.js │ │ │ │ ├── usePointerLock │ │ │ │ │ └── usePointerLock.js │ │ │ │ ├── usePostMessage │ │ │ │ │ └── usePostMessage.js │ │ │ │ ├── usePreferredColorScheme │ │ │ │ │ └── usePreferredColorScheme.js │ │ │ │ ├── usePreferredContrast │ │ │ │ │ └── usePreferredContrast.js │ │ │ │ ├── usePreferredDark │ │ │ │ │ └── usePreferredDark.js │ │ │ │ ├── usePreferredLanguages │ │ │ │ │ └── usePreferredLanguages.js │ │ │ │ ├── usePreferredReducedMotion │ │ │ │ │ └── usePreferredReducedMotion.js │ │ │ │ ├── usePrevious │ │ │ │ │ └── usePrevious.js │ │ │ │ ├── useQuery │ │ │ │ │ └── useQuery.js │ │ │ │ ├── useQueue │ │ │ │ │ └── useQueue.js │ │ │ │ ├── useRaf │ │ │ │ │ └── useRaf.js │ │ │ │ ├── useRafValue │ │ │ │ │ └── useRafValue.js │ │ │ │ ├── useRefState │ │ │ │ │ └── useRefState.js │ │ │ │ ├── useRenderCount │ │ │ │ │ └── useRenderCount.js │ │ │ │ ├── useRenderInfo │ │ │ │ │ └── useRenderInfo.js │ │ │ │ ├── useRerender │ │ │ │ │ └── useRerender.js │ │ │ │ ├── useResizeObserver │ │ │ │ │ └── useResizeObserver.js │ │ │ │ ├── useScreenOrientation │ │ │ │ │ └── useScreenOrientation.js │ │ │ │ ├── useScript │ │ │ │ │ └── useScript.js │ │ │ │ ├── useScroll │ │ │ │ │ └── useScroll.js │ │ │ │ ├── useScrollIntoView │ │ │ │ │ └── useScrollIntoView.js │ │ │ │ ├── useScrollTo │ │ │ │ │ └── useScrollTo.js │ │ │ │ ├── useSessionStorage │ │ │ │ │ └── useSessionStorage.js │ │ │ │ ├── useSet │ │ │ │ │ └── useSet.js │ │ │ │ ├── useShare │ │ │ │ │ └── useShare.js │ │ │ │ ├── useSpeechRecognition │ │ │ │ │ └── useSpeechRecognition.js │ │ │ │ ├── useSpeechSynthesis │ │ │ │ │ └── useSpeechSynthesis.js │ │ │ │ ├── useStateHistory │ │ │ │ │ └── useStateHistory.js │ │ │ │ ├── useStep │ │ │ │ │ └── useStep.js │ │ │ │ ├── useSticky │ │ │ │ │ └── useSticky.js │ │ │ │ ├── useStopwatch │ │ │ │ │ └── useStopwatch.js │ │ │ │ ├── useStorage │ │ │ │ │ └── useStorage.js │ │ │ │ ├── useTextDirection │ │ │ │ │ └── useTextDirection.js │ │ │ │ ├── useTextSelection │ │ │ │ │ └── useTextSelection.js │ │ │ │ ├── useThrottleCallback │ │ │ │ │ └── useThrottleCallback.js │ │ │ │ ├── useThrottleValue │ │ │ │ │ └── useThrottleValue.js │ │ │ │ ├── useTime │ │ │ │ │ └── useTime.js │ │ │ │ ├── useTimeout │ │ │ │ │ └── useTimeout.js │ │ │ │ ├── useTimer │ │ │ │ │ └── useTimer.js │ │ │ │ ├── useToggle │ │ │ │ │ └── useToggle.js │ │ │ │ ├── useUnmount │ │ │ │ │ └── useUnmount.js │ │ │ │ ├── useUrlSearchParams │ │ │ │ │ └── useUrlSearchParams.js │ │ │ │ ├── useVibrate │ │ │ │ │ └── useVibrate.js │ │ │ │ ├── useWakeLock │ │ │ │ │ └── useWakeLock.js │ │ │ │ ├── useWebSocket │ │ │ │ │ └── useWebSocket.js │ │ │ │ ├── useWindowEvent │ │ │ │ │ └── useWindowEvent.js │ │ │ │ ├── useWindowFocus │ │ │ │ │ └── useWindowFocus.js │ │ │ │ ├── useWindowScroll │ │ │ │ │ └── useWindowScroll.js │ │ │ │ ├── useWindowSize │ │ │ │ │ └── useWindowSize.js │ │ │ │ └── useWizard │ │ │ │ │ └── useWizard.js │ │ │ ├── index.js │ │ │ └── utils │ │ │ │ └── helpers │ │ │ │ ├── cookie.js │ │ │ │ ├── copy.js │ │ │ │ ├── debounce.js │ │ │ │ ├── getDate.js │ │ │ │ ├── getElement.js │ │ │ │ ├── getRetry.js │ │ │ │ ├── index.js │ │ │ │ ├── isTarget.js │ │ │ │ ├── throttle.js │ │ │ │ └── time │ │ │ │ └── getDate.js │ │ ├── helpers │ │ │ ├── createContext │ │ │ │ ├── createContext.demo.tsx │ │ │ │ ├── createContext.test.tsx │ │ │ │ └── createContext.tsx │ │ │ ├── createReactiveContext │ │ │ │ ├── createReactiveContext.demo.tsx │ │ │ │ ├── createReactiveContext.test.tsx │ │ │ │ └── createReactiveContext.ts │ │ │ ├── createStore │ │ │ │ ├── createStore.demo.tsx │ │ │ │ ├── createStore.test.ts │ │ │ │ └── createStore.ts │ │ │ └── index.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useActiveElement │ │ │ │ ├── useActiveElement.demo.tsx │ │ │ │ └── useActiveElement.ts │ │ │ ├── useAsync │ │ │ │ ├── useAsync.demo.tsx │ │ │ │ └── useAsync.ts │ │ │ ├── useBattery │ │ │ │ ├── useBattery.demo.tsx │ │ │ │ ├── useBattery.test.ts │ │ │ │ └── useBattery.ts │ │ │ ├── useBluetooth │ │ │ │ ├── useBluetooth.demo.tsx │ │ │ │ └── useBluetooth.ts │ │ │ ├── useBoolean │ │ │ │ ├── useBoolean.demo.tsx │ │ │ │ ├── useBoolean.test.ts │ │ │ │ └── useBoolean.ts │ │ │ ├── useBreakpoints │ │ │ │ ├── useBreakpoints.demo.tsx │ │ │ │ └── useBreakpoints.ts │ │ │ ├── useBrowserLanguage │ │ │ │ ├── useBrowserLanguage.demo.tsx │ │ │ │ └── useBrowserLanguage.ts │ │ │ ├── useClickOutside │ │ │ │ ├── useClickOutside.demo.tsx │ │ │ │ ├── useClickOutside.test.ts │ │ │ │ └── useClickOutside.ts │ │ │ ├── useClipboard │ │ │ │ ├── useClipboard.demo.tsx │ │ │ │ ├── useClipboard.test.ts │ │ │ │ └── useClipboard.ts │ │ │ ├── useConst │ │ │ │ ├── useConst.demo.tsx │ │ │ │ ├── useConst.test.ts │ │ │ │ └── useConst.ts │ │ │ ├── useCookie │ │ │ │ ├── useCookie.demo.tsx │ │ │ │ └── useCookie.ts │ │ │ ├── useCookies │ │ │ │ ├── useCookies.demo.tsx │ │ │ │ └── useCookies.ts │ │ │ ├── useCopy │ │ │ │ ├── useCopy.demo.tsx │ │ │ │ └── useCopy.ts │ │ │ ├── useCounter │ │ │ │ ├── useCounter.demo.tsx │ │ │ │ ├── useCounter.test.ts │ │ │ │ └── useCounter.ts │ │ │ ├── useCssVar │ │ │ │ ├── useCssVar.demo.tsx │ │ │ │ └── useCssVar.ts │ │ │ ├── useDebounceCallback │ │ │ │ ├── useDebounceCallback.demo.tsx │ │ │ │ └── useDebounceCallback.ts │ │ │ ├── useDebounceValue │ │ │ │ ├── useDebounceValue.demo.tsx │ │ │ │ └── useDebounceValue.ts │ │ │ ├── useDefault │ │ │ │ ├── useDefault.demo.tsx │ │ │ │ ├── useDefault.test.ts │ │ │ │ └── useDefault.ts │ │ │ ├── useDeviceMotion │ │ │ │ ├── useDeviceMotion.demo.tsx │ │ │ │ └── useDeviceMotion.ts │ │ │ ├── useDeviceOrientation │ │ │ │ ├── useDeviceOrientation.demo.tsx │ │ │ │ ├── useDeviceOrientation.test.ts │ │ │ │ └── useDeviceOrientation.ts │ │ │ ├── useDevicePixelRatio │ │ │ │ ├── useDevicePixelRatio.demo.tsx │ │ │ │ ├── useDevicePixelRatio.test.ts │ │ │ │ └── useDevicePixelRatio.ts │ │ │ ├── useDidUpdate │ │ │ │ ├── useDidUpdate.demo.tsx │ │ │ │ ├── useDidUpdate.test.ts │ │ │ │ └── useDidUpdate.ts │ │ │ ├── useDisclosure │ │ │ │ ├── useDisclosure.demo.tsx │ │ │ │ ├── useDisclosure.test.ts │ │ │ │ └── useDisclosure.ts │ │ │ ├── useDisplayMedia │ │ │ │ ├── useDisplayMedia.demo.tsx │ │ │ │ ├── useDisplayMedia.test.ts │ │ │ │ └── useDisplayMedia.ts │ │ │ ├── useDocumentEvent │ │ │ │ ├── useDocumentEvent.demo.tsx │ │ │ │ ├── useDocumentEvent.test.ts │ │ │ │ └── useDocumentEvent.ts │ │ │ ├── useDocumentTitle │ │ │ │ ├── useDocumentTitle.demo.tsx │ │ │ │ ├── useDocumentTitle.test.ts │ │ │ │ └── useDocumentTitle.ts │ │ │ ├── useDocumentVisibility │ │ │ │ ├── useDocumentVisibility.demo.tsx │ │ │ │ ├── useDocumentVisibility.test.ts │ │ │ │ └── useDocumentVisibility.ts │ │ │ ├── useDoubleClick │ │ │ │ ├── useDoubleClick.demo.tsx │ │ │ │ └── useDoubleClick.ts │ │ │ ├── useDropZone │ │ │ │ ├── useDropZone.demo.tsx │ │ │ │ └── useDropZone.ts │ │ │ ├── useElementSize │ │ │ │ ├── useElementSize.demo.tsx │ │ │ │ ├── useElementSize.test.ts │ │ │ │ └── useElementSize.ts │ │ │ ├── useEvent │ │ │ │ ├── useEvent.demo.tsx │ │ │ │ ├── useEvent.test.ts │ │ │ │ └── useEvent.ts │ │ │ ├── useEventListener │ │ │ │ ├── useEventListener.demo.tsx │ │ │ │ └── useEventListener.ts │ │ │ ├── useEventSource │ │ │ │ ├── useEventSource.demo.tsx │ │ │ │ └── useEventSource.ts │ │ │ ├── useEyeDropper │ │ │ │ ├── useEyeDropper.demo.tsx │ │ │ │ └── useEyeDropper.ts │ │ │ ├── useFavicon │ │ │ │ ├── useFavicon.demo.tsx │ │ │ │ ├── useFavicon.test.ts │ │ │ │ └── useFavicon.ts │ │ │ ├── useField │ │ │ │ ├── useField.demo.tsx │ │ │ │ └── useField.ts │ │ │ ├── useFileDialog │ │ │ │ ├── useFileDialog.demo.tsx │ │ │ │ └── useFileDialog.ts │ │ │ ├── useFocus │ │ │ │ ├── useFocus.demo.tsx │ │ │ │ └── useFocus.ts │ │ │ ├── useFps │ │ │ │ ├── useFps.demo.tsx │ │ │ │ └── useFps.ts │ │ │ ├── useFul │ │ │ │ ├── useFul.demo.tsx │ │ │ │ ├── useFul.test.ts │ │ │ │ └── useFul.ts │ │ │ ├── useFullscreen │ │ │ │ ├── useFullscreen.demo.tsx │ │ │ │ └── useFullscreen.ts │ │ │ ├── useGamepad │ │ │ │ ├── useGamepad.demo.tsx │ │ │ │ └── useGamepad.ts │ │ │ ├── useGeolocation │ │ │ │ ├── useGeolocation.demo.tsx │ │ │ │ ├── useGeolocation.test.ts │ │ │ │ └── useGeolocation.ts │ │ │ ├── useHash │ │ │ │ ├── useHash.demo.tsx │ │ │ │ └── useHash.ts │ │ │ ├── useHotkeys │ │ │ │ ├── useHotkeys.demo.tsx │ │ │ │ └── useHotkeys.ts │ │ │ ├── useHover │ │ │ │ ├── useHover.demo.tsx │ │ │ │ ├── useHover.test.ts │ │ │ │ └── useHover.ts │ │ │ ├── useIdle │ │ │ │ ├── useIdle.demo.tsx │ │ │ │ ├── useIdle.test.ts │ │ │ │ └── useIdle.ts │ │ │ ├── useImage │ │ │ │ ├── useImage.demo.tsx │ │ │ │ └── useImage.ts │ │ │ ├── useInfiniteScroll │ │ │ │ ├── useInfiniteScroll.demo.tsx │ │ │ │ └── useInfiniteScroll.ts │ │ │ ├── useIntersectionObserver │ │ │ │ ├── useIntersectionObserver.demo.tsx │ │ │ │ └── useIntersectionObserver.ts │ │ │ ├── useInterval │ │ │ │ ├── useInterval.demo.tsx │ │ │ │ ├── useInterval.test.ts │ │ │ │ └── useInterval.ts │ │ │ ├── useIsFirstRender │ │ │ │ ├── useIsFirstRender.demo.tsx │ │ │ │ ├── useIsFirstRender.test.ts │ │ │ │ └── useIsFirstRender.ts │ │ │ ├── useIsomorphicLayoutEffect │ │ │ │ ├── useIsomorphicLayoutEffect-node.test.ts │ │ │ │ ├── useIsomorphicLayoutEffect.demo.tsx │ │ │ │ ├── useIsomorphicLayoutEffect.test.ts │ │ │ │ └── useIsomorphicLayoutEffect.ts │ │ │ ├── useKeyPress │ │ │ │ ├── useKeyPress.demo.tsx │ │ │ │ └── useKeyPress.ts │ │ │ ├── useKeyPressEvent │ │ │ │ ├── useKeyPressEvent.demo.tsx │ │ │ │ └── useKeyPressEvent.ts │ │ │ ├── useKeyboard │ │ │ │ ├── useKeyboard.demo.tsx │ │ │ │ └── useKeyboard.ts │ │ │ ├── useKeysPressed │ │ │ │ ├── useKeysPressed.demo.tsx │ │ │ │ └── useKeysPressed.ts │ │ │ ├── useLastChanged │ │ │ │ ├── useLastChanged.demo.tsx │ │ │ │ ├── useLastChanged.test.ts │ │ │ │ └── useLastChanged.ts │ │ │ ├── useLatest │ │ │ │ ├── useLatest.demo.tsx │ │ │ │ ├── useLatest.test.ts │ │ │ │ └── useLatest.ts │ │ │ ├── useLess │ │ │ │ ├── useLess.demo.tsx │ │ │ │ ├── useLess.test.ts │ │ │ │ └── useLess.ts │ │ │ ├── useList │ │ │ │ ├── useList.demo.tsx │ │ │ │ └── useList.ts │ │ │ ├── useLocalStorage │ │ │ │ ├── useLocalStorage.demo.tsx │ │ │ │ ├── useLocalStorage.test.ts │ │ │ │ └── useLocalStorage.ts │ │ │ ├── useLockCallback │ │ │ │ ├── useLockCallback.demo.tsx │ │ │ │ ├── useLockCallback.test.ts │ │ │ │ └── useLockCallback.ts │ │ │ ├── useLogger │ │ │ │ ├── useLogger.demo.tsx │ │ │ │ ├── useLogger.test.ts │ │ │ │ └── useLogger.ts │ │ │ ├── useLongPress │ │ │ │ ├── useLongPress.demo.tsx │ │ │ │ └── useLongPress.ts │ │ │ ├── useMap │ │ │ │ ├── useMap.demo.tsx │ │ │ │ ├── useMap.test.ts │ │ │ │ └── useMap.ts │ │ │ ├── useMeasure │ │ │ │ ├── useMeasure.demo.tsx │ │ │ │ └── useMeasure.ts │ │ │ ├── useMediaQuery │ │ │ │ ├── useMediaQuery.demo.tsx │ │ │ │ ├── useMediaQuery.test.ts │ │ │ │ └── useMediaQuery.ts │ │ │ ├── useMemory │ │ │ │ ├── useMemory.demo.tsx │ │ │ │ ├── useMemory.test.ts │ │ │ │ └── useMemory.ts │ │ │ ├── useMount │ │ │ │ ├── useMount.demo.tsx │ │ │ │ ├── useMount.test.ts │ │ │ │ └── useMount.ts │ │ │ ├── useMouse │ │ │ │ ├── useMouse.demo.tsx │ │ │ │ └── useMouse.ts │ │ │ ├── useMutation │ │ │ │ ├── useMutation.demo.tsx │ │ │ │ └── useMutation.ts │ │ │ ├── useMutationObserver │ │ │ │ ├── useMutationObserver.demo.tsx │ │ │ │ └── useMutationObserver.ts │ │ │ ├── useNetwork │ │ │ │ ├── useNetwork.demo.tsx │ │ │ │ ├── useNetwork.test.ts │ │ │ │ └── useNetwork.ts │ │ │ ├── useOffsetPagination │ │ │ │ ├── useOffsetPagination.demo.tsx │ │ │ │ └── useOffsetPagination.ts │ │ │ ├── useOnce │ │ │ │ ├── useOnce.demo.tsx │ │ │ │ ├── useOnce.test.ts │ │ │ │ └── useOnce.ts │ │ │ ├── useOnline │ │ │ │ ├── useOnline.demo.tsx │ │ │ │ ├── useOnline.test.ts │ │ │ │ └── useOnline.ts │ │ │ ├── useOperatingSystem │ │ │ │ ├── useOperatingSystem.demo.tsx │ │ │ │ ├── useOperatingSystem.test.ts │ │ │ │ └── useOperatingSystem.ts │ │ │ ├── useOptimistic │ │ │ │ ├── useOptimistic.demo.tsx │ │ │ │ └── useOptimistic.ts │ │ │ ├── useOrientation │ │ │ │ ├── useOrientation.demo.tsx │ │ │ │ ├── useOrientation.test.ts │ │ │ │ └── useOrientation.ts │ │ │ ├── useOtpCredential │ │ │ │ ├── useOtpCredential.demo.tsx │ │ │ │ └── useOtpCredential.ts │ │ │ ├── usePageLeave │ │ │ │ ├── usePageLeave.demo.tsx │ │ │ │ ├── usePageLeave.test.ts │ │ │ │ └── usePageLeave.ts │ │ │ ├── usePaint │ │ │ │ ├── usePaint.demo.tsx │ │ │ │ └── usePaint.ts │ │ │ ├── useParallax │ │ │ │ ├── useParallax.demo.tsx │ │ │ │ └── useParallax.ts │ │ │ ├── usePerformanceObserver │ │ │ │ ├── usePerformanceObserver.demo.tsx │ │ │ │ └── usePerformanceObserver.ts │ │ │ ├── usePermission │ │ │ │ ├── usePermission.demo.tsx │ │ │ │ └── usePermission.ts │ │ │ ├── usePointerLock │ │ │ │ ├── usePointerLock.demo.tsx │ │ │ │ └── usePointerLock.ts │ │ │ ├── usePostMessage │ │ │ │ ├── usePostMessage.demo.tsx │ │ │ │ └── usePostMessage.ts │ │ │ ├── usePreferredColorScheme │ │ │ │ ├── usePreferredColorScheme.demo.tsx │ │ │ │ └── usePreferredColorScheme.ts │ │ │ ├── usePreferredContrast │ │ │ │ ├── usePreferredContrast.demo.tsx │ │ │ │ └── usePreferredContrast.ts │ │ │ ├── usePreferredDark │ │ │ │ ├── usePreferredDark.demo.tsx │ │ │ │ └── usePreferredDark.ts │ │ │ ├── usePreferredLanguages │ │ │ │ ├── usePreferredLanguages.demo.tsx │ │ │ │ ├── usePreferredLanguages.test.ts │ │ │ │ └── usePreferredLanguages.ts │ │ │ ├── usePreferredReducedMotion │ │ │ │ ├── usePreferredReducedMotion.demo.tsx │ │ │ │ └── usePreferredReducedMotion.ts │ │ │ ├── usePrevious │ │ │ │ ├── usePrevious.demo.tsx │ │ │ │ ├── usePrevious.test.ts │ │ │ │ └── usePrevious.ts │ │ │ ├── useQuery │ │ │ │ ├── useQuery.demo.tsx │ │ │ │ ├── useQuery.test.ts │ │ │ │ └── useQuery.ts │ │ │ ├── useQueue │ │ │ │ ├── useQueue.demo.tsx │ │ │ │ ├── useQueue.test.ts │ │ │ │ └── useQueue.ts │ │ │ ├── useRaf │ │ │ │ ├── useRaf.demo.tsx │ │ │ │ └── useRaf.ts │ │ │ ├── useRafValue │ │ │ │ ├── useRafValue.demo.tsx │ │ │ │ └── useRafValue.ts │ │ │ ├── useRefState │ │ │ │ ├── useRefState.demo.tsx │ │ │ │ ├── useRefState.test.ts │ │ │ │ └── useRefState.ts │ │ │ ├── useRenderCount │ │ │ │ ├── useRenderCount.demo.tsx │ │ │ │ ├── useRenderCount.test.ts │ │ │ │ └── useRenderCount.ts │ │ │ ├── useRenderInfo │ │ │ │ ├── useRenderInfo.demo.tsx │ │ │ │ ├── useRenderInfo.test.ts │ │ │ │ └── useRenderInfo.ts │ │ │ ├── useRerender │ │ │ │ ├── useRerender.demo.tsx │ │ │ │ ├── useRerender.test.ts │ │ │ │ └── useRerender.ts │ │ │ ├── useResizeObserver │ │ │ │ ├── useResizeObserver.demo.tsx │ │ │ │ └── useResizeObserver.ts │ │ │ ├── useScreenOrientation │ │ │ │ ├── useScreenOrientation.demo.tsx │ │ │ │ └── useScreenOrientation.ts │ │ │ ├── useScript │ │ │ │ ├── useScript.demo.tsx │ │ │ │ ├── useScript.test.ts │ │ │ │ └── useScript.ts │ │ │ ├── useScroll │ │ │ │ ├── useScroll.demo.tsx │ │ │ │ └── useScroll.ts │ │ │ ├── useScrollIntoView │ │ │ │ ├── useScrollIntoView.demo.tsx │ │ │ │ └── useScrollIntoView.ts │ │ │ ├── useScrollTo │ │ │ │ ├── useScrollTo.demo.tsx │ │ │ │ └── useScrollTo.ts │ │ │ ├── useSessionStorage │ │ │ │ ├── useSessionStorage.demo.tsx │ │ │ │ ├── useSessionStorage.test.ts │ │ │ │ └── useSessionStorage.ts │ │ │ ├── useSet │ │ │ │ ├── useSet.demo.tsx │ │ │ │ ├── useSet.test.ts │ │ │ │ └── useSet.ts │ │ │ ├── useShare │ │ │ │ ├── useShare.demo.tsx │ │ │ │ ├── useShare.test.ts │ │ │ │ └── useShare.ts │ │ │ ├── useSpeechRecognition │ │ │ │ ├── useSpeechRecognition.demo.tsx │ │ │ │ └── useSpeechRecognition.ts │ │ │ ├── useSpeechSynthesis │ │ │ │ ├── useSpeechSynthesis.demo.tsx │ │ │ │ └── useSpeechSynthesis.ts │ │ │ ├── useStateHistory │ │ │ │ ├── useStateHistory.demo.tsx │ │ │ │ ├── useStateHistory.test.ts │ │ │ │ └── useStateHistory.ts │ │ │ ├── useStep │ │ │ │ ├── useStep.demo.tsx │ │ │ │ ├── useStep.test.ts │ │ │ │ └── useStep.ts │ │ │ ├── useSticky │ │ │ │ ├── useSticky.demo.tsx │ │ │ │ └── useSticky.ts │ │ │ ├── useStopwatch │ │ │ │ ├── useStopwatch.demo.tsx │ │ │ │ └── useStopwatch.ts │ │ │ ├── useStorage │ │ │ │ ├── useStorage.demo.tsx │ │ │ │ └── useStorage.ts │ │ │ ├── useTextDirection │ │ │ │ ├── useTextDirection.demo.tsx │ │ │ │ ├── useTextDirection.test.ts │ │ │ │ └── useTextDirection.ts │ │ │ ├── useTextSelection │ │ │ │ ├── useTextSelection.demo.tsx │ │ │ │ └── useTextSelection.ts │ │ │ ├── useThrottleCallback │ │ │ │ ├── useThrottleCallback.demo.tsx │ │ │ │ └── useThrottleCallback.ts │ │ │ ├── useThrottleValue │ │ │ │ ├── useThrottleValue.demo.tsx │ │ │ │ └── useThrottleValue.ts │ │ │ ├── useTime │ │ │ │ ├── useTime.demo.tsx │ │ │ │ ├── useTime.test.ts │ │ │ │ └── useTime.ts │ │ │ ├── useTimeout │ │ │ │ ├── useTimeout.demo.tsx │ │ │ │ ├── useTimeout.test.ts │ │ │ │ └── useTimeout.ts │ │ │ ├── useTimer │ │ │ │ ├── useTimer.demo.tsx │ │ │ │ ├── useTimer.test.ts │ │ │ │ └── useTimer.ts │ │ │ ├── useToggle │ │ │ │ ├── useToggle.demo.tsx │ │ │ │ ├── useToggle.test.ts │ │ │ │ └── useToggle.ts │ │ │ ├── useUnmount │ │ │ │ ├── useUnmount.demo.tsx │ │ │ │ ├── useUnmount.test.ts │ │ │ │ └── useUnmount.ts │ │ │ ├── useUrlSearchParams │ │ │ │ ├── useUrlSearchParams.demo.tsx │ │ │ │ └── useUrlSearchParams.ts │ │ │ ├── useVibrate │ │ │ │ ├── useVibrate.demo.tsx │ │ │ │ └── useVibrate.ts │ │ │ ├── useWakeLock │ │ │ │ ├── useWakeLock.demo.tsx │ │ │ │ └── useWakeLock.ts │ │ │ ├── useWebSocket │ │ │ │ ├── useWebSocket.demo.tsx │ │ │ │ └── useWebSocket.ts │ │ │ ├── useWindowEvent │ │ │ │ ├── useWindowEvent.demo.tsx │ │ │ │ ├── useWindowEvent.test.ts │ │ │ │ └── useWindowEvent.ts │ │ │ ├── useWindowFocus │ │ │ │ ├── useWindowFocus.demo.tsx │ │ │ │ └── useWindowFocus.ts │ │ │ ├── useWindowScroll │ │ │ │ ├── useWindowScroll.demo.tsx │ │ │ │ └── useWindowScroll.ts │ │ │ ├── useWindowSize │ │ │ │ ├── useWindowSize.demo.tsx │ │ │ │ ├── useWindowSize.test.ts │ │ │ │ └── useWindowSize.ts │ │ │ └── useWizard │ │ │ │ ├── useWizard.demo.tsx │ │ │ │ └── useWizard.ts │ │ ├── index.ts │ │ └── utils │ │ │ └── helpers │ │ │ ├── copy.ts │ │ │ ├── debounce.ts │ │ │ ├── getDate.test.ts │ │ │ ├── getDate.ts │ │ │ ├── getElement.ts │ │ │ ├── getRetry.ts │ │ │ ├── index.ts │ │ │ ├── isTarget.ts │ │ │ └── throttle.ts │ ├── tests │ │ ├── createTrigger.ts │ │ ├── index.ts │ │ ├── renderHookServer.tsx │ │ └── setupTests.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── vite.config.mts │ └── vitest.config.mts └── docs │ ├── README.md │ ├── app │ ├── .vitepress │ │ ├── config.mts │ │ └── theme │ │ │ ├── global.css │ │ │ └── index.ts │ ├── cli.md │ ├── functions │ │ └── hooks │ │ │ ├── [name].md │ │ │ └── [name].paths.mts │ ├── index.md │ ├── installation.md │ ├── installation │ │ ├── nextjs.md │ │ └── vite.md │ ├── introduction.md │ ├── public │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── logo.svg │ │ └── manifest.json │ ├── reactuse-json.md │ └── target.md │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ ├── components │ │ ├── api.vue │ │ ├── badges.vue │ │ ├── code.vue │ │ ├── contributors.vue │ │ ├── demo.vue │ │ ├── framework.vue │ │ ├── meta.vue │ │ └── source.vue │ └── utils │ │ ├── docs │ │ ├── checkTest.ts │ │ ├── getContent.ts │ │ ├── getContentFile.ts │ │ ├── getContentItems.ts │ │ └── index.ts │ │ ├── index.ts │ │ ├── isDefaultType.ts │ │ ├── matchJsdoc.ts │ │ ├── parseHookJsdoc.ts │ │ └── utils │ │ ├── cn.ts │ │ └── index.ts │ └── tsconfig.json ├── pnpm-lock.yaml └── pnpm-workspace.yaml /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | emoji=$(git config user.emoji || echo "🧊") 2 | branch=$(git symbolic-ref --short HEAD) 3 | message=$(cat "$1") 4 | 5 | if echo "$message" | (! grep -q "^${branch} ${emoji}*") &&\ 6 | echo "$message" | (! grep -q "^Merge branch*") &&\ 7 | echo "$message" | (! grep -q "^Merge remote-tracking branch*");\ 8 | then 9 | echo "$branch $emoji $(cat $1)" > "$1" 10 | fi -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | pnpm lint-staged 4 | 5 | if git diff --quiet HEAD -- packages/core; then 6 | echo "No changes in packages/core. Skipping build." 7 | else 8 | echo "Running tests for packages/core..." 9 | pnpm unit-test run 10 | 11 | echo "Changes detected in packages/core. Running build:js..." 12 | pnpm core:build:js 13 | 14 | git add packages/core/src/bundle 15 | echo "Built files added to git." 16 | 17 | git add packages/cli/src/registry 18 | echo "Registry built and added to git." 19 | fi 20 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/dist 2 | **/node_modules 3 | **/cache 4 | **/pnpm-lock.yaml -------------------------------------------------------------------------------- /.prettierrc.mjs: -------------------------------------------------------------------------------- 1 | import { prettier } from '@siberiacancode/prettier'; 2 | 3 | /** @type {import('prettier').Config} */ 4 | export default { ...prettier, plugins: ['prettier-plugin-tailwindcss'] }; 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 siberiacancode 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚀 React Use 2 | 3 | the largest and most useful hook library 4 | 5 | ## 🦉 Philosophy 6 | 7 | **🚀 React Use** this is a library that will allow you to easy and simple to use React hooks. Unlike its competitors, this package takes into account the features of React and also contains a huge number of useful hooks. 8 | 9 | ## Features 10 | 11 | - **TypeScript support out of the box** - full typed package 12 | - **SSR** - package work with server side render 13 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you believe you have found a security vulnerability in our library, we encourage you to let us know right away. We will investigate all legitimate reports and do our best to quickly fix the problem. 6 | 7 | Please let us know by creating an issue in this repository 8 | -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- 1 | # useverse 2 | 3 | A CLI for adding react hooks to your project. 4 | 5 | ## Usage 6 | 7 | Use the `init` command to initialize dependencies for a new project. 8 | 9 | The `init` command installs dependencies, adds the `cn` util, configures `tailwind.config.js`, and CSS variables for the project. 10 | 11 | ```bash 12 | npx useverse init 13 | ``` 14 | 15 | ## add 16 | 17 | Use the `add` command to add components to your project. 18 | 19 | The `add` command adds a hook to your project and installs all required dependencies. 20 | 21 | ```bash 22 | npx useverse add [hook] 23 | ``` 24 | 25 | ### Example 26 | 27 | ```bash 28 | npx useverse add useCounter 29 | ``` 30 | 31 | You can also run the command without any arguments to view a list of all available hooks: 32 | 33 | ```bash 34 | npx useverse add 35 | ``` 36 | -------------------------------------------------------------------------------- /packages/cli/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { eslint } from '@siberiacancode/eslint'; 2 | 3 | /** @type {import('eslint').Linter.FlatConfig} */ 4 | export default eslint( 5 | { 6 | typescript: true 7 | }, 8 | { 9 | name: 'siberiacancode/cli/rewrite', 10 | rules: { 11 | 'node/prefer-global/process': 'off', 12 | 'node/prefer-global/buffer': 'off' 13 | } 14 | } 15 | ); 16 | -------------------------------------------------------------------------------- /packages/cli/src/bin.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { cli } from './cli.js'; 4 | 5 | cli(); 6 | 7 | export {}; 8 | -------------------------------------------------------------------------------- /packages/cli/src/cli.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs'; 2 | import { hideBin } from 'yargs/helpers'; 3 | 4 | import { add } from './add'; 5 | import { init } from './init'; 6 | 7 | export const cli = () => { 8 | const processArgv = hideBin(process.argv); 9 | 10 | if (processArgv.includes('init')) return init(); 11 | 12 | yargs(processArgv) 13 | .scriptName('reactuse') 14 | .usage('$0 [args]') 15 | .command(add) 16 | .epilogue('More info: https://siberiacancode.github.io/reactuse/cli') 17 | .version() 18 | .alias('v', 'version') 19 | .help() 20 | .alias('h', 'help') 21 | .parse(); 22 | }; 23 | -------------------------------------------------------------------------------- /packages/cli/src/registry/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './extractDependencies'; 2 | -------------------------------------------------------------------------------- /packages/cli/src/utils/constants/appPath.ts: -------------------------------------------------------------------------------- 1 | export const APP_PATH = process.cwd(); 2 | -------------------------------------------------------------------------------- /packages/cli/src/utils/constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from './appPath'; 2 | export * from './urls'; 3 | -------------------------------------------------------------------------------- /packages/cli/src/utils/constants/urls.ts: -------------------------------------------------------------------------------- 1 | export const BASE_URL = 'https://raw.githubusercontent.com/siberiacancode/reactuse/main'; 2 | export const REPO_URLS = { 3 | TS: `${BASE_URL}/packages/core/src`, 4 | JS: `${BASE_URL}/packages/core/src/bundle` 5 | }; 6 | -------------------------------------------------------------------------------- /packages/cli/src/utils/helpers/getConfig.ts: -------------------------------------------------------------------------------- 1 | import { cosmiconfig } from 'cosmiconfig'; 2 | 3 | import { configSchema } from '@/utils/types'; 4 | 5 | export const getConfig = async (cwd: string) => { 6 | const explorer = cosmiconfig('configHooks', { 7 | searchPlaces: ['reactuse.json'] 8 | }); 9 | 10 | try { 11 | const configResult = (await explorer.search(cwd))!; 12 | return configSchema.parse(configResult.config); 13 | } catch (error) { 14 | throw new Error(`Invalid configuration found in ${cwd}/reactuse.json. Error - ${error}`); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /packages/cli/src/utils/helpers/getPackageManager.ts: -------------------------------------------------------------------------------- 1 | import { detect } from '@antfu/ni'; 2 | 3 | export const getPackageManager = async (targetDir: string) => { 4 | const packageManager = await detect({ programmatic: true, cwd: targetDir }); 5 | 6 | if (packageManager === 'yarn@berry') return 'yarn'; 7 | if (packageManager === 'pnpm@6') return 'pnpm'; 8 | if (packageManager === 'bun') return 'bun'; 9 | if (packageManager === 'deno') return 'deno'; 10 | 11 | return packageManager ?? 'npm'; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/cli/src/utils/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './getConfig'; 2 | export * from './getPackageManager'; 3 | -------------------------------------------------------------------------------- /packages/cli/src/utils/types/index.ts: -------------------------------------------------------------------------------- 1 | import * as z from 'zod'; 2 | 3 | export interface HookRegistry { 4 | hooks: string[]; 5 | name: string; 6 | packages: string[]; 7 | utils: string[]; 8 | } 9 | 10 | export interface Registry { 11 | [key: string]: HookRegistry; 12 | } 13 | 14 | export const addOptionsSchema = z.object({ 15 | hooks: z.array(z.string()), 16 | all: z.boolean(), 17 | registry: z.string(), 18 | overwrite: z.boolean(), 19 | cwd: z.string() 20 | }); 21 | 22 | export type AddOptionsSchema = z.infer; 23 | 24 | export const configSchema = z 25 | .object({ 26 | ts: z.boolean().optional(), 27 | aliases: z.object({ 28 | hooks: z.string(), 29 | utils: z.string() 30 | }) 31 | }) 32 | .strict(); 33 | 34 | export type ConfigSchema = z.infer; 35 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | "composite": false, 5 | "baseUrl": ".", 6 | "module": "ESNext", 7 | "moduleResolution": "node", 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | }, 11 | "resolveJsonModule": true, 12 | "strict": true, 13 | "noUnusedLocals": false, 14 | "noUnusedParameters": false, 15 | "declaration": true, 16 | "declarationMap": true, 17 | "inlineSources": false, 18 | "esModuleInterop": true, 19 | "forceConsistentCasingInFileNames": true, 20 | "preserveWatchOutput": true, 21 | "isolatedModules": true, 22 | "skipLibCheck": true 23 | }, 24 | "include": ["src"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /packages/cli/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig({ 4 | clean: true, 5 | dts: true, 6 | entry: ['src/bin.ts'], 7 | format: ['esm'], 8 | sourcemap: true, 9 | minify: true, 10 | target: 'esnext', 11 | outDir: 'dist' 12 | }); 13 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # 🚀 reactuse core 2 | 3 | the largest and most useful hook library 4 | 5 | ## 🦉 Philosophy 6 | 7 | **🚀 React Use** this is a library that will allow you to easy and simple to use React hooks. Unlike its competitors, this package takes into account the features of React and also contains a huge number of useful hooks. 8 | 9 | ## Features 10 | 11 | - **TypeScript support out of the box** - full typed package 12 | - **SSR** - package work with server side render 13 | -------------------------------------------------------------------------------- /packages/core/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { eslint } from '@siberiacancode/eslint'; 2 | 3 | /** @type {import('eslint').Linter.FlatConfig} */ 4 | export default eslint( 5 | { 6 | typescript: true, 7 | javascript: true, 8 | react: true, 9 | jsx: true, 10 | vue: true 11 | }, 12 | { 13 | name: 'siberiacancode/core/ignores', 14 | ignores: ['**/bundle/**/*.js'] 15 | }, 16 | { 17 | name: 'siberiacancode/core/hooks', 18 | files: ['**/{hooks,helpers}/**/*.{ts,tsx}'], 19 | rules: { 20 | 'react-dom/no-flush-sync': 'warn', 21 | 'jsdoc/no-defaults': 'off', 22 | 'react-hooks/rules-of-hooks': 'warn', 23 | 'react/no-use-context': 'off', 24 | 'react/no-context-provider': 'off' 25 | } 26 | }, 27 | { 28 | name: 'siberiacancode/core/demo', 29 | files: ['**/*.demo.tsx'], 30 | rules: { 31 | 'no-alert': 'off' 32 | } 33 | } 34 | ); 35 | -------------------------------------------------------------------------------- /packages/core/src/bundle/helpers/index.js: -------------------------------------------------------------------------------- 1 | export * from './createContext/createContext'; 2 | export * from './createReactiveContext/createReactiveContext'; 3 | export * from './createStore/createStore'; 4 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useBoolean/useBoolean.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | /** 3 | * @name useBoolean 4 | * @description - Hook provides opportunity to manage boolean state 5 | * @category Utilities 6 | * 7 | * @param {boolean} [initialValue=false] The initial boolean value 8 | * @returns {UseBooleanReturn} An object containing the boolean state value and utility functions to manipulate the state 9 | * 10 | * @example 11 | * const [on, toggle] = useBoolean() 12 | */ 13 | export const useBoolean = (initialValue = false) => { 14 | const [value, setValue] = useState(initialValue); 15 | const toggle = (value) => setValue((prevValue) => value ?? !prevValue); 16 | return [value, toggle]; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useBreakpoints/helpers/index.js: -------------------------------------------------------------------------------- 1 | export * from './breakpoints'; 2 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useBrowserLanguage/useBrowserLanguage.js: -------------------------------------------------------------------------------- 1 | import { useSyncExternalStore } from 'react'; 2 | const getSnapshot = () => navigator.language; 3 | const getServerSnapshot = () => 'undetermined'; 4 | const subscribe = (callback) => { 5 | window.addEventListener('languagechange', callback); 6 | return () => window.removeEventListener('languagechange', callback); 7 | }; 8 | /** 9 | * @name useBrowserLanguage 10 | * @description - Hook that returns the current browser language 11 | * @category Browser 12 | * 13 | * @browserapi navigator.language https://developer.mozilla.org/en-US/docs/Web/API/Navigator/language 14 | * 15 | * @returns {string} The current browser language 16 | * 17 | * @example 18 | * const browserLanguage = useBrowserLanguage(); 19 | */ 20 | export const useBrowserLanguage = () => 21 | useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); 22 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useConst/useConst.js: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react'; 2 | /** 3 | * @name useConst 4 | * @description - Hook that returns the constant value 5 | * @category Utilities 6 | * 7 | * @template Value The type of the value 8 | * @param {(() => Value) | Value} initialValue The initial value of the constant 9 | * @returns {Value} The constant value 10 | * 11 | * @example 12 | * const value = useConst('value'); 13 | */ 14 | export const useConst = (initialValue) => 15 | useRef(typeof initialValue === 'function' ? initialValue() : initialValue).current; 16 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useCookie/helpers/clearCookies.js: -------------------------------------------------------------------------------- 1 | import { removeCookie } from './removeCookie'; 2 | export const clearCookies = () => { 3 | document.cookie.split('; ').forEach((cookie) => { 4 | const [name] = cookie.split('='); 5 | removeCookie(name); 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useCookie/helpers/getCookies.js: -------------------------------------------------------------------------------- 1 | export const getCookies = () => 2 | Object.fromEntries( 3 | document.cookie.split('; ').map((cookie) => { 4 | const [key, ...value] = cookie.split('='); 5 | const decodedValue = decodeURIComponent(value.join('=')); 6 | return [key, decodedValue]; 7 | }) 8 | ); 9 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useCookie/helpers/index.js: -------------------------------------------------------------------------------- 1 | export * from './clearCookies'; 2 | export * from './getCookies'; 3 | export * from './removeCookie'; 4 | export * from './setCookie'; 5 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useCookie/helpers/removeCookie.js: -------------------------------------------------------------------------------- 1 | export const removeCookie = (key, options = {}) => { 2 | document.cookie = `${encodeURIComponent(key)}=; expires=Thu, 01 Jan 1970 00:00:00 GMT${options.path ? `; path=${options.path}` : ''}${options.domain ? `; domain=${options.domain}` : ''}${options.maxAge ? `; max-age=0` : ''}${options.expires ? `; expires=Thu, 01 Jan 1970 00:00:00 GMT` : ''}${options.secure ? `; secure` : ''}${options.sameSite ? `; samesite=${options.sameSite}` : ''}`; 3 | }; 4 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useCookie/helpers/setCookie.js: -------------------------------------------------------------------------------- 1 | export const setCookie = (key, value, options = {}) => { 2 | const cookie = [`${encodeURIComponent(key)}=${encodeURIComponent(value)}`]; 3 | if (options.path) cookie.push(`path=${options.path}`); 4 | if (options.domain) cookie.push(`domain=${options.domain}`); 5 | if (typeof options.maxAge === 'number') cookie.push(`max-age=${options.maxAge}`); 6 | if (options.expires) cookie.push(`expires=${options.expires.toUTCString()}`); 7 | if (options.secure) cookie.push(`secure`); 8 | if (options.httpOnly) cookie.push(`httpOnly`); 9 | if (options.sameSite) cookie.push(`samesite=${options.sameSite}`); 10 | document.cookie = cookie.join('; '); 11 | }; 12 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useCopy/useCopy.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { copy } from '@/utils/helpers'; 3 | /** 4 | * @name useCopy 5 | * @description - Hook that manages copying text with status reset 6 | * @category Browser 7 | * 8 | * @browserapi navigator.clipboard https://developer.mozilla.org/en-US/docs/Web/API/Navigator/clipboard 9 | * 10 | * @param {number} [delay=1000] Delay in ms before resetting copied status 11 | * @returns {UseCopyReturn} An object containing the copied value, status and copy function 12 | * 13 | * @example 14 | * const { copied, value, copy } = useCopy(); 15 | */ 16 | export const useCopy = (delay = 1000) => { 17 | const [value, setValue] = useState(null); 18 | const [copied, setCopied] = useState(false); 19 | const copyToClipboard = async (text) => { 20 | await copy(text); 21 | setValue(text); 22 | setCopied(true); 23 | setTimeout(() => setCopied(false), delay); 24 | }; 25 | return { value, copied, copy: copyToClipboard }; 26 | }; 27 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useDebounceCallback/useDebounceCallback.js: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import { debounce } from '@/utils/helpers'; 3 | import { useEvent } from '../useEvent/useEvent'; 4 | /** 5 | * @name useDebounceCallback 6 | * @description - Hook that creates a debounced callback 7 | * @category Utilities 8 | * 9 | * @template Params The type of the params 10 | * @template Return The type of the return 11 | * @param {(...args: Params) => Return} callback The callback function 12 | * @param {number} delay The delay in milliseconds 13 | * @returns {(...args: Params) => Return} The callback with debounce 14 | * 15 | * @example 16 | * const debouncedCallback = useDebounceCallback(() => console.log('callback'), 500); 17 | */ 18 | export const useDebounceCallback = (callback, delay) => { 19 | const internalCallback = useEvent(callback); 20 | const debounced = useMemo(() => debounce(internalCallback, delay), [delay]); 21 | return debounced; 22 | }; 23 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useDebounceValue/useDebounceValue.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from 'react'; 2 | import { useDebounceCallback } from '../useDebounceCallback/useDebounceCallback'; 3 | /** 4 | * @name useDebounceValue 5 | * @description - Hook that creates a debounced value 6 | * @category Utilities 7 | * 8 | * @template Value The type of the value 9 | * @param {Value} value The value to be debounced 10 | * @param {number} delay The delay in milliseconds 11 | * @returns {Value} The debounced value 12 | * 13 | * @example 14 | * const debouncedValue = useDebounceValue(value, 500); 15 | */ 16 | export const useDebounceValue = (value, delay) => { 17 | const previousValueRef = useRef(value); 18 | const [debouncedValue, setDebounceValue] = useState(value); 19 | const debouncedSetState = useDebounceCallback(setDebounceValue, delay); 20 | useEffect(() => { 21 | if (previousValueRef.current === value) return; 22 | debouncedSetState(value); 23 | previousValueRef.current = value; 24 | }, [value]); 25 | return debouncedValue; 26 | }; 27 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useDefault/useDefault.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | /** 3 | * @name useDefault 4 | * @description - Hook that returns the default value 5 | * @category Utilities 6 | * 7 | * @template Value The type of the value 8 | * @param {Value} initialValue The initial value 9 | * @param {Value} defaultValue The default value 10 | * @returns {[Value, (value: Value) => void]} An array containing the current value and a function to set the value 11 | * 12 | * @example 13 | * const [value, setValue] = useDefault(initialValue, defaultValue); 14 | */ 15 | export const useDefault = (initialValue, defaultValue) => { 16 | const [value, setValue] = useState(initialValue); 17 | return [value === undefined || value === null ? defaultValue : value, setValue]; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useDidUpdate/useDidUpdate.js: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react'; 2 | import { useIsomorphicLayoutEffect } from '../useIsomorphicLayoutEffect/useIsomorphicLayoutEffect'; 3 | /** 4 | * @name useDidUpdate 5 | * @description – Hook that triggers the effect callback on updates 6 | * @category Lifecycle 7 | * 8 | * @param {EffectCallback} effect The effect callback 9 | * @param {DependencyList} [deps] The dependencies list for the effect 10 | * 11 | * @example 12 | * useDidUpdate(() => console.log("effect runs on updates"), deps); 13 | */ 14 | export const useDidUpdate = (effect, deps) => { 15 | const mounted = useRef(false); 16 | useIsomorphicLayoutEffect( 17 | () => () => { 18 | mounted.current = false; 19 | }, 20 | [] 21 | ); 22 | useIsomorphicLayoutEffect(() => { 23 | if (mounted.current) { 24 | return effect(); 25 | } 26 | mounted.current = true; 27 | return undefined; 28 | }, deps); 29 | }; 30 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useDocumentEvent/useDocumentEvent.js: -------------------------------------------------------------------------------- 1 | import { target } from '@/utils/helpers'; 2 | import { useEventListener } from '../useEventListener/useEventListener'; 3 | /** 4 | * @name useDocumentEvent 5 | * @description - Hook attaches an event listener to the document object for the specified event 6 | * @category Browser 7 | * 8 | * @template Event Key of document event map. 9 | * @param {Event} event The event to listen for. 10 | * @param {(event: DocumentEventMap[Event]) => void} listener The callback function to be executed when the event is triggered 11 | * @param {UseEventListenerOptions} [options] The options for the event listener 12 | * @returns {void} 13 | * 14 | * @example 15 | * useDocumentEvent('click', () => console.log('clicked')); 16 | */ 17 | export const useDocumentEvent = (event, listener, options) => 18 | useEventListener(target(document), event, listener, options); 19 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useDocumentVisibility/useDocumentVisibility.js: -------------------------------------------------------------------------------- 1 | import { useSyncExternalStore } from 'react'; 2 | const getSnapshot = () => document.visibilityState; 3 | const getServerSnapshot = () => 'hidden'; 4 | const subscribe = (callback) => { 5 | document.addEventListener('visibilitychange', callback); 6 | return () => { 7 | document.removeEventListener('visibilitychange', callback); 8 | }; 9 | }; 10 | /** 11 | * @name useDocumentVisibility 12 | * @description – Hook that provides the current visibility state of the document 13 | * @category Browser 14 | * 15 | * @returns {DocumentVisibilityState} The current visibility state of the document, which can be 'visible' or 'hidden' 16 | * 17 | * @example 18 | * const visibilityState = useDocumentVisibility(); 19 | */ 20 | export const useDocumentVisibility = () => 21 | useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); 22 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useEvent/useEvent.js: -------------------------------------------------------------------------------- 1 | import { useCallback, useRef } from 'react'; 2 | /** 3 | * @name useEvent 4 | * @description - Hook that creates an event and returns a stable reference of it 5 | * @category Browser 6 | * 7 | * @template Params The type of the params 8 | * @template Return The type of the return 9 | * @param {(...args: Params) => Return} callback The callback function 10 | * @returns {(...args: Params) => Return} The callback 11 | * 12 | * @example 13 | * const onClick = useEvent(() => console.log('clicked')); 14 | */ 15 | export const useEvent = (callback) => { 16 | const callbackRef = useRef(callback); 17 | callbackRef.current = callback; 18 | return useCallback((...args) => { 19 | const fn = callbackRef.current; 20 | return fn(...args); 21 | }, []); 22 | }; 23 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useFul/useFul.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | /** 3 | * @name useFul 4 | * @description - Hook that can be so useful 5 | * @category Humor 6 | * 7 | * @warning - This hook is a joke. Please do not use it in production code! 8 | * 9 | * @template Value The type of the value 10 | * @param {Value} [value] The value to be returned 11 | * @returns {Value} The value passed to the hook 12 | * 13 | * @example 14 | * const value = useFul(state); 15 | */ 16 | export const useFul = (value) => { 17 | useEffect(() => { 18 | console.warn("Warning: You forgot to delete the 'useFul' hook."); 19 | }, []); 20 | return value; 21 | }; 22 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useGamepad/helpers/index.js: -------------------------------------------------------------------------------- 1 | export * from './mapGamepadToXbox360Controller'; 2 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useGamepad/helpers/mapGamepadToXbox360Controller.js: -------------------------------------------------------------------------------- 1 | export const mapGamepadToXbox360Controller = (gamepad) => ({ 2 | buttons: { 3 | a: gamepad.buttons[0], 4 | b: gamepad.buttons[1], 5 | x: gamepad.buttons[2], 6 | y: gamepad.buttons[3] 7 | }, 8 | bumper: { 9 | left: gamepad.buttons[4], 10 | right: gamepad.buttons[5] 11 | }, 12 | triggers: { 13 | left: gamepad.buttons[6], 14 | right: gamepad.buttons[7] 15 | }, 16 | stick: { 17 | left: { 18 | horizontal: gamepad.axes[0], 19 | vertical: gamepad.axes[1], 20 | button: gamepad.buttons[10] 21 | }, 22 | right: { 23 | horizontal: gamepad.axes[2], 24 | vertical: gamepad.axes[3], 25 | button: gamepad.buttons[11] 26 | } 27 | }, 28 | dpad: { 29 | up: gamepad.buttons[12], 30 | down: gamepad.buttons[13], 31 | left: gamepad.buttons[14], 32 | right: gamepad.buttons[15] 33 | }, 34 | back: gamepad.buttons[8], 35 | start: gamepad.buttons[9] 36 | }); 37 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useHash/useHash.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | const getHash = () => decodeURIComponent(window.location.hash.replace('#', '')); 3 | /** 4 | * @name useHash 5 | * @description - Hook that manages the hash value 6 | * @category Browser 7 | * 8 | * @returns {UseHashReturn} An array containing the hash value and a function to set the hash value 9 | * 10 | * @example 11 | * const [hash, setHash] = useHash(); 12 | */ 13 | export const useHash = () => { 14 | const [hash, setHash] = useState(window ? getHash() : ''); 15 | const set = (value) => { 16 | window.location.hash = value; 17 | setHash(value); 18 | }; 19 | useEffect(() => { 20 | const onHashChange = () => setHash(getHash()); 21 | window.addEventListener('hashchange', onHashChange); 22 | return () => { 23 | window.removeEventListener('hashchange', onHashChange); 24 | }; 25 | }); 26 | return [hash, set]; 27 | }; 28 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useIsFirstRender/useIsFirstRender.js: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react'; 2 | /** 3 | * @name useIsFirstRender 4 | * @description - Hook that returns true if the component is first render 5 | * @category Lifecycle 6 | * 7 | * @returns {boolean} True if the component is first render 8 | * 9 | * @example 10 | * const isFirstRender = useIsFirstRender(); 11 | */ 12 | export const useIsFirstRender = () => { 13 | const renderRef = useRef(true); 14 | if (renderRef.current === true) { 15 | renderRef.current = false; 16 | return true; 17 | } 18 | return renderRef.current; 19 | }; 20 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useLayoutEffect } from 'react'; 2 | /** 3 | * @name useIsomorphicLayoutEffect 4 | * @description - Hook conditionally selects either `useLayoutEffect` or `useEffect` based on the environment 5 | * @category Lifecycle 6 | * 7 | * @example 8 | * useIsomorphicLayoutEffect(() => console.log('effect'), []) 9 | */ 10 | export const useIsomorphicLayoutEffect = 11 | typeof window !== 'undefined' ? useLayoutEffect : useEffect; 12 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useLastChanged/useLastChanged.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { useDidUpdate } from '../useDidUpdate/useDidUpdate'; 3 | /** 4 | * @name useLastChanged 5 | * @description - Hook for records the timestamp of the last change 6 | * @category Time 7 | * 8 | * @param {any} source The source of the last change 9 | * @param {number | null} [options.initialValue=null] The initial value 10 | * @returns {number | null} Return timestamp of the last change 11 | * 12 | * @example 13 | * const lastChanged = useLastChanged(source); 14 | */ 15 | export const useLastChanged = (source, options) => { 16 | const [lastChanged, setLastChanged] = useState(options?.initialValue ?? null); 17 | useDidUpdate(() => setLastChanged(Date.now()), [source]); 18 | return lastChanged; 19 | }; 20 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useLatest/useLatest.js: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react'; 2 | /** 3 | * @name useLatest 4 | * @description - Hook that returns the stable reference of the value 5 | * @category Utilities 6 | * 7 | * @template Value The type of the value 8 | * @param {Value} value The value to get the previous value 9 | * @returns {Value} The previous value 10 | * 11 | * @example 12 | * const latestValue = useLatest(value); 13 | */ 14 | export const useLatest = (value) => { 15 | const valueRef = useRef(value); 16 | valueRef.current = value; 17 | return valueRef.current; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useLess/useLess.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | /** 3 | * @name useLess 4 | * @description - Hook that can be so useless 5 | * @category Humor 6 | * 7 | * @warning - This hook is a joke. Please do not use it in production code! 8 | * 9 | * @template Value The type of the value 10 | * @param {Value} [value] The value to be returned 11 | * @returns {Value} The value passed to the hook 12 | * 13 | * @example 14 | * const value = useLess(state); 15 | */ 16 | export const useLess = (value) => { 17 | useEffect(() => { 18 | console.warn("Warning: You forgot to delete the 'useLess' hook."); 19 | }, []); 20 | return value; 21 | }; 22 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useLocalStorage/useLocalStorage.js: -------------------------------------------------------------------------------- 1 | import { useStorage } from '../useStorage/useStorage'; 2 | /** 3 | * @name useLocalStorage 4 | * @description - Hook that manages local storage value 5 | * @category Browser 6 | * 7 | * @browserapi localStorage https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage 8 | * 9 | * @template Value The type of the value 10 | * @param {string} key The key of the storage 11 | * @param {UseStorageInitialValue} [initialValue] The initial value of the storage 12 | * @param {UseStorageOptions} [options] The options of the storage 13 | * 14 | * @example 15 | * const { value, set, remove } = useLocalStorage('key', 'value'); 16 | */ 17 | export const useLocalStorage = (key, initialValue, options) => 18 | useStorage(key, { 19 | ...options, 20 | initialValue, 21 | storage: typeof window !== 'undefined' ? window.localStorage : undefined 22 | }); 23 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useLockCallback/useLockCallback.js: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react'; 2 | /** 3 | * @name useLockCallback 4 | * @description - Hook that prevents a callback from being executed multiple times simultaneously 5 | * @category Utilities 6 | * 7 | * @param {Function} callback The callback to be locked 8 | * @returns {Function} The locked callback 9 | * 10 | * @example 11 | * const lockedCallback = useLockCallback(() => promise()); 12 | */ 13 | export const useLockCallback = (callback) => { 14 | const lockRef = useRef(false); 15 | const callbackRef = useRef(callback); 16 | callbackRef.current = callback; 17 | return async (...args) => { 18 | if (lockRef.current) return; 19 | lockRef.current = true; 20 | try { 21 | return await callbackRef.current(...args); 22 | } finally { 23 | lockRef.current = false; 24 | } 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useLogger/useLogger.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { useDidUpdate } from '../useDidUpdate/useDidUpdate'; 3 | /** 4 | * @name useLogger 5 | * @description - Hook for debugging lifecycle 6 | * @category Lifecycle 7 | * 8 | * @param {string} name The name or identifier for the logger 9 | * @param {unknown[]} params Additional arguments to be logged 10 | * 11 | * @example 12 | * useLogger('Component', [1, 2, 3]); 13 | */ 14 | export const useLogger = (name, params) => { 15 | useEffect(() => { 16 | console.log(`${name} mounted`, ...params); 17 | return () => console.log(`${name} unmounted`); 18 | }, []); 19 | useDidUpdate(() => { 20 | console.log(`${name} updated`, ...params); 21 | }, params); 22 | }; 23 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useMediaQuery/useMediaQuery.js: -------------------------------------------------------------------------------- 1 | import { useCallback, useSyncExternalStore } from 'react'; 2 | const getServerSnapshot = () => false; 3 | /** 4 | * @name useMediaQuery 5 | * @description - Hook that manages a media query 6 | * @category Browser 7 | * 8 | * @browserapi window.matchMedia https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia 9 | * 10 | * @param {string} query The media query string 11 | * @returns {boolean} A boolean indicating if the media query matches 12 | * 13 | * @example 14 | * const matches = useMediaQuery('(max-width: 768px)'); 15 | */ 16 | export const useMediaQuery = (query) => { 17 | const subscribe = useCallback( 18 | (callback) => { 19 | const matchMedia = window.matchMedia(query); 20 | matchMedia.addEventListener('change', callback); 21 | return () => { 22 | matchMedia.removeEventListener('change', callback); 23 | }; 24 | }, 25 | [query] 26 | ); 27 | const getSnapshot = () => window.matchMedia(query).matches; 28 | return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); 29 | }; 30 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useMemory/useMemory.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { useInterval } from '../useInterval/useInterval'; 3 | /** 4 | * @name useMemory 5 | * @description - Hook that gives you current memory usage 6 | * @category Browser 7 | * 8 | * @browserapi performance.memory https://developer.mozilla.org/en-US/docs/Web/API/Performance/memory 9 | * 10 | * @returns {UseMemoryReturn} An object containing the current memory usage 11 | * 12 | * @example 13 | * const { supported, value } = useMemory(); 14 | */ 15 | export const useMemory = () => { 16 | const supported = performance && 'memory' in performance && !!performance.memory; 17 | const [value, setValue] = useState( 18 | performance?.memory ?? { 19 | jsHeapSizeLimit: 0, 20 | totalJSHeapSize: 0, 21 | usedJSHeapSize: 0 22 | } 23 | ); 24 | useInterval(() => setValue(performance.memory), 1000, { 25 | immediately: supported 26 | }); 27 | return { supported, value }; 28 | }; 29 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useMount/useMount.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | /** 3 | * @name useMount 4 | * @description - Hook that executes a callback when the component mounts 5 | * @category Lifecycle 6 | * 7 | * @param {EffectCallback} effect The callback to execute 8 | * 9 | * @example 10 | * useMount(() => console.log('This effect runs on the initial render')); 11 | */ 12 | export const useMount = (effect) => useEffect(effect, []); 13 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useOnce/useOnce.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | /** 3 | * @name useEffectOnce 4 | * @description - Hook that runs an effect only once. Please do not use it in production code! 5 | * @category Humor 6 | * 7 | * @warning - This hook will run effect only once even in strict mode. Please do not use it in production code! 8 | * 9 | * @param {EffectCallback} effect The effect to run 10 | * 11 | * @example 12 | * useOnce(() => console.log('effect once')); 13 | */ 14 | export function useOnce(effect) { 15 | const cleanupRef = useRef(undefined); 16 | const hasRunRef = useRef(false); 17 | const hasRenderedAfterRun = useRef(false); 18 | if (hasRunRef.current) { 19 | hasRenderedAfterRun.current = true; 20 | } 21 | useEffect(() => { 22 | if (hasRunRef.current) return; 23 | hasRunRef.current = true; 24 | cleanupRef.current = effect(); 25 | return () => { 26 | if (!hasRenderedAfterRun.current) return; 27 | if (typeof cleanupRef.current === 'function') { 28 | cleanupRef.current(); 29 | } 30 | }; 31 | }, []); 32 | } 33 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useOnline/useOnline.js: -------------------------------------------------------------------------------- 1 | import { useSyncExternalStore } from 'react'; 2 | const getSnapshot = () => navigator.onLine; 3 | const getServerSnapshot = () => false; 4 | const subscribe = (callback) => { 5 | window.addEventListener('online', callback); 6 | window.addEventListener('offline', callback); 7 | return () => { 8 | window.removeEventListener('online', callback); 9 | window.removeEventListener('offline', callback); 10 | }; 11 | }; 12 | /** 13 | * @name useOnline 14 | * @description - Hook that manages if the user is online 15 | * @category Sensors 16 | * 17 | * @browserapi navigator.onLine https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine 18 | * 19 | * @returns {boolean} A boolean indicating if the user is online 20 | * 21 | * @example 22 | * const online = useOnline(); 23 | */ 24 | export const useOnline = () => useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); 25 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useOperatingSystem/useOperatingSystem.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | export const getOperatingSystem = () => { 3 | if (typeof window === 'undefined') return 'undetermined'; 4 | const { userAgent } = window.navigator; 5 | if (/Macintosh|MacIntel|MacPPC|Mac68K/i.test(userAgent)) return 'macos'; 6 | if (/iPhone|iPad|iPod/i.test(userAgent)) return 'ios'; 7 | if (/Win32|Win64|Windows|WinCE/i.test(userAgent)) return 'windows'; 8 | if (/Android/i.test(userAgent)) return 'android'; 9 | if (/Linux/i.test(userAgent)) return 'linux'; 10 | return 'undetermined'; 11 | }; 12 | /** 13 | * @name useOperatingSystem 14 | * @description - Hook that returns the operating system of the current browser 15 | * @category Browser 16 | * 17 | * @returns {OperatingSystem} The operating system 18 | * 19 | * @example 20 | * const operatingSystem = useOperatingSystem(); 21 | */ 22 | export const useOperatingSystem = () => { 23 | const [osOperatingSystem] = useState(getOperatingSystem()); 24 | return osOperatingSystem; 25 | }; 26 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useOrientation/useOrientation.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | /** 3 | * @name useOrientation 4 | * @description - Hook that returns the current screen orientation 5 | * @category Browser 6 | * 7 | * @browserapi window.screen.orientation https://developer.mozilla.org/en-US/docs/Web/API/Screen/orientation 8 | * 9 | * @returns {UseOrientationReturn} An object containing the current screen orientation 10 | * 11 | * @example 12 | * const { angle, type } = useOrientation(); 13 | */ 14 | export const useOrientation = () => { 15 | const [orientation, setOrientation] = useState({ angle: 0, type: 'landscape-primary' }); 16 | useEffect(() => { 17 | const onChange = () => setOrientation(window.screen.orientation); 18 | window.screen.orientation.addEventListener('change', onChange); 19 | return () => { 20 | window.screen.orientation.removeEventListener('change', onChange); 21 | }; 22 | }, []); 23 | return orientation; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/usePaint/helpers/Pointer.js: -------------------------------------------------------------------------------- 1 | export class Pointer { 2 | x; 3 | y; 4 | constructor(x, y) { 5 | this.x = x; 6 | this.y = y; 7 | } 8 | update(point) { 9 | this.x = point.x; 10 | this.y = point.y; 11 | } 12 | getDifferenceTo(point) { 13 | return new Pointer(this.x - point.x, this.y - point.y); 14 | } 15 | getDistanceTo(point) { 16 | const diff = this.getDifferenceTo(point); 17 | return Math.sqrt(diff.x ** 2 + diff.y ** 2); 18 | } 19 | getAngleTo(point) { 20 | const diff = this.getDifferenceTo(point); 21 | return Math.atan2(diff.y, diff.x); 22 | } 23 | equalsTo(point) { 24 | return this.x === point.x && this.y === point.y; 25 | } 26 | moveByAngle( 27 | // The angle in radians 28 | angle, 29 | // How much the point should be moved 30 | distance 31 | ) { 32 | // Rotate the angle based on the browser coordinate system ([0,0] in the top left) 33 | const angleRotated = angle + Math.PI / 2; 34 | this.x += Math.sin(angleRotated) * distance; 35 | this.y -= Math.cos(angleRotated) * distance; 36 | return this; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/usePaint/helpers/index.js: -------------------------------------------------------------------------------- 1 | export * from './Paint'; 2 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/usePreferredColorScheme/usePreferredColorScheme.js: -------------------------------------------------------------------------------- 1 | import { useMediaQuery } from '../useMediaQuery/useMediaQuery'; 2 | /** 3 | * @name usePreferredColorScheme 4 | * @description - Hook that returns user preferred color scheme 5 | * @category Browser 6 | * 7 | * @returns {UsePreferredColorSchemeReturn} String of preferred color scheme 8 | * 9 | * @example 10 | * const colorScheme = usePreferredColorScheme(); 11 | */ 12 | export const usePreferredColorScheme = () => { 13 | const isLight = useMediaQuery('(prefers-color-scheme: light)'); 14 | const isDark = useMediaQuery('(prefers-color-scheme: dark)'); 15 | if (isLight) return 'light'; 16 | if (isDark) return 'dark'; 17 | return 'no-preference'; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/usePreferredContrast/usePreferredContrast.js: -------------------------------------------------------------------------------- 1 | import { useMediaQuery } from '../useMediaQuery/useMediaQuery'; 2 | /** 3 | * @name usePreferredContrast 4 | * @description - Hook that returns the contrast preference 5 | * @category Browser 6 | * 7 | * @returns {UsePreferredContrastReturn} The contrast preference 8 | * 9 | * @example 10 | * const contrast = usePreferredContrast(); 11 | */ 12 | export const usePreferredContrast = () => { 13 | const more = useMediaQuery('(prefers-contrast: more)'); 14 | const less = useMediaQuery('(prefers-contrast: less)'); 15 | const custom = useMediaQuery('(prefers-contrast: custom)'); 16 | return more ? 'more' : less ? 'less' : custom ? 'custom' : 'no-preference'; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/usePreferredDark/usePreferredDark.js: -------------------------------------------------------------------------------- 1 | import { useMediaQuery } from '../useMediaQuery/useMediaQuery'; 2 | /** 3 | * @name usePreferredDark 4 | * @description - Hook that returns if the user prefers dark mode 5 | * @category Browser 6 | * 7 | * @example 8 | * const isDark = usePreferredDark(); 9 | */ 10 | export const usePreferredDark = () => useMediaQuery('(prefers-color-scheme: dark)'); 11 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/usePreferredLanguages/usePreferredLanguages.js: -------------------------------------------------------------------------------- 1 | import { useSyncExternalStore } from 'react'; 2 | const getSnapshot = () => window.navigator.languages; 3 | const getServerSnapshot = () => []; 4 | const subscribe = (callback) => { 5 | window.addEventListener('languagechange', callback); 6 | return () => { 7 | window.removeEventListener('languagechange', callback); 8 | }; 9 | }; 10 | /** 11 | * @name usePreferredLanguages 12 | * @description Hook that returns a browser preferred languages from navigator 13 | * @category Browser 14 | * 15 | * @browserapi navigator.languages https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages 16 | * 17 | * @returns {readonly string[]} An array of strings representing the user's preferred languages 18 | * 19 | * @example 20 | * const languages = usePreferredLanguages(); 21 | */ 22 | export const usePreferredLanguages = () => 23 | useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); 24 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/usePreferredReducedMotion/usePreferredReducedMotion.js: -------------------------------------------------------------------------------- 1 | import { useMediaQuery } from '../useMediaQuery/useMediaQuery'; 2 | /** 3 | * @name usePreferredReducedMotion 4 | * @description - Hook that returns the reduced motion preference 5 | * @category Browser 6 | * 7 | * @returns {UsePreferredReducedMotionReturn} The reduced motion preference 8 | * 9 | * @example 10 | * const reduced = usePreferredReducedMotion(); 11 | */ 12 | export const usePreferredReducedMotion = () => { 13 | const reduced = useMediaQuery('(prefers-reduced-motion: reduce)'); 14 | return reduced ? 'reduce' : 'no-preference'; 15 | }; 16 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/usePrevious/usePrevious.js: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react'; 2 | /** 3 | * @name usePrevious 4 | * @description - Hook that returns the previous value 5 | * @category Utilities 6 | * 7 | * @template Value The type of the value 8 | * @param {Value} value The value to get the previous value 9 | * @param {(a: Value, b: Value) => boolean} [options.equality] The custom equality function to determine if the value has changed 10 | * @returns {Value | undefined} The previous value 11 | * 12 | * @example 13 | * const prevValue = usePrevious(value); 14 | */ 15 | export const usePrevious = (value, options) => { 16 | const currentRef = useRef(value); 17 | const previousRef = useRef(undefined); 18 | const equality = options?.equality ?? Object.is; 19 | if (!equality(value, currentRef.current)) { 20 | previousRef.current = currentRef.current; 21 | currentRef.current = value; 22 | } 23 | return previousRef.current; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useRafValue/useRafValue.js: -------------------------------------------------------------------------------- 1 | import { useRef, useState } from 'react'; 2 | import { useUnmount } from '../useUnmount/useUnmount'; 3 | /** 4 | * @name useRafValue 5 | * @description - Hook that returns the value and a function to set the value 6 | * @category Utilities 7 | * 8 | * @template Value The type of the value 9 | * @param {Value} initialValue The initial value 10 | * @returns {UseRafValueReturn} An array containing the value and a function to set the value 11 | * 12 | * @example 13 | * const [value, setValue] = useRafValue(initialValue); 14 | */ 15 | export const useRafValue = (initialValue) => { 16 | const rafIdRef = useRef(0); 17 | const [value, setValue] = useState(initialValue); 18 | const set = (value) => { 19 | cancelAnimationFrame(rafIdRef.current); 20 | rafIdRef.current = requestAnimationFrame(() => setValue(value)); 21 | }; 22 | useUnmount(() => cancelAnimationFrame(rafIdRef.current)); 23 | return [value, set]; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useRenderCount/useRenderCount.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | /** 3 | * @name useRenderCount 4 | * @description - Hook returns count component render times 5 | * @category Lifecycle 6 | * 7 | * @returns {number} A number which determines how many times component renders 8 | * 9 | * @example 10 | * const renderCount = useRenderCount(); 11 | */ 12 | export const useRenderCount = () => { 13 | const renderCountRef = useRef(0); 14 | useEffect(() => { 15 | renderCountRef.current += 1; 16 | }); 17 | return renderCountRef.current; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useRerender/useRerender.js: -------------------------------------------------------------------------------- 1 | import { useReducer } from 'react'; 2 | /** 3 | * @name useRerender 4 | * @description - Hook that defines the logic to force rerender a component 5 | * @category Lifecycle 6 | * 7 | * @returns {UseRerenderReturn} The rerender function 8 | * 9 | * @example 10 | * const rerender = useRerender(); 11 | */ 12 | export const useRerender = () => { 13 | const rerender = useReducer(() => ({}), {})[1]; 14 | return rerender; 15 | }; 16 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useSessionStorage/useSessionStorage.js: -------------------------------------------------------------------------------- 1 | import { useStorage } from '../useStorage/useStorage'; 2 | /** 3 | * @name useSessionStorage 4 | * @description - Hook that manages session storage value 5 | * @category Browser 6 | * 7 | * @browserapi sessionStorage https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage 8 | * 9 | * @template Value The type of the value 10 | * @param {string} key The key of the storage 11 | * @param {UseStorageInitialValue} [initialValue] The initial value of the storage 12 | * @param {UseStorageOptions} [options] The options of the storage 13 | * 14 | * @example 15 | * const { value, set, remove } = useSessionStorage('key', 'value'); 16 | */ 17 | export const useSessionStorage = (key, initialValue, options) => 18 | useStorage(key, { 19 | ...options, 20 | initialValue, 21 | storage: typeof window !== 'undefined' ? window.sessionStorage : undefined 22 | }); 23 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useShare/useShare.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @name useShare 3 | * @description - Hook that utilizes the share api 4 | * @category Browser 5 | * 6 | * @param {UseShareParams} [params] The use share options 7 | * @returns {UseShareReturn} 8 | * 9 | * @example 10 | * const { share, supported } = useShare(); 11 | */ 12 | export const useShare = (params) => { 13 | const supported = typeof navigator !== 'undefined' && 'share' in navigator; 14 | const share = async (shareParams) => { 15 | if (!supported) return; 16 | const data = { 17 | ...params, 18 | ...shareParams 19 | }; 20 | if (data.files && navigator.canShare({ files: data.files })) navigator.share(data); 21 | return navigator.share(data); 22 | }; 23 | return { share, supported }; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useThrottleCallback/useThrottleCallback.js: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import { throttle } from '@/utils/helpers'; 3 | import { useEvent } from '../useEvent/useEvent'; 4 | /** 5 | * @name useThrottleCallback 6 | * @description - Hook that creates a throttled callback 7 | * @category Utilities 8 | * 9 | * @template Params The type of the params 10 | * @template Return The type of the return 11 | * @param {(...args: Params) => Return} callback The callback function 12 | * @param {number} delay The delay in milliseconds 13 | * @returns {(...args: Params) => Return} The callback with throttle 14 | * 15 | * @example 16 | * const throttled = useThrottleCallback(() => console.log('callback'), 500); 17 | */ 18 | export const useThrottleCallback = (callback, delay) => { 19 | const internalCallback = useEvent(callback); 20 | const throttled = useMemo(() => throttle(internalCallback, delay), [delay]); 21 | return throttled; 22 | }; 23 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useThrottleValue/useThrottleValue.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from 'react'; 2 | import { useThrottleCallback } from '../useThrottleCallback/useThrottleCallback'; 3 | /** 4 | * @name useThrottleValue 5 | * @description - Hook that creates a throttled value 6 | * @category Utilities 7 | * 8 | * @template Value The type of the value 9 | * @param {Value} value The value to be throttled 10 | * @param {number} delay The delay in milliseconds 11 | * @returns {Value} The throttled value 12 | * 13 | * @example 14 | * const throttledValue = useThrottleValue(value, 500); 15 | */ 16 | export const useThrottleValue = (value, delay) => { 17 | const previousValueRef = useRef(value); 18 | const [throttledValue, setThrottleValue] = useState(value); 19 | const throttledSetState = useThrottleCallback(setThrottleValue, delay); 20 | useEffect(() => { 21 | if (previousValueRef.current === value) return; 22 | throttledSetState(value); 23 | previousValueRef.current = value; 24 | }, [value]); 25 | return throttledValue; 26 | }; 27 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useTime/useTime.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { getDate } from '@/utils/helpers'; 3 | import { useInterval } from '../useInterval/useInterval'; 4 | /** 5 | * @name useTime 6 | * @description - Hook that gives you current time in different values 7 | * @category Time 8 | * 9 | * @returns {UseTimeReturn} An object containing the current time 10 | * 11 | * @example 12 | * const { seconds, minutes, hours, meridiemHours, day, month, year, timestamp } = useTime(); 13 | */ 14 | export const useTime = () => { 15 | const [time, setTime] = useState(getDate()); 16 | useInterval(() => setTime(getDate()), 1000); 17 | return time; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useToggle/useToggle.js: -------------------------------------------------------------------------------- 1 | import { useReducer } from 'react'; 2 | /** 3 | * @name useToggle 4 | * @description - Hook that create toggle 5 | * @category Utilities 6 | * 7 | * @template Value The type of the value 8 | * @param {Value[]} [values=[false, true]] The values to toggle 9 | * 10 | * @example 11 | * const [on, toggle] = useToggle(); 12 | * 13 | * @example 14 | * const [value, toggle] = useToggle(['light', 'dark'] as const); 15 | */ 16 | export const useToggle = (values = [false, true]) => { 17 | const [[option], toggle] = useReducer((state, action) => { 18 | const value = typeof action === 'function' ? action(state[0]) : action; 19 | const index = Math.abs(state.indexOf(value)); 20 | return state.slice(index).concat(state.slice(0, index)); 21 | }, values); 22 | return [option, toggle]; 23 | }; 24 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useUnmount/useUnmount.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | /** 3 | * @name useUnmount 4 | * @description - Hook that defines the logic when unmounting a component 5 | * @category Lifecycle 6 | * 7 | * @param {() => void} callback The callback function to be invoked on component unmount 8 | * @returns {void} 9 | * 10 | * @example 11 | * useUnmount(() => console.log('This effect runs on component unmount')); 12 | */ 13 | export const useUnmount = (callback) => { 14 | const internalCallbackRef = useRef(callback); 15 | internalCallbackRef.current = callback; 16 | useEffect( 17 | () => () => { 18 | internalCallbackRef.current(); 19 | }, 20 | [] 21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useWindowEvent/useWindowEvent.js: -------------------------------------------------------------------------------- 1 | import { target } from '@/utils/helpers'; 2 | import { useEventListener } from '../useEventListener/useEventListener'; 3 | /** 4 | * @name useWindowEvent 5 | * @description - Hook attaches an event listener to the window object for the specified event 6 | * @category Browser 7 | * 8 | * @template Event Key of window event map. 9 | * @param {Event} event The event to listen for. 10 | * @param {(event: WindowEventMap[Event]) => void} listener The callback function to be executed when the event is triggered 11 | * @param {UseEventListenerOptions} [options] The options for the event listener 12 | * @returns {void} 13 | * 14 | * @example 15 | * useWindowEvent('click', () => console.log('clicked')); 16 | */ 17 | export const useWindowEvent = (event, listener, options) => 18 | useEventListener(target(window), event, listener, options); 19 | -------------------------------------------------------------------------------- /packages/core/src/bundle/hooks/useWindowFocus/useWindowFocus.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | /** 3 | * @name useWindowFocus 4 | * @description - Hook that provides the current focus state of the window 5 | * @category Elements 6 | * 7 | * @returns {boolean} The current focus state of the window 8 | * 9 | * @example 10 | * const focused = useWindowFocus(); 11 | * 12 | * @see {@link https://siberiacancode.github.io/reactuse/functions/hooks/useWindowFocus.html} 13 | */ 14 | export const useWindowFocus = () => { 15 | const [focused, setFocused] = useState(false); 16 | useEffect(() => { 17 | const onFocus = () => setFocused(true); 18 | const onBlur = () => setFocused(false); 19 | window.addEventListener('focus', onFocus); 20 | window.addEventListener('blur', onBlur); 21 | return () => { 22 | window.removeEventListener('focus', onFocus); 23 | window.removeEventListener('blur', onBlur); 24 | }; 25 | }); 26 | return focused; 27 | }; 28 | -------------------------------------------------------------------------------- /packages/core/src/bundle/index.js: -------------------------------------------------------------------------------- 1 | export * from './helpers'; 2 | export * from './hooks'; 3 | export * from './utils/helpers'; 4 | -------------------------------------------------------------------------------- /packages/core/src/bundle/utils/helpers/copy.js: -------------------------------------------------------------------------------- 1 | export const legacyCopyToClipboard = (value) => { 2 | const tempTextArea = document.createElement('textarea'); 3 | tempTextArea.value = value; 4 | tempTextArea.readOnly = true; 5 | tempTextArea.style.fontSize = '16px'; 6 | document.body.appendChild(tempTextArea); 7 | tempTextArea.select(); 8 | document.execCommand('copy'); 9 | document.body.removeChild(tempTextArea); 10 | }; 11 | export const copy = async (value) => { 12 | try { 13 | try { 14 | await navigator.clipboard.writeText(value); 15 | } catch { 16 | return legacyCopyToClipboard(value); 17 | } 18 | } catch { 19 | return legacyCopyToClipboard(value); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /packages/core/src/bundle/utils/helpers/debounce.js: -------------------------------------------------------------------------------- 1 | export function debounce(callback, delay) { 2 | let timer; 3 | return function (...args) { 4 | clearTimeout(timer); 5 | timer = setTimeout(() => callback.apply(this, args), delay); 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/bundle/utils/helpers/getDate.js: -------------------------------------------------------------------------------- 1 | export const getDate = (now = new Date()) => { 2 | const seconds = now.getSeconds(); 3 | const minutes = now.getMinutes(); 4 | const hours = now.getHours(); 5 | const meridiemHours = hours % 12 === 0 ? 12 : hours % 12; 6 | const meridiemType = hours >= 12 ? 'pm' : 'am'; 7 | const day = now.getDate(); 8 | const month = now.getMonth() + 1; 9 | const year = now.getFullYear(); 10 | const timestamp = now.getTime(); 11 | return { 12 | seconds, 13 | minutes, 14 | hours, 15 | meridiemHours: { value: meridiemHours, type: meridiemType }, 16 | day, 17 | month, 18 | year, 19 | timestamp 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /packages/core/src/bundle/utils/helpers/getElement.js: -------------------------------------------------------------------------------- 1 | export const targetSymbol = Symbol('target'); 2 | export const target = (target) => ({ 3 | value: target, 4 | type: targetSymbol 5 | }); 6 | export const getElement = (target) => { 7 | if ('current' in target) { 8 | return target.current; 9 | } 10 | if (typeof target.value === 'function') { 11 | return target.value(); 12 | } 13 | if (typeof target.value === 'string') { 14 | return document.querySelector(target.value); 15 | } 16 | if (target.value instanceof Document) { 17 | return target.value; 18 | } 19 | if (target.value instanceof Window) { 20 | return target.value; 21 | } 22 | if (target.value instanceof Element) { 23 | return target.value; 24 | } 25 | return target.value; 26 | }; 27 | -------------------------------------------------------------------------------- /packages/core/src/bundle/utils/helpers/getRetry.js: -------------------------------------------------------------------------------- 1 | export const getRetry = (retry) => { 2 | if (typeof retry === 'number') return retry; 3 | return retry ? 1 : 0; 4 | }; 5 | -------------------------------------------------------------------------------- /packages/core/src/bundle/utils/helpers/index.js: -------------------------------------------------------------------------------- 1 | export * from './copy'; 2 | export * from './debounce'; 3 | export * from './getDate'; 4 | export * from './getElement'; 5 | export * from './getRetry'; 6 | export * from './isTarget'; 7 | export * from './throttle'; 8 | -------------------------------------------------------------------------------- /packages/core/src/bundle/utils/helpers/isTarget.js: -------------------------------------------------------------------------------- 1 | import { targetSymbol } from './getElement'; 2 | export const isTarget = (target) => 3 | typeof target === 'object' && ('current' in target || target.type === targetSymbol); 4 | -------------------------------------------------------------------------------- /packages/core/src/bundle/utils/helpers/throttle.js: -------------------------------------------------------------------------------- 1 | export const throttle = (callback, delay) => { 2 | let isCalled = false; 3 | let lastArgs = null; 4 | const timer = () => { 5 | if (!lastArgs) { 6 | isCalled = false; 7 | return; 8 | } 9 | callback.apply(this, lastArgs); 10 | lastArgs = null; 11 | setTimeout(timer, delay); 12 | }; 13 | return function (...args) { 14 | if (isCalled) { 15 | lastArgs = args; 16 | return; 17 | } 18 | callback.apply(this, args); 19 | isCalled = true; 20 | setTimeout(timer, delay); 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /packages/core/src/bundle/utils/helpers/time/getDate.js: -------------------------------------------------------------------------------- 1 | export const getDate = (now = new Date()) => { 2 | const seconds = now.getSeconds(); 3 | const minutes = now.getMinutes(); 4 | const hours = now.getHours(); 5 | const meridiemHours = hours % 12 === 0 ? 12 : hours % 12; 6 | const meridiemType = hours >= 12 ? 'pm' : 'am'; 7 | const day = now.getDate(); 8 | const month = now.getMonth() + 1; 9 | const year = now.getFullYear(); 10 | const timestamp = now.getTime(); 11 | return { 12 | seconds, 13 | minutes, 14 | hours, 15 | meridiemHours: { value: meridiemHours, type: meridiemType }, 16 | day, 17 | month, 18 | year, 19 | timestamp 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /packages/core/src/helpers/createContext/createContext.test.tsx: -------------------------------------------------------------------------------- 1 | import { act, renderHook } from '@testing-library/react'; 2 | 3 | import { createContext } from './createContext'; 4 | 5 | const countContext = createContext(0); 6 | 7 | it('Should return initial value from provider', () => { 8 | const { result } = renderHook(() => countContext.useSelect(), { 9 | wrapper: ({ children }) => ( 10 | {children} 11 | ) 12 | }); 13 | 14 | expect(result.current.value).toBe(1); 15 | expect(result.current.set).toBeTypeOf('function'); 16 | }); 17 | 18 | it('Should update value', () => { 19 | const { result } = renderHook(() => countContext.useSelect(), { 20 | wrapper: ({ children }) => ( 21 | {children} 22 | ) 23 | }); 24 | 25 | expect(result.current.value).toBe(1); 26 | 27 | act(() => result.current.set(2)); 28 | 29 | expect(result.current.value).toBe(2); 30 | }); 31 | -------------------------------------------------------------------------------- /packages/core/src/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './createContext/createContext'; 2 | export * from './createReactiveContext/createReactiveContext'; 3 | export * from './createStore/createStore'; 4 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useActiveElement/useActiveElement.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useActiveElement } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const activeElement = useActiveElement(); 5 | const activeElementId = activeElement?.dataset?.id ?? 'null'; 6 | 7 | return ( 8 | <> 9 |
10 | {Array.from({ length: 6 }, (_, i) => i + 1).map((id) => ( 11 | 18 | ))} 19 |
20 | 21 |

22 | current active element: {activeElementId} 23 |

24 | 25 | ); 26 | }; 27 | 28 | export default Demo; 29 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useBoolean/useBoolean.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useBoolean } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const [on, toggle] = useBoolean(); 5 | 6 | return ( 7 |
8 |

9 | Value: {String(on)} 10 |

11 | 14 | 17 | 20 |
21 | ); 22 | }; 23 | 24 | export default Demo; 25 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useBoolean/useBoolean.test.ts: -------------------------------------------------------------------------------- 1 | import { act, renderHook } from '@testing-library/react'; 2 | 3 | import { useBoolean } from './useBoolean'; 4 | 5 | it('Should use counter', () => { 6 | const { result } = renderHook(useBoolean); 7 | const [on, toggle] = result.current; 8 | 9 | expect(on).toBeFalsy(); 10 | expect(toggle).toBeTypeOf('function'); 11 | }); 12 | 13 | it('Should set initial value', () => { 14 | const { result } = renderHook(() => useBoolean(true)); 15 | const [on] = result.current; 16 | 17 | expect(on).toBeTruthy(); 18 | }); 19 | 20 | it('Should toggle boolean', () => { 21 | const { result } = renderHook(useBoolean); 22 | const toggle = result.current[1]; 23 | 24 | act(toggle); 25 | expect(result.current[0]).toBeTruthy(); 26 | 27 | act(toggle); 28 | expect(result.current[0]).toBeFalsy(); 29 | 30 | act(() => toggle(false)); 31 | expect(result.current[0]).toBeFalsy(); 32 | 33 | act(() => toggle(true)); 34 | expect(result.current[0]).toBeTruthy(); 35 | }); 36 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useBoolean/useBoolean.ts: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | /** The use boolean return type */ 4 | export type UseBooleanReturn = [ 5 | /** The current boolean state value */ 6 | value: boolean, 7 | /** Function to toggle the boolean state */ 8 | toggle: (value?: boolean) => void 9 | ]; 10 | 11 | /** 12 | * @name useBoolean 13 | * @description - Hook provides opportunity to manage boolean state 14 | * @category Utilities 15 | * 16 | * @param {boolean} [initialValue=false] The initial boolean value 17 | * @returns {UseBooleanReturn} An object containing the boolean state value and utility functions to manipulate the state 18 | * 19 | * @example 20 | * const [on, toggle] = useBoolean() 21 | */ 22 | export const useBoolean = (initialValue = false): UseBooleanReturn => { 23 | const [value, setValue] = useState(initialValue); 24 | const toggle = (value?: boolean) => setValue((prevValue) => value ?? !prevValue); 25 | 26 | return [value, toggle]; 27 | }; 28 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useBrowserLanguage/useBrowserLanguage.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useBrowserLanguage } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const browserLanguage = useBrowserLanguage(); 5 | 6 | return ( 7 |

8 | Browser language: {browserLanguage} 9 |

10 | ); 11 | }; 12 | 13 | export default Demo; 14 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useBrowserLanguage/useBrowserLanguage.ts: -------------------------------------------------------------------------------- 1 | import { useSyncExternalStore } from 'react'; 2 | 3 | const getSnapshot = () => navigator.language; 4 | const getServerSnapshot = () => 'undetermined'; 5 | const subscribe = (callback: () => void) => { 6 | window.addEventListener('languagechange', callback); 7 | return () => window.removeEventListener('languagechange', callback); 8 | }; 9 | 10 | /** 11 | * @name useBrowserLanguage 12 | * @description - Hook that returns the current browser language 13 | * @category Browser 14 | * 15 | * @browserapi navigator.language https://developer.mozilla.org/en-US/docs/Web/API/Navigator/language 16 | * 17 | * @returns {string} The current browser language 18 | * 19 | * @example 20 | * const browserLanguage = useBrowserLanguage(); 21 | */ 22 | export const useBrowserLanguage = () => 23 | useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); 24 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useClickOutside/useClickOutside.demo.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '@siberiacancode/docs/utils'; 2 | import { useClickOutside, useCounter } from '@siberiacancode/reactuse'; 3 | 4 | const Demo = () => { 5 | const counter = useCounter(); 6 | 7 | const clickOutsideRef = useClickOutside(() => { 8 | console.log('click outside'); 9 | counter.inc(); 10 | }); 11 | 12 | return ( 13 |
14 |

15 | Click more than five times: {counter.value} 16 |

17 | 18 |
5 } 23 | )} 24 | > 25 | {counter.value <= 5 && 'Click outside'} 26 | {counter.value > 5 && counter.value <= 25 && 'Nice work'} 27 | {counter.value > 25 && 'That are a lot of clicks'} 28 |
29 |
30 | ); 31 | }; 32 | 33 | export default Demo; 34 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useClipboard/useClipboard.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useClipboard, useField } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const clipboard = useClipboard(); 5 | const textField = useField(); 6 | 7 | return ( 8 | <> 9 |

10 | Copied value: {clipboard.value || 'Nothing is copied yet!'} 11 |

12 | 13 | 14 | 17 | 18 | ); 19 | }; 20 | 21 | export default Demo; 22 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useConst/useConst.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useConst } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const mountTime = useConst(() => new Date().toTimeString()); 5 | const obj = useConst({ a: Math.random() }); 6 | 7 | return ( 8 |
9 |

10 | Mount time: {mountTime} 11 |

12 |

13 | Value from constant object: {obj.a} 14 |

15 |
16 | ); 17 | }; 18 | 19 | export default Demo; 20 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useConst/useConst.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react'; 2 | 3 | import { useConst } from './useConst'; 4 | 5 | it('Should use const', () => { 6 | const { result } = renderHook(() => useConst('value')); 7 | 8 | expect(result.current).toBe('value'); 9 | }); 10 | 11 | it('should return the same constant value', () => { 12 | const { result, rerender } = renderHook(() => useConst('value')); 13 | expect(result.current).toBe('value'); 14 | 15 | rerender(); 16 | 17 | expect(result.current).toBe('value'); 18 | }); 19 | 20 | it('Should call initializer function', () => { 21 | const init = vitest.fn(() => 99); 22 | const { result } = renderHook(() => useConst(init)); 23 | 24 | expect(result.current).toBe(99); 25 | expect(init).toHaveBeenCalledOnce(); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useConst/useConst.ts: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react'; 2 | 3 | /** 4 | * @name useConst 5 | * @description - Hook that returns the constant value 6 | * @category Utilities 7 | * 8 | * @template Value The type of the value 9 | * @param {(() => Value) | Value} initialValue The initial value of the constant 10 | * @returns {Value} The constant value 11 | * 12 | * @example 13 | * const value = useConst('value'); 14 | */ 15 | export const useConst = (initialValue: (() => Value) | Value) => 16 | useRef(typeof initialValue === 'function' ? (initialValue as () => Value)() : initialValue) 17 | .current; 18 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useCookie/useCookie.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useCookie } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const { value, set, remove } = useCookie('siberiacancode-use-cookie', 0); 5 | 6 | return ( 7 |
8 |

9 | Count: {value ?? 'value is undefined'} 10 |

11 | {value !== undefined && ( 12 | <> 13 | 16 | 19 | 20 | )} 21 | {value === undefined && ( 22 | 25 | )} 26 | 29 |
30 | ); 31 | }; 32 | 33 | export default Demo; 34 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useCookies/useCookies.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useCookie, useCookies } from '@siberiacancode/reactuse'; 2 | 3 | const POKEMONS = [ 4 | { name: 'Pikachu', index: 25 }, 5 | { name: 'Bulbasaur', index: 1 }, 6 | { name: 'Charmander', index: 4 }, 7 | { name: 'Squirtle', index: 7 }, 8 | { name: 'Jigglypuff', index: 39 }, 9 | { name: 'Gengar', index: 94 }, 10 | { name: 'Mewtwo', index: 150 }, 11 | { name: 'Mew', index: 151 }, 12 | { name: 'Charizard', index: 6 }, 13 | { name: 'Blastoise', index: 9 }, 14 | { name: 'Venusaur', index: 3 } 15 | ]; 16 | 17 | const Demo = () => { 18 | const pokemonsCookie = useCookie('pokemon', POKEMONS[0]); 19 | const cookies = useCookies<{ name: string; id: number }>(); 20 | 21 | return ( 22 |
23 |
Cookies
24 |
{JSON.stringify(cookies.value, null, 2)}
25 | 26 | 31 | 32 |
33 | ); 34 | }; 35 | 36 | export default Demo; 37 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useCounter/useCounter.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useCounter } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const counter = useCounter(); 5 | 6 | return ( 7 | <> 8 |

9 | Count: {counter.value} 10 |

11 | 14 | 17 | 20 | 23 | 24 | ); 25 | }; 26 | 27 | export default Demo; 28 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useCssVar/useCssVar.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useCssVar } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const colorVar = useCssVar('--color', '#7fa998'); 5 | 6 | const switchColor = () => { 7 | if (colorVar.value === '#df8543') colorVar.set('#7fa998'); 8 | else colorVar.set('#df8543'); 9 | }; 10 | 11 | return ( 12 | <> 13 |

Sample text, {colorVar.value}

14 | 17 | 18 | ); 19 | }; 20 | 21 | export default Demo; 22 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDebounceCallback/useDebounceCallback.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useCounter, useDebounceCallback } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const counter = useCounter(); 5 | 6 | const debouncedCounterInc = useDebounceCallback(counter.inc, 500); 7 | const debouncedCounterDec = useDebounceCallback(counter.dec, 500); 8 | 9 | return ( 10 |
11 |

12 | Value: {counter.value} 13 |

14 | 17 | 20 |
21 | ); 22 | }; 23 | 24 | export default Demo; 25 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDebounceCallback/useDebounceCallback.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | 3 | import { debounce } from '@/utils/helpers'; 4 | 5 | import { useEvent } from '../useEvent/useEvent'; 6 | 7 | /** 8 | * @name useDebounceCallback 9 | * @description - Hook that creates a debounced callback 10 | * @category Utilities 11 | * 12 | * @template Params The type of the params 13 | * @template Return The type of the return 14 | * @param {(...args: Params) => Return} callback The callback function 15 | * @param {number} delay The delay in milliseconds 16 | * @returns {(...args: Params) => Return} The callback with debounce 17 | * 18 | * @example 19 | * const debouncedCallback = useDebounceCallback(() => console.log('callback'), 500); 20 | */ 21 | export const useDebounceCallback = ( 22 | callback: (...args: Params) => Return, 23 | delay: number 24 | ) => { 25 | const internalCallback = useEvent(callback); 26 | const debounced = useMemo(() => debounce(internalCallback, delay), [delay]); 27 | 28 | return debounced; 29 | }; 30 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDebounceValue/useDebounceValue.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useCounter, useDebounceValue } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const counter = useCounter(); 5 | 6 | const debouncedCounterCount = useDebounceValue(counter.value, 500); 7 | 8 | return ( 9 |
10 |

11 | Value: {counter.value} 12 |

13 |

14 | Debounced value: {debouncedCounterCount} 15 |

16 | 19 | 22 |
23 | ); 24 | }; 25 | 26 | export default Demo; 27 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDebounceValue/useDebounceValue.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from 'react'; 2 | 3 | import { useDebounceCallback } from '../useDebounceCallback/useDebounceCallback'; 4 | 5 | /** 6 | * @name useDebounceValue 7 | * @description - Hook that creates a debounced value 8 | * @category Utilities 9 | * 10 | * @template Value The type of the value 11 | * @param {Value} value The value to be debounced 12 | * @param {number} delay The delay in milliseconds 13 | * @returns {Value} The debounced value 14 | * 15 | * @example 16 | * const debouncedValue = useDebounceValue(value, 500); 17 | */ 18 | export const useDebounceValue = (value: Value, delay: number) => { 19 | const previousValueRef = useRef(value); 20 | const [debouncedValue, setDebounceValue] = useState(value); 21 | 22 | const debouncedSetState = useDebounceCallback(setDebounceValue, delay); 23 | 24 | useEffect(() => { 25 | if (previousValueRef.current === value) return; 26 | debouncedSetState(value); 27 | previousValueRef.current = value; 28 | }, [value]); 29 | 30 | return debouncedValue; 31 | }; 32 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDefault/useDefault.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useDefault } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const initialUser = { name: 'Dima' }; 5 | const defaultUser = { name: 'Danila' }; 6 | const [user, setUser] = useDefault(initialUser, defaultUser); 7 | 8 | return ( 9 |
10 |

11 | User: {user.name} 12 |

13 | setUser({ name: event.target.value })} /> 14 | 17 |
18 | ); 19 | }; 20 | 21 | export default Demo; 22 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDefault/useDefault.ts: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | /** 4 | * @name useDefault 5 | * @description - Hook that returns the default value 6 | * @category Utilities 7 | * 8 | * @template Value The type of the value 9 | * @param {Value} initialValue The initial value 10 | * @param {Value} defaultValue The default value 11 | * @returns {[Value, (value: Value) => void]} An array containing the current value and a function to set the value 12 | * 13 | * @example 14 | * const [value, setValue] = useDefault(initialValue, defaultValue); 15 | */ 16 | export const useDefault = (initialValue: (() => Value) | Value, defaultValue: Value) => { 17 | const [value, setValue] = useState(initialValue); 18 | return [value === undefined || value === null ? defaultValue : value, setValue] as const; 19 | }; 20 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDeviceMotion/useDeviceMotion.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useDeviceMotion } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const deviceMotion = useDeviceMotion(); 5 | 6 | return ( 7 |
 8 |       Device motion data:
 9 |       

{JSON.stringify(deviceMotion, null, 2)}

10 |
11 | ); 12 | }; 13 | 14 | export default Demo; 15 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDeviceOrientation/useDeviceOrientation.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useDeviceOrientation } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const deviceOrientation = useDeviceOrientation(); 5 | 6 | if (!deviceOrientation.supported) 7 | return ( 8 |

9 | Api not supported, make sure to check for compatibility with different browsers when using 10 | this{' '} 11 | 16 | api 17 | 18 |

19 | ); 20 | 21 | return ( 22 |
23 |       Device orientation data:
24 |       

{JSON.stringify(deviceOrientation.value, null, 2)}

25 |
26 | ); 27 | }; 28 | 29 | export default Demo; 30 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDevicePixelRatio/useDevicePixelRatio.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useDevicePixelRatio } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const { supported, ratio } = useDevicePixelRatio(); 5 | 6 | if (!supported) 7 | return ( 8 |

9 | Api not supported, make sure to check for compatibility with different browsers when using 10 | this{' '} 11 | 16 | api 17 | 18 |

19 | ); 20 | 21 | return ( 22 |

23 | Device pixel ratio (try to zoom page in and out): {ratio} 24 |

25 | ); 26 | }; 27 | 28 | export default Demo; 29 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDidUpdate/useDidUpdate.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useCounter, useDidUpdate } from '@siberiacancode/reactuse'; 2 | import { useEffect, useState } from 'react'; 3 | 4 | const Demo = () => { 5 | const counter = useCounter(); 6 | const [useEffectTriggered, setUseEffectTriggered] = useState(false); 7 | const [useDidUpdateTriggered, setUseDidUpdateTriggered] = useState(false); 8 | 9 | useDidUpdate(() => setUseDidUpdateTriggered(true), [counter.value]); 10 | 11 | useEffect(() => { 12 | setUseEffectTriggered(true); 13 | }, [counter.value]); 14 | 15 | return ( 16 |
17 |

18 | Count: {counter.value} 19 |

20 |

21 | 22 | Use effect triggered: {String(useEffectTriggered)}, use non initial effect 23 | triggered: {String(useDidUpdateTriggered)} 24 | 25 |

26 | 29 |
30 | ); 31 | }; 32 | 33 | export default Demo; 34 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDidUpdate/useDidUpdate.ts: -------------------------------------------------------------------------------- 1 | import type { DependencyList, EffectCallback } from 'react'; 2 | 3 | import { useRef } from 'react'; 4 | 5 | import { useIsomorphicLayoutEffect } from '../useIsomorphicLayoutEffect/useIsomorphicLayoutEffect'; 6 | 7 | /** 8 | * @name useDidUpdate 9 | * @description – Hook that triggers the effect callback on updates 10 | * @category Lifecycle 11 | * 12 | * @param {EffectCallback} effect The effect callback 13 | * @param {DependencyList} [deps] The dependencies list for the effect 14 | * 15 | * @example 16 | * useDidUpdate(() => console.log("effect runs on updates"), deps); 17 | */ 18 | export const useDidUpdate = (effect: EffectCallback, deps?: DependencyList) => { 19 | const mounted = useRef(false); 20 | 21 | useIsomorphicLayoutEffect( 22 | () => () => { 23 | mounted.current = false; 24 | }, 25 | [] 26 | ); 27 | 28 | useIsomorphicLayoutEffect(() => { 29 | if (mounted.current) { 30 | return effect(); 31 | } 32 | 33 | mounted.current = true; 34 | return undefined; 35 | }, deps); 36 | }; 37 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDisclosure/useDisclosure.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useDisclosure } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const modal = useDisclosure(); 5 | 6 | return ( 7 |
8 |

9 | Opened: {String(modal.opened)} 10 |

11 | {modal.opened && ( 12 |

13 | Modal content 14 |

15 | )} 16 | 19 | 22 | 25 |
26 | ); 27 | }; 28 | 29 | export default Demo; 30 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDocumentEvent/useDocumentEvent.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useDocumentEvent } from '@siberiacancode/reactuse'; 2 | import { useState } from 'react'; 3 | 4 | const Demo = () => { 5 | const [count, setCount] = useState(0); 6 | 7 | useDocumentEvent('click', () => setCount(count + 1)); 8 | 9 | return

Document click count: {count}

; 10 | }; 11 | 12 | export default Demo; 13 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDocumentEvent/useDocumentEvent.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react'; 2 | import { vi } from 'vitest'; 3 | 4 | import { useDocumentEvent } from './useDocumentEvent'; 5 | 6 | it('Should use document event', () => { 7 | const listener = vi.fn(); 8 | renderHook(() => useDocumentEvent('click', listener)); 9 | document.dispatchEvent(new Event('click')); 10 | expect(listener).toHaveBeenCalled(); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDocumentTitle/useDocumentTitle.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useDocumentTitle } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const documentTitle = useDocumentTitle(); 5 | 6 | return ( 7 |
8 |

Title: {documentTitle.value}

9 | documentTitle.set(event.target.value)} 12 | /> 13 |
14 | ); 15 | }; 16 | 17 | export default Demo; 18 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDocumentVisibility/useDocumentVisibility.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useDidUpdate, useDocumentVisibility, useTimer } from '@siberiacancode/reactuse'; 2 | import { useState } from 'react'; 3 | 4 | const START_MESSAGE = '💡 Minimize the page or switch tab then return'; 5 | 6 | const Demo = () => { 7 | const [message, setMessage] = useState(START_MESSAGE); 8 | const documentVisibility = useDocumentVisibility(); 9 | 10 | const timer = useTimer(3000, () => { 11 | setMessage(START_MESSAGE); 12 | }); 13 | 14 | useDidUpdate(() => { 15 | if (documentVisibility === 'visible') { 16 | setMessage('🎉 Welcome back!'); 17 | timer.start(); 18 | } 19 | }, [documentVisibility]); 20 | 21 | return

{message}

; 22 | }; 23 | 24 | export default Demo; 25 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDocumentVisibility/useDocumentVisibility.ts: -------------------------------------------------------------------------------- 1 | import { useSyncExternalStore } from 'react'; 2 | 3 | const getSnapshot = () => document.visibilityState; 4 | const getServerSnapshot = () => 'hidden' as const; 5 | const subscribe = (callback: () => void) => { 6 | document.addEventListener('visibilitychange', callback); 7 | return () => { 8 | document.removeEventListener('visibilitychange', callback); 9 | }; 10 | }; 11 | 12 | /** 13 | * @name useDocumentVisibility 14 | * @description – Hook that provides the current visibility state of the document 15 | * @category Browser 16 | * 17 | * @returns {DocumentVisibilityState} The current visibility state of the document, which can be 'visible' or 'hidden' 18 | * 19 | * @example 20 | * const visibilityState = useDocumentVisibility(); 21 | */ 22 | export const useDocumentVisibility = () => 23 | useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); 24 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useDoubleClick/useDoubleClick.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useCounter, useDoubleClick } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const counter = useCounter(); 5 | const doubleClickRef = useDoubleClick(() => counter.inc()); 6 | 7 | return ( 8 | <> 9 |

10 | Double clicked {counter.value} times 11 |

12 | 15 | 16 | ); 17 | }; 18 | 19 | export default Demo; 20 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useElementSize/useElementSize.demo.tsx: -------------------------------------------------------------------------------- 1 | import { useElementSize } from '@siberiacancode/reactuse'; 2 | 3 | const Demo = () => { 4 | const elementSize = useElementSize(); 5 | 6 | return ( 7 |
8 |

Resize the box to see changes

9 |