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