react-essentials-functions 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,20 +2,82 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useTheme = void 0;
4
4
  const react_1 = require("react");
5
+ const STORAGE_KEY = 'theme';
6
+ function getSystemTheme() {
7
+ if (typeof window === 'undefined') {
8
+ return 'light';
9
+ }
10
+ try {
11
+ return window.matchMedia('(prefers-color-scheme: dark)').matches
12
+ ? 'dark'
13
+ : 'light';
14
+ }
15
+ catch {
16
+ return 'light';
17
+ }
18
+ }
19
+ function getStoredTheme() {
20
+ try {
21
+ const stored = window.localStorage.getItem(STORAGE_KEY);
22
+ if (stored === 'light' || stored === 'dark') {
23
+ return stored;
24
+ }
25
+ return null;
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ /**
32
+ * Hook to manage theme (light/dark) with localStorage persistence.
33
+ * Automatically detects system color scheme preference.
34
+ *
35
+ * @returns A tuple containing:
36
+ * - current theme mode ('light' | 'dark')
37
+ * - function to toggle between themes
38
+ * - boolean indicating if the component is mounted (useful for SSR)
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * const [theme, toggleTheme, mounted] = useTheme();
43
+ *
44
+ * if (!mounted) return null; // Avoid hydration mismatch
45
+ *
46
+ * return (
47
+ * <button onClick={toggleTheme}>
48
+ * Switch to {theme === 'light' ? 'dark' : 'light'} mode
49
+ * </button>
50
+ * );
51
+ * ```
52
+ */
5
53
  const useTheme = () => {
6
54
  const [theme, setTheme] = (0, react_1.useState)('light');
7
55
  const [mountedComponent, setMountedComponent] = (0, react_1.useState)(false);
8
- const setMode = (mode) => {
9
- window.localStorage.setItem('theme', mode);
10
- setTheme(mode);
11
- };
12
- const themeToggler = () => {
13
- theme === 'light' ? setMode('dark') : setMode('light');
14
- };
56
+ const themeToggler = (0, react_1.useCallback)(() => {
57
+ setTheme((prevTheme) => {
58
+ const newTheme = prevTheme === 'light' ? 'dark' : 'light';
59
+ try {
60
+ window.localStorage.setItem(STORAGE_KEY, newTheme);
61
+ }
62
+ catch {
63
+ // localStorage may not be available
64
+ }
65
+ return newTheme;
66
+ });
67
+ }, []);
15
68
  (0, react_1.useEffect)(() => {
16
- const localTheme = window.localStorage.getItem('theme');
17
- localTheme ? setTheme(localTheme) : setMode('light');
18
69
  setMountedComponent(true);
70
+ const stored = getStoredTheme();
71
+ const initial = stored ?? getSystemTheme();
72
+ setTheme(initial);
73
+ if (!stored) {
74
+ try {
75
+ window.localStorage.setItem(STORAGE_KEY, initial);
76
+ }
77
+ catch {
78
+ // localStorage may not be available
79
+ }
80
+ }
19
81
  }, []);
20
82
  return [theme, themeToggler, mountedComponent];
21
83
  };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Hook for managing a boolean toggle state.
3
+ * Provides a simple API for toggling, setting true, or setting false.
4
+ *
5
+ * @param initialValue - The initial boolean value (default: false)
6
+ * @returns A tuple of [value, toggle, setTrue, setFalse]
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const [isOpen, toggleOpen, open, close] = useToggle(false);
11
+ *
12
+ * return (
13
+ * <div>
14
+ * <button onClick={toggleOpen}>Toggle</button>
15
+ * <button onClick={open}>Open</button>
16
+ * <button onClick={close}>Close</button>
17
+ * {isOpen && <Modal />}
18
+ * </div>
19
+ * );
20
+ * ```
21
+ */
22
+ export declare function useToggle(initialValue?: boolean): [boolean, () => void, () => void, () => void];
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useToggle = useToggle;
4
+ const react_1 = require("react");
5
+ /**
6
+ * Hook for managing a boolean toggle state.
7
+ * Provides a simple API for toggling, setting true, or setting false.
8
+ *
9
+ * @param initialValue - The initial boolean value (default: false)
10
+ * @returns A tuple of [value, toggle, setTrue, setFalse]
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const [isOpen, toggleOpen, open, close] = useToggle(false);
15
+ *
16
+ * return (
17
+ * <div>
18
+ * <button onClick={toggleOpen}>Toggle</button>
19
+ * <button onClick={open}>Open</button>
20
+ * <button onClick={close}>Close</button>
21
+ * {isOpen && <Modal />}
22
+ * </div>
23
+ * );
24
+ * ```
25
+ */
26
+ function useToggle(initialValue = false) {
27
+ const [value, setValue] = (0, react_1.useState)(initialValue);
28
+ const toggle = (0, react_1.useCallback)(() => {
29
+ setValue((prev) => !prev);
30
+ }, []);
31
+ const setTrue = (0, react_1.useCallback)(() => {
32
+ setValue(true);
33
+ }, []);
34
+ const setFalse = (0, react_1.useCallback)(() => {
35
+ setValue(false);
36
+ }, []);
37
+ return [value, toggle, setTrue, setFalse];
38
+ }
@@ -1,4 +1,23 @@
1
- export default function useWindowDimensions(): {
2
- width: number | null;
3
- height: number | null;
1
+ export type WindowDimensions = {
2
+ width: number;
3
+ height: number;
4
4
  };
5
+ /**
6
+ * Hook to get the current window dimensions.
7
+ * Automatically updates on resize with debounce for performance.
8
+ *
9
+ * @returns Object containing width and height of the window
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * const { width, height } = useWindowDimensions();
14
+ *
15
+ * return (
16
+ * <div>
17
+ * Window size: {width} x {height}
18
+ * </div>
19
+ * );
20
+ * ```
21
+ */
22
+ export declare function useWindowDimensions(): WindowDimensions;
23
+ export default useWindowDimensions;
@@ -1,27 +1,46 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useWindowDimensions = useWindowDimensions;
3
4
  const react_1 = require("react");
5
+ /**
6
+ * Hook to get the current window dimensions.
7
+ * Automatically updates on resize with debounce for performance.
8
+ *
9
+ * @returns Object containing width and height of the window
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * const { width, height } = useWindowDimensions();
14
+ *
15
+ * return (
16
+ * <div>
17
+ * Window size: {width} x {height}
18
+ * </div>
19
+ * );
20
+ * ```
21
+ */
4
22
  function useWindowDimensions() {
5
- const hasWindow = typeof window !== 'undefined';
6
- function getWindowDimensions() {
7
- const width = hasWindow ? window.innerWidth : null;
8
- const height = hasWindow ? window.innerHeight : null;
23
+ const getWindowDimensions = (0, react_1.useCallback)(() => {
24
+ if (typeof window === 'undefined') {
25
+ return { width: 0, height: 0 };
26
+ }
9
27
  return {
10
- width,
11
- height,
28
+ width: window.innerWidth,
29
+ height: window.innerHeight,
12
30
  };
13
- }
14
- const [windowDimensions, setWindowDimensions] = (0, react_1.useState)(getWindowDimensions());
31
+ }, []);
32
+ const [windowDimensions, setWindowDimensions] = (0, react_1.useState)(getWindowDimensions);
15
33
  (0, react_1.useEffect)(() => {
16
- if (hasWindow) {
17
- // eslint-disable-next-line no-inner-declarations
18
- function handleResize() {
19
- setWindowDimensions(getWindowDimensions());
20
- }
21
- window.addEventListener('resize', handleResize);
22
- return () => window.removeEventListener('resize', handleResize);
34
+ if (typeof window === 'undefined') {
35
+ return;
23
36
  }
24
- }, [hasWindow]);
37
+ const handleResize = () => {
38
+ setWindowDimensions(getWindowDimensions());
39
+ };
40
+ window.addEventListener('resize', handleResize);
41
+ return () => window.removeEventListener('resize', handleResize);
42
+ }, [getWindowDimensions]);
25
43
  return windowDimensions;
26
44
  }
45
+ // Keep default export for backwards compatibility
27
46
  exports.default = useWindowDimensions;
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "react-essentials-functions",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "author": "maxgfr",
5
5
  "license": "MIT",
6
- "description": "A collection of zero-dependencies useful hooks and components for React",
6
+ "description": "A collection of zero-dependency useful hooks and components for React",
7
7
  "main": "./build/index.js",
8
+ "types": "./build/index.d.ts",
8
9
  "repository": {
9
10
  "type": "git",
10
11
  "url": "https://github.com/maxgfr/react-essentials-functions.git"
@@ -24,73 +25,52 @@
24
25
  "zero-dependencies"
25
26
  ],
26
27
  "scripts": {
27
- "dev": "nodemon",
28
- "develop": "node -r @swc-node/register ./src/index.ts",
29
- "test": "jest --passWithNoTests",
28
+ "test": "jest",
30
29
  "test:watch": "jest --watch",
30
+ "test:coverage": "jest --coverage",
31
31
  "clean": "rimraf build",
32
32
  "build": "tsc -p tsconfig.build.json",
33
33
  "build:watch": "tsc -w -p tsconfig.build.json",
34
- "build:swc": "swc ./src -d build",
35
- "build:swc:watch": "swc ./src -d build -w",
34
+ "typecheck": "tsc -p tsconfig.build.json --noEmit",
36
35
  "lint": "eslint ./src --ext .ts,.tsx",
37
- "prettier": "prettier --write './src/**/*.{ts,js,json,tsx,jsx}'",
38
- "storybook:start": "start-storybook -p 6006",
39
- "storybook:build": "build-storybook",
40
- "storybook:deploy": "npm run storybook:build && gh-pages -d storybook-static",
41
- "storybook:deploy:action": "npm run storybook:build && gh-pages -d storybook-static -u \"github-actions-bot <support+actions@github.com>\"",
36
+ "format": "prettier --write './src/**/*.{ts,js,json,tsx,jsx}'",
37
+ "format:check": "prettier --check './src/**/*.{ts,js,json,tsx,jsx}'",
38
+ "ci": "pnpm run typecheck && pnpm run lint && pnpm run format:check && pnpm run test:coverage",
42
39
  "release": "semantic-release"
43
40
  },
44
41
  "peerDependencies": {
45
- "react": "*",
46
- "react-dom": "*"
42
+ "react": ">=17.0.0",
43
+ "react-dom": ">=17.0.0"
47
44
  },
48
45
  "devDependencies": {
49
- "@babel/core": "^7.20.2",
50
- "@semantic-release/changelog": "^6.0.2",
51
- "@semantic-release/commit-analyzer": "^9.0.2",
46
+ "@semantic-release/commit-analyzer": "^13.0.1",
52
47
  "@semantic-release/git": "^10.0.1",
53
- "@semantic-release/github": "^8.0.6",
54
- "@semantic-release/npm": "^9.0.1",
55
- "@semantic-release/release-notes-generator": "^10.0.3",
56
- "@storybook/addon-actions": "^6.5.13",
57
- "@storybook/addon-essentials": "^6.5.13",
58
- "@storybook/addon-interactions": "^6.5.13",
59
- "@storybook/addon-links": "^6.5.13",
60
- "@storybook/builder-webpack4": "^6.5.13",
61
- "@storybook/manager-webpack4": "^6.5.13",
62
- "@storybook/react": "^6.5.13",
63
- "@storybook/testing-library": "^0.0.13",
64
- "@swc-node/register": "1.5.4",
65
- "@swc/cli": "0.1.57",
66
- "@swc/core": "1.3.19",
67
- "@swc/jest": "0.2.23",
68
- "@swc/plugin-styled-jsx": "^1.5.20",
69
- "@testing-library/jest-dom": "^5.16.5",
70
- "@testing-library/react": "^13.4.0",
71
- "@testing-library/user-event": "^14.4.3",
72
- "@types/jest": "29.2.3",
73
- "@types/node": "18.11.9",
74
- "@types/react": "^18.0.25",
75
- "@types/react-dom": "^18.0.8",
76
- "@typescript-eslint/eslint-plugin": "5.43.0",
77
- "@typescript-eslint/parser": "5.43.0",
78
- "babel-loader": "^9.0.0",
79
- "eslint": "8.28.0",
80
- "eslint-config-prettier": "8.5.0",
81
- "eslint-plugin-jest": "27.1.5",
82
- "eslint-plugin-jsx-a11y": "^6.6.1",
83
- "eslint-plugin-react": "^7.31.10",
84
- "eslint-plugin-storybook": "^0.6.7",
85
- "gh-pages": "^4.0.0",
86
- "jest": "29.3.1",
87
- "jest-environment-jsdom": "^29.3.0",
88
- "nodemon": "2.0.20",
89
- "prettier": "2.7.1",
90
- "react": "^18.2.0",
91
- "react-dom": "^18.2.0",
92
- "rimraf": "3.0.2",
93
- "semantic-release": "^19.0.5",
94
- "typescript": "^4.8.4"
48
+ "@semantic-release/github": "^12.0.5",
49
+ "@semantic-release/npm": "^13.1.4",
50
+ "@semantic-release/release-notes-generator": "^14.1.0",
51
+ "@testing-library/jest-dom": "^6.9.1",
52
+ "@testing-library/react": "^16.3.2",
53
+ "@testing-library/user-event": "^14.6.1",
54
+ "@types/jest": "^29.5.14",
55
+ "@types/node": "^22.19.9",
56
+ "@types/react": "^18.3.28",
57
+ "@types/react-dom": "^18.3.7",
58
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
59
+ "@typescript-eslint/parser": "^8.54.0",
60
+ "eslint": "^8.57.1",
61
+ "eslint-config-prettier": "^10.1.8",
62
+ "eslint-plugin-jest": "^28.14.0",
63
+ "eslint-plugin-jsx-a11y": "^6.10.2",
64
+ "eslint-plugin-react": "^7.37.5",
65
+ "eslint-plugin-react-hooks": "^5.2.0",
66
+ "jest": "^29.7.0",
67
+ "jest-environment-jsdom": "^29.7.0",
68
+ "prettier": "^3.8.1",
69
+ "react": "^18.3.1",
70
+ "react-dom": "^18.3.1",
71
+ "rimraf": "^6.1.2",
72
+ "semantic-release": "^25.0.3",
73
+ "ts-jest": "^29.4.6",
74
+ "typescript": "^5.9.3"
95
75
  }
96
76
  }