├── 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