@meonode/ui 0.1.18 → 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 CHANGED
@@ -1,25 +1,30 @@
1
+ [file name]: README.md
2
+ [file content begin]
1
3
  # @meonode/ui
2
4
 
3
5
  [![NPM version](https://img.shields.io/npm/v/@meonode/ui.svg?style=flat)](https://www.npmjs.com/package/@meonode/ui)
4
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@meonode/ui)](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
- ```ts
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 themes
40
- - 🎨 **CSS-in-JS Without Runtime** - Write styles directly in props with theme references
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
- - 🌐 **Theme Propagation** - Contextual theming that works with any component structure
43
- - ⚡ **Zero Dependencies** - Lightweight core (under 15kb gzipped)
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
- ```shell
53
+ ```bash
54
+ # Using npm
48
55
  npm install @meonode/ui react
49
- # or
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
- ```ts
72
+ ```tsx
60
73
  import { Node, Div, H1 } from '@meonode/ui';
61
74
 
62
- // Using Node factory
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
- // Using pre-built components
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 consume themes with dot-notation:
93
+ Define themes and access values using dot-notation:
81
94
 
82
- ```ts
83
- const theme = {
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
- ```ts
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
- ariaRole: 'article',
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 properties are automatically merged with style object |
135
- | **Theme Resolution** | `theme.` references resolve through component hierarchy |
136
- | **Type Safety** | Autocomplete for CSS properties and theme paths |
137
- | **HOC Support** | Wrap existing components with `Component()` for seamless integration |
138
- | **Dynamic Children** | Function-as-child pattern with automatic theme propagation |
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 Usage
163
+ ## Advanced Patterns
141
164
 
142
165
  ### Component Composition
143
166
 
144
- ```ts
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 })
@@ -161,98 +181,268 @@ const Dashboard = Component(() =>
161
181
  );
162
182
  ```
163
183
 
164
- ### With Conditional Children That Contains Hook
184
+ ### Material UI Integration
185
+
186
+ ```bash
187
+ yarn add @meonode/mui @mui/material
188
+ ```
189
+
190
+ ```tsx
191
+ import { Button, TextField } from '@meonode/mui';
192
+
193
+ const LoginForm = Component(() =>
194
+ Div({
195
+ maxWidth: '400px',
196
+ margin: '0 auto',
197
+ children: [
198
+ TextField({ label: 'Email', fullWidth: true }),
199
+ TextField({ label: 'Password', type: 'password' }),
200
+ Button({
201
+ variant: 'contained',
202
+ children: 'Sign In'
203
+ })
204
+ ]
205
+ })
206
+ );
207
+ ```
165
208
 
166
- ```ts
209
+ ## Comprehensive Example: Theme-Switching & Conditional Components
210
+ ```tsx
167
211
  'use client'
168
212
  /**
169
- * This file demonstrates integration between React hooks and BaseNode components
170
- * using the @meonode/ui library for declarative UI construction.
171
- * It explores various rendering patterns, Higher-Order Component (HOC) usage,
172
- * and theme propagation with @meonode/ui components.
213
+ * This file showcases the integration of React hooks with @meonode/ui components
214
+ * for building declarative user interfaces. It demonstrates different rendering
215
+ * approaches, the use of Higher-Order Components (HOCs), and how theme context
216
+ * is managed and propagated within the @meonode/ui component tree.
173
217
  */
174
- import { Component, Column, Row, Div, P, Node } from '@meonode/ui' // Theme type is not explicitly imported or used as a prop type in this file's components.
175
- import { useState, useEffect } from 'react'
176
- import { CssBaseline, TextField } from '@meonode/mui'
218
+ import { Component, Column, Row, P, Node, Button, Theme, Center, NodeInstance, Absolute } from '@meonode/ui'
219
+ import { useState, useEffect, ReactElement, ReactNode } from 'react'
220
+ import { CssBaseline, FormControlLabel, TextField } from '@meonode/mui'
221
+ import { Switch as MUISwitch } from '@mui/material'
222
+ import { styled } from '@mui/material'
223
+
224
+ const MaterialUISwitch = styled(MUISwitch)(({ theme }) => ({
225
+ width: 62,
226
+ height: 34,
227
+ padding: 7,
228
+ '& .MuiSwitch-switchBase': {
229
+ margin: 1,
230
+ padding: 0,
231
+ transform: 'translateX(6px)',
232
+ '&.Mui-checked': {
233
+ color: '#fff',
234
+ transform: 'translateX(22px)',
235
+ '& .MuiSwitch-thumb:before': {
236
+ 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(
237
+ '#fff',
238
+ )}" 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>')`,
239
+ },
240
+ '& + .MuiSwitch-track': {
241
+ opacity: 1,
242
+ backgroundColor: '#aab4be',
243
+ ...theme.applyStyles('dark', {
244
+ backgroundColor: '#8796A5',
245
+ }),
246
+ },
247
+ },
248
+ },
249
+ '& .MuiSwitch-thumb': {
250
+ backgroundColor: '#001e3c',
251
+ width: 32,
252
+ height: 32,
253
+ '&::before': {
254
+ content: "''",
255
+ position: 'absolute',
256
+ width: '100%',
257
+ height: '100%',
258
+ left: 0,
259
+ top: 0,
260
+ backgroundRepeat: 'no-repeat',
261
+ backgroundPosition: 'center',
262
+ 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(
263
+ '#fff',
264
+ )}" 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>')`,
265
+ },
266
+ ...theme.applyStyles('dark', {
267
+ backgroundColor: '#003892',
268
+ }),
269
+ },
270
+ '& .MuiSwitch-track': {
271
+ opacity: 1,
272
+ backgroundColor: '#aab4be',
273
+ borderRadius: 20 / 2,
274
+ ...theme.applyStyles('dark', {
275
+ backgroundColor: '#8796A5',
276
+ }),
277
+ },
278
+ }))
177
279
 
