@cleen/ui-core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -0
- package/dist/index.d.ts +258 -0
- package/dist/index.js +758 -0
- package/dist/styles.css +4560 -0
- package/package.json +42 -42
- package/tailwind-entry.css +81 -81
- package/tailwind.config.js +10 -10
- package/tailwind.preset.js +30 -30
- package/src/hooks/useAnimateNumber.ts +0 -56
- package/src/hooks/useControlled.ts +0 -40
- package/src/hooks/useDebounce.ts +0 -17
- package/src/hooks/useDisclosure.ts +0 -33
- package/src/hooks/useForm.ts +0 -38
- package/src/hooks/useOutsideClick.ts +0 -42
- package/src/hooks/usePaginationState.ts +0 -39
- package/src/hooks/usePositionClose.ts +0 -69
- package/src/hooks/useValidation.ts +0 -33
- package/src/hooks/useWatchResize.ts +0 -52
- package/src/index.ts +0 -21
- package/src/store/colors.ts +0 -98
- package/src/types/position.ts +0 -9
- package/src/types/styles.ts +0 -24
- package/src/types/utils.ts +0 -6
- package/src/utils/audio.ts +0 -69
- package/src/utils/cn.ts +0 -13
- package/src/utils/colors.ts +0 -159
- package/src/utils/images.ts +0 -42
- package/src/utils/object.ts +0 -86
- package/src/utils/position.ts +0 -140
- package/src/utils/string.ts +0 -27
- package/styles/react-day-styles.css +0 -457
- package/tsconfig.json +0 -27
- package/tsup.config.ts +0 -10
package/package.json
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@cleen/ui-core",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"private": false,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./dist/index.js",
|
|
7
|
-
"module": "./dist/index.js",
|
|
8
|
-
"types": "./dist/index.d.ts",
|
|
9
|
-
"exports": {
|
|
10
|
-
".": {
|
|
11
|
-
"import": "./dist/index.js",
|
|
12
|
-
"types": "./dist/index.d.ts"
|
|
13
|
-
},
|
|
14
|
-
"./tailwind-preset": "./tailwind.preset.js",
|
|
15
|
-
"./tailwind-entry.css": "./tailwind-entry.css",
|
|
16
|
-
"./styles.css": "./dist/styles.css"
|
|
17
|
-
},
|
|
18
|
-
"scripts": {
|
|
19
|
-
"dev": "concurrently \"tsup --watch --onSuccess 'tsc'\" \"tailwindcss -w -c ./tailwind.config.js -i ./tailwind-entry.css -o ./dist/styles.css\"",
|
|
20
|
-
"build": "tsup && tailwindcss -m -c ./tailwind.config.js -i ./tailwind-entry.css -o ./dist/styles.css",
|
|
21
|
-
"typecheck": "tsc --noEmit",
|
|
22
|
-
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
23
|
-
"format": "prettier --write \"src/**/*.{ts,tsx}\""
|
|
24
|
-
},
|
|
25
|
-
"dependencies": {
|
|
26
|
-
"clsx": "^2.1.1",
|
|
27
|
-
"color2k": "^2.0.3",
|
|
28
|
-
"tailwind-merge": "^2.6.0",
|
|
29
|
-
"zustand": "^5.0.8"
|
|
30
|
-
},
|
|
31
|
-
"peerDependencies": {
|
|
32
|
-
"react": "^18.3.1",
|
|
33
|
-
"react-dom": "^18.3.1"
|
|
34
|
-
},
|
|
35
|
-
"devDependencies": {
|
|
36
|
-
"@types/react": "^19.1.10",
|
|
37
|
-
"concurrently": "^9.2.1",
|
|
38
|
-
"tailwindcss": "^3.4.17",
|
|
39
|
-
"tsup": "^8.5.1",
|
|
40
|
-
"typescript": "~5.8.3"
|
|
41
|
-
}
|
|
42
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@cleen/ui-core",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./tailwind-preset": "./tailwind.preset.js",
|
|
15
|
+
"./tailwind-entry.css": "./tailwind-entry.css",
|
|
16
|
+
"./styles.css": "./dist/styles.css"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "concurrently \"tsup --watch --onSuccess 'tsc'\" \"tailwindcss -w -c ./tailwind.config.js -i ./tailwind-entry.css -o ./dist/styles.css\"",
|
|
20
|
+
"build": "tsup && tailwindcss -m -c ./tailwind.config.js -i ./tailwind-entry.css -o ./dist/styles.css",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
23
|
+
"format": "prettier --write \"src/**/*.{ts,tsx}\""
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"clsx": "^2.1.1",
|
|
27
|
+
"color2k": "^2.0.3",
|
|
28
|
+
"tailwind-merge": "^2.6.0",
|
|
29
|
+
"zustand": "^5.0.8"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"react": "^18.3.1",
|
|
33
|
+
"react-dom": "^18.3.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/react": "^19.1.10",
|
|
37
|
+
"concurrently": "^9.2.1",
|
|
38
|
+
"tailwindcss": "^3.4.17",
|
|
39
|
+
"tsup": "^8.5.1",
|
|
40
|
+
"typescript": "~5.8.3"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/tailwind-entry.css
CHANGED
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
@import 'react-toastify/dist/ReactToastify.css';
|
|
2
|
-
@import './styles/react-day-styles.css';
|
|
3
|
-
|
|
4
|
-
@tailwind base;
|
|
5
|
-
@tailwind components;
|
|
6
|
-
@tailwind utilities;
|
|
7
|
-
|
|
8
|
-
/* Hide scrollbar for Chrome, Safari and Opera */
|
|
9
|
-
.cleen-no-scrollbar::-webkit-scrollbar {
|
|
10
|
-
display: none;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/* Hide scrollbar for IE, Edge and Firefox */
|
|
14
|
-
.cleen-no-scrollbar {
|
|
15
|
-
-ms-overflow-style: none; /* IE and Edge */
|
|
16
|
-
scrollbar-width: none; /* Firefox */
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
:root {
|
|
20
|
-
--cleen-white: 255, 255, 255;
|
|
21
|
-
--cleen-black: 0, 0, 0;
|
|
22
|
-
--cleen-gray: 38, 38, 38;
|
|
23
|
-
--cleen-pink: 193, 21, 116;
|
|
24
|
-
--cleen-purple: 89, 37, 220;
|
|
25
|
-
--cleen-indigo: 53, 56, 205;
|
|
26
|
-
--cleen-blue: 23, 92, 211;
|
|
27
|
-
--cleen-light-gray: 200, 200, 200;
|
|
28
|
-
|
|
29
|
-
--cleen-primary: 0, 133, 211;
|
|
30
|
-
--cleen-success: 6, 118, 71;
|
|
31
|
-
--cleen-warning: 181, 71, 8;
|
|
32
|
-
--cleen-error: 180, 35, 24;
|
|
33
|
-
|
|
34
|
-
--cleen-brand: 68, 129, 193;
|
|
35
|
-
--cleen-sidebar: 249, 250, 251;
|
|
36
|
-
--cleen-background: 255, 255, 255;
|
|
37
|
-
--cleen-accent: 65, 70, 81;
|
|
38
|
-
|
|
39
|
-
--toastify-color-light: rgb(var(--cleen-background));
|
|
40
|
-
--toastify-text-color-light: rgba(var(--cleen-gray), 0.6);
|
|
41
|
-
--toastify-toast-shadow: 0px 4px 12px rgba(var(--cleen-black), 0.1);
|
|
42
|
-
|
|
43
|
-
--toastify-color-info: rgb(var(--cleen-primary));
|
|
44
|
-
--toastify-color-success: rgb(var(--cleen-success));
|
|
45
|
-
--toastify-color-warning: rgb(var(--cleen-warning));
|
|
46
|
-
--toastify-color-error: rgb(var(--cleen-error));
|
|
47
|
-
|
|
48
|
-
.Toastify__close-button {
|
|
49
|
-
color: rgb(var(--cleen-black));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.rdp-root {
|
|
53
|
-
--rdp-accent-color: rgb(var(--cleen-primary));
|
|
54
|
-
--rdp-accent-background-color: rgb(var(--cleen-background));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.cleen-waveform ::part(region-handle) {
|
|
58
|
-
border-color: rgb(var(--cleen-black)) !important;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.dark {
|
|
63
|
-
--cleen-white: 3, 7, 18;
|
|
64
|
-
--cleen-black: 243, 244, 246;
|
|
65
|
-
--cleen-gray: 209, 213, 219;
|
|
66
|
-
--cleen-pink: 236, 72, 153;
|
|
67
|
-
--cleen-purple: 168, 85, 247;
|
|
68
|
-
--cleen-indigo: 99, 102, 241;
|
|
69
|
-
--cleen-blue: 59, 130, 246;
|
|
70
|
-
--cleen-light-gray: 100, 100, 100;
|
|
71
|
-
|
|
72
|
-
--cleen-primary: 0, 157, 248;
|
|
73
|
-
--cleen-success: 34, 197, 94;
|
|
74
|
-
--cleen-warning: 251, 191, 36;
|
|
75
|
-
--cleen-error: 239, 68, 68;
|
|
76
|
-
|
|
77
|
-
--cleen-brand: 0, 122, 204;
|
|
78
|
-
--cleen-sidebar: 17, 24, 39;
|
|
79
|
-
--cleen-background: 3, 7, 18;
|
|
80
|
-
--cleen-accent: 255, 255, 255;
|
|
81
|
-
}
|
|
1
|
+
@import 'react-toastify/dist/ReactToastify.css';
|
|
2
|
+
@import './styles/react-day-styles.css';
|
|
3
|
+
|
|
4
|
+
@tailwind base;
|
|
5
|
+
@tailwind components;
|
|
6
|
+
@tailwind utilities;
|
|
7
|
+
|
|
8
|
+
/* Hide scrollbar for Chrome, Safari and Opera */
|
|
9
|
+
.cleen-no-scrollbar::-webkit-scrollbar {
|
|
10
|
+
display: none;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* Hide scrollbar for IE, Edge and Firefox */
|
|
14
|
+
.cleen-no-scrollbar {
|
|
15
|
+
-ms-overflow-style: none; /* IE and Edge */
|
|
16
|
+
scrollbar-width: none; /* Firefox */
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
:root {
|
|
20
|
+
--cleen-white: 255, 255, 255;
|
|
21
|
+
--cleen-black: 0, 0, 0;
|
|
22
|
+
--cleen-gray: 38, 38, 38;
|
|
23
|
+
--cleen-pink: 193, 21, 116;
|
|
24
|
+
--cleen-purple: 89, 37, 220;
|
|
25
|
+
--cleen-indigo: 53, 56, 205;
|
|
26
|
+
--cleen-blue: 23, 92, 211;
|
|
27
|
+
--cleen-light-gray: 200, 200, 200;
|
|
28
|
+
|
|
29
|
+
--cleen-primary: 0, 133, 211;
|
|
30
|
+
--cleen-success: 6, 118, 71;
|
|
31
|
+
--cleen-warning: 181, 71, 8;
|
|
32
|
+
--cleen-error: 180, 35, 24;
|
|
33
|
+
|
|
34
|
+
--cleen-brand: 68, 129, 193;
|
|
35
|
+
--cleen-sidebar: 249, 250, 251;
|
|
36
|
+
--cleen-background: 255, 255, 255;
|
|
37
|
+
--cleen-accent: 65, 70, 81;
|
|
38
|
+
|
|
39
|
+
--toastify-color-light: rgb(var(--cleen-background));
|
|
40
|
+
--toastify-text-color-light: rgba(var(--cleen-gray), 0.6);
|
|
41
|
+
--toastify-toast-shadow: 0px 4px 12px rgba(var(--cleen-black), 0.1);
|
|
42
|
+
|
|
43
|
+
--toastify-color-info: rgb(var(--cleen-primary));
|
|
44
|
+
--toastify-color-success: rgb(var(--cleen-success));
|
|
45
|
+
--toastify-color-warning: rgb(var(--cleen-warning));
|
|
46
|
+
--toastify-color-error: rgb(var(--cleen-error));
|
|
47
|
+
|
|
48
|
+
.Toastify__close-button {
|
|
49
|
+
color: rgb(var(--cleen-black));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.rdp-root {
|
|
53
|
+
--rdp-accent-color: rgb(var(--cleen-primary));
|
|
54
|
+
--rdp-accent-background-color: rgb(var(--cleen-background));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.cleen-waveform ::part(region-handle) {
|
|
58
|
+
border-color: rgb(var(--cleen-black)) !important;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.dark {
|
|
63
|
+
--cleen-white: 3, 7, 18;
|
|
64
|
+
--cleen-black: 243, 244, 246;
|
|
65
|
+
--cleen-gray: 209, 213, 219;
|
|
66
|
+
--cleen-pink: 236, 72, 153;
|
|
67
|
+
--cleen-purple: 168, 85, 247;
|
|
68
|
+
--cleen-indigo: 99, 102, 241;
|
|
69
|
+
--cleen-blue: 59, 130, 246;
|
|
70
|
+
--cleen-light-gray: 100, 100, 100;
|
|
71
|
+
|
|
72
|
+
--cleen-primary: 0, 157, 248;
|
|
73
|
+
--cleen-success: 34, 197, 94;
|
|
74
|
+
--cleen-warning: 251, 191, 36;
|
|
75
|
+
--cleen-error: 239, 68, 68;
|
|
76
|
+
|
|
77
|
+
--cleen-brand: 0, 122, 204;
|
|
78
|
+
--cleen-sidebar: 17, 24, 39;
|
|
79
|
+
--cleen-background: 3, 7, 18;
|
|
80
|
+
--cleen-accent: 255, 255, 255;
|
|
81
|
+
}
|
package/tailwind.config.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import preset from './tailwind.preset.js';
|
|
2
|
-
|
|
3
|
-
/** @type {import('tailwindcss').Config} */
|
|
4
|
-
export default {
|
|
5
|
-
presets: [preset],
|
|
6
|
-
content: [
|
|
7
|
-
'../cleen-ui-free/src/**/*.{ts,tsx,js,jsx}',
|
|
8
|
-
'../cleen-ui-pro/src/**/*.{ts,tsx,js,jsx}',
|
|
9
|
-
],
|
|
10
|
-
};
|
|
1
|
+
import preset from './tailwind.preset.js';
|
|
2
|
+
|
|
3
|
+
/** @type {import('tailwindcss').Config} */
|
|
4
|
+
export default {
|
|
5
|
+
presets: [preset],
|
|
6
|
+
content: [
|
|
7
|
+
'../cleen-ui-free/src/**/*.{ts,tsx,js,jsx}',
|
|
8
|
+
'../cleen-ui-pro/src/**/*.{ts,tsx,js,jsx}',
|
|
9
|
+
],
|
|
10
|
+
};
|
package/tailwind.preset.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
/** @type {import('tailwindcss').Config} */
|
|
2
|
-
export default {
|
|
3
|
-
important: '.cleen',
|
|
4
|
-
prefix: 'cleen-',
|
|
5
|
-
darkMode: ['class', '[class*="dark"]'],
|
|
6
|
-
theme: {
|
|
7
|
-
extend: {
|
|
8
|
-
colors: {
|
|
9
|
-
white: 'rgba(var(--cleen-white))',
|
|
10
|
-
black: 'rgba(var(--cleen-black))',
|
|
11
|
-
gray: 'rgba(var(--cleen-gray))',
|
|
12
|
-
pink: 'rgba(var(--cleen-pink))',
|
|
13
|
-
purple: 'rgba(var(--cleen-purple))',
|
|
14
|
-
indigo: 'rgba(var(--cleen-indigo))',
|
|
15
|
-
blue: 'rgba(var(--cleen-blue))',
|
|
16
|
-
|
|
17
|
-
primary: 'rgba(var(--cleen-primary))',
|
|
18
|
-
success: 'rgba(var(--cleen-success))',
|
|
19
|
-
warning: 'rgba(var(--cleen-warning))',
|
|
20
|
-
error: 'rgba(var(--cleen-error))',
|
|
21
|
-
|
|
22
|
-
brand: 'rgba(var(--cleen-brand))',
|
|
23
|
-
sidebar: 'rgba(var(--cleen-sidebar))',
|
|
24
|
-
background: 'rgba(var(--cleen-background))',
|
|
25
|
-
accent: 'rgba(var(--cleen-accent))',
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
plugins: [],
|
|
30
|
-
};
|
|
1
|
+
/** @type {import('tailwindcss').Config} */
|
|
2
|
+
export default {
|
|
3
|
+
important: '.cleen',
|
|
4
|
+
prefix: 'cleen-',
|
|
5
|
+
darkMode: ['class', '[class*="dark"]'],
|
|
6
|
+
theme: {
|
|
7
|
+
extend: {
|
|
8
|
+
colors: {
|
|
9
|
+
white: 'rgba(var(--cleen-white))',
|
|
10
|
+
black: 'rgba(var(--cleen-black))',
|
|
11
|
+
gray: 'rgba(var(--cleen-gray))',
|
|
12
|
+
pink: 'rgba(var(--cleen-pink))',
|
|
13
|
+
purple: 'rgba(var(--cleen-purple))',
|
|
14
|
+
indigo: 'rgba(var(--cleen-indigo))',
|
|
15
|
+
blue: 'rgba(var(--cleen-blue))',
|
|
16
|
+
|
|
17
|
+
primary: 'rgba(var(--cleen-primary))',
|
|
18
|
+
success: 'rgba(var(--cleen-success))',
|
|
19
|
+
warning: 'rgba(var(--cleen-warning))',
|
|
20
|
+
error: 'rgba(var(--cleen-error))',
|
|
21
|
+
|
|
22
|
+
brand: 'rgba(var(--cleen-brand))',
|
|
23
|
+
sidebar: 'rgba(var(--cleen-sidebar))',
|
|
24
|
+
background: 'rgba(var(--cleen-background))',
|
|
25
|
+
accent: 'rgba(var(--cleen-accent))',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
plugins: [],
|
|
30
|
+
};
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
|
-
|
|
3
|
-
interface UseAnimateNumberProps {
|
|
4
|
-
targetNumber: number;
|
|
5
|
-
defaultNumber?: number;
|
|
6
|
-
duration?: number;
|
|
7
|
-
disabled?: boolean;
|
|
8
|
-
easeOut?: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// Easing function for smooth deceleration
|
|
12
|
-
const easeOutQuad = (progress: number): number => {
|
|
13
|
-
return 1 - (1 - progress) * (1 - progress);
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export const useAnimateNumber = ({
|
|
17
|
-
targetNumber,
|
|
18
|
-
defaultNumber = 0,
|
|
19
|
-
duration = 1000,
|
|
20
|
-
disabled = false,
|
|
21
|
-
easeOut = true,
|
|
22
|
-
}: UseAnimateNumberProps): number => {
|
|
23
|
-
const [animatedNumber, setAnimatedNumber] = useState(defaultNumber);
|
|
24
|
-
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
if (disabled) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
let start: number | null = null;
|
|
31
|
-
|
|
32
|
-
const animate = (timestamp: number) => {
|
|
33
|
-
if (!start) start = timestamp;
|
|
34
|
-
const progressTime = timestamp - start;
|
|
35
|
-
let progress = Math.min(progressTime / duration, 1);
|
|
36
|
-
|
|
37
|
-
if (easeOut) {
|
|
38
|
-
progress = easeOutQuad(progress);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const progressValue = progress * targetNumber;
|
|
42
|
-
setAnimatedNumber(progressValue);
|
|
43
|
-
if (progressValue < targetNumber) {
|
|
44
|
-
requestAnimationFrame(animate);
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
requestAnimationFrame(animate);
|
|
49
|
-
}, [targetNumber, duration, disabled, easeOut]);
|
|
50
|
-
|
|
51
|
-
if (disabled) {
|
|
52
|
-
return targetNumber;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return animatedNumber;
|
|
56
|
-
};
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
|
-
|
|
3
|
-
interface UseControlledProps<T> {
|
|
4
|
-
value?: T;
|
|
5
|
-
defaultValue?: T;
|
|
6
|
-
onChange?: (value: T) => void;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const useControlled = <T>({
|
|
10
|
-
value,
|
|
11
|
-
defaultValue,
|
|
12
|
-
onChange,
|
|
13
|
-
}: UseControlledProps<T>) => {
|
|
14
|
-
const isControlled = value !== undefined;
|
|
15
|
-
const [internalValue, setInternalValue] = useState<T | undefined>(
|
|
16
|
-
defaultValue
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
const currentValue = isControlled ? value : internalValue;
|
|
20
|
-
|
|
21
|
-
const handleChange = (newValue: T) => {
|
|
22
|
-
if (!isControlled) {
|
|
23
|
-
setInternalValue(newValue);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
onChange?.(newValue);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
if (!isControlled) {
|
|
31
|
-
setInternalValue(value ?? defaultValue ?? undefined);
|
|
32
|
-
}
|
|
33
|
-
}, [value, defaultValue, isControlled]);
|
|
34
|
-
|
|
35
|
-
return {
|
|
36
|
-
value: currentValue,
|
|
37
|
-
isControlled,
|
|
38
|
-
handleChange,
|
|
39
|
-
};
|
|
40
|
-
};
|
package/src/hooks/useDebounce.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
|
-
|
|
3
|
-
export const useDebounce = <T>(value: T, delay: number): T => {
|
|
4
|
-
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
|
5
|
-
|
|
6
|
-
useEffect(() => {
|
|
7
|
-
const handler = setTimeout(() => {
|
|
8
|
-
setDebouncedValue(value);
|
|
9
|
-
}, delay);
|
|
10
|
-
|
|
11
|
-
return () => {
|
|
12
|
-
clearTimeout(handler);
|
|
13
|
-
};
|
|
14
|
-
}, [value, delay]);
|
|
15
|
-
|
|
16
|
-
return debouncedValue;
|
|
17
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
|
|
3
|
-
interface UseDisclosureProps {
|
|
4
|
-
value?: boolean;
|
|
5
|
-
setValue?: (value: boolean) => void;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export const useDisclosure = (props?: UseDisclosureProps) => {
|
|
9
|
-
const [isOpenInternal, setIsOpenInternal] = useState(false);
|
|
10
|
-
const isOpen = props?.value !== undefined ? props.value : isOpenInternal;
|
|
11
|
-
|
|
12
|
-
const open = () => {
|
|
13
|
-
setIsOpenInternal(true);
|
|
14
|
-
props?.setValue?.(true);
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const close = () => {
|
|
18
|
-
setIsOpenInternal(false);
|
|
19
|
-
props?.setValue?.(false);
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const toggle = () => {
|
|
23
|
-
setIsOpenInternal(prev => !prev);
|
|
24
|
-
props?.setValue?.(!isOpen);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
isOpen,
|
|
29
|
-
open,
|
|
30
|
-
close,
|
|
31
|
-
toggle,
|
|
32
|
-
};
|
|
33
|
-
};
|
package/src/hooks/useForm.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
2
|
-
|
|
3
|
-
interface UseFormProps<T> {
|
|
4
|
-
defaultValue: T | null;
|
|
5
|
-
resetOnDefaultValueChange?: boolean;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export const useForm = <T extends Record<string, unknown>>({
|
|
9
|
-
defaultValue,
|
|
10
|
-
resetOnDefaultValueChange = true,
|
|
11
|
-
}: UseFormProps<T>) => {
|
|
12
|
-
const [form, setForm] = useState<T | null>(defaultValue);
|
|
13
|
-
|
|
14
|
-
const reset = () => setForm(defaultValue);
|
|
15
|
-
|
|
16
|
-
const setField = (field: keyof T, value: T[keyof T]) => {
|
|
17
|
-
setForm(prev => ({ ...prev, [field]: value }) as T);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const isDirty = useMemo(
|
|
21
|
-
() => JSON.stringify(form) !== JSON.stringify(defaultValue),
|
|
22
|
-
[form, defaultValue]
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
if (defaultValue && resetOnDefaultValueChange) {
|
|
27
|
-
setForm(defaultValue);
|
|
28
|
-
}
|
|
29
|
-
}, [defaultValue, resetOnDefaultValueChange]);
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
form,
|
|
33
|
-
isDirty,
|
|
34
|
-
setForm,
|
|
35
|
-
setField,
|
|
36
|
-
reset,
|
|
37
|
-
};
|
|
38
|
-
};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { useEffect, type RefObject } from 'react';
|
|
2
|
-
|
|
3
|
-
interface UseOutsideClickProps {
|
|
4
|
-
refs: RefObject<HTMLElement | null>[];
|
|
5
|
-
handler: (event: MouseEvent | TouchEvent) => void;
|
|
6
|
-
enabled?: boolean;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function useOutsideClick({
|
|
10
|
-
refs,
|
|
11
|
-
handler,
|
|
12
|
-
enabled = true,
|
|
13
|
-
}: UseOutsideClickProps) {
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
if (!enabled) return;
|
|
16
|
-
|
|
17
|
-
const listener = (event: MouseEvent | TouchEvent) => {
|
|
18
|
-
// Check if click is inside any of the referenced elements
|
|
19
|
-
const clickedInside = refs.some(ref => {
|
|
20
|
-
const el = ref.current;
|
|
21
|
-
if (!el) return false;
|
|
22
|
-
return el.contains(event.target as Node);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
// If click is inside at least one element — ignore
|
|
26
|
-
if (clickedInside) return;
|
|
27
|
-
|
|
28
|
-
// Defer handler to next tick to allow all listeners to evaluate
|
|
29
|
-
// before any DOM mutations occur (prevents conflicts when multiple
|
|
30
|
-
// components use this hook simultaneously)
|
|
31
|
-
setTimeout(() => handler(event), 0);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
document.addEventListener('mousedown', listener);
|
|
35
|
-
document.addEventListener('touchstart', listener);
|
|
36
|
-
|
|
37
|
-
return () => {
|
|
38
|
-
document.removeEventListener('mousedown', listener);
|
|
39
|
-
document.removeEventListener('touchstart', listener);
|
|
40
|
-
};
|
|
41
|
-
}, [refs, handler, enabled]);
|
|
42
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
|
|
3
|
-
interface PaginationOptions {
|
|
4
|
-
initialPage?: number;
|
|
5
|
-
initialPageSize?: number;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
interface PaginationState {
|
|
9
|
-
page: number;
|
|
10
|
-
setPage: (page: number) => void;
|
|
11
|
-
pageSize: number;
|
|
12
|
-
setPageSize: (size: number) => void;
|
|
13
|
-
handleNextPage: (page: number) => void;
|
|
14
|
-
handlePreviousPage: (page: number) => void;
|
|
15
|
-
handlePageChange: (page: number) => void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function usePaginationState(
|
|
19
|
-
options: PaginationOptions = {}
|
|
20
|
-
): PaginationState {
|
|
21
|
-
const { initialPage = 1, initialPageSize = 10 } = options;
|
|
22
|
-
|
|
23
|
-
const [page, setPage] = useState<number>(initialPage);
|
|
24
|
-
const [pageSize, setPageSize] = useState<number>(initialPageSize);
|
|
25
|
-
|
|
26
|
-
const handleNextPage = (nextPage: number) => setPage(nextPage);
|
|
27
|
-
const handlePreviousPage = (prevPage: number) => setPage(prevPage);
|
|
28
|
-
const handlePageChange = (newPage: number) => setPage(newPage);
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
page,
|
|
32
|
-
setPage,
|
|
33
|
-
pageSize,
|
|
34
|
-
setPageSize,
|
|
35
|
-
handleNextPage,
|
|
36
|
-
handlePreviousPage,
|
|
37
|
-
handlePageChange,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import type { Position } from '@/types/position';
|
|
2
|
-
import {
|
|
3
|
-
calculateOptimalPosition,
|
|
4
|
-
calculatePositionValues,
|
|
5
|
-
} from '@/utils/position';
|
|
6
|
-
import type { RefObject } from 'react';
|
|
7
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
8
|
-
|
|
9
|
-
interface UsePositionCloseProps {
|
|
10
|
-
triggerRef: RefObject<HTMLElement | null>;
|
|
11
|
-
targetRef: RefObject<HTMLElement | null>;
|
|
12
|
-
position?: Position;
|
|
13
|
-
offset?: number;
|
|
14
|
-
isOpen: boolean;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Hook to calculate fixed positioning for overlays relative to trigger elements.
|
|
19
|
-
* Handles viewport-aware position optimization and click-outside detection.
|
|
20
|
-
*/
|
|
21
|
-
export const usePositionClose = ({
|
|
22
|
-
triggerRef,
|
|
23
|
-
targetRef,
|
|
24
|
-
position = 'bottom-left',
|
|
25
|
-
offset = 8,
|
|
26
|
-
isOpen,
|
|
27
|
-
}: UsePositionCloseProps) => {
|
|
28
|
-
const [isMounted, setIsMounted] = useState(false);
|
|
29
|
-
|
|
30
|
-
// Calculate optimal position based on viewport constraints
|
|
31
|
-
const { positionStyles, optimalPosition } = useMemo(() => {
|
|
32
|
-
if (isMounted && targetRef.current && triggerRef.current) {
|
|
33
|
-
const overlayRect = targetRef.current.getBoundingClientRect();
|
|
34
|
-
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
35
|
-
|
|
36
|
-
// Find the best position to avoid viewport overflow
|
|
37
|
-
const optimalPosition = calculateOptimalPosition(
|
|
38
|
-
overlayRect,
|
|
39
|
-
triggerRect,
|
|
40
|
-
position
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
const coords = calculatePositionValues(
|
|
44
|
-
overlayRect,
|
|
45
|
-
triggerRect,
|
|
46
|
-
optimalPosition,
|
|
47
|
-
offset
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
return { positionStyles: coords, optimalPosition };
|
|
51
|
-
}
|
|
52
|
-
return { positionStyles: { top: 0, left: 0 }, optimal: position };
|
|
53
|
-
}, [triggerRef, targetRef, position, offset, isMounted]);
|
|
54
|
-
|
|
55
|
-
// Track mounting state for smooth positioning
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
if (isOpen) {
|
|
58
|
-
setIsMounted(true);
|
|
59
|
-
} else {
|
|
60
|
-
setIsMounted(false);
|
|
61
|
-
}
|
|
62
|
-
}, [isOpen]);
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
positionStyles,
|
|
66
|
-
optimalPosition,
|
|
67
|
-
isMounted,
|
|
68
|
-
};
|
|
69
|
-
};
|