@meonode/ui 0.1.1 → 0.1.3

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 CHANGED
@@ -2,197 +2,295 @@
2
2
 
3
3
  [![NPM version](https://img.shields.io/npm/v/@meonode/ui.svg?style=flat)](https://www.npmjs.com/package/@meonode/ui)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- <!-- Add other badges as needed: build status, coverage, etc. -->
6
5
 
7
- `@meonode/ui` is a lightweight yet powerful utility for the programmatic creation and manipulation of React elements. It
8
- offers an enhanced, structured way to define components, manage props (separating CSS from DOM attributes), handle
9
- theming, and compose children *before* they are rendered by React. This provides greater control and flexibility,
10
- especially for dynamic UIs and design systems.
11
-
12
- ---
6
+ **Build React UIs with Type-Safe Fluency**
7
+ A structured approach to component composition with built-in theming, prop separation, and dynamic children handling.
8
+
9
+ ```tsx
10
+ // Quick Start Example
11
+ import { Component, Div, H1, Button } from '@meonode/ui';
12
+
13
+ const BlueButton = Component((props) =>
14
+ Button({
15
+ padding: '12px 24px',
16
+ borderRadius: '8px',
17
+ backgroundColor: 'dodgerblue',
18
+ color: 'white',
19
+ ...props
20
+ })
21
+ );
22
+
23
+ const App = Component(() =>
24
+ Div({
25
+ padding: '40px',
26
+ children: [
27
+ H1({ fontSize: '2rem' }, 'Welcome to Meonode'),
28
+ BlueButton({
29
+ onClick: () => alert('Hello World!'),
30
+ children: 'Get Started'
31
+ })
32
+ ]
33
+ })
34
+ );
35
+ ```
13
36
 
14
- ## Key Features
37
+ ## Why @meonode/ui?
15
38
 
16
- * **Programmatic React Element Construction:** Create complex React element trees using a fluent and intuitive API.
17
- * **Advanced Prop Handling:** Automatically separates CSS style properties from other DOM attributes, simplifying
18
- component logic.
19
- * **Integrated Theming System:** Pass theme objects down the tree and reference theme values (e.g.,
20
- `theme.colors.primary`) directly in style properties.
21
- * **Flexible Children Management:** Supports various child types, including primitives, other `@meonode/ui` nodes,
22
- standard React elements, and functions for dynamic rendering (function-as-child pattern).
23
- * **Type-Safe:** Written entirely in TypeScript, providing excellent autocompletion and compile-time safety.
24
- * **Seamless Integration:** Works with existing React components and fits naturally into any React workflow.
25
- * **`Component` HOC:** A higher-order component to easily wrap functions that return `@meonode/ui` instances, making
26
- them standard React components.
39
+ - 🎯 **Type-Safe Design** - Full TypeScript support with autocomplete for styles and themes
40
+ - 🎨 **CSS-in-JS Without Runtime** - Write styles directly in props with theme references
41
+ - 🧩 **Component-First Architecture** - Compose UIs from structured nodes instead of JSX
42
+ - 🌐 **Theme Propagation** - Contextual theming that works with any component structure
43
+ - **Zero Dependencies** - Lightweight core (under 15kb gzipped)
27
44
 
28
45
  ## Installation
29
46
 
30
- First, ensure you have `react` installed as a dependency.
31
-
32
- ```shell
33
- yarn add react @meonode/ui
34
- ```
35
-
36
- To use the built-in integration with Material UI, install the following:
37
-
38
- ```shell
39
- yarn add react @mui/material @meonode/ui @meonode/mui
47
+ ```bash
48
+ npm install @meonode/ui react
49
+ # or
50
+ yarn add @meonode/ui react
40
51
  ```
41
52
 
42
- ---
43
-
44
53
  ## Core Concepts
45
54
 
46
- ### `Node(element, props)`
47
-
48
- The primary factory function to create `@meonode/ui` instances (internally `BaseNode`).
49
-
50
- * `element`: The React element type (e.g., `'div'`, `MyReactComponent`, another `Node` instance).
51
- * `props`: An object containing properties for the element, including standard HTML attributes, event handlers, `children`, `theme`, and direct CSS style properties.
52
-
53
- ### `BaseNode`
54
-
55
- The internal representation of a React element within `@meonode/ui`. It holds the element type, processed props (with styles and DOM attributes separated), and processed children. You typically don't interact with `BaseNode` directly but through the `Node` factory. Each `BaseNode` instance has a `render()` method to convert it into a renderable React element.
56
-
57
- ### `Component(componentFunction)`
58
-
59
- A Higher-Order Component (HOC) that wraps a function. This function receives props and should return either a `@meonode/ui` instance (created via `Node()`) or a standard `ReactNode`. The `Component` HOC ensures that if a `@meonode/ui` instance is returned, its `render()` method is called, making it renderable by React.
60
-
61
- ### Theming
62
-
63
- Pass a `theme` object via the `theme` prop to any `Node`. This theme becomes available to that `Node` and its descendants. Style properties can then reference theme values using a dot-path string:
64
-
65
- ---
66
-
67
- ## Usage Examples
68
-
69
- ### 1. Basic Usage with `Node()`
70
-
71
- ### 2. Applying Styles Directly root Prop
72
-
73
- `@meonode/ui` intelligently separates CSS properties from other props. You can provide CSS properties directly at the root of the `props` object.
74
-
75
-
76
- ### 3. Using Themes
77
-
78
- Themes allow for centralized styling and easy reuse of design tokens.
79
-
80
- ### 4. Handling Children
81
-
82
- `@meonode/ui` offers flexible ways to define children:
83
-
84
- **Note on Function Children and Themes:** When a function child returns a `BaseNode` instance (e.g., `() => Node(...)`), `@meonode/ui` ensures that the parent's theme is propagated to this returned `BaseNode` if it doesn't already have its own theme. This allows `BaseNode`s created within the function to resolve theme-based styles like `color: 'theme.colors.highlight'`.
85
-
86
- ### 5. Creating Reusable Components with `Component` HOC
55
+ ### 1. Component Creation
87
56
 
88
- The `Component` HOC simplifies creating standard React components from functions that return `@meonode/ui` nodes.
57
+ Create elements using the `Node` factory or pre-built components:
89
58
 
90
- ### 6. Using with Existing React Components
59
+ ```tsx
60
+ import { Node, Div, H1 } from '@meonode/ui';
91
61
 
92
- You can easily incorporate existing React components into your `@meonode/ui` structures.
62
+ // Using Node factory
63
+ const Card = Node('div', {
64
+ padding: '20px',
65
+ borderRadius: '8px',
66
+ boxShadow: '0 2px 12px rgba(0,0,0,0.1)'
67
+ });
93
68
 
94
- ---
95
-
96
- ## Usage Implementation
97
-
98
- The example implementation can be seen in the docs folder:
99
- 1. [Basic Usage](./docs/basic-usage.md)
100
- 2. [Conditional Component With Hook](./docs/conditional-component-with-hook.md)
101
-
102
- ---
103
-
104
- ## API Overview
105
-
106
- ### `Node<E extends NodeElement>(element: E, props: Partial<NodeProps<E>> = {}): BaseNodeInstance<E>`
107
-
108
- * The main factory function to create `BaseNode` instances.
109
- * `element`: A string (e.g., 'div'), a React component, or another `BaseNodeInstance`.
110
- * `props`: Object containing element properties, styles, `children`, and `theme`.
111
- * Returns: A `BaseNodeInstance`. Call `.render()` on it to get a renderable React element.
112
-
113
- ### `Component<T extends Record<string, any>>(component: (props: T) => BaseNodeInstance<any> | ReactNode): (props: T) => ReactNode`
114
-
115
- * A Higher-Order Component.
116
- * `component`: A function that accepts props and returns a `BaseNodeInstance` or any `ReactNode`.
117
- * Returns: A standard React functional component.
118
-
119
- ### `BaseNodeInstance.render(): ReactNode`
120
-
121
- * A method on `BaseNodeInstance` (the object returned by `Node()`).
122
- * Converts the internal `BaseNode` representation into a standard, renderable React Node tree using `React.createElement`.
123
-
124
- ---
69
+ // Using pre-built components
70
+ const Header = () =>
71
+ Div({
72
+ padding: '20px',
73
+ backgroundColor: 'navy',
74
+ children: H1({ color: 'white' }, 'App Header')
75
+ });
76
+ ```
125
77
 
126
- ## How It Works (Briefly)
78
+ ### 2. Theming System
79
+
80
+ Define and consume themes with dot-notation:
81
+
82
+ ```tsx
83
+ const theme = {
84
+ colors: {
85
+ primary: '#2196F3',
86
+ text: {
87
+ primary: '#1A237E',
88
+ secondary: '#455A64'
89
+ }
90
+ },
91
+ spacing: {
92
+ md: '16px',
93
+ lg: '24px'
94
+ }
95
+ };
96
+
97
+ const ThemedCard = Component(() =>
98
+ Div({
99
+ theme,
100
+ padding: 'theme.spacing.lg',
101
+ backgroundColor: 'theme.colors.primary',
102
+ children: [
103
+ H1({ color: 'theme.colors.text.primary' }, 'Themed Title'),
104
+ P({ color: 'theme.colors.text.secondary' }, 'Content...')
105
+ ]
106
+ })
107
+ );
108
+ ```
127
109
 
128
- 1. **Node Creation:**
129
- - When you call `Node(element, props)`, a `BaseNode` instance is created.
110
+ ### 3. Prop Handling
111
+
112
+ Automatic separation of CSS props from DOM attributes:
113
+
114
+ ```tsx
115
+ const ProfileCard = Component(({ user }) =>
116
+ Div({
117
+ // CSS Props
118
+ padding: '20px',
119
+ borderRadius: '8px',
120
+ // DOM Props
121
+ ariaRole: 'article',
122
+ tabIndex: 0,
123
+ // Children
124
+ children: `Welcome ${user.name}!`
125
+ })
126
+ );
127
+ ```
130
128
 
131
- 2. **Props Processing:**
132
- - The `BaseNode` constructor analyzes the `props`
133
- - It uses helper functions (`getCSSProps`, `getDOMProps`) to separate valid CSS style properties from other DOM
134
- attributes
135
- - If a `theme` (or `nodeTheme`) is provided, style values like `'theme.colors.primary'` are resolved against this
136
- theme object using `getValueByPath`
129
+ ## Key Features
137
130
 
138
- 3. **Children Processing:**
139
- - Children are recursively processed
140
- - If a child is a primitive, React element, or another `BaseNode`, it's adapted
141
- - Function children are wrapped in a special internal `_functionRenderer` `BaseNode`. This renderer calls the function
142
- during the render phase and ensures that if the function returns a `BaseNode`, the parent's theme is correctly
143
- propagated to it
131
+ | Feature | Description |
132
+ |------------------------|-----------------------------------------------------------------------------|
133
+ | **Smart Prop Merge** | CSS properties are automatically merged with style object |
134
+ | **Theme Resolution** | `theme.` references resolve through component hierarchy |
135
+ | **Type Safety** | Autocomplete for CSS properties and theme paths |
136
+ | **HOC Support** | Wrap existing components with `Component()` for seamless integration |
137
+ | **Dynamic Children** | Function-as-child pattern with automatic theme propagation |
138
+
139
+ ## Advanced Usage
140
+
141
+ ### Component Composition
142
+
143
+ ```tsx
144
+ const Dashboard = Component(() =>
145
+ Div({
146
+ display: 'grid',
147
+ gridTemplateColumns: '1fr 3fr',
148
+ gap: '20px',
149
+ children: [
150
+ Sidebar({
151
+ width: '240px',
152
+ items: navItems
153
+ }),
154
+ MainContent({
155
+ padding: '40px',
156
+ children: AnalyticsChart({ dataset })
157
+ })
158
+ ]
159
+ })
160
+ );
161
+ ```
144
162
 
145
- 4. **Rendering:**
146
- - The `render()` method on a `BaseNode` instance recursively traverses its structure
147
- - Calls `render()` on any child `BaseNode`s
148
- - Ultimately uses `React.createElement` to construct the final tree of React elements
149
- - The `nodeTheme` prop is removed before passing props to `createElement`
163
+ ### With Conditional Children That Contains Hook
164
+
165
+ also add this
166
+
167
+ ```ts
168
+
169
+ // Wraps a hook-capable component into a BaseNode-compatible Client Component
170
+ import { Component, Column, Row, Div, P } from '@meonode/ui'
171
+ import { useState, useEffect } from 'react'
172
+
173
+ // Shared theme object passed into components. This may be written in a different file and imported.
174
+ const theme = {
175
+ background: { primary: 'lightgreen', secondary: 'lightyellow' },
176
+ }
177
+
178
+ // Exported component rendered via <Component /> (client wrapper)
179
+ export default Component(() => {
180
+ // React hook for conditional UI
181
+ const [showDetails, setShowDetails] = useState(false)
182
+
183
+ // Declarative layout using Column as root container
184
+ return Column({
185
+ theme,
186
+ padding: 20,
187
+ gap: 15,
188
+ children: [
189
+ // Header row with a toggle button
190
+ Row({
191
+ gap: 10,
192
+ children: [
193
+ Div({
194
+ onClick: () => setShowDetails(prev => !prev),
195
+ style: {
196
+ cursor: 'pointer',
197
+ userSelect: 'none',
198
+ padding: '10px 20px',
199
+ backgroundColor: 'theme.background.primary', // Node engine will handle this
200
+ borderRadius: 5,
201
+ fontWeight: 'bold',
202
+ },
203
+ children: showDetails ? 'Hide Details' : 'Show Details',
204
+ }),
205
+ ],
206
+ }),
207
+
208
+ // Conditionally render DetailComponent via function wrapper
209
+ // Ensures it's treated as a renderable function (deferred React class or element that is NOT called directly)
210
+ // Node engine will handle this like magic
211
+ showDetails && (() => DetailComponent({ info: 'Here are some details!' })), // Works like `Component(() => DetailComponent({ info: 'Here some details!' }))`,
212
+ showDetails && DetailComponent({ info: 'Here are some details!' })).render() // Works
213
+ ],
214
+ })
215
+ })
216
+
217
+ // A stateful detail section using useEffect and styled Div
218
+ const DetailComponent = ({ info }: { info: string }) => {
219
+ useEffect(() => {
220
+ console.log('DetailComponent mounted')
221
+ return () => {
222
+ console.log('DetailComponent unmounted')
223
+ }
224
+ }, [])
225
+
226
+ return Div({
227
+ padding: 15,
228
+ border: '1px solid green',
229
+ borderRadius: 6,
230
+ backgroundColor: 'theme.background.secondary', // Node engine will handle this
231
+ children: P({ children: info }),
232
+ })
233
+ }
150
234
 
151
- ---
235
+ ```
152
236
 
153
- ## TypeScript Support
237
+ ### Material UI Integration
154
238
 
155
- `@meonode/ui` is written in TypeScript and exports all necessary types for a robust development experience. You'll get autocompletion for props, style properties, and theme usage.
239
+ ```bash
240
+ yarn add @meonode/mui @mui/material
241
+ ```
156
242
 
157
- Key types like `NodeElement`, `NodeProps`, and `BaseNodeInstance` are available if you need to work with them directly, though typically `Node` and `Component` are sufficient.
243
+ ```ts
244
+ import { MuiButton, MuiTextField } from '@meonode/mui';
245
+
246
+ const MuiLoginForm = Component(() =>
247
+ Div({
248
+ maxWidth: '400px',
249
+ margin: '0 auto',
250
+ children: [
251
+ MuiTextField({
252
+ label: 'Email',
253
+ fullWidth: true,
254
+ margin: 'normal'
255
+ }),
256
+ MuiTextField({
257
+ label: 'Password',
258
+ type: 'password',
259
+ fullWidth: true,
260
+ margin: 'normal'
261
+ }),
262
+ MuiButton({
263
+ variant: 'contained',
264
+ color: 'primary',
265
+ children: 'Sign In'
266
+ })
267
+ ]
268
+ })
269
+ );
270
+ ```
158
271
 
159
- ---
272
+ ## API Reference
160
273
 
161
- ## When to Use `@meonode/ui`?
274
+ ### Core Functions
162
275
 
163
- * Perfect for design systems and UI libraries requiring consistent structure, theming, and maintainable patterns
164
- * Ideal for generating dynamic UIs from configurations, metadata, schemas or API responses
165
- * Powerful solution for centralized styling logic, prop transformations and runtime customization
166
- * Excellent choice for complex component hierarchies needing programmatic manipulation
167
- * Great alternative to JSX when you prefer a more functional programming approach
276
+ | Function | Parameters | Description |
277
+ |----------------|-----------------------------------------|-------------------------------------------------|
278
+ | `Node` | `element: string \| Component`, `props` | Creates a new UI node |
279
+ | `Component` | `(props) => Node \| ReactNode` | Converts node trees to React components |
168
280
 
169
- ---
170
281
 
171
282
  ## Contributing
172
283
 
173
- Contributions are welcome! Please feel free to contact me on discord: **[l7aromeo](https://discordapp.com/users/704803255561224264)**.
174
-
175
- ---
284
+ We welcome contributions! Please follow these steps:
285
+ 1. Fork the repository
286
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
287
+ 3. Commit your changes
288
+ 4. Push to the branch
289
+ 5. Open a Pull Request
176
290
 
177
- ## [License](https://github.com/l7aromeo/meonode-ui/blob/main/LICENSE)
291
+ Contact me on [Discord](https://discordapp.com/users/704803255561224264) for discussions.
178
292
 
179
- The MIT License (MIT)
180
- Copyright (c) 2025 Ukasyah Rahmatullah Zada
181
-
182
- Permission is hereby granted, free of charge, to any person obtaining a copy
183
- of this software and associated documentation files (the "Software"), to deal
184
- in the Software without restriction, including without limitation the rights
185
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
186
- copies of the Software, and to permit persons to whom the Software is
187
- furnished to do so, subject to the following conditions:
188
-
189
- The above copyright notice and this permission notice shall be included in all
190
- copies or substantial portions of the Software.
293
+ ---
191
294
 
192
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
193
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
194
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
195
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
196
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
197
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
198
- SOFTWARE.
295
+ **MIT Licensed** | Copyright © 2024 Ukasyah Rahmatullah Zada
296
+ *Empowering developers to build better UIs*
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meonode/ui",
3
- "description": "`@meonode/ui` is a lightweight yet powerful utility for the programmatic creation and manipulation of React elements. It offers an enhanced, structured way to define components, manage props (separating CSS from DOM attributes), handle theming, and compose children *before* they are rendered by React. This provides greater control and flexibility, especially for dynamic UIs and design systems.",
4
- "version": "0.1.1",
3
+ "description": "A structured approach to component composition with built-in theming, prop separation, and dynamic children handling.",
4
+ "version": "0.1.3",
5
5
  "type": "module",
6
6
  "main": "./dist/main.js",
7
7
  "types": "./dist/main.d.ts",
@@ -13,7 +13,6 @@
13
13
  },
14
14
  "files": [
15
15
  "dist",
16
- "docs",
17
16
  "package.json",
18
17
  "LICENSE",
19
18
  "README.md"
@@ -1,63 +0,0 @@
1
- ```ts
2
- // Component wraps a React Server Component into a Client Component
3
- // Think of it like a factory that transforms JSX-like node definitions into actual React components
4
- import { Component, Column, Row, Div, Img, P } from '@meonode/ui'
5
-
6
- const theme = { background: { primary: 'red', secondary: 'blue' } }
7
-
8
- // This is the actual exported component
9
- export default Component(() => {
10
- return Card({
11
- title: 'Genshin Impact',
12
- subtitle: 'Adventure awaits!',
13
- imageUrl: 'https://upload-os-bbs.mihoyo.com/upload/2021/03/05/75387538/f37ce39baf72ffb84cb5c0040bdcbf10_2126420710320647353.jpg',
14
- })
15
- })
16
-
17
- // This function returns a structured layout using html.node helpers only
18
- const Card = ({ title, subtitle, imageUrl }: { title: string; subtitle: string; imageUrl: string }) =>
19
- Column({
20
- theme,
21
- // Column is just a <div> with `display: flex; flex-direction: column`
22
- borderRadius: 10,
23
- overflow: 'hidden',
24
- border: '1px solid theme.background.primary', // Can use theme references
25
- width: 300,
26
- children: [
27
- // Img translates to a real <img> tag
28
- Img({
29
- src: imageUrl,
30
- width: '100%',
31
- height: 180,
32
- style: {
33
- objectFit: 'cover', // Ensures the image fills the area without distortion
34
- },
35
- }),
36
- // Div is a basic <div> wrapper
37
- Div({
38
- padding: 10,
39
- backgroundColor: 'theme.background.secondary',
40
- children: Column({
41
- gap: 5,
42
- children: [
43
- // P maps directly to <p>, with styling
44
- P({
45
- style: {
46
- fontSize: 18,
47
- fontWeight: 600,
48
- },
49
- children: title,
50
- }),
51
- P({
52
- style: {
53
- fontSize: 14,
54
- color: 'gray',
55
- },
56
- children: subtitle,
57
- }),
58
- ],
59
- }),
60
- }),
61
- ],
62
- })
63
- ```
@@ -1,68 +0,0 @@
1
- ```ts
2
-
3
- // Wraps a hook-capable component into a BaseNode-compatible Client Component
4
- import { Component, Column, Row, Div, P } from '@meonode/ui'
5
- import { useState, useEffect } from 'react'
6
-
7
- // Shared theme object passed into components. This may be written in a different file and imported.
8
- const theme = {
9
- background: { primary: 'lightgreen', secondary: 'lightyellow' },
10
- }
11
-
12
- // Exported component rendered via <Component /> (client wrapper)
13
- export default Component(() => {
14
- // React hook for conditional UI
15
- const [showDetails, setShowDetails] = useState(false)
16
-
17
- // Declarative layout using Column as root container
18
- return Column({
19
- theme,
20
- padding: 20,
21
- gap: 15,
22
- children: [
23
- // Header row with a toggle button
24
- Row({
25
- gap: 10,
26
- children: [
27
- Div({
28
- onClick: () => setShowDetails(prev => !prev),
29
- style: {
30
- cursor: 'pointer',
31
- userSelect: 'none',
32
- padding: '10px 20px',
33
- backgroundColor: 'theme.background.primary', // Node engine will handle this
34
- borderRadius: 5,
35
- fontWeight: 'bold',
36
- },
37
- children: showDetails ? 'Hide Details' : 'Show Details',
38
- }),
39
- ],
40
- }),
41
-
42
- // Conditionally render DetailComponent via function wrapper
43
- // Ensures it's treated as a renderable function (deferred React class or element that is NOT called directly)
44
- // Node engine will handle this like magic
45
- showDetails ? () => DetailComponent({ info: 'Here are some details!' }) : null,
46
- ],
47
- })
48
- })
49
-
50
- // A stateful detail section using useEffect and styled Div
51
- const DetailComponent = ({ info }: { info: string }) => {
52
- useEffect(() => {
53
- console.log('DetailComponent mounted')
54
- return () => {
55
- console.log('DetailComponent unmounted')
56
- }
57
- }, [])
58
-
59
- return Div({
60
- padding: 15,
61
- border: '1px solid green',
62
- borderRadius: 6,
63
- backgroundColor: 'theme.background.secondary', // Node engine will handle this
64
- children: P({ children: info }),
65
- })
66
- }
67
-
68
- ```