@meonode/ui 0.1.19 → 0.1.20
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 +146 -62
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
|
+
[file name]: README.md
|
|
2
|
+
[file content begin]
|
|
1
3
|
# @meonode/ui
|
|
2
4
|
|
|
3
5
|
[](https://www.npmjs.com/package/@meonode/ui)
|
|
4
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://bundlephobia.com/package/@meonode/ui)
|
|
5
8
|
|
|
6
9
|
**Build React UIs with Type-Safe Fluency**
|
|
7
10
|
A structured approach to component composition with built-in theming, prop separation, and dynamic children handling.
|
|
8
11
|
|
|
9
|
-
```
|
|
12
|
+
```tsx
|
|
10
13
|
// Quick Start Example
|
|
11
14
|
import { Component, Div, H1, Button } from '@meonode/ui';
|
|
12
15
|
|
|
16
|
+
// Create a reusable styled component
|
|
13
17
|
const BlueButton = Component((props) =>
|
|
14
18
|
Button('Blue', {
|
|
15
19
|
padding: '12px 24px',
|
|
16
20
|
borderRadius: '8px',
|
|
17
21
|
backgroundColor: 'dodgerblue',
|
|
18
22
|
color: 'white',
|
|
19
|
-
...props
|
|
23
|
+
...props // Merge with incoming props
|
|
20
24
|
})
|
|
21
25
|
);
|
|
22
26
|
|
|
27
|
+
// Compose your app
|
|
23
28
|
const App = Component(() =>
|
|
24
29
|
Div({
|
|
25
30
|
padding: '40px',
|
|
@@ -36,37 +41,45 @@ const App = Component(() =>
|
|
|
36
41
|
|
|
37
42
|
## Why @meonode/ui?
|
|
38
43
|
|
|
39
|
-
- 🎯 **Type-Safe Design** - Full TypeScript support with autocomplete for styles and
|
|
40
|
-
- 🎨 **
|
|
44
|
+
- 🎯 **Type-Safe Design** - Full TypeScript support with autocomplete for styles, props, and theme paths
|
|
45
|
+
- 🎨 **Theme-Aware Styles** - Write styles directly in props with dynamic theme resolution
|
|
41
46
|
- 🧩 **Component-First Architecture** - Compose UIs from structured nodes instead of JSX
|
|
42
|
-
- 🌐 **
|
|
43
|
-
- ⚡ **
|
|
47
|
+
- 🌐 **Contextual Theming** - Theme values propagate automatically through nested components
|
|
48
|
+
- ⚡ **Optimized Bundle** - Efficient core with tree-shaking support
|
|
49
|
+
- 🔄 **Seamless React Integration** - Works with hooks, HOCs, and React 18+ features
|
|
44
50
|
|
|
45
51
|
## Installation
|
|
46
52
|
|
|
47
|
-
```
|
|
53
|
+
```bash
|
|
54
|
+
# Using npm
|
|
48
55
|
npm install @meonode/ui react
|
|
49
|
-
|
|
56
|
+
|
|
57
|
+
# Using yarn
|
|
50
58
|
yarn add @meonode/ui react
|
|
59
|
+
|
|
60
|
+
# Using pnpm
|
|
61
|
+
pnpm add @meonode/ui react
|
|
51
62
|
```
|
|
52
63
|
|
|
64
|
+
---
|
|
65
|
+
|
|
53
66
|
## Core Concepts
|
|
54
67
|
|
|
55
68
|
### 1. Component Creation
|
|
56
69
|
|
|
57
|
-
Create elements using the `Node` factory or pre-built components:
|
|
70
|
+
Create elements using either the `Node` factory or pre-built components:
|
|
58
71
|
|
|
59
|
-
```
|
|
72
|
+
```tsx
|
|
60
73
|
import { Node, Div, H1 } from '@meonode/ui';
|
|
61
74
|
|
|
62
|
-
//
|
|
75
|
+
// Method 1: Node factory for custom elements
|
|
63
76
|
const Card = Node('div', {
|
|
64
77
|
padding: '20px',
|
|
65
78
|
borderRadius: '8px',
|
|
66
79
|
boxShadow: '0 2px 12px rgba(0,0,0,0.1)'
|
|
67
80
|
});
|
|
68
81
|
|
|
69
|
-
//
|
|
82
|
+
// Method 2: Pre-built semantic components
|
|
70
83
|
const Header = () =>
|
|
71
84
|
Div({
|
|
72
85
|
padding: '20px',
|
|
@@ -77,10 +90,11 @@ const Header = () =>
|
|
|
77
90
|
|
|
78
91
|
### 2. Theming System
|
|
79
92
|
|
|
80
|
-
Define and
|
|
93
|
+
Define themes and access values using dot-notation:
|
|
81
94
|
|
|
82
|
-
```
|
|
83
|
-
|
|
95
|
+
```tsx
|
|
96
|
+
// theme.ts
|
|
97
|
+
export const theme = {
|
|
84
98
|
colors: {
|
|
85
99
|
primary: '#2196F3',
|
|
86
100
|
text: {
|
|
@@ -94,9 +108,13 @@ const theme = {
|
|
|
94
108
|
}
|
|
95
109
|
};
|
|
96
110
|
|
|
111
|
+
// ThemedComponent.tsx
|
|
112
|
+
import { Component, Div, H1, P } from '@meonode/ui';
|
|
113
|
+
import { theme } from './theme';
|
|
114
|
+
|
|
97
115
|
const ThemedCard = Component(() =>
|
|
98
116
|
Div({
|
|
99
|
-
theme,
|
|
117
|
+
theme, // Provide theme context
|
|
100
118
|
padding: 'theme.spacing.lg',
|
|
101
119
|
backgroundColor: 'theme.colors.primary',
|
|
102
120
|
children: [
|
|
@@ -111,47 +129,49 @@ const ThemedCard = Component(() =>
|
|
|
111
129
|
|
|
112
130
|
Automatic separation of CSS props from DOM attributes:
|
|
113
131
|
|
|
114
|
-
```
|
|
132
|
+
```tsx
|
|
115
133
|
const ProfileCard = Component(({ user }) =>
|
|
116
134
|
Div({
|
|
117
135
|
// CSS Props
|
|
118
136
|
padding: '20px',
|
|
119
137
|
borderRadius: '8px',
|
|
138
|
+
|
|
120
139
|
// DOM Props
|
|
121
|
-
|
|
140
|
+
'aria-role': 'article',
|
|
122
141
|
tabIndex: 0,
|
|
142
|
+
|
|
123
143
|
// Children
|
|
124
144
|
children: `Welcome ${user.name}!`
|
|
125
145
|
})
|
|
126
146
|
);
|
|
127
147
|
```
|
|
128
148
|
|
|
149
|
+
---
|
|
129
150
|
|
|
130
151
|
## Key Features
|
|
131
152
|
|
|
132
|
-
| Feature | Description
|
|
133
|
-
|
|
134
|
-
| **Smart Prop Merge** | CSS
|
|
135
|
-
| **Theme Resolution** | `theme.` references resolve through component hierarchy
|
|
136
|
-
| **Type
|
|
137
|
-
| **HOC Support** | Wrap existing components with `Component()` for
|
|
138
|
-
| **Dynamic Children** |
|
|
153
|
+
| Feature | Description |
|
|
154
|
+
|----------------------|-----------------------------------------------------------------------------|
|
|
155
|
+
| **Smart Prop Merge** | CSS props merge with style objects; DOM props pass to underlying elements |
|
|
156
|
+
| **Theme Resolution** | `theme.` references resolve through component hierarchy |
|
|
157
|
+
| **Type Guards** | Autocomplete for CSS properties and theme paths with strict type checks |
|
|
158
|
+
| **HOC Support** | Wrap existing components with `Component()` for theme integration |
|
|
159
|
+
| **Dynamic Children** | Supports function-as-child pattern with automatic theme propagation |
|
|
160
|
+
|
|
161
|
+
---
|
|
139
162
|
|
|
140
|
-
## Advanced
|
|
163
|
+
## Advanced Patterns
|
|
141
164
|
|
|
142
165
|
### Component Composition
|
|
143
166
|
|
|
144
|
-
```
|
|
167
|
+
```tsx
|
|
145
168
|
const Dashboard = Component(() =>
|
|
146
169
|
Div({
|
|
147
170
|
display: 'grid',
|
|
148
171
|
gridTemplateColumns: '1fr 3fr',
|
|
149
172
|
gap: '20px',
|
|
150
173
|
children: [
|
|
151
|
-
Sidebar({
|
|
152
|
-
width: '240px',
|
|
153
|
-
items: navItems
|
|
154
|
-
}),
|
|
174
|
+
Sidebar({ width: '240px' }),
|
|
155
175
|
MainContent({
|
|
156
176
|
padding: '40px',
|
|
157
177
|
children: AnalyticsChart({ dataset })
|
|
@@ -163,32 +183,22 @@ const Dashboard = Component(() =>
|
|
|
163
183
|
|
|
164
184
|
### Material UI Integration
|
|
165
185
|
|
|
166
|
-
```
|
|
186
|
+
```bash
|
|
167
187
|
yarn add @meonode/mui @mui/material
|
|
168
188
|
```
|
|
169
189
|
|
|
170
|
-
```
|
|
190
|
+
```tsx
|
|
171
191
|
import { Button, TextField } from '@meonode/mui';
|
|
172
192
|
|
|
173
|
-
const
|
|
193
|
+
const LoginForm = Component(() =>
|
|
174
194
|
Div({
|
|
175
195
|
maxWidth: '400px',
|
|
176
196
|
margin: '0 auto',
|
|
177
197
|
children: [
|
|
178
|
-
TextField({
|
|
179
|
-
|
|
180
|
-
fullWidth: true,
|
|
181
|
-
margin: 'normal'
|
|
182
|
-
}),
|
|
183
|
-
TextField({
|
|
184
|
-
label: 'Password',
|
|
185
|
-
type: 'password',
|
|
186
|
-
fullWidth: true,
|
|
187
|
-
margin: 'normal'
|
|
188
|
-
}),
|
|
198
|
+
TextField({ label: 'Email', fullWidth: true }),
|
|
199
|
+
TextField({ label: 'Password', type: 'password' }),
|
|
189
200
|
Button({
|
|
190
201
|
variant: 'contained',
|
|
191
|
-
color: 'primary',
|
|
192
202
|
children: 'Sign In'
|
|
193
203
|
})
|
|
194
204
|
]
|
|
@@ -196,9 +206,8 @@ const MuiLoginForm = Component(() =>
|
|
|
196
206
|
);
|
|
197
207
|
```
|
|
198
208
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
```ts
|
|
209
|
+
## Comprehensive Example: Theme-Switching & Conditional Components
|
|
210
|
+
```tsx
|
|
202
211
|
'use client'
|
|
203
212
|
/**
|
|
204
213
|
* This file showcases the integration of React hooks with @meonode/ui components
|
|
@@ -206,7 +215,7 @@ const MuiLoginForm = Component(() =>
|
|
|
206
215
|
* approaches, the use of Higher-Order Components (HOCs), and how theme context
|
|
207
216
|
* is managed and propagated within the @meonode/ui component tree.
|
|
208
217
|
*/
|
|
209
|
-
import { Component, Column, Row, P, Node, Button, Theme, Center, NodeInstance } from '@meonode/ui'
|
|
218
|
+
import { Component, Column, Row, P, Node, Button, Theme, Center, NodeInstance, Absolute } from '@meonode/ui'
|
|
210
219
|
import { useState, useEffect, ReactElement, ReactNode } from 'react'
|
|
211
220
|
import { CssBaseline, FormControlLabel, TextField } from '@meonode/mui'
|
|
212
221
|
import { Switch as MUISwitch } from '@mui/material'
|
|
@@ -322,7 +331,7 @@ const darkTheme: Theme = {
|
|
|
322
331
|
*/
|
|
323
332
|
export default Component(() => {
|
|
324
333
|
// State hook to control the visibility of additional content sections.
|
|
325
|
-
const [showMore, setShowDetails] = useState(false)
|
|
334
|
+
const [showMore, setShowDetails] = useState(false)
|
|
326
335
|
const [mode, setMode] = useState<'dark' | 'light'>('light')
|
|
327
336
|
const theme = mode === 'dark' ? darkTheme : lightTheme
|
|
328
337
|
|
|
@@ -344,6 +353,7 @@ export default Component(() => {
|
|
|
344
353
|
backgroundColor: 'theme.background',
|
|
345
354
|
color: 'theme.foreground',
|
|
346
355
|
children: [
|
|
356
|
+
CssBaseline, // Applies baseline Material UI styles for consistent rendering.
|
|
347
357
|
// Theme toggle switch using MUI components wrapped with @meonode/ui's Node HOC.
|
|
348
358
|
Center({
|
|
349
359
|
children: FormControlLabel({
|
|
@@ -355,6 +365,17 @@ export default Component(() => {
|
|
|
355
365
|
onChange: () => setMode(prev => (prev === 'dark' ? 'light' : 'dark')),
|
|
356
366
|
}),
|
|
357
367
|
}),
|
|
368
|
+
// Button to show modal.
|
|
369
|
+
Button('Show Modal', {
|
|
370
|
+
onClick: () => Modal({ theme }), // Click handler to show modal immedietelly.
|
|
371
|
+
cursor: 'pointer', // Visual cue for clickability.
|
|
372
|
+
userSelect: 'none', // Prevents text selection on the button.
|
|
373
|
+
padding: '10px 20px',
|
|
374
|
+
backgroundColor: 'theme.primary', // Background color sourced from the theme context.
|
|
375
|
+
borderRadius: 5,
|
|
376
|
+
fontWeight: 'bold',
|
|
377
|
+
color: 'white',
|
|
378
|
+
}),
|
|
358
379
|
// Button to toggle the visibility of the detail sections.
|
|
359
380
|
Button(showMore ? 'Hide' : 'Show More', {
|
|
360
381
|
onClick: () => setShowDetails(prev => !prev), // Click handler to toggle the 'showMore' state.
|
|
@@ -366,7 +387,6 @@ export default Component(() => {
|
|
|
366
387
|
fontWeight: 'bold',
|
|
367
388
|
color: 'white',
|
|
368
389
|
}),
|
|
369
|
-
CssBaseline, // Applies baseline Material UI styles for consistent rendering.
|
|
370
390
|
|
|
371
391
|
/**
|
|
372
392
|
* --- Unconditional Rendering Examples ---
|
|
@@ -500,28 +520,92 @@ const ReturnRenderedDetailComponent = ({ info }: { info: string }): ReactNode =>
|
|
|
500
520
|
children: [P(info, { flex: 1, padding: '0 20px' }), TextField({ flex: 1, sx: { background: 'theme.primary' } })],
|
|
501
521
|
}).render() // Explicitly renders to ReactNode.
|
|
502
522
|
}
|
|
523
|
+
|
|
524
|
+
const Modal = ({ theme }: { theme: Theme }) => {
|
|
525
|
+
const modal = Absolute({
|
|
526
|
+
theme: theme.colors,
|
|
527
|
+
top: 0,
|
|
528
|
+
left: 0,
|
|
529
|
+
right: 0,
|
|
530
|
+
bottom: 0,
|
|
531
|
+
display: 'flex',
|
|
532
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
533
|
+
justifyContent: 'center',
|
|
534
|
+
alignItems: 'center',
|
|
535
|
+
onClick: e => {
|
|
536
|
+
if (e.target === e.currentTarget) {
|
|
537
|
+
modal?.unmount()
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
children: Column({
|
|
541
|
+
width: '50%',
|
|
542
|
+
height: '50%',
|
|
543
|
+
backgroundColor: 'theme.background',
|
|
544
|
+
borderRadius: '8px',
|
|
545
|
+
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
|
|
546
|
+
transition: 'transform 0.2s ease-in-out',
|
|
547
|
+
padding: 10,
|
|
548
|
+
gap: 10,
|
|
549
|
+
color: 'theme.foreground',
|
|
550
|
+
children: [
|
|
551
|
+
Center({ fontWeight: 'bold', children: 'Hello There' }),
|
|
552
|
+
TextField({
|
|
553
|
+
sx: {
|
|
554
|
+
'& .MuiFormLabel-root': {
|
|
555
|
+
color: 'theme.foreground',
|
|
556
|
+
'&.Mui-focused': {
|
|
557
|
+
color: 'theme.foreground',
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
'& .MuiOutlinedInput-root': {
|
|
561
|
+
color: 'theme.foreground',
|
|
562
|
+
'& fieldset': {
|
|
563
|
+
borderColor: 'theme.foreground',
|
|
564
|
+
},
|
|
565
|
+
'&:hover fieldset': {
|
|
566
|
+
borderColor: 'theme.foreground',
|
|
567
|
+
},
|
|
568
|
+
'&.Mui-focused fieldset': {
|
|
569
|
+
borderColor: 'theme.foreground',
|
|
570
|
+
},
|
|
571
|
+
borderRadius: 2,
|
|
572
|
+
},
|
|
573
|
+
},
|
|
574
|
+
label: 'Hello',
|
|
575
|
+
fullWidth: true,
|
|
576
|
+
}),
|
|
577
|
+
],
|
|
578
|
+
}),
|
|
579
|
+
}).toPortal()
|
|
580
|
+
}
|
|
503
581
|
```
|
|
504
582
|
|
|
583
|
+
---
|
|
584
|
+
|
|
505
585
|
## API Reference
|
|
506
586
|
|
|
507
587
|
### Core Functions
|
|
508
588
|
|
|
509
|
-
| Function
|
|
510
|
-
|
|
511
|
-
| `Node`
|
|
512
|
-
| `Component`
|
|
589
|
+
| Function | Parameters | Description |
|
|
590
|
+
|-------------|-----------------------------------------|----------------------------------------------|
|
|
591
|
+
| `Node` | `element: string | React.ComponentType`, `baseProps: object` | Creates a configurable UI node |
|
|
592
|
+
| `Component` | `(props: P) => Node | ReactNode` | Converts node trees to React components |
|
|
513
593
|
|
|
594
|
+
---
|
|
514
595
|
|
|
515
596
|
## Contributing
|
|
516
597
|
|
|
517
598
|
We welcome contributions! Please follow these steps:
|
|
518
|
-
1. Fork the repository
|
|
519
|
-
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
520
|
-
3. Commit your changes
|
|
521
|
-
4. Push to the branch
|
|
522
|
-
5. Open a Pull Request
|
|
523
599
|
|
|
524
|
-
|
|
600
|
+
1. **Fork** the repository
|
|
601
|
+
2. **Clone** your fork: `git clone https://github.com/your-username/meonode-ui.git`
|
|
602
|
+
3. **Install dependencies**: `yarn install` (or npm/pnpm)
|
|
603
|
+
4. **Create a feature branch**: `git checkout -b feature/amazing-feature`
|
|
604
|
+
5. **Commit changes** with descriptive messages
|
|
605
|
+
6. **Push** to your branch: `git push origin feature/amazing-feature`
|
|
606
|
+
7. **Open a Pull Request**
|
|
607
|
+
|
|
608
|
+
For major changes, please open an issue first to discuss your proposal.
|
|
525
609
|
|
|
526
610
|
---
|
|
527
611
|
|
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.20",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/main.js",
|
|
7
7
|
"types": "./dist/main.d.ts",
|