@meonode/ui 0.1.73 → 0.1.75
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 +30 -467
- package/dist/hoc/component.hoc.d.ts +1 -0
- package/dist/hoc/component.hoc.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://bundlephobia.com/package/@meonode/ui)
|
|
6
6
|
|
|
7
|
-
**Build React UIs with Type-Safe Fluency**
|
|
7
|
+
**Build React UIs with Type-Safe Fluency**
|
|
8
8
|
A structured approach to component composition with built-in theming, prop separation, and dynamic children handling.
|
|
9
9
|
|
|
10
10
|
```tsx
|
|
@@ -117,10 +117,10 @@ const ThemedCard = Component(() =>
|
|
|
117
117
|
backgroundColor: 'theme.colors.primary',
|
|
118
118
|
children: [
|
|
119
119
|
H1('Themed Title', { color: 'theme.colors.text.primary' }),
|
|
120
|
-
P('Content...', { color: 'theme.colors.text.secondary' })
|
|
120
|
+
P('Content...', { color: 'theme.colors.text.secondary' })
|
|
121
121
|
]
|
|
122
122
|
})
|
|
123
|
-
)
|
|
123
|
+
)
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
### 3. Prop Handling
|
|
@@ -128,20 +128,24 @@ const ThemedCard = Component(() =>
|
|
|
128
128
|
Automatic separation of CSS props from DOM attributes:
|
|
129
129
|
|
|
130
130
|
```tsx
|
|
131
|
-
|
|
131
|
+
type User = {
|
|
132
|
+
name: string
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const ProfileCard = Component<{ user: User }>(({ user }) =>
|
|
132
136
|
Div({
|
|
133
137
|
// CSS Props
|
|
134
138
|
padding: '20px',
|
|
135
139
|
borderRadius: '8px',
|
|
136
|
-
|
|
140
|
+
|
|
137
141
|
// DOM Props
|
|
138
142
|
'aria-role': 'article',
|
|
139
143
|
tabIndex: 0,
|
|
140
|
-
|
|
144
|
+
|
|
141
145
|
// Children
|
|
142
146
|
children: `Welcome ${user.name}!`
|
|
143
147
|
})
|
|
144
|
-
)
|
|
148
|
+
)
|
|
145
149
|
```
|
|
146
150
|
|
|
147
151
|
---
|
|
@@ -158,463 +162,6 @@ const ProfileCard = Component(({ user }) =>
|
|
|
158
162
|
|
|
159
163
|
---
|
|
160
164
|
|
|
161
|
-
## Advanced Patterns
|
|
162
|
-
|
|
163
|
-
### Component Composition
|
|
164
|
-
|
|
165
|
-
```tsx
|
|
166
|
-
const Dashboard = Component(() =>
|
|
167
|
-
Div({
|
|
168
|
-
display: 'grid',
|
|
169
|
-
gridTemplateColumns: '1fr 3fr',
|
|
170
|
-
gap: '20px',
|
|
171
|
-
children: [
|
|
172
|
-
Sidebar({ width: '240px' }),
|
|
173
|
-
MainContent({
|
|
174
|
-
padding: '40px',
|
|
175
|
-
children: AnalyticsChart({ dataset })
|
|
176
|
-
})
|
|
177
|
-
]
|
|
178
|
-
})
|
|
179
|
-
);
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### Material UI Integration
|
|
183
|
-
|
|
184
|
-
```bash
|
|
185
|
-
yarn add @meonode/mui @mui/material
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
```tsx
|
|
189
|
-
import { Button, TextField } from '@meonode/mui';
|
|
190
|
-
|
|
191
|
-
const LoginForm = Component(() =>
|
|
192
|
-
Div({
|
|
193
|
-
maxWidth: '400px',
|
|
194
|
-
margin: '0 auto',
|
|
195
|
-
children: [
|
|
196
|
-
TextField({ label: 'Email', fullWidth: true }),
|
|
197
|
-
TextField({ label: 'Password', type: 'password' }),
|
|
198
|
-
Button({
|
|
199
|
-
variant: 'contained',
|
|
200
|
-
children: 'Sign In'
|
|
201
|
-
})
|
|
202
|
-
]
|
|
203
|
-
})
|
|
204
|
-
);
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
## Comprehensive Example: Theme-Switching & Conditional Components
|
|
208
|
-
|
|
209
|
-
```tsx
|
|
210
|
-
'use client'
|
|
211
|
-
/**
|
|
212
|
-
* This file showcases the integration of React hooks with `@meonode/ui` components
|
|
213
|
-
* for building declarative user interfaces. It demonstrates different rendering
|
|
214
|
-
* approaches, the use of Higher-Order Components (HOCs), and how theme context
|
|
215
|
-
* is managed and propagated within the @meonode/ui component tree.
|
|
216
|
-
*/
|
|
217
|
-
import {
|
|
218
|
-
Button,
|
|
219
|
-
Center,
|
|
220
|
-
Column,
|
|
221
|
-
Component,
|
|
222
|
-
Fixed,
|
|
223
|
-
Node,
|
|
224
|
-
type NodeInstance,
|
|
225
|
-
P,
|
|
226
|
-
Portal,
|
|
227
|
-
Row,
|
|
228
|
-
type Theme
|
|
229
|
-
} from '@meonode/ui'
|
|
230
|
-
import { useState, useEffect, ReactElement, ReactNode } from 'react'
|
|
231
|
-
import { CssBaseline, FormControlLabel, TextField } from '@meonode/mui'
|
|
232
|
-
import { Switch as MUISwitch } from '@mui/material'
|
|
233
|
-
import { styled } from '@mui/material'
|
|
234
|
-
|
|
235
|
-
// Styled Material UI Switch component for theme toggling
|
|
236
|
-
const MaterialUISwitch = styled(MUISwitch)(({ theme }) => ({
|
|
237
|
-
width: 62,
|
|
238
|
-
height: 34,
|
|
239
|
-
padding: 7,
|
|
240
|
-
'& .MuiSwitch-switchBase': {
|
|
241
|
-
margin: 1,
|
|
242
|
-
padding: 0,
|
|
243
|
-
transform: 'translateX(6px)',
|
|
244
|
-
'&.Mui-checked': {
|
|
245
|
-
color: '#fff',
|
|
246
|
-
transform: 'translateX(22px)',
|
|
247
|
-
'& .MuiSwitch-thumb:before': {
|
|
248
|
-
backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 20 20"><path fill="${encodeURIComponent(
|
|
249
|
-
'#fff',
|
|
250
|
-
)}" d="M4.2 2.5l-.7 1.8-1.8.7 1.8.7.7 1.8.6-1.8L6.7 5l-1.9-.7-.6-1.8zm15 8.3a6.7 6.7 0 11-6.6-6.6 5.8 5.8 0 006.6 6.6z"/></svg>')`,
|
|
251
|
-
},
|
|
252
|
-
'& + .MuiSwitch-track': {
|
|
253
|
-
opacity: 1,
|
|
254
|
-
backgroundColor: '#aab4be',
|
|
255
|
-
...theme.applyStyles('dark', {
|
|
256
|
-
backgroundColor: '#8796A5',
|
|
257
|
-
}),
|
|
258
|
-
},
|
|
259
|
-
},
|
|
260
|
-
},
|
|
261
|
-
'& .MuiSwitch-thumb': {
|
|
262
|
-
backgroundColor: '#001e3c',
|
|
263
|
-
width: 32,
|
|
264
|
-
height: 32,
|
|
265
|
-
'&::before': {
|
|
266
|
-
content: "''",
|
|
267
|
-
position: 'absolute',
|
|
268
|
-
width: '100%',
|
|
269
|
-
height: '100%',
|
|
270
|
-
left: 0,
|
|
271
|
-
top: 0,
|
|
272
|
-
backgroundRepeat: 'no-repeat',
|
|
273
|
-
backgroundPosition: 'center',
|
|
274
|
-
backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 20 20"><path fill="${encodeURIComponent(
|
|
275
|
-
'#fff',
|
|
276
|
-
)}" d="M9.305 1.667V3.75h1.389V1.667h-1.39zm-4.707 1.95l-.982.982L5.09 6.072l.982-.982-1.473-1.473zm10.802 0L13.927 5.09l.982.982 1.473-1.473-.982-.982zM10 5.139a4.872 4.872 0 00-4.862 4.86A4.872 4.872 0 0010 14.862 4.872 4.872 0 0014.86 10 4.872 4.872 0 0010 5.139zm0 1.389A3.462 3.462 0 0113.471 10a3.462 3.462 0 01-3.473 3.472A3.462 3.462 0 016.527 10 3.462 3.462 0 0110 6.528zM1.665 9.305v1.39h2.083v-1.39H1.666zm14.583 0v1.39h2.084v-1.39h-2.084zM5.09 13.928L3.616 15.4l.982.982 1.473-1.473-.982-.982zm9.82 0l-.982.982 1.473 1.473.982-.982-1.473-1.473zM9.305 16.25v2.083h1.389V16.25h-1.39z"/></svg>')`,
|
|
277
|
-
},
|
|
278
|
-
...theme.applyStyles('dark', {
|
|
279
|
-
backgroundColor: '#003892',
|
|
280
|
-
}),
|
|
281
|
-
},
|
|
282
|
-
'& .MuiSwitch-track': {
|
|
283
|
-
opacity: 1,
|
|
284
|
-
backgroundColor: '#aab4be',
|
|
285
|
-
borderRadius: 20 / 2,
|
|
286
|
-
...theme.applyStyles('dark', {
|
|
287
|
-
backgroundColor: '#8796A5',
|
|
288
|
-
}),
|
|
289
|
-
},
|
|
290
|
-
}))
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Light theme configuration containing color palette values.
|
|
294
|
-
* Used by `@meonode/ui` components when resolving theme references in light mode.
|
|
295
|
-
*/
|
|
296
|
-
const lightTheme: Theme = {
|
|
297
|
-
mode: 'light',
|
|
298
|
-
colors: {
|
|
299
|
-
primary: '#2563eb',
|
|
300
|
-
secondary: '#64748b',
|
|
301
|
-
accent: '#10b981',
|
|
302
|
-
background: '#ffffff',
|
|
303
|
-
foreground: '#0f172a',
|
|
304
|
-
border: '#e2e8f0',
|
|
305
|
-
muted: '#f8fafc',
|
|
306
|
-
success: '#16a34a',
|
|
307
|
-
warning: '#eab308',
|
|
308
|
-
danger: '#dc2626',
|
|
309
|
-
},
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Dark theme configuration containing color palette values.
|
|
314
|
-
* Used by `@meonode/ui` components when resolving theme references in dark mode.
|
|
315
|
-
*/
|
|
316
|
-
const darkTheme: Theme = {
|
|
317
|
-
mode: 'dark',
|
|
318
|
-
colors: {
|
|
319
|
-
primary: '#3b82f6',
|
|
320
|
-
secondary: '#94a3b8',
|
|
321
|
-
accent: '#34d399',
|
|
322
|
-
background: '#0f172a',
|
|
323
|
-
foreground: '#f8fafc',
|
|
324
|
-
border: '#334155',
|
|
325
|
-
muted: '#1e293b',
|
|
326
|
-
success: '#22c55e',
|
|
327
|
-
warning: '#facc15',
|
|
328
|
-
danger: '#ef4444',
|
|
329
|
-
},
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Main page component using React hooks for state management.
|
|
334
|
-
* Manages theme mode and visibility of additional content sections.
|
|
335
|
-
* Wrapped by `Component` HOC to ensure React compatibility and SSR/CSR support.
|
|
336
|
-
*/
|
|
337
|
-
export default Component(() => {
|
|
338
|
-
const [showMore, setShowDetails] = useState(false)
|
|
339
|
-
const [mode, setMode] = useState<'dark' | 'light'>('light')
|
|
340
|
-
const theme = mode === 'dark' ? darkTheme : lightTheme
|
|
341
|
-
|
|
342
|
-
return Column({
|
|
343
|
-
theme: theme.colors,
|
|
344
|
-
padding: 20,
|
|
345
|
-
gap: 15,
|
|
346
|
-
minHeight: '100vh',
|
|
347
|
-
backgroundColor: 'theme.background',
|
|
348
|
-
color: 'theme.foreground',
|
|
349
|
-
children: [
|
|
350
|
-
CssBaseline,
|
|
351
|
-
Center({
|
|
352
|
-
children: FormControlLabel({
|
|
353
|
-
control: Node(MaterialUISwitch).render() as ReactElement,
|
|
354
|
-
alignItems: 'center',
|
|
355
|
-
label: mode === 'dark' ? 'Dark Mode' : 'Light Mode',
|
|
356
|
-
labelPlacement: 'start',
|
|
357
|
-
checked: mode === 'dark',
|
|
358
|
-
onChange: () => setMode(prev => (prev === 'dark' ? 'light' : 'dark')),
|
|
359
|
-
}),
|
|
360
|
-
}),
|
|
361
|
-
Button('Show Modal', {
|
|
362
|
-
onClick: () => Modal({ theme: theme.colors }),
|
|
363
|
-
cursor: 'pointer',
|
|
364
|
-
userSelect: 'none',
|
|
365
|
-
padding: '10px 20px',
|
|
366
|
-
backgroundColor: 'theme.primary',
|
|
367
|
-
borderRadius: 5,
|
|
368
|
-
fontWeight: 'bold',
|
|
369
|
-
color: 'white',
|
|
370
|
-
}),
|
|
371
|
-
Button(showMore ? 'Hide Details' : 'Show More Details', {
|
|
372
|
-
onClick: () => setShowDetails(prev => !prev),
|
|
373
|
-
cursor: 'pointer',
|
|
374
|
-
userSelect: 'none',
|
|
375
|
-
padding: '10px 20px',
|
|
376
|
-
backgroundColor: 'theme.accent',
|
|
377
|
-
borderRadius: 5,
|
|
378
|
-
fontWeight: 'bold',
|
|
379
|
-
color: 'white',
|
|
380
|
-
}),
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Component rendering examples demonstrating theme context propagation:
|
|
384
|
-
* - Direct Node instance rendering
|
|
385
|
-
* - Rendered Node instances
|
|
386
|
-
* - ReactNode components
|
|
387
|
-
* - HOC usage patterns
|
|
388
|
-
*/
|
|
389
|
-
DetailComponent({
|
|
390
|
-
info: 'Detail 1: Rendering a component that returns a @meonode/ui Node instance directly. The internal Row component correctly receives theme context from the parent Column.',
|
|
391
|
-
}),
|
|
392
|
-
|
|
393
|
-
DetailComponent({
|
|
394
|
-
info: 'Detail 2: Rendering a component that returns a @meonode/ui Node instance, then calling .render(). The internal Row component also correctly receives theme context.',
|
|
395
|
-
}).render(),
|
|
396
|
-
|
|
397
|
-
// ❌ Fails: The Node HOC expects the wrapped function to return a ReactNode, not a @meonode/ui Node instance.
|
|
398
|
-
// Node(DetailComponent, { info: 'Detail 3: Attempting to wrap a component returning a Node instance with Node HOC.' }),
|
|
399
|
-
|
|
400
|
-
ReturnRenderedDetailComponent({
|
|
401
|
-
info: 'Detail 4: Rendering a component that explicitly returns a ReactNode (.render() is called internally). The internal Row component correctly receives theme context from the parent Column.',
|
|
402
|
-
}),
|
|
403
|
-
|
|
404
|
-
Node(ReturnRenderedDetailComponent, {
|
|
405
|
-
info: "Detail 5: Wrapping a component returning ReactNode with Node HOC (without .render()). Renders successfully. However, the Node HOC does NOT propagate theme context to the wrapped component's children.",
|
|
406
|
-
}),
|
|
407
|
-
|
|
408
|
-
Node(ReturnRenderedDetailComponent, {
|
|
409
|
-
info: 'Detail 6: Wrapping a component returning ReactNode with Node HOC, then calling .render(). Renders successfully. Theme context is NOT propagated by the Node HOC.',
|
|
410
|
-
}).render(),
|
|
411
|
-
|
|
412
|
-
WrappedDetailComponent({
|
|
413
|
-
info: 'Detail 7: Using Component HOC with Node instance returns. Theme context is correctly propagated.',
|
|
414
|
-
}),
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* Conditional rendering examples (visible when showMore is true)
|
|
418
|
-
* Demonstrates:
|
|
419
|
-
* - Inline function wrappers
|
|
420
|
-
* - Component HOC usage
|
|
421
|
-
* - Theme context propagation patterns
|
|
422
|
-
*/
|
|
423
|
-
showMore &&
|
|
424
|
-
(() =>
|
|
425
|
-
DetailComponent({
|
|
426
|
-
info: 'Detail 8: Conditional rendering of a Node instance component using inline function wrapper. Theme context is correctly received.',
|
|
427
|
-
})),
|
|
428
|
-
|
|
429
|
-
showMore &&
|
|
430
|
-
(() =>
|
|
431
|
-
DetailComponent({
|
|
432
|
-
info: 'Detail 9: Conditional rendering of a Node instance component with .render() call. Theme context is correctly propagated.',
|
|
433
|
-
}).render()),
|
|
434
|
-
|
|
435
|
-
showMore &&
|
|
436
|
-
WrappedDetailComponent({
|
|
437
|
-
info: 'Detail 10: Conditional rendering using Component HOC with Node instance. Theme context is correctly propagated.',
|
|
438
|
-
}),
|
|
439
|
-
|
|
440
|
-
showMore &&
|
|
441
|
-
(() =>
|
|
442
|
-
ReturnRenderedDetailComponent({
|
|
443
|
-
info: 'Detail 11: Conditional rendering of ReactNode component using inline function. Theme context is properly propagated.',
|
|
444
|
-
})),
|
|
445
|
-
|
|
446
|
-
showMore &&
|
|
447
|
-
Component(() =>
|
|
448
|
-
ReturnRenderedDetailComponent({
|
|
449
|
-
info: 'Detail 12: Conditional rendering with Component HOC wrapping ReactNode component. Note that theme context is not propagated in this case.',
|
|
450
|
-
}),
|
|
451
|
-
),
|
|
452
|
-
|
|
453
|
-
// showMore && ReturnRenderedDetailComponent({
|
|
454
|
-
// info: 'Detail 15: Direct component call violates Rules of Hooks!'
|
|
455
|
-
// }), // ❌ Fails: Direct call to a component function using hooks inside render logic without a React-aware wrapper.
|
|
456
|
-
],
|
|
457
|
-
})
|
|
458
|
-
})
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* Styled detail section component returning a Node instance.
|
|
462
|
-
* Uses useEffect for lifecycle logging.
|
|
463
|
-
* Theme context is received from parent @meonode/ui components.
|
|
464
|
-
*/
|
|
465
|
-
const DetailComponent = ({ info }: { info: string }): NodeInstance => {
|
|
466
|
-
useEffect(() => {
|
|
467
|
-
console.log('DetailComponent mounted:', info)
|
|
468
|
-
return () => {
|
|
469
|
-
console.log('DetailComponent unmounted:', info)
|
|
470
|
-
}
|
|
471
|
-
}, [info])
|
|
472
|
-
|
|
473
|
-
return Row({
|
|
474
|
-
alignItems: 'center',
|
|
475
|
-
gap: 10,
|
|
476
|
-
padding: 4,
|
|
477
|
-
border: '2px solid theme.accent',
|
|
478
|
-
borderRadius: 6,
|
|
479
|
-
backgroundColor: 'theme.warning',
|
|
480
|
-
color: 'theme.danger',
|
|
481
|
-
children: [P(info, { flex: 1, padding: '0 20px' }), TextField({ flex: 1, sx: { background: 'theme.primary' } })],
|
|
482
|
-
})
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* Styled detail section component wrapped with Component HOC.
|
|
487
|
-
* Similar to DetailComponent but demonstrates HOC pattern.
|
|
488
|
-
* Theme context is correctly propagated through the HOC.
|
|
489
|
-
*/
|
|
490
|
-
const WrappedDetailComponent = Component(({ info }): NodeInstance => {
|
|
491
|
-
useEffect(() => {
|
|
492
|
-
console.log('DetailComponent mounted')
|
|
493
|
-
return () => {
|
|
494
|
-
console.log('DetailComponent unmounted')
|
|
495
|
-
}
|
|
496
|
-
}, [info])
|
|
497
|
-
|
|
498
|
-
return Row({
|
|
499
|
-
alignItems: 'center',
|
|
500
|
-
gap: 10,
|
|
501
|
-
padding: 4,
|
|
502
|
-
border: '2px solid theme.accent',
|
|
503
|
-
borderRadius: 6,
|
|
504
|
-
backgroundColor: 'theme.warning',
|
|
505
|
-
color: 'theme.danger',
|
|
506
|
-
children: [P(info, { flex: 1, padding: '0 20px' }), TextField({ flex: 1, sx: { background: 'theme.primary' } })],
|
|
507
|
-
})
|
|
508
|
-
})
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
* Alternative detail component implementation returning ReactNode.
|
|
512
|
-
* Explicitly calls .render() for React compatibility.
|
|
513
|
-
* Demonstrates theme context usage with standard React patterns.
|
|
514
|
-
*/
|
|
515
|
-
const ReturnRenderedDetailComponent = ({ info }: { info: string }): ReactNode => {
|
|
516
|
-
useEffect(() => {
|
|
517
|
-
console.log('ReturnRenderedDetailComponent mounted')
|
|
518
|
-
return () => {
|
|
519
|
-
console.log('ReturnRenderedDetailComponent unmounted')
|
|
520
|
-
}
|
|
521
|
-
}, [info])
|
|
522
|
-
|
|
523
|
-
return Row({
|
|
524
|
-
alignItems: 'center',
|
|
525
|
-
gap: 10,
|
|
526
|
-
padding: 4,
|
|
527
|
-
border: '2px solid theme.accent',
|
|
528
|
-
borderRadius: 6,
|
|
529
|
-
backgroundColor: 'theme.warning',
|
|
530
|
-
color: 'theme.danger',
|
|
531
|
-
children: [P(info, { flex: 1, padding: '0 20px' }), TextField({ flex: 1, sx: { background: 'theme.primary' } })],
|
|
532
|
-
}).render()
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
/**
|
|
536
|
-
* Modal component using Portal HOC for DOM placement.
|
|
537
|
-
* Demonstrates theme-aware content rendering outside main hierarchy.
|
|
538
|
-
* Includes nested modal support and Material UI integration.
|
|
539
|
-
*/
|
|
540
|
-
const Modal = Portal(({ theme, portal }) => {
|
|
541
|
-
useEffect(() => {
|
|
542
|
-
console.log('Modal mounted')
|
|
543
|
-
return () => {
|
|
544
|
-
console.log('Modal unmounted')
|
|
545
|
-
}
|
|
546
|
-
}, [])
|
|
547
|
-
|
|
548
|
-
return Fixed({
|
|
549
|
-
theme,
|
|
550
|
-
top: 0,
|
|
551
|
-
left: 0,
|
|
552
|
-
right: 0,
|
|
553
|
-
bottom: 0,
|
|
554
|
-
display: 'flex',
|
|
555
|
-
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
556
|
-
justifyContent: 'center',
|
|
557
|
-
alignItems: 'center',
|
|
558
|
-
onClick: e => {
|
|
559
|
-
if (e.target === e.currentTarget) {
|
|
560
|
-
portal.unmount()
|
|
561
|
-
}
|
|
562
|
-
},
|
|
563
|
-
children: [
|
|
564
|
-
Column({
|
|
565
|
-
width: '50%',
|
|
566
|
-
height: '80%',
|
|
567
|
-
backgroundColor: 'theme.background',
|
|
568
|
-
borderRadius: '8px',
|
|
569
|
-
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
|
|
570
|
-
padding: 10,
|
|
571
|
-
gap: 10,
|
|
572
|
-
color: 'theme.foreground',
|
|
573
|
-
children: [
|
|
574
|
-
Button('More Modal', {
|
|
575
|
-
onClick: () => Modal({ theme }),
|
|
576
|
-
cursor: 'pointer',
|
|
577
|
-
userSelect: 'none',
|
|
578
|
-
padding: '10px 20px',
|
|
579
|
-
backgroundColor: 'theme.primary',
|
|
580
|
-
borderRadius: 5,
|
|
581
|
-
fontWeight: 'bold',
|
|
582
|
-
color: 'white',
|
|
583
|
-
}),
|
|
584
|
-
Center({ fontWeight: 'bold', children: 'Modal' }),
|
|
585
|
-
Center({ children: Math.random() * 1000 }),
|
|
586
|
-
TextField({
|
|
587
|
-
sx: {
|
|
588
|
-
'& .MuiFormLabel-root': {
|
|
589
|
-
color: 'theme.foreground',
|
|
590
|
-
'&.Mui-focused': {
|
|
591
|
-
color: 'theme.foreground',
|
|
592
|
-
},
|
|
593
|
-
},
|
|
594
|
-
'& .MuiOutlinedInput-root': {
|
|
595
|
-
color: 'theme.foreground',
|
|
596
|
-
'& fieldset': {
|
|
597
|
-
borderColor: 'theme.foreground',
|
|
598
|
-
},
|
|
599
|
-
'&:hover fieldset': {
|
|
600
|
-
borderColor: 'theme.foreground',
|
|
601
|
-
},
|
|
602
|
-
'&.Mui-focused fieldset': {
|
|
603
|
-
borderColor: 'theme.foreground',
|
|
604
|
-
},
|
|
605
|
-
borderRadius: 2,
|
|
606
|
-
},
|
|
607
|
-
},
|
|
608
|
-
label: 'Hello',
|
|
609
|
-
fullWidth: true,
|
|
610
|
-
}),
|
|
611
|
-
],
|
|
612
|
-
}),
|
|
613
|
-
],
|
|
614
|
-
})
|
|
615
|
-
})
|
|
616
|
-
```
|
|
617
|
-
|
|
618
165
|
## Passing Context Wrapper To Portal
|
|
619
166
|
```ts
|
|
620
167
|
import { Provider, useSelector } from 'react-redux'
|
|
@@ -664,12 +211,28 @@ const Modal = Portal(ReduxProvider, ({ portal }) => {
|
|
|
664
211
|
console.log('Redux State value: ', someReduxState)
|
|
665
212
|
}, [])
|
|
666
213
|
|
|
667
|
-
|
|
214
|
+
return Div({
|
|
215
|
+
padding: 10,
|
|
216
|
+
backgroundColor: 'white',
|
|
217
|
+
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
|
|
218
|
+
children: [
|
|
219
|
+
P('Content...', { color: 'theme.colors.text.secondary' }),
|
|
220
|
+
Button({ children: 'Close', onClick: () => portal.unmount() })
|
|
221
|
+
]
|
|
222
|
+
})
|
|
668
223
|
})
|
|
669
224
|
```
|
|
670
225
|
|
|
671
226
|
---
|
|
672
227
|
|
|
228
|
+
## Repo Example Usage
|
|
229
|
+
|
|
230
|
+
This section provides a practical example of how to integrate `@meonode/ui` within a Next.js application. The linked repository showcases proper theme handling, especially when utilizing Redux with a preloaded state, and demonstrates its usage within a Server Component (RootLayout) environment. Crucially, it also illustrates how to effectively manage **conditional components that contain React hooks**, providing a robust pattern for dynamic UI rendering. This example is particularly useful for understanding how to set up a robust UI system with `@meonode/ui` in a complex React ecosystem like Next.js.
|
|
231
|
+
|
|
232
|
+
[Example Usage Of React Meonode in NextJS](https://github.com/l7aromeo/react-meonode)
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
673
236
|
## API Reference
|
|
674
237
|
|
|
675
238
|
### Core Functions
|
|
@@ -678,7 +241,7 @@ const Modal = Portal(ReduxProvider, ({ portal }) => {
|
|
|
678
241
|
|-------------|---------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
679
242
|
| `Node` | `element: NodeElement \| React.ComponentType`, `baseProps: object` | Constructs a configurable UI node that supports flexible properties and dynamic styling. |
|
|
680
243
|
| `Component` | `(props: P) => ComponentNode` | Transforms node trees into reusable React components with built-in type safety and seamless integration. |
|
|
681
|
-
| `Portal` | • `(component: (props: P) => ComponentNode)` or <br/> • `(
|
|
244
|
+
| `Portal` | • `(component: (props: P) => ComponentNode)` or <br/> • `(provider: NodeElement, component: (props: P) => ComponentNode)` | Creates a React Portal component. Accepts either a component function directly, or a provider (e.g. Redux Provider) and the component. The component receives portal controls for mounting/unmounting. |
|
|
682
245
|
|
|
683
246
|
---
|
|
684
247
|
|
|
@@ -698,5 +261,5 @@ For major changes, please open an issue first to discuss your proposal.
|
|
|
698
261
|
|
|
699
262
|
---
|
|
700
263
|
|
|
701
|
-
**MIT Licensed** | Copyright © 2024 Ukasyah Rahmatullah Zada
|
|
264
|
+
**MIT Licensed** | Copyright © 2024 Ukasyah Rahmatullah Zada
|
|
702
265
|
*Empowering developers to build better UIs*
|
|
@@ -23,6 +23,7 @@ import type { ReactNode } from 'react';
|
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
25
25
|
export declare function Component<P extends Record<string, any>>(component: (props: P & {
|
|
26
|
+
props?: Partial<Omit<P, 'children'>>;
|
|
26
27
|
children?: NodeElement;
|
|
27
28
|
}) => ComponentNode): (props?: Partial<P>) => ReactNode;
|
|
28
29
|
//# sourceMappingURL=component.hoc.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.hoc.d.ts","sourceRoot":"","sources":["../../src/hoc/component.hoc.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrD,SAAS,EAAE,CACT,KAAK,EAAE,CAAC,GAAG;IACT,QAAQ,CAAC,EAAE,WAAW,CAAA;CACvB,KACE,aAAa,IAiBG,QAAO,OAAO,CAAC,CAAC,CAAM,eAG5C"}
|
|
1
|
+
{"version":3,"file":"component.hoc.d.ts","sourceRoot":"","sources":["../../src/hoc/component.hoc.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrD,SAAS,EAAE,CACT,KAAK,EAAE,CAAC,GAAG;IACT,KAAK,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAA;IACpC,QAAQ,CAAC,EAAE,WAAW,CAAA;CACvB,KACE,aAAa,IAiBG,QAAO,OAAO,CAAC,CAAC,CAAM,eAG5C"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meonode/ui",
|
|
3
3
|
"description": "A structured approach to component composition with built-in theming, prop separation, and dynamic children handling.",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.75",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/main.js",
|
|
7
7
|
"types": "./dist/main.d.ts",
|