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