ali-mohammadi-design-system 1.0.0 → 2.0.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 +287 -0
- package/dist/index.cjs +25 -0
- package/dist/index.js +742 -0
- package/package.json +38 -19
- package/src/components/Button/Button.jsx +66 -15
- package/src/components/index.js +5 -0
- package/src/index.js +19 -5
- package/src/theme/ThemeProvider.jsx +107 -0
- package/src/theme/generateTheme.js +56 -38
- package/src/theme/index.js +5 -0
- package/src/theme/injectCSS.js +21 -256
- package/src/tokens/index.js +271 -0
- package/src/utils/useTokens.js +100 -0
package/package.json
CHANGED
|
@@ -1,27 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
1
|
{
|
|
4
|
-
|
|
5
|
-
"version": "
|
|
2
|
+
"name": "ali-mohammadi-design-system",
|
|
3
|
+
"version": "2.0.1",
|
|
6
4
|
"description": "خنثی و dynamic Design System — tokens از پروژه میاد",
|
|
7
|
-
"main": "
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
8
|
"files": [
|
|
9
|
-
"
|
|
10
|
-
"
|
|
9
|
+
"dist",
|
|
10
|
+
"src"
|
|
11
11
|
],
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
12
|
+
"keywords": [
|
|
13
|
+
"design-system",
|
|
14
|
+
"ui",
|
|
15
|
+
"react",
|
|
16
|
+
"tokens",
|
|
17
|
+
"sass"
|
|
18
|
+
],
|
|
19
|
+
"author": "ali mohammadi",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "vite build",
|
|
26
|
+
"prepublishOnly": "npm run build"
|
|
15
27
|
},
|
|
16
28
|
"peerDependencies": {
|
|
17
|
-
"
|
|
18
|
-
"
|
|
29
|
+
"prop-types": "^15.8.1",
|
|
30
|
+
"react": ">=18 <20",
|
|
31
|
+
"react-dom": ">=18 <20"
|
|
19
32
|
},
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"import": "./dist/index.js",
|
|
36
|
+
"require": "./dist/index.cjs"
|
|
37
|
+
},
|
|
38
|
+
"./theme": "./dist/theme/index.js",
|
|
39
|
+
"./tokens": "./dist/tokens/index.js",
|
|
40
|
+
"./components": "./dist/components/index.js"
|
|
41
|
+
},
|
|
42
|
+
"type": "module",
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@vitejs/plugin-react": "^5.1.2"
|
|
45
|
+
}
|
|
25
46
|
}
|
|
26
|
-
|
|
27
|
-
}
|
|
@@ -1,41 +1,92 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
+
import { useTokens } from '../../utils/useTokens.js';
|
|
3
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Button Component - کامپوننت دکمه با پشتیبانی از تم
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```jsx
|
|
10
|
+
* import { Button } from 'ali-mohammadi-design-system';
|
|
11
|
+
*
|
|
12
|
+
* <Button variant="primary" size="md">Click me</Button>
|
|
13
|
+
* <Button variant="outline" size="lg">Outline Button</Button>
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
4
16
|
const Button = ({ variant = 'primary', size = 'md', children, className = '', ...props }) => {
|
|
17
|
+
const { color, spacing, radius, typography } = useTokens();
|
|
18
|
+
|
|
5
19
|
const variantStyles = {
|
|
6
20
|
primary: {
|
|
7
|
-
backgroundColor: '
|
|
8
|
-
color: '
|
|
21
|
+
backgroundColor: color('primary.500'),
|
|
22
|
+
color: '#ffffff',
|
|
23
|
+
border: 'none',
|
|
9
24
|
},
|
|
10
25
|
outline: {
|
|
11
26
|
backgroundColor: 'transparent',
|
|
12
|
-
border:
|
|
13
|
-
color: '
|
|
27
|
+
border: `2px solid ${color('primary.500')}`,
|
|
28
|
+
color: color('primary.500'),
|
|
14
29
|
},
|
|
15
30
|
text: {
|
|
16
31
|
backgroundColor: 'transparent',
|
|
17
|
-
|
|
32
|
+
border: 'none',
|
|
33
|
+
color: color('primary.500'),
|
|
34
|
+
},
|
|
35
|
+
secondary: {
|
|
36
|
+
backgroundColor: color('neutral.200'),
|
|
37
|
+
color: color('neutral.900'),
|
|
38
|
+
border: 'none',
|
|
39
|
+
},
|
|
40
|
+
success: {
|
|
41
|
+
backgroundColor: color('success.500'),
|
|
42
|
+
color: '#ffffff',
|
|
43
|
+
border: 'none',
|
|
44
|
+
},
|
|
45
|
+
error: {
|
|
46
|
+
backgroundColor: color('error.500'),
|
|
47
|
+
color: '#ffffff',
|
|
48
|
+
border: 'none',
|
|
18
49
|
},
|
|
19
50
|
};
|
|
20
51
|
|
|
21
|
-
const
|
|
22
|
-
sm:
|
|
23
|
-
|
|
24
|
-
|
|
52
|
+
const sizeStyles = {
|
|
53
|
+
sm: {
|
|
54
|
+
padding: `${spacing(2)} ${spacing(4)}`,
|
|
55
|
+
fontSize: typography('sizes.14') || '14px',
|
|
56
|
+
},
|
|
57
|
+
md: {
|
|
58
|
+
padding: `${spacing(3)} ${spacing(6)}`,
|
|
59
|
+
fontSize: typography('sizes.16') || '16px',
|
|
60
|
+
},
|
|
61
|
+
lg: {
|
|
62
|
+
padding: `${spacing(4)} ${spacing(8)}`,
|
|
63
|
+
fontSize: typography('sizes.18') || '18px',
|
|
64
|
+
},
|
|
25
65
|
};
|
|
26
66
|
|
|
27
67
|
return (
|
|
28
68
|
<button
|
|
29
|
-
className={`ds-button ${className}`}
|
|
69
|
+
className={`ds-button ds-button--${variant} ds-button--${size} ${className}`}
|
|
30
70
|
style={{
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
fontSize: '1rem',
|
|
71
|
+
borderRadius: radius('md'),
|
|
72
|
+
fontFamily: typography('fontFamily'),
|
|
73
|
+
fontWeight: '500',
|
|
35
74
|
cursor: 'pointer',
|
|
75
|
+
transition: 'all 0.2s ease',
|
|
36
76
|
...variantStyles[variant],
|
|
77
|
+
...sizeStyles[size],
|
|
37
78
|
...props.style,
|
|
38
79
|
}}
|
|
80
|
+
onMouseEnter={(e) => {
|
|
81
|
+
if (variant === 'primary') {
|
|
82
|
+
e.currentTarget.style.opacity = '0.9';
|
|
83
|
+
}
|
|
84
|
+
}}
|
|
85
|
+
onMouseLeave={(e) => {
|
|
86
|
+
if (variant === 'primary') {
|
|
87
|
+
e.currentTarget.style.opacity = '1';
|
|
88
|
+
}
|
|
89
|
+
}}
|
|
39
90
|
{...props}
|
|
40
91
|
>
|
|
41
92
|
{children}
|
|
@@ -45,7 +96,7 @@ const Button = ({ variant = 'primary', size = 'md', children, className = '', ..
|
|
|
45
96
|
|
|
46
97
|
Button.propTypes = {
|
|
47
98
|
children: PropTypes.node.isRequired,
|
|
48
|
-
variant: PropTypes.oneOf(['primary', 'outline', 'text']),
|
|
99
|
+
variant: PropTypes.oneOf(['primary', 'outline', 'text', 'secondary', 'success', 'error']),
|
|
49
100
|
size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
50
101
|
className: PropTypes.string,
|
|
51
102
|
};
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
export * from './tokens/colors';
|
|
1
|
+
// Export کامپوننتها
|
|
2
|
+
export * from './components/index.js';
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
// Export Theme Provider و Hooks
|
|
5
|
+
export { ThemeProvider, useTheme } from './theme/ThemeProvider.jsx';
|
|
6
|
+
export { useTokens } from './utils/useTokens.js';
|
|
7
|
+
|
|
8
|
+
// Export توکنها
|
|
9
|
+
export { defaultTokens, baseTokens, createTokens, defaultColors } from './tokens/index.js';
|
|
10
|
+
|
|
11
|
+
// Export توابع تم
|
|
12
|
+
export { generateTheme } from './theme/generateTheme.js';
|
|
13
|
+
export { injectCSS } from './theme/injectCSS.js';
|
|
14
|
+
export { generateSCSS } from './theme/generateSCSS.js';
|
|
15
|
+
|
|
16
|
+
// Export پیشفرض
|
|
17
|
+
export { defaultConfig } from './config/index.js';
|
|
18
|
+
|
|
19
|
+
// Default export برای راحتی
|
|
20
|
+
export { ThemeProvider as default } from './theme/ThemeProvider.jsx';
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { createTokens } from '../tokens/index.js';
|
|
4
|
+
import { generateTheme } from './generateTheme.js';
|
|
5
|
+
|
|
6
|
+
const ThemeContext = createContext(null);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ThemeProvider - مدیریت تم و توکنها در پروژه
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```jsx
|
|
13
|
+
* import { ThemeProvider } from 'ali-mohammadi-design-system';
|
|
14
|
+
*
|
|
15
|
+
* const customTokens = {
|
|
16
|
+
* colors: {
|
|
17
|
+
* primary: { 500: '#6366f1' }
|
|
18
|
+
* }
|
|
19
|
+
* };
|
|
20
|
+
*
|
|
21
|
+
* <ThemeProvider tokens={customTokens}>
|
|
22
|
+
* <App />
|
|
23
|
+
* </ThemeProvider>
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function ThemeProvider({ tokens = {}, children }) {
|
|
27
|
+
const mergedTokens = useMemo(() => {
|
|
28
|
+
return createTokens(tokens);
|
|
29
|
+
}, [tokens]);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
// تزریق CSS variables به DOM
|
|
33
|
+
generateTheme(mergedTokens);
|
|
34
|
+
}, [mergedTokens]);
|
|
35
|
+
|
|
36
|
+
const value = useMemo(() => ({
|
|
37
|
+
tokens: mergedTokens,
|
|
38
|
+
getToken: (path) => {
|
|
39
|
+
const keys = path.split('.');
|
|
40
|
+
let value = mergedTokens;
|
|
41
|
+
for (const key of keys) {
|
|
42
|
+
if (value && typeof value === 'object' && key in value) {
|
|
43
|
+
value = value[key];
|
|
44
|
+
} else {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
49
|
+
},
|
|
50
|
+
}), [mergedTokens]);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<ThemeContext.Provider value={value}>
|
|
54
|
+
{children}
|
|
55
|
+
</ThemeContext.Provider>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
ThemeProvider.propTypes = {
|
|
60
|
+
tokens: PropTypes.object,
|
|
61
|
+
children: PropTypes.node.isRequired,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Hook برای دسترسی به تم و توکنها
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```jsx
|
|
69
|
+
* import { useTheme } from 'ali-mohammadi-design-system';
|
|
70
|
+
*
|
|
71
|
+
* function MyComponent() {
|
|
72
|
+
* const { tokens, getToken } = useTheme();
|
|
73
|
+
* const primaryColor = getToken('colors.primary.500');
|
|
74
|
+
* return <div style={{ color: primaryColor }}>Hello</div>;
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export function useTheme() {
|
|
79
|
+
const context = useContext(ThemeContext);
|
|
80
|
+
|
|
81
|
+
if (!context) {
|
|
82
|
+
// اگر ThemeProvider استفاده نشده، از توکنهای پیشفرض استفاده کن
|
|
83
|
+
console.warn('useTheme must be used within ThemeProvider. Using default tokens.');
|
|
84
|
+
const { createTokens } = require('../tokens/index.js');
|
|
85
|
+
const defaultTokens = createTokens();
|
|
86
|
+
return {
|
|
87
|
+
tokens: defaultTokens,
|
|
88
|
+
getToken: (path) => {
|
|
89
|
+
const keys = path.split('.');
|
|
90
|
+
let value = defaultTokens;
|
|
91
|
+
for (const key of keys) {
|
|
92
|
+
if (value && typeof value === 'object' && key in value) {
|
|
93
|
+
value = value[key];
|
|
94
|
+
} else {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return value;
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return context;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export default ThemeProvider;
|
|
107
|
+
|
|
@@ -1,34 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
const defaultTokens = {
|
|
3
|
-
colors: {
|
|
4
|
-
primary: { 500: '#6366f1' },
|
|
5
|
-
neutral: { 900: '#111827', 700: '#374151', 500: '#6b7280', 300: '#d1d5db', 100: '#f3f4f6', 0: '#ffffff' },
|
|
6
|
-
success: { 500: '#10b981' },
|
|
7
|
-
error: { 500: '#ef4444' },
|
|
8
|
-
warning: { 500: '#f59e0b' },
|
|
9
|
-
info: { 500: '#3b82f6' },
|
|
10
|
-
},
|
|
11
|
-
typography: {
|
|
12
|
-
fontFamily: 'system-ui, sans-serif',
|
|
13
|
-
baseSize: 16,
|
|
14
|
-
minSize: 14,
|
|
15
|
-
scaleRatio: 1.25,
|
|
16
|
-
},
|
|
17
|
-
spacing: { unit: 4 },
|
|
18
|
-
radius: { sm: 4, md: 8, lg: 12, xl: 16, full: 9999 },
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const tokens = {
|
|
22
|
-
colors: { ...defaultTokens.colors, ...(userTokens.colors || {}) },
|
|
23
|
-
typography: { ...defaultTokens.typography, ...(userTokens.typography || {}) },
|
|
24
|
-
spacing: { ...defaultTokens.spacing, ...(userTokens.spacing || {}) },
|
|
25
|
-
radius: { ...defaultTokens.radius, ...(userTokens.radius || {}) },
|
|
26
|
-
};
|
|
1
|
+
import { createTokens } from '../tokens/index.js';
|
|
27
2
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
3
|
+
/**
|
|
4
|
+
* تولید CSS variables از توکنها و تزریق به DOM
|
|
5
|
+
* @param {Object} userTokens - توکنهای سفارشی کاربر
|
|
6
|
+
*/
|
|
7
|
+
export function generateTheme(userTokens = {}) {
|
|
8
|
+
const tokens = createTokens(userTokens);
|
|
32
9
|
|
|
33
10
|
const style = document.createElement('style');
|
|
34
11
|
style.id = 'ds-theme';
|
|
@@ -45,23 +22,64 @@ export function generateTheme(userTokens = {}) {
|
|
|
45
22
|
});
|
|
46
23
|
|
|
47
24
|
// Typography
|
|
25
|
+
const t = tokens.typography;
|
|
48
26
|
css += ` --font-family: ${t.fontFamily};\n`;
|
|
49
|
-
|
|
50
|
-
|
|
27
|
+
|
|
28
|
+
if (t.baseSize && t.minSize) {
|
|
29
|
+
const slope = (t.baseSize - t.minSize) / (1920 - 320);
|
|
30
|
+
const yIntersection = -320 * slope + t.minSize;
|
|
31
|
+
const preferredValue = `${yIntersection}px + ${slope * 100}vw`;
|
|
32
|
+
css += ` --font-size-base: clamp(${t.minSize}px, ${preferredValue}, ${t.baseSize}px);\n`;
|
|
33
|
+
} else {
|
|
34
|
+
css += ` --font-size-base: ${t.baseSize || 16}px;\n`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
css += ` --type-scale: ${t.scaleRatio || 1.25};\n`;
|
|
38
|
+
|
|
39
|
+
// Typography sizes
|
|
40
|
+
if (t.sizes) {
|
|
41
|
+
Object.entries(t.sizes).forEach(([key, value]) => {
|
|
42
|
+
css += ` --font-size-${key}: ${value}px;\n`;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Typography line heights
|
|
47
|
+
if (t.lineHeights) {
|
|
48
|
+
Object.entries(t.lineHeights).forEach(([key, value]) => {
|
|
49
|
+
css += ` --line-height-${key}: ${value}px;\n`;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Typography weights
|
|
54
|
+
if (t.weights) {
|
|
55
|
+
Object.entries(t.weights).forEach(([key, value]) => {
|
|
56
|
+
css += ` --font-weight-${key}: ${value};\n`;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
51
59
|
|
|
52
60
|
// Spacing
|
|
53
|
-
|
|
54
|
-
css += ` --space-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
const spacingUnit = tokens.spacing.unit || 4;
|
|
62
|
+
css += ` --space-unit: ${spacingUnit}px;\n`;
|
|
63
|
+
|
|
64
|
+
// Spacing values
|
|
65
|
+
Object.entries(tokens.spacing).forEach(([key, value]) => {
|
|
66
|
+
if (key !== 'unit') {
|
|
67
|
+
css += ` --space-${key}: ${value}px;\n`;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
59
70
|
|
|
60
71
|
// Radius
|
|
61
72
|
Object.entries(tokens.radius).forEach(([k, v]) => {
|
|
62
73
|
css += ` --radius-${k}: ${v}px;\n`;
|
|
63
74
|
});
|
|
64
75
|
|
|
76
|
+
// Shadows
|
|
77
|
+
if (tokens.shadows) {
|
|
78
|
+
Object.entries(tokens.shadows).forEach(([k, v]) => {
|
|
79
|
+
css += ` --shadow-${k}: ${v};\n`;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
65
83
|
css += '}\n';
|
|
66
84
|
css += 'html { font-size: var(--font-size-base); }\n';
|
|
67
85
|
css += 'body { font-family: var(--font-family); }\n';
|