@fluixi/dom 1.0.0-alpha.53
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/LICENSE +21 -0
- package/README.md +657 -0
- package/dist/cdn/dom.cjs +1 -0
- package/dist/cdn/dom.d.ts +16 -0
- package/dist/cdn/dom.d.ts.map +1 -0
- package/dist/cdn/dom.global.js +1 -0
- package/dist/cdn/dom.js +15 -0
- package/dist/cdn/dom.mjs +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.mjs +1 -0
- package/dist/lib/component/index.cjs +1 -0
- package/dist/lib/component/index.d.ts +2 -0
- package/dist/lib/component/index.d.ts.map +1 -0
- package/dist/lib/component/index.js +1 -0
- package/dist/lib/component/index.mjs +1 -0
- package/dist/lib/component/modifiers.cjs +1 -0
- package/dist/lib/component/modifiers.d.ts +21 -0
- package/dist/lib/component/modifiers.d.ts.map +1 -0
- package/dist/lib/component/modifiers.js +171 -0
- package/dist/lib/component/modifiers.mjs +1 -0
- package/dist/lib/dom/delegate.cjs +1 -0
- package/dist/lib/dom/delegate.d.ts +17 -0
- package/dist/lib/dom/delegate.d.ts.map +1 -0
- package/dist/lib/dom/delegate.js +50 -0
- package/dist/lib/dom/delegate.mjs +1 -0
- package/dist/lib/dom/hydration-state.cjs +1 -0
- package/dist/lib/dom/hydration-state.d.ts +45 -0
- package/dist/lib/dom/hydration-state.d.ts.map +1 -0
- package/dist/lib/dom/hydration-state.js +94 -0
- package/dist/lib/dom/hydration-state.mjs +1 -0
- package/dist/lib/dom/hydration.cjs +1 -0
- package/dist/lib/dom/hydration.d.ts +10 -0
- package/dist/lib/dom/hydration.d.ts.map +1 -0
- package/dist/lib/dom/hydration.js +143 -0
- package/dist/lib/dom/hydration.mjs +1 -0
- package/dist/lib/dom/index.cjs +1 -0
- package/dist/lib/dom/index.d.ts +9 -0
- package/dist/lib/dom/index.d.ts.map +1 -0
- package/dist/lib/dom/index.js +12 -0
- package/dist/lib/dom/index.mjs +1 -0
- package/dist/lib/dom/integration.cjs +1 -0
- package/dist/lib/dom/integration.d.ts +16 -0
- package/dist/lib/dom/integration.d.ts.map +1 -0
- package/dist/lib/dom/integration.js +25 -0
- package/dist/lib/dom/integration.mjs +1 -0
- package/dist/lib/dom/island.cjs +1 -0
- package/dist/lib/dom/island.d.ts +23 -0
- package/dist/lib/dom/island.d.ts.map +1 -0
- package/dist/lib/dom/island.js +100 -0
- package/dist/lib/dom/island.mjs +1 -0
- package/dist/lib/dom/runtime.cjs +1 -0
- package/dist/lib/dom/runtime.d.ts +131 -0
- package/dist/lib/dom/runtime.d.ts.map +1 -0
- package/dist/lib/dom/runtime.js +1425 -0
- package/dist/lib/dom/runtime.mjs +1 -0
- package/dist/lib/dom/server/fx-data.cjs +1 -0
- package/dist/lib/dom/server/fx-data.d.ts +36 -0
- package/dist/lib/dom/server/fx-data.d.ts.map +1 -0
- package/dist/lib/dom/server/fx-data.js +60 -0
- package/dist/lib/dom/server/fx-data.mjs +1 -0
- package/dist/lib/dom/server/host.cjs +1 -0
- package/dist/lib/dom/server/host.d.ts +14 -0
- package/dist/lib/dom/server/host.d.ts.map +1 -0
- package/dist/lib/dom/server/host.js +38 -0
- package/dist/lib/dom/server/host.mjs +1 -0
- package/dist/lib/dom/server/index.cjs +1 -0
- package/dist/lib/dom/server/index.d.ts +13 -0
- package/dist/lib/dom/server/index.d.ts.map +1 -0
- package/dist/lib/dom/server/index.js +12 -0
- package/dist/lib/dom/server/index.mjs +1 -0
- package/dist/lib/dom/server/nodes.cjs +1 -0
- package/dist/lib/dom/server/nodes.d.ts +105 -0
- package/dist/lib/dom/server/nodes.d.ts.map +1 -0
- package/dist/lib/dom/server/nodes.js +304 -0
- package/dist/lib/dom/server/nodes.mjs +1 -0
- package/dist/lib/dom/server/render.cjs +1 -0
- package/dist/lib/dom/server/render.d.ts +45 -0
- package/dist/lib/dom/server/render.d.ts.map +1 -0
- package/dist/lib/dom/server/render.js +174 -0
- package/dist/lib/dom/server/render.mjs +1 -0
- package/dist/lib/dom/server/request-context.cjs +1 -0
- package/dist/lib/dom/server/request-context.d.ts +85 -0
- package/dist/lib/dom/server/request-context.d.ts.map +1 -0
- package/dist/lib/dom/server/request-context.js +122 -0
- package/dist/lib/dom/server/request-context.mjs +1 -0
- package/dist/lib/dom/server/serialize.cjs +1 -0
- package/dist/lib/dom/server/serialize.d.ts +2 -0
- package/dist/lib/dom/server/serialize.d.ts.map +1 -0
- package/dist/lib/dom/server/serialize.js +98 -0
- package/dist/lib/dom/server/serialize.mjs +1 -0
- package/dist/lib/dom/server-renderer.cjs +1 -0
- package/dist/lib/dom/server-renderer.d.ts +2 -0
- package/dist/lib/dom/server-renderer.d.ts.map +1 -0
- package/dist/lib/dom/server-renderer.js +5 -0
- package/dist/lib/dom/server-renderer.mjs +1 -0
- package/dist/lib/dom/types.cjs +1 -0
- package/dist/lib/dom/types.d.ts +119 -0
- package/dist/lib/dom/types.d.ts.map +1 -0
- package/dist/lib/dom/types.js +1 -0
- package/dist/lib/dom/types.mjs +0 -0
- package/dist/lib/dom/utils.cjs +1 -0
- package/dist/lib/dom/utils.d.ts +411 -0
- package/dist/lib/dom/utils.d.ts.map +1 -0
- package/dist/lib/dom/utils.js +746 -0
- package/dist/lib/dom/utils.mjs +1 -0
- package/dist/lib/element/index.cjs +1 -0
- package/dist/lib/element/index.d.ts +2 -0
- package/dist/lib/element/index.d.ts.map +1 -0
- package/dist/lib/element/index.js +1 -0
- package/dist/lib/element/index.mjs +0 -0
- package/dist/lib/element/types.cjs +1 -0
- package/dist/lib/element/types.d.ts +129 -0
- package/dist/lib/element/types.d.ts.map +1 -0
- package/dist/lib/element/types.js +5 -0
- package/dist/lib/element/types.mjs +0 -0
- package/dist/lib/flow/class-map.cjs +1 -0
- package/dist/lib/flow/class-map.d.ts +40 -0
- package/dist/lib/flow/class-map.d.ts.map +1 -0
- package/dist/lib/flow/class-map.js +44 -0
- package/dist/lib/flow/class-map.mjs +1 -0
- package/dist/lib/flow/dynamic.cjs +1 -0
- package/dist/lib/flow/dynamic.d.ts +27 -0
- package/dist/lib/flow/dynamic.d.ts.map +1 -0
- package/dist/lib/flow/dynamic.js +30 -0
- package/dist/lib/flow/dynamic.mjs +1 -0
- package/dist/lib/flow/error-boundary.cjs +1 -0
- package/dist/lib/flow/error-boundary.d.ts +34 -0
- package/dist/lib/flow/error-boundary.d.ts.map +1 -0
- package/dist/lib/flow/error-boundary.js +49 -0
- package/dist/lib/flow/error-boundary.mjs +1 -0
- package/dist/lib/flow/for.cjs +1 -0
- package/dist/lib/flow/for.d.ts +64 -0
- package/dist/lib/flow/for.d.ts.map +1 -0
- package/dist/lib/flow/for.js +28 -0
- package/dist/lib/flow/for.mjs +1 -0
- package/dist/lib/flow/index-flow.cjs +1 -0
- package/dist/lib/flow/index-flow.d.ts +34 -0
- package/dist/lib/flow/index-flow.d.ts.map +1 -0
- package/dist/lib/flow/index-flow.js +27 -0
- package/dist/lib/flow/index-flow.mjs +1 -0
- package/dist/lib/flow/index.cjs +1 -0
- package/dist/lib/flow/index.d.ts +20 -0
- package/dist/lib/flow/index.d.ts.map +1 -0
- package/dist/lib/flow/index.js +11 -0
- package/dist/lib/flow/index.mjs +1 -0
- package/dist/lib/flow/portal.cjs +1 -0
- package/dist/lib/flow/portal.d.ts +30 -0
- package/dist/lib/flow/portal.d.ts.map +1 -0
- package/dist/lib/flow/portal.js +44 -0
- package/dist/lib/flow/portal.mjs +1 -0
- package/dist/lib/flow/show.cjs +1 -0
- package/dist/lib/flow/show.d.ts +35 -0
- package/dist/lib/flow/show.d.ts.map +1 -0
- package/dist/lib/flow/show.js +162 -0
- package/dist/lib/flow/show.mjs +1 -0
- package/dist/lib/flow/style-map.cjs +1 -0
- package/dist/lib/flow/style-map.d.ts +49 -0
- package/dist/lib/flow/style-map.d.ts.map +1 -0
- package/dist/lib/flow/style-map.js +58 -0
- package/dist/lib/flow/style-map.mjs +1 -0
- package/dist/lib/flow/switch.cjs +1 -0
- package/dist/lib/flow/switch.d.ts +63 -0
- package/dist/lib/flow/switch.d.ts.map +1 -0
- package/dist/lib/flow/switch.js +69 -0
- package/dist/lib/flow/switch.mjs +1 -0
- package/dist/lib/flow/utils.cjs +1 -0
- package/dist/lib/flow/utils.d.ts +19 -0
- package/dist/lib/flow/utils.d.ts.map +1 -0
- package/dist/lib/flow/utils.js +34 -0
- package/dist/lib/flow/utils.mjs +1 -0
- package/dist/lib/index.cjs +1 -0
- package/dist/lib/index.d.ts +21 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +30 -0
- package/dist/lib/index.mjs +1 -0
- package/dist/lib/jsx/control-flow/flow.cjs +1 -0
- package/dist/lib/jsx/control-flow/flow.d.ts +2 -0
- package/dist/lib/jsx/control-flow/flow.d.ts.map +1 -0
- package/dist/lib/jsx/control-flow/flow.js +627 -0
- package/dist/lib/jsx/control-flow/flow.mjs +0 -0
- package/dist/lib/jsx/control-flow/for-flow.cjs +1 -0
- package/dist/lib/jsx/control-flow/for-flow.d.ts +2 -0
- package/dist/lib/jsx/control-flow/for-flow.d.ts.map +1 -0
- package/dist/lib/jsx/control-flow/for-flow.js +1 -0
- package/dist/lib/jsx/control-flow/for-flow.mjs +0 -0
- package/dist/lib/shared/types.cjs +1 -0
- package/dist/lib/shared/types.d.ts +47 -0
- package/dist/lib/shared/types.d.ts.map +1 -0
- package/dist/lib/shared/types.js +1 -0
- package/dist/lib/shared/types.mjs +0 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +99 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fluixi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/fluixi/assets/main/logos/128x128.png" alt="Fluixi" width="120" height="120" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# @fluixi/dom
|
|
6
|
+
|
|
7
|
+
**Reactive DOM runtime with surgical fine-grained updates, SSR and lit-html integration.**
|
|
8
|
+
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+

