@meonode/ui 0.1.121 โ 0.2.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.
- package/README.md +1481 -165
- package/dist/core.node.d.ts.map +1 -1
- package/dist/core.node.js +1 -1
- package/package.json +1 -5
- package/dist/hook/index.d.ts +0 -2
- package/dist/hook/index.d.ts.map +0 -1
- package/dist/hook/index.js +0 -1
- package/dist/hook/useClasses.d.ts +0 -30
- package/dist/hook/useClasses.d.ts.map +0 -1
- package/dist/hook/useClasses.js +0 -83
package/README.md
CHANGED
|
@@ -1,55 +1,58 @@
|
|
|
1
1
|
# @meonode/ui
|
|
2
2
|
|
|
3
|
-
[
|
|
4
|
-
[
|
|
5
|
-
[
|
|
3
|
+
[](https://www.npmjs.com/package/@meonode/ui)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://bundlephobia.com/package/@meonode/ui)
|
|
6
6
|
|
|
7
7
|
## **Build React UIs with Type-Safe Fluency Without JSX Syntax**
|
|
8
8
|
|
|
9
|
-
A
|
|
9
|
+
A revolutionary approach to React component composition featuring function-based syntax, direct CSS-first prop styling, built-in theming system, smart prop handling with raw property pass-through, dynamic children management, and powerful portal capabilities.
|
|
10
|
+
|
|
11
|
+
### โจ Quick Start Example
|
|
10
12
|
|
|
11
|
-
### Quick Example
|
|
12
13
|
```tsx
|
|
13
|
-
|
|
14
|
-
import { Component, Div, H1, Button } from '@meonode/ui';
|
|
14
|
+
import { Component, Root, Center, Column, H1, Button, Text } from '@meonode/ui';
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
backgroundColor: 'dodgerblue',
|
|
22
|
-
color: 'white',
|
|
23
|
-
...props // Merge with incoming props
|
|
24
|
-
})
|
|
25
|
-
);
|
|
16
|
+
const theme = {
|
|
17
|
+
primary: { default: '#FF6B6B', content: '#4A0000' },
|
|
18
|
+
secondary: { default: '#6BCB77', content: '#0A3B0F' },
|
|
19
|
+
base: { default: '#F8F8F8', content: '#333333' }
|
|
20
|
+
};
|
|
26
21
|
|
|
27
|
-
// Compose your app
|
|
28
22
|
const App = Component(() =>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
23
|
+
Root({
|
|
24
|
+
theme,
|
|
25
|
+
backgroundColor: 'theme.base.default',
|
|
26
|
+
children: Center({
|
|
27
|
+
padding: 40,
|
|
28
|
+
children: Column({
|
|
29
|
+
gap: 24,
|
|
30
|
+
textAlign: 'center',
|
|
31
|
+
children: [
|
|
32
|
+
H1('Welcome to MeoNode', {
|
|
33
|
+
fontSize: '3rem',
|
|
34
|
+
color: 'theme.primary.default',
|
|
35
|
+
marginBottom: 16
|
|
36
|
+
}),
|
|
37
|
+
Button('Get Started', {
|
|
38
|
+
backgroundColor: 'theme.primary.default',
|
|
39
|
+
color: 'theme.primary.content',
|
|
40
|
+
padding: '12px 24px',
|
|
41
|
+
borderRadius: 8,
|
|
42
|
+
fontSize: '1.1rem',
|
|
43
|
+
cursor: 'pointer',
|
|
44
|
+
onClick: () => alert('Hello MeoNode!')
|
|
45
|
+
})
|
|
46
|
+
]
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
})
|
|
39
50
|
);
|
|
40
51
|
```
|
|
41
52
|
|
|
42
|
-
|
|
53
|
+
-----
|
|
43
54
|
|
|
44
|
-
|
|
45
|
-
- ๐จ **Theme-Aware Styles** - Write styles directly in props with dynamic theme resolution
|
|
46
|
-
- ๐งฉ **Component-First Architecture** - Compose UIs from structured nodes instead of JSX
|
|
47
|
-
- ๐งฉ **CSS-First Prop Styling** - Style components directly via props using CSS property names
|
|
48
|
-
- ๐ **Contextual Theming** - Theme values propagate automatically through nested components
|
|
49
|
-
- โก **Optimized Bundle** - Efficient core with tree-shaking support
|
|
50
|
-
- ๐ **Seamless React Integration** - Works with hooks, HOCs, and React 18+ features
|
|
51
|
-
|
|
52
|
-
## Installation
|
|
55
|
+
## ๐ฆ Installation
|
|
53
56
|
|
|
54
57
|
```bash
|
|
55
58
|
# Using npm
|
|
@@ -62,198 +65,1511 @@ yarn add @meonode/ui react
|
|
|
62
65
|
pnpm add @meonode/ui react
|
|
63
66
|
```
|
|
64
67
|
|
|
65
|
-
|
|
68
|
+
-----
|
|
69
|
+
|
|
70
|
+
## โ๏ธ CSS Engine Architecture
|
|
71
|
+
|
|
72
|
+
MeoNode UI is powered by **@emotion/react** under the hood, providing a robust and performant CSS-in-JS solution that enables all the advanced styling capabilities you see in the library.
|
|
73
|
+
|
|
74
|
+
### How It Works
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
// Behind the scenes, MeoNode transforms this:
|
|
78
|
+
const StyledComponent = Component(() =>
|
|
79
|
+
Div({
|
|
80
|
+
padding: '20px',
|
|
81
|
+
backgroundColor: 'theme.primary.default',
|
|
82
|
+
css: {
|
|
83
|
+
'&:hover': {
|
|
84
|
+
transform: 'scale(1.05)',
|
|
85
|
+
boxShadow: '0 8px 16px rgba(0,0,0,0.2)'
|
|
86
|
+
},
|
|
87
|
+
'@media (max-width: 768px)': {
|
|
88
|
+
padding: '12px'
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// Into this Emotion-powered component:
|
|
95
|
+
import { css } from '@emotion/react';
|
|
66
96
|
|
|
67
|
-
|
|
97
|
+
const emotionStyles = css`
|
|
98
|
+
padding: 20px;
|
|
99
|
+
background-color: ${theme.primary.default};
|
|
100
|
+
|
|
101
|
+
&:hover {
|
|
102
|
+
transform: scale(1.05);
|
|
103
|
+
box-shadow: 0 8px 16px rgba(0,0,0,0.2);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@media (max-width: 768px) {
|
|
107
|
+
padding: 12px;
|
|
108
|
+
}
|
|
109
|
+
`;
|
|
110
|
+
```
|
|
68
111
|
|
|
69
|
-
###
|
|
112
|
+
### Key Benefits of Emotion Integration
|
|
70
113
|
|
|
71
|
-
|
|
114
|
+
- **๐ฏ Performance**: Automatic CSS optimization and dead code elimination
|
|
115
|
+
- **๐งฉ Dynamic Styling**: Runtime theme value resolution and conditional styles
|
|
116
|
+
- **๐ Server-Side Rendering**: Full SSR support with automatic critical CSS extraction
|
|
117
|
+
- **๐ฑ Responsive Design**: Native media query support with optimal performance
|
|
118
|
+
- **๐จ Advanced Features**: Pseudo-classes, keyframe animations, and complex selectors
|
|
119
|
+
|
|
120
|
+
### Theme Resolution Engine
|
|
72
121
|
|
|
73
122
|
```tsx
|
|
74
|
-
|
|
123
|
+
// MeoNode's theme engine automatically resolves nested theme paths:
|
|
124
|
+
const theme = {
|
|
125
|
+
colors: {
|
|
126
|
+
primary: {
|
|
127
|
+
500: '#3B82F6',
|
|
128
|
+
600: '#2563EB'
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// This syntax:
|
|
134
|
+
backgroundColor: 'theme.colors.primary.500'
|
|
135
|
+
|
|
136
|
+
// Gets resolved by the engine to:
|
|
137
|
+
backgroundColor: '#3B82F6'
|
|
138
|
+
|
|
139
|
+
// The resolution happens through Emotion's theming system:
|
|
140
|
+
import { ThemeProvider } from '@emotion/react';
|
|
75
141
|
|
|
76
|
-
//
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
142
|
+
// MeoNode wraps your components automatically:
|
|
143
|
+
const ThemedComponent = () => (
|
|
144
|
+
<ThemeProvider theme={theme}>
|
|
145
|
+
<div css={{
|
|
146
|
+
backgroundColor: theme.colors.primary[500], // Resolved automatically
|
|
147
|
+
'&:hover': {
|
|
148
|
+
backgroundColor: theme.colors.primary[600]
|
|
149
|
+
}
|
|
150
|
+
}}>
|
|
151
|
+
Content
|
|
152
|
+
</div>
|
|
153
|
+
</ThemeProvider>
|
|
154
|
+
);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Style Processing Pipeline
|
|
158
|
+
|
|
159
|
+
1. **Parse Props**: MeoNode separates CSS properties from DOM attributes
|
|
160
|
+
2. **Resolve Theme**: Theme path strings are resolved to actual values
|
|
161
|
+
3. **Generate Emotion CSS**: Styles are converted to Emotion's CSS format
|
|
162
|
+
4. **Optimize**: Emotion handles deduplication, vendor prefixing, and optimization
|
|
163
|
+
5. **Inject**: Styles are injected into the document head with unique class names
|
|
164
|
+
|
|
165
|
+
### Advanced Emotion Features Exposed
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
// Access to Emotion's advanced features through MeoNode:
|
|
169
|
+
const AnimatedComponent = Component(() =>
|
|
170
|
+
Div({
|
|
171
|
+
css: {
|
|
172
|
+
// Emotion's css prop supports all CSS features
|
|
173
|
+
animation: 'slideIn 0.5s ease-out',
|
|
174
|
+
|
|
175
|
+
// Advanced selectors
|
|
176
|
+
'& > *:nth-of-type(odd)': {
|
|
177
|
+
backgroundColor: '#f0f0f0'
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
// CSS custom properties
|
|
181
|
+
'--primary-color': '#3B82F6',
|
|
182
|
+
color: 'var(--primary-color)',
|
|
183
|
+
|
|
184
|
+
// Container queries (when supported)
|
|
185
|
+
'@container (min-width: 400px)': {
|
|
186
|
+
padding: '2rem'
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
// Emotion's interpolation functions
|
|
190
|
+
[`@media (min-width: ${theme.breakpoints.md})`]: {
|
|
191
|
+
fontSize: '1.2rem'
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Performance Optimizations
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
// MeoNode leverages Emotion's performance features:
|
|
202
|
+
|
|
203
|
+
// 1. Automatic style memoization
|
|
204
|
+
const MemoizedStyles = Component(() => {
|
|
205
|
+
// These styles are automatically memoized by Emotion
|
|
206
|
+
const expensiveStyles = {
|
|
207
|
+
background: `linear-gradient(45deg,
|
|
208
|
+
${theme.colors.primary.default},
|
|
209
|
+
${theme.colors.secondary.default}
|
|
210
|
+
)`,
|
|
211
|
+
css: {
|
|
212
|
+
// Complex animations are optimized
|
|
213
|
+
'@keyframes complexAnimation': {
|
|
214
|
+
'0%': { transform: 'rotate(0deg) scale(1)' },
|
|
215
|
+
'50%': { transform: 'rotate(180deg) scale(1.2)' },
|
|
216
|
+
'100%': { transform: 'rotate(360deg) scale(1)' }
|
|
217
|
+
},
|
|
218
|
+
animation: 'complexAnimation 2s infinite'
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
return Div(expensiveStyles);
|
|
81
223
|
});
|
|
82
224
|
|
|
83
|
-
//
|
|
84
|
-
|
|
225
|
+
// 2. Critical CSS extraction for SSR
|
|
226
|
+
// MeoNode automatically handles Emotion's SSR setup:
|
|
227
|
+
import { CacheProvider } from '@emotion/react';
|
|
228
|
+
import createEmotionServer from '@emotion/server/create-instance';
|
|
229
|
+
import createCache from '@emotion/cache';
|
|
230
|
+
|
|
231
|
+
// This is handled internally by MeoNode for optimal SSR performance
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
-----
|
|
235
|
+
|
|
236
|
+
## ๐ Core Concepts
|
|
237
|
+
|
|
238
|
+
### 1\. ๐๏ธ Component Architecture
|
|
239
|
+
|
|
240
|
+
MeoNode uses function-based component creation for maximum flexibility:
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
import { Node, Component, Div, H1, P } from '@meonode/ui';
|
|
244
|
+
|
|
245
|
+
// Method 1: Direct element creation
|
|
246
|
+
const SimpleCard = () =>
|
|
85
247
|
Div({
|
|
86
248
|
padding: '20px',
|
|
87
|
-
|
|
88
|
-
|
|
249
|
+
borderRadius: '12px',
|
|
250
|
+
backgroundColor: 'white',
|
|
251
|
+
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
|
|
252
|
+
children: [
|
|
253
|
+
H1('Card Title', { marginBottom: 8 }),
|
|
254
|
+
P('Card content goes here...')
|
|
255
|
+
]
|
|
89
256
|
});
|
|
257
|
+
|
|
258
|
+
// Method 2: Custom element factory
|
|
259
|
+
const Card = Node('article', {
|
|
260
|
+
padding: '24px',
|
|
261
|
+
borderRadius: '16px',
|
|
262
|
+
backgroundColor: 'white',
|
|
263
|
+
border: '1px solid #e0e0e0'
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Method 3: Reusable component with props
|
|
267
|
+
const UserCard = Component<{ user: { name: string; role: string } }>(({ user }) =>
|
|
268
|
+
Card({
|
|
269
|
+
children: [
|
|
270
|
+
H1(user.name, { fontSize: '1.5rem', marginBottom: 4 }),
|
|
271
|
+
P(user.role, { color: '#666', fontSize: '0.9rem' })
|
|
272
|
+
]
|
|
273
|
+
})
|
|
274
|
+
);
|
|
90
275
|
```
|
|
91
276
|
|
|
92
|
-
### 2
|
|
277
|
+
### 2\. ๐จ Advanced Theming System
|
|
93
278
|
|
|
94
|
-
|
|
279
|
+
Create comprehensive design systems with nested theme objects:
|
|
95
280
|
|
|
96
281
|
```tsx
|
|
97
|
-
// theme
|
|
98
|
-
|
|
282
|
+
// Enhanced theme configuration
|
|
283
|
+
const theme = {
|
|
99
284
|
colors: {
|
|
100
|
-
primary:
|
|
285
|
+
primary: {
|
|
286
|
+
50: '#E3F2FD',
|
|
287
|
+
500: '#2196F3',
|
|
288
|
+
900: '#0D47A1',
|
|
289
|
+
gradient: 'linear-gradient(135deg, #2196F3, #21CBF3)'
|
|
290
|
+
},
|
|
291
|
+
semantic: {
|
|
292
|
+
success: '#4CAF50',
|
|
293
|
+
warning: '#FF9800',
|
|
294
|
+
error: '#F44336'
|
|
295
|
+
},
|
|
101
296
|
text: {
|
|
102
|
-
primary: '#
|
|
103
|
-
secondary: '#
|
|
297
|
+
primary: '#212121',
|
|
298
|
+
secondary: '#757575',
|
|
299
|
+
disabled: '#BDBDBD'
|
|
104
300
|
}
|
|
105
301
|
},
|
|
106
302
|
spacing: {
|
|
303
|
+
xs: '4px',
|
|
304
|
+
sm: '8px',
|
|
107
305
|
md: '16px',
|
|
108
|
-
lg: '24px'
|
|
306
|
+
lg: '24px',
|
|
307
|
+
xl: '32px'
|
|
308
|
+
},
|
|
309
|
+
typography: {
|
|
310
|
+
fontFamily: '"Inter", -apple-system, sans-serif',
|
|
311
|
+
sizes: {
|
|
312
|
+
xs: '0.75rem',
|
|
313
|
+
sm: '0.875rem',
|
|
314
|
+
base: '1rem',
|
|
315
|
+
lg: '1.125rem',
|
|
316
|
+
xl: '1.25rem',
|
|
317
|
+
'2xl': '1.5rem'
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
borderRadius: {
|
|
321
|
+
sm: '4px',
|
|
322
|
+
md: '8px',
|
|
323
|
+
lg: '12px',
|
|
324
|
+
xl: '16px'
|
|
109
325
|
}
|
|
110
326
|
};
|
|
111
327
|
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
328
|
+
// Usage with theme paths
|
|
329
|
+
const ThemedButton = Component(() =>
|
|
330
|
+
Button('Primary Action', {
|
|
331
|
+
theme, // Provide theme context
|
|
332
|
+
backgroundColor: 'theme.colors.primary.500',
|
|
333
|
+
color: 'white',
|
|
334
|
+
padding: 'theme.spacing.md theme.spacing.lg',
|
|
335
|
+
borderRadius: 'theme.borderRadius.md',
|
|
336
|
+
fontFamily: 'theme.typography.fontFamily',
|
|
337
|
+
fontSize: 'theme.typography.sizes.base'
|
|
338
|
+
})
|
|
339
|
+
);
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### 3\. ๐ฏ Advanced CSS Properties & Selectors
|
|
115
343
|
|
|
116
|
-
|
|
344
|
+
MeoNode provides powerful CSS manipulation through the `css` property for complex styling scenarios:
|
|
345
|
+
|
|
346
|
+
```tsx
|
|
347
|
+
import { Component, Div, Button, H2, P } from '@meonode/ui';
|
|
348
|
+
|
|
349
|
+
const InteractiveCard = Component(() =>
|
|
117
350
|
Div({
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
backgroundColor: '
|
|
351
|
+
padding: '24px',
|
|
352
|
+
borderRadius: '12px',
|
|
353
|
+
backgroundColor: 'white',
|
|
354
|
+
transition: 'all 0.3s ease',
|
|
355
|
+
|
|
356
|
+
// Advanced CSS with pseudo-classes, hover states, and nested selectors
|
|
357
|
+
css: {
|
|
358
|
+
// Hover effects on the card itself
|
|
359
|
+
'&:hover': {
|
|
360
|
+
transform: 'translateY(-4px)',
|
|
361
|
+
boxShadow: '0 12px 24px rgba(0,0,0,0.15)'
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
// Style child buttons within this card
|
|
365
|
+
'& button': {
|
|
366
|
+
borderRadius: '8px',
|
|
367
|
+
border: 'none',
|
|
368
|
+
transition: 'all 0.2s ease'
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
// Specific button hover states
|
|
372
|
+
'& button:hover': {
|
|
373
|
+
backgroundColor: '#2196F3',
|
|
374
|
+
color: 'white',
|
|
375
|
+
transform: 'scale(1.05)',
|
|
376
|
+
boxShadow: '0 4px 12px rgba(33, 150, 243, 0.3)'
|
|
377
|
+
},
|
|
378
|
+
|
|
379
|
+
// Focus states for accessibility
|
|
380
|
+
'& button:focus': {
|
|
381
|
+
outline: '2px solid #2196F3',
|
|
382
|
+
outlineOffset: '2px'
|
|
383
|
+
},
|
|
384
|
+
|
|
385
|
+
// Media queries for responsive design
|
|
386
|
+
'@media (max-width: 768px)': {
|
|
387
|
+
padding: '16px',
|
|
388
|
+
margin: '8px'
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
// Advanced selectors
|
|
392
|
+
'& h2 + p': {
|
|
393
|
+
marginTop: '8px',
|
|
394
|
+
color: '#666'
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
// Pseudo-elements
|
|
398
|
+
'&::before': {
|
|
399
|
+
content: '""',
|
|
400
|
+
position: 'absolute',
|
|
401
|
+
top: 0,
|
|
402
|
+
left: 0,
|
|
403
|
+
right: 0,
|
|
404
|
+
height: '4px',
|
|
405
|
+
background: 'linear-gradient(90deg, #FF6B6B, #6BCB77)',
|
|
406
|
+
borderRadius: '12px 12px 0 0'
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
|
|
121
410
|
children: [
|
|
122
|
-
|
|
123
|
-
|
|
411
|
+
H2('Interactive Component', {
|
|
412
|
+
fontSize: '1.5rem',
|
|
413
|
+
marginBottom: 12,
|
|
414
|
+
color: '#333'
|
|
415
|
+
}),
|
|
416
|
+
P('This card demonstrates advanced CSS capabilities with hover effects, responsive design, and complex selectors.', {
|
|
417
|
+
lineHeight: 1.6,
|
|
418
|
+
marginBottom: 16
|
|
419
|
+
}),
|
|
420
|
+
Button('Try Hovering!', {
|
|
421
|
+
padding: '10px 20px',
|
|
422
|
+
backgroundColor: '#f5f5f5',
|
|
423
|
+
color: '#333',
|
|
424
|
+
cursor: 'pointer'
|
|
425
|
+
})
|
|
124
426
|
]
|
|
125
427
|
})
|
|
126
|
-
)
|
|
428
|
+
);
|
|
127
429
|
```
|
|
128
430
|
|
|
129
|
-
###
|
|
431
|
+
### 4\. ๐ง Smart Prop Handling
|
|
130
432
|
|
|
131
|
-
Automatic
|
|
433
|
+
Automatic differentiation between CSS properties and DOM attributes:
|
|
132
434
|
|
|
133
435
|
```tsx
|
|
134
|
-
|
|
135
|
-
|
|
436
|
+
interface CardProps {
|
|
437
|
+
title: string;
|
|
438
|
+
urgent?: boolean;
|
|
136
439
|
}
|
|
137
440
|
|
|
138
|
-
const
|
|
441
|
+
const SmartCard = Component<CardProps>(({ title, urgent, ...restProps }) =>
|
|
139
442
|
Div({
|
|
140
|
-
// CSS
|
|
443
|
+
// CSS Properties (automatically recognized)
|
|
141
444
|
padding: '20px',
|
|
142
445
|
borderRadius: '8px',
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
446
|
+
backgroundColor: urgent ? '#FFF3E0' : 'white',
|
|
447
|
+
borderLeft: urgent ? '4px solid #FF9800' : '4px solid transparent',
|
|
448
|
+
|
|
449
|
+
// DOM Attributes (passed through)
|
|
450
|
+
'data-testid': 'smart-card',
|
|
451
|
+
'aria-label': `${title} card`,
|
|
452
|
+
role: 'article',
|
|
146
453
|
tabIndex: 0,
|
|
454
|
+
|
|
455
|
+
// Event Handlers
|
|
456
|
+
onClick: (e) => console.log('Card clicked:', title),
|
|
457
|
+
onKeyDown: (e) => {
|
|
458
|
+
if (e.key === 'Enter') console.log('Card activated:', title);
|
|
459
|
+
},
|
|
460
|
+
|
|
461
|
+
// Merge any additional props
|
|
462
|
+
...restProps,
|
|
463
|
+
|
|
464
|
+
children: [
|
|
465
|
+
H2(title, {
|
|
466
|
+
color: urgent ? '#E65100' : '#333',
|
|
467
|
+
marginBottom: 12
|
|
468
|
+
}),
|
|
469
|
+
P('Smart prop handling automatically separates CSS from DOM attributes.')
|
|
470
|
+
]
|
|
471
|
+
})
|
|
472
|
+
);
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### 5\. ๐ Dynamic Children & Composition
|
|
147
476
|
|
|
148
|
-
|
|
149
|
-
|
|
477
|
+
Handle complex child patterns with ease:
|
|
478
|
+
|
|
479
|
+
```tsx
|
|
480
|
+
const DynamicList = Component<{ items: string[]; header?: string }>(({ items, header }) =>
|
|
481
|
+
Div({
|
|
482
|
+
children: [
|
|
483
|
+
// Conditional header
|
|
484
|
+
header && H2(header, { marginBottom: 16 }),
|
|
485
|
+
|
|
486
|
+
// Dynamic list items
|
|
487
|
+
...items.map((item, index) =>
|
|
488
|
+
Div({
|
|
489
|
+
key: index,
|
|
490
|
+
padding: '12px',
|
|
491
|
+
borderBottom: index < items.length - 1 ? '1px solid #eee' : 'none',
|
|
492
|
+
children: P(item)
|
|
493
|
+
})
|
|
494
|
+
),
|
|
495
|
+
|
|
496
|
+
// Conditional footer
|
|
497
|
+
items.length === 0 && P('No items found', {
|
|
498
|
+
color: '#999',
|
|
499
|
+
fontStyle: 'italic',
|
|
500
|
+
textAlign: 'center',
|
|
501
|
+
padding: '20px'
|
|
502
|
+
})
|
|
503
|
+
]
|
|
150
504
|
})
|
|
151
|
-
)
|
|
505
|
+
);
|
|
152
506
|
```
|
|
153
507
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
import
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
508
|
+
### 6\. ๐ช Portal System with Context Integration
|
|
509
|
+
|
|
510
|
+
Create modals, tooltips, and overlays with full context access:
|
|
511
|
+
|
|
512
|
+
```tsx
|
|
513
|
+
import { Portal, Center, Column, Button, Text } from '@meonode/ui';
|
|
514
|
+
import { Provider, useSelector } from 'react-redux';
|
|
515
|
+
import store from './store';
|
|
516
|
+
|
|
517
|
+
// Redux Provider wrapper for portal components
|
|
518
|
+
const ReduxProvider = Node(Provider, { store });
|
|
519
|
+
|
|
520
|
+
// Modal with Redux integration
|
|
521
|
+
const NotificationModal = Portal<{ message: string }>(
|
|
522
|
+
ReduxProvider, // Context provider wrapper
|
|
523
|
+
({ portal, message }) => {
|
|
524
|
+
const userPreferences = useSelector(state => state.user.preferences);
|
|
525
|
+
|
|
526
|
+
useEffect(() => {
|
|
527
|
+
// Auto-close after 3 seconds
|
|
528
|
+
const timer = setTimeout(portal.unmount, 3000);
|
|
529
|
+
return () => clearTimeout(timer);
|
|
530
|
+
}, [portal]);
|
|
531
|
+
|
|
532
|
+
return Center({
|
|
533
|
+
position: 'fixed',
|
|
534
|
+
top: 0,
|
|
535
|
+
left: 0,
|
|
536
|
+
right: 0,
|
|
537
|
+
bottom: 0,
|
|
538
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
539
|
+
backdropFilter: 'blur(8px)',
|
|
540
|
+
zIndex: 1000,
|
|
541
|
+
|
|
542
|
+
// Click outside to close
|
|
543
|
+
onClick: (e) => {
|
|
544
|
+
if (e.currentTarget === e.target) portal.unmount();
|
|
545
|
+
},
|
|
546
|
+
|
|
547
|
+
children: Column({
|
|
548
|
+
backgroundColor: 'white',
|
|
549
|
+
borderRadius: '16px',
|
|
550
|
+
padding: '32px',
|
|
551
|
+
maxWidth: '400px',
|
|
552
|
+
margin: '20px',
|
|
553
|
+
boxShadow: '0 20px 40px rgba(0,0,0,0.15)',
|
|
554
|
+
|
|
555
|
+
css: {
|
|
556
|
+
// Entrance animation
|
|
557
|
+
animation: 'slideIn 0.3s ease-out',
|
|
558
|
+
'@keyframes slideIn': {
|
|
559
|
+
from: {
|
|
560
|
+
opacity: 0,
|
|
561
|
+
transform: 'translateY(-20px) scale(0.95)'
|
|
562
|
+
},
|
|
563
|
+
to: {
|
|
564
|
+
opacity: 1,
|
|
565
|
+
transform: 'translateY(0) scale(1)'
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
|
|
570
|
+
children: [
|
|
571
|
+
Text(message, {
|
|
572
|
+
fontSize: '1.2rem',
|
|
573
|
+
marginBottom: 20,
|
|
574
|
+
textAlign: 'center',
|
|
575
|
+
color: userPreferences.darkMode ? '#fff' : '#333'
|
|
576
|
+
}),
|
|
577
|
+
Button('Close', {
|
|
578
|
+
backgroundColor: '#2196F3',
|
|
579
|
+
color: 'white',
|
|
580
|
+
padding: '10px 20px',
|
|
581
|
+
borderRadius: '8px',
|
|
582
|
+
cursor: 'pointer',
|
|
583
|
+
onClick: portal.unmount
|
|
584
|
+
})
|
|
585
|
+
]
|
|
586
|
+
})
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
);
|
|
590
|
+
|
|
591
|
+
// Usage
|
|
592
|
+
const App = Component(() => {
|
|
593
|
+
const showModal = () => NotificationModal({
|
|
594
|
+
message: 'Portal with Redux context access!'
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
return Button('Show Modal', { onClick: showModal });
|
|
598
|
+
});
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
-----
|
|
602
|
+
|
|
603
|
+
## ๐ฏ Why Choose @meonode/ui?
|
|
604
|
+
|
|
605
|
+
### **Revolutionary Development Experience**
|
|
606
|
+
|
|
607
|
+
- **๐ฏ Type-Safe by Design** - Complete TypeScript integration with intelligent autocomplete for styles, props, and theme paths
|
|
608
|
+
- **๐จ Theme-Aware Everything** - Write styles directly in props with automatic theme value resolution and inheritance
|
|
609
|
+
- **๐งฉ Function-Based Composition** - Intuitive component building using structured function calls instead of JSX complexity
|
|
610
|
+
- **๐ซ Advanced CSS Control** - Full CSS capabilities including pseudo-classes, media queries, and complex selectors
|
|
611
|
+
- **๐ Contextual Theming** - Theme values propagate automatically through nested component hierarchies
|
|
612
|
+
- **โก Performance Optimized** - Fast theme resolution and efficient CSS in JS powered by @emotion/react
|
|
613
|
+
|
|
614
|
+
### **Enterprise-Ready Features**
|
|
615
|
+
|
|
616
|
+
- **๐ React Ecosystem Compatible** - Seamless integration with hooks, HOCs, context, and React 18+ concurrent features
|
|
617
|
+
- **๐ช Powerful Portal System** - Advanced portal management with context provider integration for modals and overlays
|
|
618
|
+
- **๐ฑ Responsive by Default** - Built-in responsive design patterns with media query support
|
|
619
|
+
- **โฟ Accessibility First** - Semantic HTML output with ARIA support and keyboard navigation
|
|
620
|
+
|
|
621
|
+
-----
|
|
622
|
+
|
|
623
|
+
## ๐ API Reference
|
|
624
|
+
|
|
625
|
+
### Core Functions
|
|
626
|
+
|
|
627
|
+
| Function | Signature | Description |
|
|
628
|
+
| :--- | :--- | :--- |
|
|
629
|
+
| `Node` | `(element: string \| ComponentType, baseProps?: object) => NodeFactory` | Creates a configurable UI node factory that supports flexible properties, dynamic styling, and theme resolution. |
|
|
630
|
+
| `Component` | `(render: (props: P) => ComponentNode) => React.Component<P>` | Transforms node trees into reusable React components with built-in type safety, prop handling, and seamless React integration. |
|
|
631
|
+
| `Portal` | `(component: (props: P & PortalProps) => ComponentNode) \| (provider: NodeElement, component: (props: P & PortalProps) => ComponentNode)` | Creates React Portal components with optional context provider wrapping. Components receive portal controls for programmatic mounting/unmounting and lifecycle management. |
|
|
632
|
+
|
|
633
|
+
### Pre-built Components
|
|
634
|
+
|
|
635
|
+
| Component Category | Components | Description |
|
|
636
|
+
| :--- | :--- | :--- |
|
|
637
|
+
| **Layout** | `Root`, `Center`, `Column`, `Row`, `Div`, `Section`, `Header`, `Footer`, `Main`, `Nav` | Semantic layout primitives with flexbox and grid support |
|
|
638
|
+
| **Typography** | `H1`, `H2`, `H3`, `H4`, `H5`, `H6`, `P`, `Text`, `Span`, `Strong`, `Em` | Typography elements with theme-aware styling |
|
|
639
|
+
| **Interactive** | `Button`, `Link`, `Input`, `Select`, `Textarea`, `Checkbox`, `Radio` | Form controls and interactive elements |
|
|
640
|
+
| **Media** | `Img`, `Video`, `Audio`, `Canvas`, `Svg` | Media and graphics components |
|
|
641
|
+
| **Semantic** | `Article`, `Aside`, `Details`, `Summary`, `Figure`, `Figcaption` | HTML5 semantic elements |
|
|
642
|
+
|
|
643
|
+
### PortalProps Interface
|
|
644
|
+
|
|
645
|
+
```tsx
|
|
646
|
+
interface PortalProps {
|
|
647
|
+
portal: {
|
|
648
|
+
unmount: () => void;
|
|
649
|
+
mount: (component: ComponentNode) => void;
|
|
650
|
+
isVisible: boolean;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
```
|
|
207
654
|
|
|
655
|
+
-----
|
|
656
|
+
|
|
657
|
+
## ๐จ Design Patterns
|
|
658
|
+
|
|
659
|
+
### 1\. Theme-First Design System
|
|
660
|
+
|
|
661
|
+
```tsx
|
|
662
|
+
// Complete design system setup
|
|
663
|
+
const designSystem = {
|
|
664
|
+
colors: {
|
|
665
|
+
brand: {
|
|
666
|
+
primary: '#6366F1',
|
|
667
|
+
secondary: '#8B5CF6',
|
|
668
|
+
accent: '#F59E0B'
|
|
669
|
+
},
|
|
670
|
+
neutral: {
|
|
671
|
+
50: '#F9FAFB',
|
|
672
|
+
100: '#F3F4F6',
|
|
673
|
+
500: '#6B7280',
|
|
674
|
+
900: '#111827'
|
|
675
|
+
},
|
|
676
|
+
semantic: {
|
|
677
|
+
success: '#10B981',
|
|
678
|
+
warning: '#F59E0B',
|
|
679
|
+
error: '#EF4444',
|
|
680
|
+
info: '#3B82F6'
|
|
681
|
+
}
|
|
682
|
+
},
|
|
683
|
+
typography: {
|
|
684
|
+
fontFamily: {
|
|
685
|
+
sans: '"Inter", system-ui, sans-serif',
|
|
686
|
+
mono: '"Fira Code", Consolas, monospace'
|
|
687
|
+
},
|
|
688
|
+
scale: {
|
|
689
|
+
xs: '0.75rem',
|
|
690
|
+
sm: '0.875rem',
|
|
691
|
+
base: '1rem',
|
|
692
|
+
lg: '1.125rem',
|
|
693
|
+
xl: '1.25rem',
|
|
694
|
+
'2xl': '1.5rem',
|
|
695
|
+
'3xl': '1.875rem',
|
|
696
|
+
'4xl': '2.25rem'
|
|
697
|
+
},
|
|
698
|
+
weight: {
|
|
699
|
+
normal: 400,
|
|
700
|
+
medium: 500,
|
|
701
|
+
semibold: 600,
|
|
702
|
+
bold: 700
|
|
703
|
+
}
|
|
704
|
+
},
|
|
705
|
+
spacing: {
|
|
706
|
+
0: '0px',
|
|
707
|
+
1: '0.25rem',
|
|
708
|
+
2: '0.5rem',
|
|
709
|
+
3: '0.75rem',
|
|
710
|
+
4: '1rem',
|
|
711
|
+
6: '1.5rem',
|
|
712
|
+
8: '2rem',
|
|
713
|
+
12: '3rem',
|
|
714
|
+
16: '4rem'
|
|
715
|
+
},
|
|
716
|
+
shadows: {
|
|
717
|
+
sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
|
|
718
|
+
md: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
|
|
719
|
+
lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',
|
|
720
|
+
xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1)'
|
|
721
|
+
},
|
|
722
|
+
borderRadius: {
|
|
723
|
+
none: '0',
|
|
724
|
+
sm: '0.125rem',
|
|
725
|
+
md: '0.375rem',
|
|
726
|
+
lg: '0.5rem',
|
|
727
|
+
xl: '0.75rem',
|
|
728
|
+
full: '9999px'
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
// Usage in components
|
|
733
|
+
const ThemedCard = Component<{ variant?: 'default' | 'success' | 'warning' | 'error' }>(
|
|
734
|
+
({ variant = 'default', children }) => {
|
|
735
|
+
const variantStyles = {
|
|
736
|
+
default: {
|
|
737
|
+
border: '1px solid theme.neutral.200',
|
|
738
|
+
backgroundColor: 'theme.neutral.50'
|
|
739
|
+
},
|
|
740
|
+
success: {
|
|
741
|
+
border: '1px solid theme.semantic.success',
|
|
742
|
+
backgroundColor: '#F0FDF4'
|
|
743
|
+
},
|
|
744
|
+
warning: {
|
|
745
|
+
border: '1px solid theme.semantic.warning',
|
|
746
|
+
backgroundColor: '#FFFBEB'
|
|
747
|
+
},
|
|
748
|
+
error: {
|
|
749
|
+
border: '1px solid theme.semantic.error',
|
|
750
|
+
backgroundColor: '#FEF2F2'
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
return Div({
|
|
755
|
+
theme: designSystem,
|
|
756
|
+
padding: 'theme.spacing.6',
|
|
757
|
+
borderRadius: 'theme.borderRadius.lg',
|
|
758
|
+
boxShadow: 'theme.shadows.md',
|
|
759
|
+
...variantStyles[variant],
|
|
760
|
+
children
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
);
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
### 2\. Responsive Design Patterns
|
|
767
|
+
|
|
768
|
+
```tsx
|
|
769
|
+
const ResponsiveGrid = Component<{ items: Array<{ title: string; content: string }> }>(
|
|
770
|
+
({ items }) =>
|
|
771
|
+
Div({
|
|
772
|
+
css: {
|
|
773
|
+
display: 'grid',
|
|
774
|
+
gap: '24px',
|
|
775
|
+
|
|
776
|
+
// Responsive grid
|
|
777
|
+
'@media (min-width: 640px)': {
|
|
778
|
+
gridTemplateColumns: 'repeat(2, 1fr)'
|
|
779
|
+
},
|
|
780
|
+
'@media (min-width: 1024px)': {
|
|
781
|
+
gridTemplateColumns: 'repeat(3, 1fr)'
|
|
782
|
+
},
|
|
783
|
+
'@media (min-width: 1280px)': {
|
|
784
|
+
gridTemplateColumns: 'repeat(4, 1fr)'
|
|
785
|
+
},
|
|
786
|
+
|
|
787
|
+
// Grid item styling
|
|
788
|
+
'& > *': {
|
|
789
|
+
transition: 'transform 0.2s ease, box-shadow 0.2s ease'
|
|
790
|
+
},
|
|
791
|
+
|
|
792
|
+
'& > *:hover': {
|
|
793
|
+
transform: 'translateY(-4px)',
|
|
794
|
+
boxShadow: '0 12px 24px rgba(0,0,0,0.15)'
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
|
|
798
|
+
children: items.map((item, index) =>
|
|
799
|
+
ThemedCard({
|
|
800
|
+
key: index,
|
|
801
|
+
children: [
|
|
802
|
+
H2(item.title, {
|
|
803
|
+
fontSize: '1.25rem',
|
|
804
|
+
fontWeight: 'bold',
|
|
805
|
+
marginBottom: 8,
|
|
806
|
+
color: 'theme.neutral.900'
|
|
807
|
+
}),
|
|
808
|
+
P(item.content, {
|
|
809
|
+
color: 'theme.neutral.600',
|
|
810
|
+
lineHeight: 1.6
|
|
811
|
+
})
|
|
812
|
+
]
|
|
813
|
+
})
|
|
814
|
+
)
|
|
815
|
+
})
|
|
816
|
+
);
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
### 3\. Form Composition
|
|
820
|
+
|
|
821
|
+
```tsx
|
|
822
|
+
const ContactForm = Component(() => {
|
|
823
|
+
const [formData, setFormData] = useState({
|
|
824
|
+
name: '',
|
|
825
|
+
email: '',
|
|
826
|
+
message: ''
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
830
|
+
e.preventDefault();
|
|
831
|
+
console.log('Form submitted:', formData);
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
return Column({
|
|
835
|
+
theme: designSystem,
|
|
836
|
+
gap: 'theme.spacing.4',
|
|
837
|
+
maxWidth: '500px',
|
|
838
|
+
|
|
839
|
+
css: {
|
|
840
|
+
'& input, & textarea': {
|
|
841
|
+
width: '100%',
|
|
842
|
+
padding: 'theme.spacing.3',
|
|
843
|
+
borderRadius: 'theme.borderRadius.md',
|
|
844
|
+
border: '1px solid theme.neutral.300',
|
|
845
|
+
fontSize: 'theme.typography.scale.base',
|
|
846
|
+
transition: 'border-color 0.2s ease, box-shadow 0.2s ease'
|
|
847
|
+
},
|
|
848
|
+
|
|
849
|
+
'& input:focus, & textarea:focus': {
|
|
850
|
+
outline: 'none',
|
|
851
|
+
borderColor: 'theme.colors.brand.primary',
|
|
852
|
+
boxShadow: '0 0 0 3px rgba(99, 102, 241, 0.1)'
|
|
853
|
+
},
|
|
854
|
+
|
|
855
|
+
'& label': {
|
|
856
|
+
fontSize: 'theme.typography.scale.sm',
|
|
857
|
+
fontWeight: 'theme.typography.weight.medium',
|
|
858
|
+
color: 'theme.neutral.700',
|
|
859
|
+
marginBottom: 'theme.spacing.2'
|
|
860
|
+
}
|
|
861
|
+
},
|
|
862
|
+
|
|
863
|
+
children: [
|
|
864
|
+
// Form element wrapper
|
|
865
|
+
Node('form', {
|
|
866
|
+
onSubmit: handleSubmit,
|
|
867
|
+
children: [
|
|
868
|
+
Column({
|
|
869
|
+
gap: 'theme.spacing.2',
|
|
870
|
+
children: [
|
|
871
|
+
Node('label', { children: 'Name' }),
|
|
872
|
+
Node('input', {
|
|
873
|
+
type: 'text',
|
|
874
|
+
value: formData.name,
|
|
875
|
+
onChange: (e) => setFormData(prev => ({ ...prev, name: e.target.value })),
|
|
876
|
+
placeholder: 'Enter your name'
|
|
877
|
+
})
|
|
878
|
+
]
|
|
879
|
+
}),
|
|
880
|
+
|
|
881
|
+
Column({
|
|
882
|
+
gap: 'theme.spacing.2',
|
|
883
|
+
children: [
|
|
884
|
+
Node('label', { children: 'Email' }),
|
|
885
|
+
Node('input', {
|
|
886
|
+
type: 'email',
|
|
887
|
+
value: formData.email,
|
|
888
|
+
onChange: (e) => setFormData(prev => ({ ...prev, email: e.target.value })),
|
|
889
|
+
placeholder: 'Enter your email'
|
|
890
|
+
})
|
|
891
|
+
]
|
|
892
|
+
}),
|
|
893
|
+
|
|
894
|
+
Column({
|
|
895
|
+
gap: 'theme.spacing.2',
|
|
896
|
+
children: [
|
|
897
|
+
Node('label', { children: 'Message' }),
|
|
898
|
+
Node('textarea', {
|
|
899
|
+
value: formData.message,
|
|
900
|
+
onChange: (e) => setFormData(prev => ({ ...prev, message: e.target.value })),
|
|
901
|
+
placeholder: 'Enter your message',
|
|
902
|
+
rows: 4
|
|
903
|
+
})
|
|
904
|
+
]
|
|
905
|
+
}),
|
|
906
|
+
|
|
907
|
+
Button('Send Message', {
|
|
908
|
+
type: 'submit',
|
|
909
|
+
backgroundColor: 'theme.colors.brand.primary',
|
|
910
|
+
color: 'white',
|
|
911
|
+
padding: 'theme.spacing.3 theme.spacing.6',
|
|
912
|
+
borderRadius: 'theme.borderRadius.md',
|
|
913
|
+
fontWeight: 'theme.typography.weight.semibold',
|
|
914
|
+
cursor: 'pointer',
|
|
915
|
+
marginTop: 'theme.spacing.4'
|
|
916
|
+
})
|
|
917
|
+
]
|
|
918
|
+
})
|
|
919
|
+
]
|
|
920
|
+
});
|
|
921
|
+
});
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
-----
|
|
925
|
+
|
|
926
|
+
## ๐ง Advanced Techniques
|
|
927
|
+
|
|
928
|
+
### Custom Hook Integration
|
|
929
|
+
|
|
930
|
+
```tsx
|
|
931
|
+
import { useState, useCallback } from 'react';
|
|
932
|
+
|
|
933
|
+
const useToggle = (initialValue = false) => {
|
|
934
|
+
const [value, setValue] = useState(initialValue);
|
|
935
|
+
const toggle = useCallback(() => setValue(v => !v), []);
|
|
936
|
+
return [value, toggle] as const;
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
const ToggleCard = Component(() => {
|
|
940
|
+
const [isExpanded, toggleExpanded] = useToggle(false);
|
|
941
|
+
|
|
208
942
|
return Div({
|
|
209
|
-
|
|
943
|
+
theme: designSystem,
|
|
944
|
+
borderRadius: 'theme.borderRadius.lg',
|
|
210
945
|
backgroundColor: 'white',
|
|
211
|
-
boxShadow: '
|
|
946
|
+
boxShadow: 'theme.shadows.md',
|
|
947
|
+
overflow: 'hidden',
|
|
948
|
+
|
|
949
|
+
css: {
|
|
950
|
+
transition: 'all 0.3s ease',
|
|
951
|
+
cursor: 'pointer',
|
|
952
|
+
|
|
953
|
+
'&:hover': {
|
|
954
|
+
boxShadow: 'theme.shadows.lg',
|
|
955
|
+
transform: 'translateY(-2px)'
|
|
956
|
+
}
|
|
957
|
+
},
|
|
958
|
+
|
|
959
|
+
onClick: toggleExpanded,
|
|
960
|
+
|
|
212
961
|
children: [
|
|
213
|
-
|
|
214
|
-
|
|
962
|
+
Div({
|
|
963
|
+
padding: 'theme.spacing.6',
|
|
964
|
+
children: [
|
|
965
|
+
H2('Expandable Card', {
|
|
966
|
+
fontSize: 'theme.typography.scale.xl',
|
|
967
|
+
marginBottom: 8
|
|
968
|
+
}),
|
|
969
|
+
P('Click to expand/collapse', {
|
|
970
|
+
color: 'theme.neutral.600'
|
|
971
|
+
})
|
|
972
|
+
]
|
|
973
|
+
}),
|
|
974
|
+
|
|
975
|
+
// Conditional expanded content
|
|
976
|
+
isExpanded && Div({
|
|
977
|
+
padding: 'theme.spacing.6',
|
|
978
|
+
paddingTop: 0,
|
|
979
|
+
borderTop: '1px solid theme.neutral.200',
|
|
980
|
+
|
|
981
|
+
css: {
|
|
982
|
+
animation: 'slideDown 0.3s ease',
|
|
983
|
+
'@keyframes slideDown': {
|
|
984
|
+
from: {
|
|
985
|
+
opacity: 0,
|
|
986
|
+
maxHeight: 0,
|
|
987
|
+
transform: 'translateY(-10px)'
|
|
988
|
+
},
|
|
989
|
+
to: {
|
|
990
|
+
opacity: 1,
|
|
991
|
+
maxHeight: '200px',
|
|
992
|
+
transform: 'translateY(0)'
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
},
|
|
996
|
+
|
|
997
|
+
children: P('This is the expanded content that appears when the card is clicked. It demonstrates conditional rendering with smooth animations.')
|
|
998
|
+
})
|
|
215
999
|
]
|
|
1000
|
+
});
|
|
1001
|
+
});
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
### Error Boundaries Integration
|
|
1005
|
+
|
|
1006
|
+
```tsx
|
|
1007
|
+
import { Component, Div, H2, P, Button } from '@meonode/ui';
|
|
1008
|
+
import { ErrorBoundary } from 'react-error-boundary';
|
|
1009
|
+
|
|
1010
|
+
const ErrorFallback = Component<{ error: Error; resetErrorBoundary: () => void }>(
|
|
1011
|
+
({ error, resetErrorBoundary }) =>
|
|
1012
|
+
Div({
|
|
1013
|
+
theme: designSystem,
|
|
1014
|
+
padding: 'theme.spacing.8',
|
|
1015
|
+
backgroundColor: 'theme.semantic.error',
|
|
1016
|
+
color: 'white',
|
|
1017
|
+
borderRadius: 'theme.borderRadius.lg',
|
|
1018
|
+
textAlign: 'center',
|
|
1019
|
+
|
|
1020
|
+
children: [
|
|
1021
|
+
H2('Something went wrong', {
|
|
1022
|
+
fontSize: 'theme.typography.scale.2xl',
|
|
1023
|
+
marginBottom: 16
|
|
1024
|
+
}),
|
|
1025
|
+
P(error.message, {
|
|
1026
|
+
marginBottom: 24,
|
|
1027
|
+
opacity: 0.9
|
|
1028
|
+
}),
|
|
1029
|
+
Button('Try Again', {
|
|
1030
|
+
backgroundColor: 'white',
|
|
1031
|
+
color: 'theme.semantic.error',
|
|
1032
|
+
padding: '12px 24px',
|
|
1033
|
+
borderRadius: 'theme.borderRadius.md',
|
|
1034
|
+
fontWeight: 'bold',
|
|
1035
|
+
cursor: 'pointer',
|
|
1036
|
+
onClick: resetErrorBoundary
|
|
1037
|
+
})
|
|
1038
|
+
]
|
|
1039
|
+
})
|
|
1040
|
+
);
|
|
1041
|
+
|
|
1042
|
+
const SafeApp = Component(() =>
|
|
1043
|
+
Node(ErrorBoundary, {
|
|
1044
|
+
FallbackComponent: ErrorFallback,
|
|
1045
|
+
onError: (error, errorInfo) => {
|
|
1046
|
+
console.error('App Error:', error, errorInfo);
|
|
1047
|
+
},
|
|
1048
|
+
children: YourMainApp()
|
|
216
1049
|
})
|
|
217
|
-
|
|
1050
|
+
);
|
|
218
1051
|
```
|
|
219
1052
|
|
|
220
|
-
|
|
1053
|
+
-----
|
|
221
1054
|
|
|
222
|
-
##
|
|
1055
|
+
## ๐ Real-World Example Application
|
|
223
1056
|
|
|
224
|
-
|
|
1057
|
+
```tsx
|
|
1058
|
+
import { Component, Root, Center, Column, Row, H1, H2, P, Button, Text, Portal } from '@meonode/ui';
|
|
1059
|
+
import { useState, useEffect } from 'react';
|
|
225
1060
|
|
|
226
|
-
|
|
1061
|
+
// Complete theme system
|
|
1062
|
+
const appTheme = {
|
|
1063
|
+
colors: {
|
|
1064
|
+
primary: {
|
|
1065
|
+
default: '#FF6B6B',
|
|
1066
|
+
content: '#4A0000',
|
|
1067
|
+
light: '#FFB3B3',
|
|
1068
|
+
dark: '#CC5555'
|
|
1069
|
+
},
|
|
1070
|
+
secondary: {
|
|
1071
|
+
default: '#6BCB77',
|
|
1072
|
+
content: '#0A3B0F',
|
|
1073
|
+
light: '#B3E6BC',
|
|
1074
|
+
dark: '#55A862'
|
|
1075
|
+
},
|
|
1076
|
+
base: {
|
|
1077
|
+
default: '#F8F8F8',
|
|
1078
|
+
content: '#333333',
|
|
1079
|
+
accent: '#88B04B',
|
|
1080
|
+
border: '#E0E0E0'
|
|
1081
|
+
}
|
|
1082
|
+
},
|
|
1083
|
+
spacing: {
|
|
1084
|
+
xs: '8px',
|
|
1085
|
+
sm: '12px',
|
|
1086
|
+
md: '16px',
|
|
1087
|
+
lg: '24px',
|
|
1088
|
+
xl: '32px',
|
|
1089
|
+
'2xl': '48px'
|
|
1090
|
+
},
|
|
1091
|
+
typography: {
|
|
1092
|
+
family: '"Inter", -apple-system, BlinkMacSystemFont, sans-serif',
|
|
1093
|
+
sizes: {
|
|
1094
|
+
sm: '0.875rem',
|
|
1095
|
+
base: '1rem',
|
|
1096
|
+
lg: '1.125rem',
|
|
1097
|
+
xl: '1.25rem',
|
|
1098
|
+
'2xl': '1.5rem',
|
|
1099
|
+
'3xl': '2rem',
|
|
1100
|
+
'4xl': '2.5rem'
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
};
|
|
227
1104
|
|
|
228
|
-
|
|
1105
|
+
const notifications = [
|
|
1106
|
+
{ id: 1, type: 'success', message: 'Component created successfully!' },
|
|
1107
|
+
{ id: 2, type: 'info', message: 'MeoNode UI is theme-aware by default.' },
|
|
1108
|
+
{ id: 3, type: 'warning', message: 'Remember to handle responsive design.' },
|
|
1109
|
+
{ id: 4, type: 'error', message: 'This is just a demo error message.' }
|
|
1110
|
+
];
|
|
229
1111
|
|
|
230
|
-
|
|
1112
|
+
// Enhanced Modal with animations and context
|
|
1113
|
+
const NotificationModal = Portal<{ notification: typeof notifications[0] }>(
|
|
1114
|
+
({ portal, notification }) => {
|
|
1115
|
+
useEffect(() => {
|
|
1116
|
+
const timer = setTimeout(portal.unmount, 4000);
|
|
1117
|
+
return () => clearTimeout(timer);
|
|
1118
|
+
}, [portal]);
|
|
231
1119
|
|
|
232
|
-
|
|
1120
|
+
const typeStyles = {
|
|
1121
|
+
success: { bg: '#F0FDF4', border: '#22C55E', text: '#15803D' },
|
|
1122
|
+
info: { bg: '#EFF6FF', border: '#3B82F6', text: '#1D4ED8' },
|
|
1123
|
+
warning: { bg: '#FFFBEB', border: '#F59E0B', text: '#D97706' },
|
|
1124
|
+
error: { bg: '#FEF2F2', border: '#EF4444', text: '#DC2626' }
|
|
1125
|
+
}[notification.type];
|
|
233
1126
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
1127
|
+
return Center({
|
|
1128
|
+
theme: appTheme,
|
|
1129
|
+
position: 'fixed',
|
|
1130
|
+
top: 0,
|
|
1131
|
+
left: 0,
|
|
1132
|
+
right: 0,
|
|
1133
|
+
bottom: 0,
|
|
1134
|
+
backgroundColor: 'rgba(0,0,0,0.4)',
|
|
1135
|
+
backdropFilter: 'blur(8px)',
|
|
1136
|
+
zIndex: 1000,
|
|
1137
|
+
|
|
1138
|
+
onClick: (e) => {
|
|
1139
|
+
if (e.currentTarget === e.target) portal.unmount();
|
|
1140
|
+
},
|
|
1141
|
+
|
|
1142
|
+
children: Column({
|
|
1143
|
+
backgroundColor: typeStyles.bg,
|
|
1144
|
+
borderRadius: 'theme.spacing.md',
|
|
1145
|
+
padding: 'theme.spacing.xl',
|
|
1146
|
+
margin: 'theme.spacing.lg',
|
|
1147
|
+
maxWidth: '400px',
|
|
1148
|
+
border: `2px solid ${typeStyles.border}`,
|
|
1149
|
+
|
|
1150
|
+
css: {
|
|
1151
|
+
animation: 'modalSlideIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1)',
|
|
1152
|
+
|
|
1153
|
+
'@keyframes modalSlideIn': {
|
|
1154
|
+
from: {
|
|
1155
|
+
opacity: 0,
|
|
1156
|
+
transform: 'scale(0.8) translateY(-40px)'
|
|
1157
|
+
},
|
|
1158
|
+
to: {
|
|
1159
|
+
opacity: 1,
|
|
1160
|
+
transform: 'scale(1) translateY(0)'
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
},
|
|
1164
|
+
|
|
1165
|
+
children: [
|
|
1166
|
+
H2(`${notification.type.toUpperCase()} Notification`, {
|
|
1167
|
+
color: typeStyles.text,
|
|
1168
|
+
fontSize: 'theme.typography.sizes.xl',
|
|
1169
|
+
marginBottom: 'theme.spacing.md',
|
|
1170
|
+
textAlign: 'center',
|
|
1171
|
+
textTransform: 'capitalize'
|
|
1172
|
+
}),
|
|
1173
|
+
|
|
1174
|
+
P(notification.message, {
|
|
1175
|
+
color: typeStyles.text,
|
|
1176
|
+
lineHeight: 1.6,
|
|
1177
|
+
textAlign: 'center',
|
|
1178
|
+
marginBottom: 'theme.spacing.lg'
|
|
1179
|
+
}),
|
|
1180
|
+
|
|
1181
|
+
Row({
|
|
1182
|
+
gap: 'theme.spacing.sm',
|
|
1183
|
+
justifyContent: 'center',
|
|
1184
|
+
children: [
|
|
1185
|
+
Button('Dismiss', {
|
|
1186
|
+
backgroundColor: 'transparent',
|
|
1187
|
+
color: typeStyles.text,
|
|
1188
|
+
border: `1px solid ${typeStyles.border}`,
|
|
1189
|
+
padding: 'theme.spacing.sm theme.spacing.md',
|
|
1190
|
+
borderRadius: 'theme.spacing.xs',
|
|
1191
|
+
cursor: 'pointer',
|
|
1192
|
+
onClick: portal.unmount
|
|
1193
|
+
}),
|
|
1194
|
+
|
|
1195
|
+
Button('OK', {
|
|
1196
|
+
backgroundColor: typeStyles.border,
|
|
1197
|
+
color: 'white',
|
|
1198
|
+
padding: 'theme.spacing.sm theme.spacing.md',
|
|
1199
|
+
borderRadius: 'theme.spacing.xs',
|
|
1200
|
+
cursor: 'pointer',
|
|
1201
|
+
fontWeight: 'bold',
|
|
1202
|
+
onClick: portal.unmount
|
|
1203
|
+
})
|
|
1204
|
+
]
|
|
1205
|
+
})
|
|
1206
|
+
]
|
|
1207
|
+
})
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
);
|
|
1211
|
+
|
|
1212
|
+
// Main application component
|
|
1213
|
+
const MeoNodeShowcase = Component(() => {
|
|
1214
|
+
const [selectedMessage, setSelectedMessage] = useState('');
|
|
1215
|
+
const [currentTheme, setCurrentTheme] = useState<'light' | 'dark'>('light');
|
|
1216
|
+
|
|
1217
|
+
const surprises = [
|
|
1218
|
+
'MeoNode makes UI development delightful! ๐',
|
|
1219
|
+
'Intuitive design meets pure simplicity.',
|
|
1220
|
+
'Build beautiful interfaces effortlessly with type safety.',
|
|
1221
|
+
'Composable, theme-aware, and a joy to use!'
|
|
1222
|
+
];
|
|
1223
|
+
|
|
1224
|
+
const getRandomSurprise = () => {
|
|
1225
|
+
const randomMessage = surprises[Math.floor(Math.random() * surprises.length)];
|
|
1226
|
+
setSelectedMessage(randomMessage);
|
|
1227
|
+
};
|
|
1228
|
+
|
|
1229
|
+
const showNotification = (type: 'success' | 'info' | 'warning' | 'error') => {
|
|
1230
|
+
const notification = notifications.find(n => n.type === type);
|
|
1231
|
+
if (notification) {
|
|
1232
|
+
NotificationModal({ notification });
|
|
1233
|
+
}
|
|
1234
|
+
};
|
|
1235
|
+
|
|
1236
|
+
return Root({
|
|
1237
|
+
theme: appTheme,
|
|
1238
|
+
backgroundColor: currentTheme === 'light' ? 'theme.base.default' : '#1a1a1a',
|
|
1239
|
+
color: currentTheme === 'light' ? 'theme.base.content' : '#ffffff',
|
|
1240
|
+
minHeight: '100vh',
|
|
1241
|
+
fontFamily: 'theme.typography.family',
|
|
1242
|
+
|
|
1243
|
+
children: Center({
|
|
1244
|
+
padding: 'theme.spacing.2xl',
|
|
1245
|
+
children: Column({
|
|
1246
|
+
gap: 'theme.spacing.xl',
|
|
1247
|
+
maxWidth: '800px',
|
|
1248
|
+
textAlign: 'center',
|
|
1249
|
+
|
|
1250
|
+
css: {
|
|
1251
|
+
// Global button styling
|
|
1252
|
+
'& button': {
|
|
1253
|
+
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
|
1254
|
+
border: 'none',
|
|
1255
|
+
outline: 'none'
|
|
1256
|
+
},
|
|
1257
|
+
|
|
1258
|
+
'& button:hover': {
|
|
1259
|
+
transform: 'translateY(-2px)',
|
|
1260
|
+
boxShadow: '0 8px 25px rgba(0,0,0,0.15)'
|
|
1261
|
+
},
|
|
1262
|
+
|
|
1263
|
+
'& button:active': {
|
|
1264
|
+
transform: 'translateY(0)',
|
|
1265
|
+
transition: 'transform 0.1s ease'
|
|
1266
|
+
},
|
|
1267
|
+
|
|
1268
|
+
// Responsive adjustments
|
|
1269
|
+
'@media (max-width: 768px)': {
|
|
1270
|
+
gap: 'theme.spacing.lg',
|
|
1271
|
+
padding: 'theme.spacing.lg',
|
|
1272
|
+
|
|
1273
|
+
'& h1': {
|
|
1274
|
+
fontSize: 'theme.typography.sizes.3xl !important'
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
},
|
|
1278
|
+
|
|
1279
|
+
children: [
|
|
1280
|
+
// Header section
|
|
1281
|
+
Column({
|
|
1282
|
+
gap: 'theme.spacing.md',
|
|
1283
|
+
children: [
|
|
1284
|
+
H1('MeoNode UI Showcase', {
|
|
1285
|
+
fontSize: 'theme.typography.sizes.4xl',
|
|
1286
|
+
background: 'linear-gradient(135deg, theme.colors.primary.default, theme.colors.secondary.default)',
|
|
1287
|
+
backgroundClip: 'text',
|
|
1288
|
+
WebkitBackgroundClip: 'text',
|
|
1289
|
+
WebkitTextFillColor: 'transparent',
|
|
1290
|
+
fontWeight: 'bold'
|
|
1291
|
+
}),
|
|
1292
|
+
|
|
1293
|
+
P('Experience the power of function-based React components with advanced theming and CSS capabilities.', {
|
|
1294
|
+
fontSize: 'theme.typography.sizes.lg',
|
|
1295
|
+
color: currentTheme === 'light' ? 'theme.base.content' : '#cccccc',
|
|
1296
|
+
lineHeight: 1.6,
|
|
1297
|
+
maxWidth: '600px',
|
|
1298
|
+
margin: '0 auto'
|
|
1299
|
+
})
|
|
1300
|
+
]
|
|
1301
|
+
}),
|
|
1302
|
+
|
|
1303
|
+
// Interactive message display
|
|
1304
|
+
Div({
|
|
1305
|
+
padding: 'theme.spacing.lg',
|
|
1306
|
+
backgroundColor: currentTheme === 'light' ? 'white' : '#2a2a2a',
|
|
1307
|
+
borderRadius: 'theme.spacing.md',
|
|
1308
|
+
border: currentTheme === 'light' ? '1px solid theme.base.border' : '1px solid #404040',
|
|
1309
|
+
minHeight: '80px',
|
|
1310
|
+
display: 'flex',
|
|
1311
|
+
alignItems: 'center',
|
|
1312
|
+
justifyContent: 'center',
|
|
1313
|
+
|
|
1314
|
+
css: {
|
|
1315
|
+
transition: 'all 0.3s ease',
|
|
1316
|
+
'&:hover': {
|
|
1317
|
+
backgroundColor: currentTheme === 'light' ? '#fafafa' : '#333333'
|
|
1318
|
+
}
|
|
1319
|
+
},
|
|
1320
|
+
|
|
1321
|
+
children: Text(selectedMessage || 'Click a button below to see MeoNode in action! โจ', {
|
|
1322
|
+
fontSize: 'theme.typography.sizes.lg',
|
|
1323
|
+
color: currentTheme === 'light' ? 'theme.base.content' : '#ffffff',
|
|
1324
|
+
textAlign: 'center',
|
|
1325
|
+
lineHeight: 1.5
|
|
1326
|
+
})
|
|
1327
|
+
}),
|
|
1328
|
+
|
|
1329
|
+
// Action buttons grid
|
|
1330
|
+
Row({
|
|
1331
|
+
gap: 'theme.spacing.md',
|
|
1332
|
+
justifyContent: 'center',
|
|
1333
|
+
flexWrap: 'wrap',
|
|
1334
|
+
children: [
|
|
1335
|
+
Button('๐ Random Surprise', {
|
|
1336
|
+
backgroundColor: 'theme.colors.primary.default',
|
|
1337
|
+
color: 'theme.colors.primary.content',
|
|
1338
|
+
padding: 'theme.spacing.md theme.spacing.lg',
|
|
1339
|
+
borderRadius: 'theme.spacing.sm',
|
|
1340
|
+
fontSize: 'theme.typography.sizes.base',
|
|
1341
|
+
fontWeight: 'bold',
|
|
1342
|
+
cursor: 'pointer',
|
|
1343
|
+
onClick: getRandomSurprise
|
|
1344
|
+
}),
|
|
1345
|
+
|
|
1346
|
+
Button('๐ Toggle Theme', {
|
|
1347
|
+
backgroundColor: currentTheme === 'light' ? '#333333' : '#ffffff',
|
|
1348
|
+
color: currentTheme === 'light' ? '#ffffff' : '#333333',
|
|
1349
|
+
padding: 'theme.spacing.md theme.spacing.lg',
|
|
1350
|
+
borderRadius: 'theme.spacing.sm',
|
|
1351
|
+
fontSize: 'theme.typography.sizes.base',
|
|
1352
|
+
fontWeight: 'bold',
|
|
1353
|
+
cursor: 'pointer',
|
|
1354
|
+
onClick: () => setCurrentTheme(t => t === 'light' ? 'dark' : 'light')
|
|
1355
|
+
})
|
|
1356
|
+
]
|
|
1357
|
+
}),
|
|
1358
|
+
|
|
1359
|
+
// Notification demo buttons
|
|
1360
|
+
Column({
|
|
1361
|
+
gap: 'theme.spacing.sm',
|
|
1362
|
+
children: [
|
|
1363
|
+
H2('Portal Notifications Demo', {
|
|
1364
|
+
fontSize: 'theme.typography.sizes.xl',
|
|
1365
|
+
marginBottom: 'theme.spacing.md',
|
|
1366
|
+
color: currentTheme === 'light' ? 'theme.base.content' : '#ffffff'
|
|
1367
|
+
}),
|
|
1368
|
+
|
|
1369
|
+
Row({
|
|
1370
|
+
gap: 'theme.spacing.sm',
|
|
1371
|
+
justifyContent: 'center',
|
|
1372
|
+
flexWrap: 'wrap',
|
|
1373
|
+
children: [
|
|
1374
|
+
Button('โ
Success', {
|
|
1375
|
+
backgroundColor: 'theme.colors.semantic.success',
|
|
1376
|
+
color: 'white',
|
|
1377
|
+
padding: 'theme.spacing.sm theme.spacing.md',
|
|
1378
|
+
borderRadius: 'theme.spacing.xs',
|
|
1379
|
+
cursor: 'pointer',
|
|
1380
|
+
onClick: () => showNotification('success')
|
|
1381
|
+
}),
|
|
1382
|
+
|
|
1383
|
+
Button('โน๏ธ Info', {
|
|
1384
|
+
backgroundColor: 'theme.colors.semantic.info',
|
|
1385
|
+
color: 'white',
|
|
1386
|
+
padding: 'theme.spacing.sm theme.spacing.md',
|
|
1387
|
+
borderRadius: 'theme.spacing.xs',
|
|
1388
|
+
cursor: 'pointer',
|
|
1389
|
+
onClick: () => showNotification('info')
|
|
1390
|
+
}),
|
|
1391
|
+
|
|
1392
|
+
Button('โ ๏ธ Warning', {
|
|
1393
|
+
backgroundColor: 'theme.colors.semantic.warning',
|
|
1394
|
+
color: 'white',
|
|
1395
|
+
padding: 'theme.spacing.sm theme.spacing.md',
|
|
1396
|
+
borderRadius: 'theme.spacing.xs',
|
|
1397
|
+
cursor: 'pointer',
|
|
1398
|
+
onClick: () => showNotification('warning')
|
|
1399
|
+
}),
|
|
1400
|
+
|
|
1401
|
+
Button('โ Error', {
|
|
1402
|
+
backgroundColor: 'theme.colors.semantic.error',
|
|
1403
|
+
color: 'white',
|
|
1404
|
+
padding: 'theme.spacing.sm theme.spacing.md',
|
|
1405
|
+
borderRadius: 'theme.spacing.xs',
|
|
1406
|
+
cursor: 'pointer',
|
|
1407
|
+
onClick: () => showNotification('error')
|
|
1408
|
+
})
|
|
1409
|
+
]
|
|
1410
|
+
})
|
|
1411
|
+
]
|
|
1412
|
+
})
|
|
1413
|
+
]
|
|
1414
|
+
})
|
|
1415
|
+
})
|
|
1416
|
+
});
|
|
1417
|
+
});
|
|
1418
|
+
|
|
1419
|
+
export default MeoNodeShowcase;
|
|
1420
|
+
```
|
|
1421
|
+
|
|
1422
|
+
-----
|
|
1423
|
+
|
|
1424
|
+
## ๐ Best Practices
|
|
1425
|
+
|
|
1426
|
+
### 1\. **Theme Organization**
|
|
1427
|
+
|
|
1428
|
+
- Structure themes hierarchically with logical groupings
|
|
1429
|
+
- Use semantic naming for colors (primary, secondary, success, etc.)
|
|
1430
|
+
- Include both light and dark variants in your theme system
|
|
1431
|
+
- Define spacing and typography scales for consistency
|
|
1432
|
+
|
|
1433
|
+
### 2\. **Component Composition**
|
|
1434
|
+
|
|
1435
|
+
- Keep components focused and single-purpose
|
|
1436
|
+
- Use the `Component` wrapper for reusable elements
|
|
1437
|
+
- Leverage prop spreading for flexible component APIs
|
|
1438
|
+
- Combine multiple simple components to create complex interfaces
|
|
1439
|
+
|
|
1440
|
+
### 3\. **Performance Optimization**
|
|
1441
|
+
|
|
1442
|
+
- Use React.memo for expensive components when needed
|
|
1443
|
+
- Leverage the built-in tree-shaking capabilities
|
|
1444
|
+
- Minimize theme object recreations in render functions
|
|
1445
|
+
- Use CSS-in-JS patterns responsibly for optimal performance
|
|
1446
|
+
|
|
1447
|
+
### 4\. **CSS Architecture**
|
|
1448
|
+
|
|
1449
|
+
- Prefer theme values over hardcoded styles
|
|
1450
|
+
- Use the `css` property for complex selectors and animations
|
|
1451
|
+
- Implement responsive design through media queries
|
|
1452
|
+
- Maintain consistent hover and focus states across components
|
|
1453
|
+
|
|
1454
|
+
-----
|
|
1455
|
+
|
|
1456
|
+
## ๐ Integration Examples
|
|
1457
|
+
|
|
1458
|
+
### Next.js Integration
|
|
1459
|
+
|
|
1460
|
+
```tsx
|
|
1461
|
+
// app/layout.tsx
|
|
1462
|
+
import { Component, Root } from '@meonode/ui';
|
|
1463
|
+
import { theme } from './theme';
|
|
1464
|
+
|
|
1465
|
+
const RootLayout = Component<{ children: React.ReactNode }>(({ children }) =>
|
|
1466
|
+
Root({
|
|
1467
|
+
theme,
|
|
1468
|
+
className: 'min-h-screen',
|
|
1469
|
+
children
|
|
1470
|
+
})
|
|
1471
|
+
);
|
|
1472
|
+
```
|
|
1473
|
+
|
|
1474
|
+
### Redux Integration
|
|
1475
|
+
|
|
1476
|
+
```tsx
|
|
1477
|
+
import { Provider } from 'react-redux';
|
|
1478
|
+
import { Node } from '@meonode/ui';
|
|
1479
|
+
import store from './store';
|
|
1480
|
+
|
|
1481
|
+
const ReduxProvider = Node(Provider, { store });
|
|
1482
|
+
|
|
1483
|
+
const AppWithRedux = Component(() =>
|
|
1484
|
+
ReduxProvider({
|
|
1485
|
+
children: YourApp()
|
|
1486
|
+
})
|
|
1487
|
+
);
|
|
1488
|
+
```
|
|
239
1489
|
|
|
240
|
-
|
|
1490
|
+
-----
|
|
241
1491
|
|
|
242
|
-
##
|
|
1492
|
+
## ๐ Community & Resources
|
|
243
1493
|
|
|
244
|
-
|
|
1494
|
+
### Example Repository
|
|
245
1495
|
|
|
246
|
-
|
|
247
|
-
2. **Clone** your fork: `git clone https://github.com/your-username/meonode-ui.git`
|
|
248
|
-
3. **Install dependencies**: `yarn install` (or npm/pnpm)
|
|
249
|
-
4. **Create a feature branch**: `git checkout -b feature/amazing-feature`
|
|
250
|
-
5. **Commit changes** with descriptive messages
|
|
251
|
-
6. **Push** to your branch: `git push origin feature/amazing-feature`
|
|
252
|
-
7. **Open a Pull Request**
|
|
1496
|
+
Explore a complete Next.js application showcasing MeoNode UI best practices...
|
|
253
1497
|
|
|
254
|
-
|
|
1498
|
+
**[๐ MeoNode + Next.js Example](https://github.com/l7aromeo/react-meonode)**
|
|
1499
|
+
**[๐ Open in CodeSandbox](https://codesandbox.io/p/github/l7aromeo/react-meonode/main?import=true)**
|
|
1500
|
+
|
|
1501
|
+
This repository demonstrates:
|
|
1502
|
+
|
|
1503
|
+
- Server Component integration
|
|
1504
|
+
- Redux state management with preloaded state
|
|
1505
|
+
- Responsive design patterns
|
|
1506
|
+
- Advanced portal usage
|
|
1507
|
+
- Theme system implementation
|
|
1508
|
+
|
|
1509
|
+
-----
|
|
1510
|
+
|
|
1511
|
+
## ๐ค Contributing
|
|
1512
|
+
|
|
1513
|
+
We welcome contributions from the community\! Here's how to get started:
|
|
1514
|
+
|
|
1515
|
+
### Development Setup
|
|
1516
|
+
|
|
1517
|
+
```bash
|
|
1518
|
+
# 1. Fork and clone the repository
|
|
1519
|
+
git clone https://github.com/your-username/meonode-ui.git
|
|
1520
|
+
cd meonode-ui
|
|
255
1521
|
|
|
256
|
-
|
|
1522
|
+
# 2. Install dependencies
|
|
1523
|
+
npm install
|
|
1524
|
+
# or
|
|
1525
|
+
yarn install
|
|
1526
|
+
# or
|
|
1527
|
+
pnpm install
|
|
1528
|
+
|
|
1529
|
+
# 3. Start development server
|
|
1530
|
+
npm run dev
|
|
1531
|
+
|
|
1532
|
+
# 4. Run tests
|
|
1533
|
+
npm test
|
|
1534
|
+
|
|
1535
|
+
# 5. Build the project
|
|
1536
|
+
npm run build
|
|
1537
|
+
```
|
|
1538
|
+
|
|
1539
|
+
### Contribution Guidelines
|
|
1540
|
+
|
|
1541
|
+
1. **๐ด Fork** the repository and create your feature branch
|
|
1542
|
+
2. **๐ง Install dependencies** and ensure tests pass
|
|
1543
|
+
3. **โจ Create your feature** with comprehensive tests
|
|
1544
|
+
4. **๐ Update documentation** for any new features
|
|
1545
|
+
5. **๐งช Test thoroughly** across different scenarios
|
|
1546
|
+
6. **๐ค Submit a Pull Request** with a clear description
|
|
1547
|
+
|
|
1548
|
+
### Development Standards
|
|
1549
|
+
|
|
1550
|
+
- **TypeScript First** - All code must include proper type definitions
|
|
1551
|
+
- **Test Coverage** - Maintain \>95% test coverage for new features
|
|
1552
|
+
- **Documentation** - Update README and examples for new features
|
|
1553
|
+
- **Performance** - Ensure no performance regressions
|
|
1554
|
+
- **Accessibility** - Follow WCAG guidelines for new components
|
|
1555
|
+
|
|
1556
|
+
For major changes or new features, please open an issue first to discuss your proposal with the maintainers.
|
|
1557
|
+
|
|
1558
|
+
-----
|
|
1559
|
+
|
|
1560
|
+
## ๐ License & Support
|
|
257
1561
|
|
|
258
1562
|
**MIT Licensed** | Copyright ยฉ 2024 Ukasyah Rahmatullah Zada
|
|
259
|
-
|
|
1563
|
+
|
|
1564
|
+
### Getting Help
|
|
1565
|
+
|
|
1566
|
+
- ๐ **Documentation**: Full API reference and examples
|
|
1567
|
+
- ๐ **Issues**: [GitHub Issues](https://github.com/l7aromeo/meonode-ui/issues)
|
|
1568
|
+
- ๐ฌ **Discussions**: [GitHub Discussions](https://github.com/l7aromeo/meonode-ui/discussions)
|
|
1569
|
+
- ๐ง **Discord**: [l7aromeo](https://discord.com/users/704803255561224264)
|
|
1570
|
+
|
|
1571
|
+
-----
|
|
1572
|
+
|
|
1573
|
+
*Empowering developers to build exceptional UIs with type-safe, theme-aware, function-based React components.*
|
|
1574
|
+
|
|
1575
|
+
**MeoNode UI - Where Function Meets Beauty** โจ
|