178
280
  /**
179
- * Global theme configuration.
180
- * Contains color palette definitions used by @meonode/ui components
181
- * when they resolve theme strings from context.
182
- * This can be extracted to a separate theme file for larger applications.
281
+ * Defines the color palette for the light theme.
282
+ * These color values are used by @meonode/ui components when they encounter
283
+ * theme string references (e.g., 'theme.primary') and the current theme mode is 'light'.
284
+ * In a larger application, this theme object would typically reside in a dedicated theme file.
183
285
  */
184
- const theme = {
185
- background: { primary: 'lightgreen', secondary: 'lightyellow' },
286
+ const lightTheme: Theme = {
287
+ mode: 'light',
288
+ colors: {
289
+ primary: '#2563eb',
290
+ secondary: '#64748b',
291
+ accent: '#10b981',
292
+ background: '#ffffff',
293
+ foreground: '#0f172a',
294
+ border: '#e2e8f0',
295
+ muted: '#f8fafc',
296
+ success: '#16a34a',
297
+ warning: '#eab308',
298
+ danger: '#dc2626',
299
+ },
186
300
  }
187
301
 
188
302
  /**
189
- * The page's main logic is defined as a function (which may use React hooks).
190
- * This function is wrapped in the `Component` HOC from @meonode/ui.
191
- * The HOC standardizes it as a React component, ensuring it returns a ReactNode.
192
- * This makes the component compatible with React's rendering process,
193
- * supporting both client-side and server-side rendering.
303
+ * Defines the color palette for the dark theme.
304
+ * Similar to the light theme, these colors are used by @meonode/ui components
305
+ * when resolving theme string references, but specifically when the current theme
306
+ * mode is 'dark'.
307
+ */
308
+ const darkTheme: Theme = {
309
+ mode: 'dark',
310
+ colors: {
311
+ primary: '#3b82f6',
312
+ secondary: '#94a3b8',
313
+ accent: '#34d399',
314
+ background: '#0f172a',
315
+ foreground: '#f8fafc',
316
+ border: '#334155',
317
+ muted: '#1e293b',
318
+ success: '#22c55e',
319
+ warning: '#facc15',
320
+ danger: '#ef4444',
321
+ },
322
+ }
323
+
324
+ /**
325
+ * The main page component, implemented as a functional component using React hooks.
326
+ * It manages the theme mode state and the visibility of additional content.
327
+ *
328
+ * This function is wrapped by the `Component` HOC from `@meonode/ui`. The `Component`
329
+ * HOC transforms the function into a standard React component that returns a `ReactNode`,
330
+ * making it compatible with React's rendering lifecycle and enabling SSR/CSR.
194
331
  */
195
332
  export default Component(() => {
196
333
  // State hook to control the visibility of additional content sections.
197
- const [showMore, setShowDetails] = useState(false) // 'showMore' controls visibility, 'setShowDetails' is the updater.
334
+ const [showMore, setShowDetails] = useState(false)
335
+ const [mode, setMode] = useState<'dark' | 'light'>('light')
336
+ const theme = mode === 'dark' ? darkTheme : lightTheme
198
337
 
199
338
  /**
200
- * The main layout is structured as a Column.
201
- * It includes:
202
- * - A header row with a button to toggle the visibility of additional content.
203
- * - A series of examples demonstrating different ways to render detail components,
339
+ * The root of the UI tree is a `Column` component from `@meonode/ui`.
340
+ * This `Column` sets the theme context for its children.
341
+ * Its children include:
342
+ * - A theme toggle switch using MUI components wrapped in `@meonode/ui`'s `Node`.
343
+ * - A button to toggle the visibility of the detail sections.
344
+ * - Various examples demonstrating how to render components that return either
345
+ * `@meonode/ui` `Node` instances or `ReactNode`s, illustrating theme propagation
204
346
  * both unconditionally and conditionally, highlighting theme propagation.
205
347
  */
206
348
  return Column({
207
- theme, // Provide the global theme to the Column and its descendants via React context.
349
+ theme: theme.colors,
208
350
  padding: 20,
209
351
  gap: 15,
352
+ minHeight: '100vh',
353
+ backgroundColor: 'theme.background',
354
+ color: 'theme.foreground',
210
355
  children: [
211
- CssBaseline, // Applies baseline MUI styles.
212
- // Interactive header section for toggling more content.
213
- Row({
214
- gap: 10,
215
- children: [
216
- Div({
217
- onClick: () => setShowDetails(prev => !prev), // Click handler to toggle the 'showMore' state.
218
- cursor: 'pointer', // Visual cue for clickability.
219
- userSelect: 'none', // Prevents text selection on the button.
220
- padding: '10px 20px',
221
- backgroundColor: 'theme.background.primary', // Background color sourced from the theme context.
222
- borderRadius: 5,
223
- fontWeight: 'bold',
224
- children: showMore ? 'Hide' : 'Show More', // Dynamically sets button text based on 'showMore' state.
225
- }),
226
- ],
356
+ CssBaseline, // Applies baseline Material UI styles for consistent rendering.
357
+ // Theme toggle switch using MUI components wrapped with @meonode/ui's Node HOC.
358
+ Center({
359
+ children: FormControlLabel({
360
+ control: Node(MaterialUISwitch).render() as ReactElement,
361
+ alignItems: 'center',
362
+ label: mode === 'dark' ? 'Dark Mode' : 'Light Mode',
363
+ labelPlacement: 'start',
364
+ checked: mode === 'dark',
365
+ onChange: () => setMode(prev => (prev === 'dark' ? 'light' : 'dark')),
366
+ }),
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
+ }),
379
+ // Button to toggle the visibility of the detail sections.
380
+ Button(showMore ? 'Hide' : 'Show More', {
381
+ onClick: () => setShowDetails(prev => !prev), // Click handler to toggle the 'showMore' state.
382
+ cursor: 'pointer', // Visual cue for clickability.
383
+ userSelect: 'none', // Prevents text selection on the button.
384
+ padding: '10px 20px',
385
+ backgroundColor: 'theme.accent', // Background color sourced from the theme context.
386
+ borderRadius: 5,
387
+ fontWeight: 'bold',
388
+ color: 'white',
227
389
  }),
228
390
 
229
391
  /**
230
- * Unconditional rendering examples:
231
- * Demonstrates rendering DetailComponent (returns a @meonode/ui Node instance) and
232
- * ReturnRenderedDetailComponent (returns a ReactNode), and usage of the Node HOC.
233
- * Pay attention to how theme is (or isn't) propagated to these components.
392
+ * --- Unconditional Rendering Examples ---
393
+ * These examples demonstrate rendering components that return either a
394
+ * `@meonode/ui` `Node` instance (`DetailComponent`) or a `ReactNode`
395
+ * (`ReturnRenderedDetailComponent`), and how the `Node` HOC affects this.
396
+ * Observe how theme context is propagated (or not) in each case.
234
397
  */
235
- DetailComponent({ info: 'Here are some details 1!' }), // Renders DetailComponent; its internal Row sources theme from parent Column's context.
236
- DetailComponent({ info: 'Here are some details 2!' }).render(), // Renders DetailComponent (invoking .render()); its internal Row sources theme from parent Column's context.
237
- // Node(DetailComponent, { info: 'Here are some details 3!' }), // ❌ Fails: Node HOC expects its first argument (a component function) to return ReactNode. DetailComponent returns a @meonode/ui Node instance.
398
+ // 1. Rendering a component that returns a @meonode/ui Node instance directly.
399
+ // The internal Row component correctly receives theme context from the parent Column.
400
+ DetailComponent({ info: 'Detail 1 (Node instance)' }),
401
+
402
+ // 2. Rendering a component that returns a @meonode/ui Node instance, then calling .render().
403
+ // The internal Row component also correctly receives theme context.
404
+ DetailComponent({ info: 'Detail 2 (Node instance + .render())' }).render(),
238
405
 
239
- ReturnRenderedDetailComponent({ info: 'Here are some details 4!' }), // Renders ReturnRenderedDetailComponent; its internal Row inherits theme from parent Column's context.
240
- Node(ReturnRenderedDetailComponent, { info: 'Here are some details 5!' }), // Node HOC with a function returning ReactNode: Renders. Theme from Column is NOT propagated by Node HOC to the internal Row.
241
- Node(ReturnRenderedDetailComponent, { info: 'Here are some details 6!' }).render(), // Node HOC (then .render()): Renders. Theme from Column is NOT propagated by Node HOC to the internal Row.
242
- // Node(DetailComponent, { info: 'Here are some details 7!' }).render(), // ❌ Fails: Same reason as above; Node HOC expects a function returning ReactNode.
406
+ // 3. Attempting to wrap a component returning a Node instance with Node HOC.
407
+ // ❌ Fails: The Node HOC expects the wrapped function to return a ReactNode, not a @meonode/ui Node instance.
408
+ // Node(DetailComponent, { info: 'Detail 3 (Node HOC on Node instance)' }),
409
+
410
+ // 4. Rendering a component that explicitly returns a ReactNode (.render() is called internally).
411
+ // The internal Row component correctly receives theme context from the parent Column.
412
+ ReturnRenderedDetailComponent({ info: 'Detail 4 (ReactNode)' }),
413
+
414
+ // 5. Wrapping a component returning ReactNode with Node HOC.
415
+ // Renders successfully. However, the Node HOC does NOT propagate theme context to the wrapped component's children.
416
+ Node(ReturnRenderedDetailComponent, { info: 'Detail 5 (Node HOC on ReactNode)' }),
417
+
418
+ // 6. Wrapping a component returning ReactNode with Node HOC, then calling .render().
419
+ // Renders successfully. Theme context is NOT propagated by the Node HOC.
420
+ Node(ReturnRenderedDetailComponent, { info: 'Detail 6 (Node HOC on ReactNode + .render())' }).render(),
243
421
 
244
422
  /**
245
423
  * Conditional rendering examples (shown when 'showMore' is true):
246
424
  * These demonstrate various wrapping techniques (inline functions, Component HOC)
247
425
  * and their effect on rendering and theme propagation for both types of detail components.
248
426
  */
249
- showMore && (() => DetailComponent({ info: 'Here are some details 8!' })), // Method 1 (inline function wrapper): Renders DetailComponent; its internal Row sources theme from context.
250
- showMore && (() => DetailComponent({ info: 'Here are some details 9!' }).render()), // Method 2 (inline function wrapper + .render()): Renders DetailComponent; its internal Row sources theme from context.
251
- showMore && Component(() => DetailComponent({ info: 'Here are some details 10!' })), // Method 3 (Component HOC wrapper): Renders DetailComponent; its internal Row sources theme from context.
427
+ // 7. Conditional rendering of a component returning a Node instance using an inline function wrapper.
428
+ // Renders successfully when `showMore` is true. The internal Row receives theme context.
429
+ showMore && (() => DetailComponent({ info: 'Detail 7 (Conditional inline function + Node instance)' })),
430
+
431
+ // 8. Conditional rendering of a component returning a Node instance using an inline function wrapper, then calling .render().
432
+ // Renders successfully when `showMore` is true. The internal Row receives theme context.
433
+ showMore && (() => DetailComponent({ info: 'Detail 8 (Conditional inline function + Node instance + .render())' }).render()),
434
+
435
+ // 9. Conditional rendering of a component returning a Node instance using the Component HOC wrapper.
436
+ // Renders successfully when `showMore` is true. The internal Row receives theme context.
437
+ showMore && Component(() => DetailComponent({ info: 'Detail 9 (Conditional Component HOC + Node instance)' })),
252
438
 
253
- showMore && (() => ReturnRenderedDetailComponent({ info: 'Here are some details 12!' })), // Method 4 (inline function wrapper): Renders ReturnRenderedDetailComponent; internal Row inherits theme from Column's context.
254
- showMore && Component(() => ReturnRenderedDetailComponent({ info: 'Here are some details 13!' })), // Method 5 (Component HOC wrapper): Renders ReturnRenderedDetailComponent; internal Row inherits theme from Column's context.
255
- showMore && Node(ReturnRenderedDetailComponent, { info: 'Here are some details 14!' }).render(), // Method 6 (Node HOC + .render()): Renders. Theme from Column is NOT propagated by Node HOC to internal Row.
439
+ // 10. Conditional rendering of a component returning ReactNode using an inline function wrapper.
440
+ // Renders successfully when `showMore` is true. The internal Row receives theme context.
441
+ showMore && (() => ReturnRenderedDetailComponent({ info: 'Detail 10 (Conditional inline function + ReactNode)' })),
442
+
443
+ // 11. Conditional rendering of a component returning ReactNode using the Component HOC wrapper.
444
+ // Renders successfully when `showMore` is true. The internal Row receives theme context.
445
+ showMore && Component(() => ReturnRenderedDetailComponent({ info: 'Detail 11 (Conditional Component HOC + ReactNode)' })),
256
446
  // showMore && ReturnRenderedDetailComponent({ info: 'Here are some details 15!' }), // ❌ Fails: Direct call to a component function using hooks (ReturnRenderedDetailComponent) inside render logic without a React-aware wrapper. This can violate Rules of Hooks.
257
447
  ],
258
448
  })
@@ -261,15 +451,17 @@ export default Component(() => {
261
451
  /**
262
452
  * A component that displays a styled detail section.
263
453
  * It uses `useEffect` for lifecycle logging. The internal `Row` component
264
- * sources its theme from the React context established by an ancestor
265
- * @meonode/ui component (e.g., the main Column in this page).
266
- * This component returns a @meonode/ui `Row` Node instance.
454
+ * sources its theme from the React context provided by an ancestor `@meonode/ui`
455
+ * component (like the main `Column` in this page).
267
456
  *
457
+ * This component returns a @meonode/ui `Row` Node instance.
458
+ * This type of component is suitable for direct inclusion as a child within other
459
+ * `@meonode/ui` components that expect `Node` instances or arrays of `Node` instances.
268
460
  * @param {object} props - Component properties.
269
461
  * @param {string} props.info - Text content to display in the detail section.
270
- * @returns {import('@meonode/ui').Node} A @meonode/ui Row Node instance.
462
+ * @returns {NodeInstance} A @meonode/ui Row Node instance.
271
463
  */
272
- const DetailComponent = ({ info }: { info: string }) => {
464
+ const DetailComponent = ({ info }: { info: string }): NodeInstance => {
273
465
  // useEffect hook for logging component mount and unmount phases (for debugging).
274
466
  useEffect(() => {
275
467
  console.log('DetailComponent mounted:', info) // Example mount log
@@ -278,31 +470,34 @@ const DetailComponent = ({ info }: { info: string }) => {
278
470
  }
279
471
  }, [info]) // Effect depends on 'info' prop.
280
472
 
281
- // Returns a @meonode/ui Row component configured with props and children.
473
+ // Returns a @meonode/ui Row Node instance configured with props and children.
282
474
  // Its styling (e.g., backgroundColor) will resolve theme strings from React context.
283
475
  return Row({
476
+ alignItems: 'center',
284
477
  gap: 10,
285
478
  padding: 4,
286
- border: '1px solid green',
479
+ border: '2px solid theme.accent',
287
480
  borderRadius: 6,
288
- color: 'red',
289
- backgroundColor: 'theme.background.secondary', // Background color sourced from theme in React context.
290
- children: [P(info), TextField({ background: 'theme.background.primary' })],
481
+ backgroundColor: 'theme.warning', // Background color sourced from theme in React context.
482
+ color: 'theme.danger',
483
+ children: [P(info, { flex: 1, padding: '0 20px' }), TextField({ flex: 1, sx: { background: 'theme.primary' } })],
291
484
  })
292
485
  }
293
486
 
294
487
  /**
295
488
  * An alternative detail component implementation that explicitly calls `.render()`
296
489
  * to return a `ReactNode` (a rendered React element) directly.
297
- * This makes it compatible with wrappers like the `Node` HOC that expect a function returning `ReactNode`.
490
+ * This makes it compatible with standard React rendering patterns and wrappers
491
+ * like the `Node` HOC that specifically expect a function returning `ReactNode`.
298
492
  * It uses `useEffect` for lifecycle logging. The internal `Row` sources its
299
493
  * theme from React context.
300
494
  *
495
+ * This component returns a `ReactNode`.
301
496
  * @param {object} props - Component properties.
302
497
  * @param {string} props.info - Text content to display.
303
498
  * @returns {React.ReactNode} A rendered React element (the result of `Row(...).render()`).
304
499
  */
305
- const ReturnRenderedDetailComponent = ({ info }: { info: string }) => {
500
+ const ReturnRenderedDetailComponent = ({ info }: { info: string }): ReactNode => {
306
501
  // useEffect hook for logging component mount and unmount phases (for debugging).
307
502
  useEffect(() => {
308
503
  console.log('ReturnRenderedDetailComponent mounted:', info) // Example mount log
@@ -315,72 +510,102 @@ const ReturnRenderedDetailComponent = ({ info }: { info: string }) => {
315
510
  // The Row itself will attempt to resolve theme strings (e.g., 'theme.background.secondary')
316
511
  // from the React context provided by an ancestor @meonode/ui component (like the main Column).
317
512
  return Row({
513
+ alignItems: 'center',
318
514
  gap: 10,
319
515
  padding: 4,
320
- border: '1px solid green',
516
+ border: '2px solid theme.accent',
321
517
  borderRadius: 6,
322
- color: 'red',
323
- backgroundColor: 'theme.background.secondary', // Theme-aware background; relies on theme from React context.
324
- children: [P(info), TextField({ background: 'theme.background.primary' })],
518
+ backgroundColor: 'theme.warning', // Theme-aware background; relies on theme from React context.
519
+ color: 'theme.danger',
520
+ children: [P(info, { flex: 1, padding: '0 20px' }), TextField({ flex: 1, sx: { background: 'theme.primary' } })],
325
521
  }).render() // Explicitly renders to ReactNode.
326
522
  }
327
- ```
328
523
 
329
- ### Material UI Integration
330
-
331
- ```shell
332
- yarn add @meonode/mui @mui/material
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
+ }
333
581
  ```
334
582
 
335
- ```ts
336
- import { Button, TextField } from '@meonode/mui';
337
-
338
- const MuiLoginForm = Component(() =>
339
- Div({
340
- maxWidth: '400px',
341
- margin: '0 auto',
342
- children: [
343
- TextField({
344
- label: 'Email',
345
- fullWidth: true,
346
- margin: 'normal'
347
- }),
348
- TextField({
349
- label: 'Password',
350
- type: 'password',
351
- fullWidth: true,
352
- margin: 'normal'
353
- }),
354
- Button({
355
- variant: 'contained',
356
- color: 'primary',
357
- children: 'Sign In'
358
- })
359
- ]
360
- })
361
- );
362
- ```
583
+ ---
363
584
 
364
585
  ## API Reference
365
586
 
366
587
  ### Core Functions
367
588
 
368
- | Function | Parameters | Description |
369
- |----------------|-----------------------------------------|-------------------------------------------------|
370
- | `Node` | `element: string \| Component`, `props` | Creates a new UI node |
371
- | `Component` | `(props) => Node \| ReactNode` | Converts node trees to React components |
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 |
372
593
 
594
+ ---
373
595
 
374
596
  ## Contributing
375
597
 
376
598
  We welcome contributions! Please follow these steps:
377
- 1. Fork the repository
378
- 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
379
- 3. Commit your changes
380
- 4. Push to the branch
381
- 5. Open a Pull Request
382
599
 
383
- Contact me on [Discord](https://discordapp.com/users/704803255561224264) for discussions.
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.
384
609
 
385
610
  ---
386
611