|
|
11
|
+

|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
A comprehensive reactive DOM runtime with fine-grained updates, inspired by SolidJS's dom-expressions. This package provides low-level DOM manipulation utilities that work seamlessly with signal and store systems to enable optimal, surgical DOM updates.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- 🎯 **Fine-Grained Reactivity**: Only updates the exact DOM nodes that need to change
|
|
21
|
+
- 🔄 **Signal & Store Integration**: Works with any reactive system (signals, stores, observables)
|
|
22
|
+
- 🎨 **Lit-HTML Support**: Seamlessly integrate lit-html templates with native JSX
|
|
23
|
+
- 🚀 **Optimized Performance**: Template caching, event delegation, and minimal re-renders
|
|
24
|
+
- 🧩 **Control Flow Components**: Built-in Show, For, Switch, Portal, and more
|
|
25
|
+
- 📦 **Zero Dependencies**: Core runtime has no external dependencies on reactive systems
|
|
26
|
+
- 🔧 **Flexible Integration**: Use standalone or integrate with your reactive library
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @fluixi/dom
|
|
32
|
+
# or
|
|
33
|
+
pnpm add @fluixi/dom
|
|
34
|
+
# or
|
|
35
|
+
yarn add @fluixi/dom
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
### Basic Usage
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { insert, createElement, setProperty } from '@fluixi/dom';
|
|
44
|
+
|
|
45
|
+
// Create an element
|
|
46
|
+
const div = createElement('div');
|
|
47
|
+
|
|
48
|
+
// Set properties
|
|
49
|
+
setProperty(div, 'className', 'container');
|
|
50
|
+
setProperty(div, 'textContent', 'Hello World');
|
|
51
|
+
|
|
52
|
+
// Insert into DOM
|
|
53
|
+
document.body.appendChild(div);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### With Signals
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { insert, createElement } from '@fluixi/dom';
|
|
60
|
+
import { createSignal } from '@fluixi/reactive/signal';
|
|
61
|
+
|
|
62
|
+
const [count, setCount] = createSignal(0);
|
|
63
|
+
|
|
64
|
+
const div = createElement('div');
|
|
65
|
+
|
|
66
|
+
// Insert reactive text
|
|
67
|
+
insert(div, () => `Count: ${count()}`);
|
|
68
|
+
|
|
69
|
+
document.body.appendChild(div);
|
|
70
|
+
|
|
71
|
+
// Updates automatically!
|
|
72
|
+
setCount(1); // DOM updates to "Count: 1"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Initialize Integration
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { initializeIntegration } from '@fluixi/dom';
|
|
79
|
+
import { createEffect, batch } from '@fluixi/reactive/signal';
|
|
80
|
+
|
|
81
|
+
// Initialize with your reactive system
|
|
82
|
+
initializeIntegration({
|
|
83
|
+
signalSystem: {
|
|
84
|
+
createEffect,
|
|
85
|
+
batch,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Core API
|
|
91
|
+
|
|
92
|
+
### DOM Manipulation
|
|
93
|
+
|
|
94
|
+
#### `insert(parent, accessor, marker?, init?)`
|
|
95
|
+
|
|
96
|
+
Insert dynamic content with automatic reactivity tracking.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { insert, createTextNode } from '@fluixi/dom';
|
|
100
|
+
import { createSignal } from '@fluixi/reactive/signal';
|
|
101
|
+
|
|
102
|
+
const [text, setText] = createSignal('Hello');
|
|
103
|
+
const div = document.createElement('div');
|
|
104
|
+
|
|
105
|
+
// Insert reactive content
|
|
106
|
+
insert(div, () => text());
|
|
107
|
+
|
|
108
|
+
// Or insert static content
|
|
109
|
+
insert(div, 'Static text');
|
|
110
|
+
|
|
111
|
+
// With a marker for positioning
|
|
112
|
+
const marker = createTextNode('');
|
|
113
|
+
div.appendChild(marker);
|
|
114
|
+
insert(div, () => text(), marker);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### `spread(options)`
|
|
118
|
+
|
|
119
|
+
Spread props onto an element with fine-grained updates.
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { spread, createElement } from '@fluixi/dom';
|
|
123
|
+
|
|
124
|
+
const element = createElement('div');
|
|
125
|
+
const props = {
|
|
126
|
+
className: 'container',
|
|
127
|
+
style: { color: 'red' },
|
|
128
|
+
onClick: () => console.log('clicked'),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
spread({ element, props });
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Attributes & Properties
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { setAttribute, setProperty, setClassName, setStyle } from '@fluixi/dom';
|
|
138
|
+
|
|
139
|
+
const div = createElement('div');
|
|
140
|
+
|
|
141
|
+
// Set attribute
|
|
142
|
+
setAttribute(div, 'data-id', '123');
|
|
143
|
+
|
|
144
|
+
// Set property
|
|
145
|
+
setProperty(div, 'value', 'text');
|
|
146
|
+
|
|
147
|
+
// Set className (handles strings, arrays, objects)
|
|
148
|
+
setClassName(div, 'btn btn-primary');
|
|
149
|
+
setClassName(div, ['btn', 'btn-primary']);
|
|
150
|
+
setClassName(div, { btn: true, 'btn-primary': true });
|
|
151
|
+
|
|
152
|
+
// Set style (handles strings or objects)
|
|
153
|
+
setStyle(div, 'color: red; font-size: 16px');
|
|
154
|
+
setStyle(div, { color: 'red', fontSize: '16px' });
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Dynamic Attributes & Properties
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import { setDynamicAttribute, setDynamicProperty } from '@fluixi/dom';
|
|
161
|
+
import { createSignal } from '@fluixi/reactive/signal';
|
|
162
|
+
|
|
163
|
+
const [color, setColor] = createSignal('red');
|
|
164
|
+
const div = createElement('div');
|
|
165
|
+
|
|
166
|
+
// Reactive attribute
|
|
167
|
+
setDynamicAttribute(div, 'data-color', color);
|
|
168
|
+
|
|
169
|
+
// Reactive property
|
|
170
|
+
setDynamicProperty(div, 'className', () => `text-${color()}`);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Event Delegation
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { delegateEvents, addDelegatedEventListener } from '@fluixi/dom';
|
|
177
|
+
|
|
178
|
+
// Setup delegation for common events
|
|
179
|
+
delegateEvents(['click', 'input', 'change']);
|
|
180
|
+
|
|
181
|
+
// Add delegated listener
|
|
182
|
+
const button = createElement('button');
|
|
183
|
+
addDelegatedEventListener(button, 'click', (e) => {
|
|
184
|
+
console.log('Button clicked!');
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Control Flow Components
|
|
189
|
+
|
|
190
|
+
### Show
|
|
191
|
+
|
|
192
|
+
Conditional rendering with optional fallback.
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
import { Show } from '@fluixi/dom';
|
|
196
|
+
import { createSignal } from '@fluixi/reactive/signal';
|
|
197
|
+
|
|
198
|
+
const [user, setUser] = createSignal(null);
|
|
199
|
+
|
|
200
|
+
// Basic usage
|
|
201
|
+
Show({
|
|
202
|
+
when: user,
|
|
203
|
+
children: (u) => `Hello, ${u.name}!`,
|
|
204
|
+
fallback: 'Loading...',
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// With reactive condition
|
|
208
|
+
Show({
|
|
209
|
+
when: () => user() !== null,
|
|
210
|
+
children: (u) => `Hello, ${u.name}!`,
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### For
|
|
215
|
+
|
|
216
|
+
Keyed list rendering with optimal updates.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
import { For } from '@fluixi/dom';
|
|
220
|
+
import { createSignal } from '@fluixi/reactive/signal';
|
|
221
|
+
|
|
222
|
+
const [items, setItems] = createSignal([
|
|
223
|
+
{ id: 1, name: 'Item 1' },
|
|
224
|
+
{ id: 2, name: 'Item 2' },
|
|
225
|
+
]);
|
|
226
|
+
|
|
227
|
+
For({
|
|
228
|
+
each: items,
|
|
229
|
+
children: (item, index) => {
|
|
230
|
+
const div = createElement('div');
|
|
231
|
+
insert(div, () => `${index()}: ${item.name}`);
|
|
232
|
+
return div;
|
|
233
|
+
},
|
|
234
|
+
fallback: 'No items',
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Index
|
|
239
|
+
|
|
240
|
+
Index-based list rendering (use when items change but positions don't).
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
import { Index } from '@fluixi/dom';
|
|
244
|
+
|
|
245
|
+
Index({
|
|
246
|
+
each: () => [1, 2, 3, 4, 5],
|
|
247
|
+
children: (item, index) => {
|
|
248
|
+
const div = createElement('div');
|
|
249
|
+
insert(div, () => `Item ${index}: ${item()}`);
|
|
250
|
+
return div;
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Switch/Match
|
|
256
|
+
|
|
257
|
+
Multi-way conditional rendering.
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import { Switch, Match } from '@fluixi/dom';
|
|
261
|
+
import { createSignal } from '@fluixi/reactive/signal';
|
|
262
|
+
|
|
263
|
+
const [state, setState] = createSignal('loading');
|
|
264
|
+
|
|
265
|
+
Switch({
|
|
266
|
+
fallback: 'Unknown state',
|
|
267
|
+
children: [
|
|
268
|
+
Match({
|
|
269
|
+
when: () => state() === 'loading',
|
|
270
|
+
children: 'Loading...',
|
|
271
|
+
}),
|
|
272
|
+
Match({
|
|
273
|
+
when: () => state() === 'success',
|
|
274
|
+
children: 'Success!',
|
|
275
|
+
}),
|
|
276
|
+
Match({
|
|
277
|
+
when: () => state() === 'error',
|
|
278
|
+
children: 'Error occurred',
|
|
279
|
+
}),
|
|
280
|
+
],
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Portal
|
|
285
|
+
|
|
286
|
+
Render content in a different DOM location.
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
import { Portal } from '@fluixi/dom';
|
|
290
|
+
|
|
291
|
+
Portal({
|
|
292
|
+
mount: document.body,
|
|
293
|
+
children: createElement('div', { textContent: 'Portal content' }),
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Dynamic
|
|
298
|
+
|
|
299
|
+
Dynamically render components based on runtime conditions.
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
import { Dynamic } from '@fluixi/dom';
|
|
303
|
+
import { createSignal } from '@fluixi/reactive/signal';
|
|
304
|
+
|
|
305
|
+
const [component, setComponent] = createSignal('div');
|
|
306
|
+
|
|
307
|
+
Dynamic({
|
|
308
|
+
component: component,
|
|
309
|
+
className: 'dynamic-element',
|
|
310
|
+
children: 'Dynamic content',
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### ErrorBoundary
|
|
315
|
+
|
|
316
|
+
Catch and handle errors in component trees.
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
import { ErrorBoundary } from '@fluixi/dom';
|
|
320
|
+
|
|
321
|
+
ErrorBoundary({
|
|
322
|
+
fallback: (error, reset) => {
|
|
323
|
+
const div = createElement('div');
|
|
324
|
+
insert(div, `Error: ${error.message}`);
|
|
325
|
+
|
|
326
|
+
const button = createElement('button');
|
|
327
|
+
button.textContent = 'Retry';
|
|
328
|
+
button.onclick = reset;
|
|
329
|
+
|
|
330
|
+
div.appendChild(button);
|
|
331
|
+
return div;
|
|
332
|
+
},
|
|
333
|
+
children: /* your components */,
|
|
334
|
+
});
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Integration System
|
|
338
|
+
|
|
339
|
+
The package provides a flexible integration system that works with any reactive library.
|
|
340
|
+
|
|
341
|
+
### Initialize with Signal System
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import { initializeIntegration } from '@fluixi/dom';
|
|
345
|
+
import * as signals from '@fluixi/reactive/signal';
|
|
346
|
+
|
|
347
|
+
initializeIntegration({
|
|
348
|
+
signalSystem: {
|
|
349
|
+
createEffect: signals.createEffect,
|
|
350
|
+
batch: signals.batch,
|
|
351
|
+
createMemo: signals.createMemo,
|
|
352
|
+
untrack: signals.untrack,
|
|
353
|
+
getOwner: signals.getOwner,
|
|
354
|
+
runWithOwner: signals.runWithOwner,
|
|
355
|
+
createRoot: signals.createRoot,
|
|
356
|
+
onCleanup: signals.onCleanup,
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Initialize with Store System
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
import { initializeIntegration } from '@fluixi/dom';
|
|
365
|
+
import * as store from '@fluixi/reactive/store';
|
|
366
|
+
|
|
367
|
+
initializeIntegration({
|
|
368
|
+
storeSystem: {
|
|
369
|
+
isStore: store.isStore,
|
|
370
|
+
unwrap: store.unwrap,
|
|
371
|
+
isStoreProxy: store.isStoreProxy,
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Auto-initialization
|
|
377
|
+
|
|
378
|
+
The package attempts to auto-initialize by detecting available reactive systems:
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
import { autoInitialize } from '@fluixi/dom';
|
|
382
|
+
|
|
383
|
+
// Manually trigger auto-initialization
|
|
384
|
+
autoInitialize();
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Global Registration
|
|
388
|
+
|
|
389
|
+
Register your reactive systems globally for automatic detection:
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
import { registerSignalSystem, registerStoreSystem } from '@fluixi/dom';
|
|
393
|
+
|
|
394
|
+
registerSignalSystem({
|
|
395
|
+
createEffect: myCreateEffect,
|
|
396
|
+
batch: myBatch,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
registerStoreSystem({
|
|
400
|
+
isStore: myIsStore,
|
|
401
|
+
unwrap: myUnwrap,
|
|
402
|
+
});
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## Lit-HTML Integration
|
|
406
|
+
|
|
407
|
+
Seamlessly use lit-html templates with reactive updates.
|
|
408
|
+
|
|
409
|
+
### Basic Usage
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
import { signal } from '@fluixi/dom/reactive';
|
|
413
|
+
import { html } from 'lit';
|
|
414
|
+
import { createSignal } from '@fluixi/reactive/signal';
|
|
415
|
+
|
|
416
|
+
const [count, setCount] = createSignal(0);
|
|
417
|
+
|
|
418
|
+
const template = html`
|
|
419
|
+
<div>
|
|
420
|
+
<p>Count: ${signal(count)}</p>
|
|
421
|
+
<button @click=${() => setCount(count() + 1)}>Increment</button>
|
|
422
|
+
</div>
|
|
423
|
+
`;
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Reactive Directives
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
import { signal, $if, $each } from '@fluixi/dom/reactive';
|
|
430
|
+
import { html } from 'lit';
|
|
431
|
+
|
|
432
|
+
const [items, setItems] = createSignal(['a', 'b', 'c']);
|
|
433
|
+
const [show, setShow] = createSignal(true);
|
|
434
|
+
|
|
435
|
+
const template = html`
|
|
436
|
+
<div>
|
|
437
|
+
${$if(show,
|
|
438
|
+
html`<p>Visible content</p>`,
|
|
439
|
+
html`<p>Hidden</p>`
|
|
440
|
+
)}
|
|
441
|
+
|
|
442
|
+
${$each(items,
|
|
443
|
+
(item) => item,
|
|
444
|
+
(item) => html`<li>${item}</li>`
|
|
445
|
+
)}
|
|
446
|
+
</div>
|
|
447
|
+
`;
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### Auto-wrapping with `rx`
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
import { rx } from '@fluixi/dom/reactive';
|
|
454
|
+
|
|
455
|
+
const [name, setName] = createSignal('World');
|
|
456
|
+
|
|
457
|
+
// Automatically wraps signal accessors
|
|
458
|
+
const template = rx`
|
|
459
|
+
<div>Hello, ${name}!</div>
|
|
460
|
+
`;
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## Advanced Usage
|
|
464
|
+
|
|
465
|
+
### Custom Effect System
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
import { registerEffectCreator } from '@fluixi/dom';
|
|
469
|
+
|
|
470
|
+
registerEffectCreator((fn) => {
|
|
471
|
+
// Your custom effect implementation
|
|
472
|
+
const dispose = myCustomEffect(fn);
|
|
473
|
+
return dispose;
|
|
474
|
+
});
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Custom Batch Function
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
import { registerBatch } from '@fluixi/dom';
|
|
481
|
+
|
|
482
|
+
registerBatch((fn) => {
|
|
483
|
+
// Your custom batching logic
|
|
484
|
+
myCustomBatch(fn);
|
|
485
|
+
});
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### Reactive Bindings
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
import { createReactiveBinding } from '@fluixi/dom';
|
|
492
|
+
|
|
493
|
+
const cleanup = createReactiveBinding(
|
|
494
|
+
() => mySignal(), // getter
|
|
495
|
+
(value) => {
|
|
496
|
+
// Update handler
|
|
497
|
+
console.log('Value changed:', value);
|
|
498
|
+
}
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
// Cleanup when done
|
|
502
|
+
cleanup();
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Memory Management
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
import { createRoot, onCleanup } from '@fluixi/dom';
|
|
509
|
+
|
|
510
|
+
createRoot((dispose) => {
|
|
511
|
+
// Set up reactive scope
|
|
512
|
+
const effect = createEffect(() => {
|
|
513
|
+
// Effect logic
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
// Register cleanup
|
|
517
|
+
onCleanup(() => {
|
|
518
|
+
effect();
|
|
519
|
+
console.log('Cleaned up!');
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
// Dispose when done
|
|
523
|
+
dispose();
|
|
524
|
+
});
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
## Performance Tips
|
|
528
|
+
|
|
529
|
+
1. **Use `For` for lists**: The `For` component uses keyed reconciliation for optimal updates
|
|
530
|
+
2. **Delegate events**: Use `delegateEvents` for better performance with many event listeners
|
|
531
|
+
3. **Batch updates**: Wrap multiple updates in `batch()` to minimize re-renders
|
|
532
|
+
4. **Memoize expensive computations**: Use `createMemo` from your signal system
|
|
533
|
+
5. **Untrack when needed**: Use `untrack()` to read reactive values without creating dependencies
|
|
534
|
+
6. **Hoist static content**: Move static elements outside reactive contexts
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
import { batch, createMemo, untrack } from '@fluixi/dom';
|
|
538
|
+
|
|
539
|
+
// Batch multiple updates
|
|
540
|
+
batch(() => {
|
|
541
|
+
setSignal1(value1);
|
|
542
|
+
setSignal2(value2);
|
|
543
|
+
setSignal3(value3);
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
// Memoize expensive computations
|
|
547
|
+
const computed = createMemo(() => {
|
|
548
|
+
return expensiveOperation(signal1(), signal2());
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
// Read without tracking
|
|
552
|
+
const value = untrack(() => signal());
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
## TypeScript Support
|
|
556
|
+
|
|
557
|
+
Full TypeScript support with comprehensive type definitions.
|
|
558
|
+
|
|
559
|
+
```typescript
|
|
560
|
+
import type {
|
|
561
|
+
Children,
|
|
562
|
+
InsertOptions,
|
|
563
|
+
SpreadOptions,
|
|
564
|
+
SignalSystem,
|
|
565
|
+
StoreSystem,
|
|
566
|
+
ShowProps,
|
|
567
|
+
ForProps,
|
|
568
|
+
} from '@fluixi/dom';
|
|
569
|
+
|
|
570
|
+
// Use types in your code
|
|
571
|
+
const mySignalSystem: SignalSystem = {
|
|
572
|
+
createEffect: (fn) => {
|
|
573
|
+
// Implementation
|
|
574
|
+
return () => {};
|
|
575
|
+
},
|
|
576
|
+
batch: (fn) => {
|
|
577
|
+
// Implementation
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
## Browser Support
|
|
583
|
+
|
|
584
|
+
- Modern browsers (Chrome, Firefox, Safari, Edge)
|
|
585
|
+
- ES2020+ required
|
|
586
|
+
- No polyfills needed for supported browsers
|
|
587
|
+
|
|
588
|
+
## Debugging
|
|
589
|
+
|
|
590
|
+
### Check Integration Status
|
|
591
|
+
|
|
592
|
+
```typescript
|
|
593
|
+
import { getIntegrationStatus, logIntegrationStatus } from '@fluixi/dom';
|
|
594
|
+
|
|
595
|
+
// Get status object
|
|
596
|
+
const status = getIntegrationStatus();
|
|
597
|
+
console.log(status);
|
|
598
|
+
|
|
599
|
+
// Pretty print to console
|
|
600
|
+
logIntegrationStatus();
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### Development Mode
|
|
604
|
+
|
|
605
|
+
Set the `development` flag when initializing:
|
|
606
|
+
|
|
607
|
+
```typescript
|
|
608
|
+
initializeIntegration({
|
|
609
|
+
signalSystem: mySignalSystem,
|
|
610
|
+
development: true, // Enables additional checks and warnings
|
|
611
|
+
});
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
## Comparison with Other Libraries
|
|
615
|
+
|
|
616
|
+
### vs SolidJS
|
|
617
|
+
|
|
618
|
+
This package is heavily inspired by SolidJS's dom-expressions but:
|
|
619
|
+
- Works as a standalone library
|
|
620
|
+
- Doesn't require a specific compiler
|
|
621
|
+
- Can integrate with any reactive system
|
|
622
|
+
- Supports both lit-html and native JSX
|
|
623
|
+
|
|
624
|
+
### vs React
|
|
625
|
+
|
|
626
|
+
- Fine-grained reactivity (no virtual DOM)
|
|
627
|
+
- No reconciliation needed
|
|
628
|
+
- Direct DOM manipulation
|
|
629
|
+
- Smaller runtime size
|
|
630
|
+
- Better performance for many use cases
|
|
631
|
+
|
|
632
|
+
### vs Lit
|
|
633
|
+
|
|
634
|
+
- Works with lit-html templates
|
|
635
|
+
- Adds fine-grained reactivity on top
|
|
636
|
+
- Can be used standalone without Lit components
|
|
637
|
+
- More flexible control flow
|
|
638
|
+
|
|
639
|
+
## Contributing
|
|
640
|
+
|
|
641
|
+
Contributions are welcome! Please see the main repository for guidelines.
|
|
642
|
+
|
|
643
|
+
## License
|
|
644
|
+
|
|
645
|
+
MIT
|
|
646
|
+
|
|
647
|
+
## Related Packages
|
|
648
|
+
|
|
649
|
+
- `@fluixi/jsx` - JSX runtime using this DOM package
|
|
650
|
+
- `@fluixi/reactive` - Signal and store implementations
|
|
651
|
+
- `lit` - Template literals for HTML
|
|
652
|
+
|
|
653
|
+
## Resources
|
|
654
|
+
|
|
655
|
+
- [SolidJS Documentation](https://www.solidjs.com/)
|
|
656
|
+
- [dom-expressions](https://github.com/ryansolid/dom-expressions)
|
|
657
|
+
- [Lit Documentation](https://lit.dev/)
|