├── src ├── vite-env.d.ts ├── main.tsx ├── lib │ ├── hooks │ │ ├── useDocTitle.tsx │ │ ├── useConsole │ │ │ ├── useConLog.tsx │ │ │ ├── useConTable.tsx │ │ │ ├── useConGroup.tsx │ │ │ └── useConTime.tsx │ │ ├── useCopyToClipboard.tsx │ │ ├── useOnScreen.tsx │ │ ├── useScroll.tsx │ │ ├── useMedia.tsx │ │ ├── useScreen.tsx │ │ ├── useOnClickOutside.tsx │ │ ├── useLocalStorage.tsx │ │ ├── useHover.tsx │ │ └── useFetch.tsx │ ├── funcs │ │ ├── funcDecimalNumber.ts │ │ ├── funcLocalStorage.ts │ │ ├── funcNumber.ts │ │ ├── funcRandom.ts │ │ └── funcArray.ts │ └── index.ts ├── index.css ├── App.css └── App.tsx ├── tsconfig.node.json ├── index.html ├── .gitignore ├── .eslintrc.cjs ├── vite.config.ts ├── tsconfig.json ├── LICENSE ├── package.json ├── README.md └── yarn.lock /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom/client'; 2 | import App from './App.tsx'; 3 | import './index.css'; 4 | 5 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /src/lib/hooks/useDocTitle.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | const useDocTitle = (title: string) => { 4 | useEffect(() => { 5 | document.title = title; 6 | }, [title]); 7 | }; 8 | 9 | export default useDocTitle; 10 | -------------------------------------------------------------------------------- /src/lib/hooks/useConsole/useConLog.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | function useConLog(...inputs: any[]) { 4 | useEffect(() => { 5 | console.log(...inputs); 6 | }, [inputs]); 7 | } 8 | 9 | export default useConLog; 10 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/hooks/useConsole/useConTable.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | function useConTable(data: any[], columns: string[]) { 4 | useEffect(() => { 5 | console.table(data, columns); 6 | }, [data, columns]); 7 | } 8 | 9 | export default useConTable; 10 | -------------------------------------------------------------------------------- /src/lib/funcs/funcDecimalNumber.ts: -------------------------------------------------------------------------------- 1 | function setPrecisionForDecimal(input: number, precision: number): number { 2 | const decimalPlaces = Math.pow(10, precision); 3 | const roundedNumber = Math.round(input * decimalPlaces) / decimalPlaces; 4 | 5 | return +roundedNumber.toFixed(precision); 6 | } 7 | 8 | export default setPrecisionForDecimal; 9 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React Hooks Plus 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | test.ts 27 | -------------------------------------------------------------------------------- /src/lib/hooks/useConsole/useConGroup.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | const useConGroup = ( 4 | content: any, 5 | groupLabel: string = 'My Console Group' 6 | ): void => { 7 | useEffect(() => { 8 | console.groupCollapsed(groupLabel); 9 | console.log(content); 10 | console.groupEnd(); 11 | }, [groupLabel, content]); 12 | }; 13 | 14 | export default useConGroup; 15 | -------------------------------------------------------------------------------- /src/lib/hooks/useCopyToClipboard.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | function useCopyToClipboard() { 4 | const [copied, setCopied] = useState(false); 5 | 6 | function copyToClipboard(text: string) { 7 | navigator.clipboard.writeText(text); 8 | setCopied(true); 9 | setTimeout(() => setCopied(false), 1500); 10 | } 11 | 12 | return { copied, copyToClipboard }; 13 | } 14 | 15 | export default useCopyToClipboard; 16 | -------------------------------------------------------------------------------- /src/lib/funcs/funcLocalStorage.ts: -------------------------------------------------------------------------------- 1 | // save to local storage function 2 | function saveToLocalStorage(key: string, data: any): void { 3 | localStorage.setItem(key, JSON.stringify(data)); 4 | } 5 | 6 | // get from local storage function 7 | function getFromLocalStorage(key: string): any { 8 | const data = localStorage.getItem(key); 9 | return data ? JSON.parse(data) : null; 10 | } 11 | 12 | export { saveToLocalStorage, getFromLocalStorage }; 13 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { browser: true, es2020: true }, 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:react-hooks/recommended', 7 | ], 8 | parser: '@typescript-eslint/parser', 9 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 10 | plugins: ['react-refresh'], 11 | rules: { 12 | 'react-refresh/only-export-components': 'warn', 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import path from 'path'; 3 | import dts from 'vite-plugin-dts'; 4 | 5 | export default defineConfig({ 6 | build: { 7 | lib: { 8 | entry: path.resolve(__dirname, 'src/lib/index.ts'), 9 | name: 'ReactHooksPlus', 10 | fileName: 'react-hooks-plus', 11 | }, 12 | rollupOptions: { 13 | external: ['react', 'react-dom'], 14 | output: { 15 | globals: { 16 | react: 'React', 17 | }, 18 | }, 19 | }, 20 | }, 21 | plugins: [dts()], 22 | }); 23 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | text-decoration: none; 5 | outline: none; 6 | border: none; 7 | } 8 | 9 | html { 10 | font-family: sans-serif; 11 | background-color: #212121; 12 | } 13 | 14 | button { 15 | width: 15rem; 16 | cursor: pointer; 17 | padding: 15px 25px; 18 | border-radius: 5px; 19 | background-color: #373737; 20 | color: #eee; 21 | transition: 200ms; 22 | } 23 | 24 | button:hover { 25 | opacity: 0.7; 26 | } 27 | 28 | button:active { 29 | transform: scale(0.95, 0.95); 30 | } 31 | 32 | p { 33 | font-size: 0.8rem; 34 | color: #ffc505; 35 | } 36 | -------------------------------------------------------------------------------- /src/lib/hooks/useOnScreen.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | function useOnScreen(ref: React.RefObject): boolean { 4 | const [isIntersecting, setIntersecting] = useState(false); 5 | 6 | useEffect(() => { 7 | const observer = new IntersectionObserver(([entry]) => { 8 | setIntersecting(entry.isIntersecting); 9 | }); 10 | 11 | if (ref.current) { 12 | observer.observe(ref.current); 13 | } 14 | 15 | return () => { 16 | observer.disconnect(); 17 | }; 18 | }, [ref]); 19 | 20 | return isIntersecting; 21 | } 22 | 23 | export default useOnScreen; 24 | -------------------------------------------------------------------------------- /src/lib/hooks/useScroll.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | interface PositionState { 4 | x: number; 5 | y: number; 6 | } 7 | 8 | const useScroll = (): PositionState => { 9 | const [position, setPosition] = useState({ x: 0, y: 0 }); 10 | 11 | useEffect(() => { 12 | const handleScroll = (): void => { 13 | setPosition({ x: window.scrollX, y: window.scrollY }); 14 | }; 15 | 16 | document.addEventListener('scroll', handleScroll); 17 | 18 | return (): void => document.removeEventListener('scroll', handleScroll); 19 | }, []); 20 | 21 | return position; 22 | }; 23 | 24 | export default useScroll; 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "module": "ESNext", 6 | "skipLibCheck": true, 7 | 8 | /* Bundler mode */ 9 | "moduleResolution": "bundler", 10 | "allowImportingTsExtensions": true, 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "noEmit": true, 14 | "jsx": "react-jsx", 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true 21 | }, 22 | "include": ["src"], 23 | "references": [{ "path": "./tsconfig.node.json" }] 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/hooks/useMedia.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | function useMedia(queries: string[], values: T[], defaultValue: T) { 4 | const mediaQueryLists = queries.map((q) => window.matchMedia(q)); 5 | const getValue = () => { 6 | const index = mediaQueryLists.findIndex((mql) => mql.matches); 7 | return typeof values[index] !== 'undefined' ? values[index] : defaultValue; 8 | }; 9 | const [value, setValue] = useState(getValue); 10 | 11 | useEffect(() => { 12 | const handler = () => setValue(getValue); 13 | mediaQueryLists.forEach((mql) => mql.addListener(handler)); 14 | return () => mediaQueryLists.forEach((mql) => mql.removeListener(handler)); 15 | }, []); 16 | 17 | return value; 18 | } 19 | 20 | export default useMedia; 21 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /src/lib/hooks/useScreen.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | interface ScreenDimensions { 4 | width: number; 5 | height: number; 6 | } 7 | const useScreen = (): ScreenDimensions => { 8 | const [screenDimensions, setScreenDimensions] = useState({ 9 | width: window.innerWidth, 10 | height: window.innerHeight, 11 | }); 12 | 13 | useEffect(() => { 14 | const handleResize = () => { 15 | setScreenDimensions({ 16 | width: window.innerWidth, 17 | height: window.innerHeight, 18 | }); 19 | }; 20 | 21 | window.addEventListener('resize', handleResize); 22 | 23 | return () => { 24 | window.removeEventListener('resize', handleResize); 25 | }; 26 | }, []); 27 | 28 | return screenDimensions; 29 | }; 30 | 31 | export default useScreen; 32 | -------------------------------------------------------------------------------- /src/lib/hooks/useOnClickOutside.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, RefObject } from 'react'; 2 | 3 | function useOnClickOutside( 4 | ref: RefObject, 5 | handler: (event: MouseEvent | TouchEvent) => void 6 | ) { 7 | useEffect(() => { 8 | const listener = (event: MouseEvent | TouchEvent) => { 9 | const el = ref?.current; 10 | if (!el || el.contains((event?.target as Node) || null)) { 11 | return; 12 | } 13 | handler(event); 14 | }; 15 | document.addEventListener('mousedown', listener); 16 | document.addEventListener('touchstart', listener); 17 | return () => { 18 | document.removeEventListener('mousedown', listener); 19 | document.removeEventListener('touchstart', listener); 20 | }; 21 | }, [ref, handler]); 22 | } 23 | 24 | export default useOnClickOutside; 25 | -------------------------------------------------------------------------------- /src/lib/hooks/useLocalStorage.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | function useLocalStorage(key: string, initialValue: any) { 4 | const [storedValue, setStoredValue] = useState(() => { 5 | try { 6 | const item = window.localStorage.getItem(key); 7 | return item ? JSON.parse(item) : initialValue; 8 | } catch (error) { 9 | console.log(error); 10 | return initialValue; 11 | } 12 | }); 13 | 14 | const setValue = (value: any) => { 15 | try { 16 | const valueToStore = 17 | value instanceof Function ? value(storedValue) : value; 18 | setStoredValue(valueToStore); 19 | window.localStorage.setItem(key, JSON.stringify(valueToStore)); 20 | } catch (error) { 21 | console.log(error); 22 | } 23 | }; 24 | 25 | return [storedValue, setValue]; 26 | } 27 | 28 | export default useLocalStorage; 29 | -------------------------------------------------------------------------------- /src/lib/hooks/useHover.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useRef } from 'react'; 2 | 3 | function useHover(): [React.MutableRefObject, boolean] { 4 | const [isHovered, setIsHovered] = useState(false); 5 | const ref = useRef(null); 6 | 7 | function handleMouseOver() { 8 | setIsHovered(true); 9 | } 10 | 11 | function handleMouseOut() { 12 | setIsHovered(false); 13 | } 14 | 15 | useEffect(() => { 16 | const node = ref.current; 17 | if (node) { 18 | node.addEventListener('mouseover', handleMouseOver); 19 | node.addEventListener('mouseout', handleMouseOut); 20 | 21 | return () => { 22 | node.removeEventListener('mouseover', handleMouseOver); 23 | node.removeEventListener('mouseout', handleMouseOut); 24 | }; 25 | } 26 | }, [ref.current]); 27 | 28 | return [ref, isHovered]; 29 | } 30 | 31 | export default useHover; 32 | -------------------------------------------------------------------------------- /src/lib/hooks/useConsole/useConTime.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | import { setPrecisionForDecimal } from '../..'; 3 | 4 | type Time = { 5 | start: number; 6 | end: number; 7 | }; 8 | 9 | const useConTime = (text: string = 'Time Taken: ') => { 10 | const [time, setTime] = useState