@llui/dom 0.0.1 → 0.0.2
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 +111 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,14 +1,122 @@
|
|
|
1
1
|
# @llui/dom
|
|
2
2
|
|
|
3
|
-
Runtime for the [LLui](https://github.com/fponticelli/llui) web framework.
|
|
3
|
+
Runtime for the [LLui](https://github.com/fponticelli/llui) web framework — The Elm Architecture with compile-time bitmask optimization.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
No virtual DOM. `view()` runs once at mount, building real DOM nodes with reactive bindings that update surgically when state changes.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
10
|
pnpm add @llui/dom
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { component, mountApp, div, button } from '@llui/dom'
|
|
17
|
+
|
|
18
|
+
type State = { count: number }
|
|
19
|
+
type Msg = { type: 'inc' } | { type: 'dec' }
|
|
20
|
+
|
|
21
|
+
const Counter = component<State, Msg, never>({
|
|
22
|
+
name: 'Counter',
|
|
23
|
+
init: () => [{ count: 0 }, []],
|
|
24
|
+
update: (state, msg) => {
|
|
25
|
+
switch (msg.type) {
|
|
26
|
+
case 'inc': return [{ ...state, count: state.count + 1 }, []]
|
|
27
|
+
case 'dec': return [{ ...state, count: state.count - 1 }, []]
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
view: ({ send, text }) => [
|
|
31
|
+
button({ onClick: () => send({ type: 'dec' }) }, [text('-')]),
|
|
32
|
+
text(s => String(s.count)),
|
|
33
|
+
button({ onClick: () => send({ type: 'inc' }) }, [text('+')]),
|
|
34
|
+
],
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
mountApp(document.getElementById('app')!, Counter)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## View<S, M> — the helper bundle
|
|
41
|
+
|
|
42
|
+
`view` receives a single `View<S, M>` bag. Destructure what you need — `send` plus any state-bound helpers. TypeScript infers `S` from the component definition, so no per-call generics:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
view: ({ send, text, show, each, branch, memo }) => [
|
|
46
|
+
text(s => s.label), // s is State — inferred
|
|
47
|
+
...show({ when: s => s.visible, render: () => [...] }),
|
|
48
|
+
...each({ items: s => s.items, key: i => i.id, render: ({ item }) => [...] }),
|
|
49
|
+
]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Element helpers (`div`, `button`, `span`, etc.) stay as imports — they're stateless and don't need the `S` binding.
|
|
53
|
+
|
|
54
|
+
## API
|
|
55
|
+
|
|
56
|
+
### Core
|
|
57
|
+
|
|
58
|
+
| Export | Purpose |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `component(def)` | Create a component definition |
|
|
61
|
+
| `mountApp(el, def)` | Mount a component to a DOM element |
|
|
62
|
+
| `hydrateApp(el, def)` | Hydrate server-rendered HTML |
|
|
63
|
+
| `flush()` | Synchronously flush all pending updates |
|
|
64
|
+
| `createView(send)` | Create a full View bundle (for tests/dynamic use) |
|
|
65
|
+
|
|
66
|
+
### View Primitives
|
|
67
|
+
|
|
68
|
+
| Primitive | Purpose |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `text(accessor)` | Reactive text node |
|
|
71
|
+
| `show({ when, render })` | Conditional rendering |
|
|
72
|
+
| `branch({ on, cases })` | Multi-case switching |
|
|
73
|
+
| `each({ items, key, render })` | Keyed list rendering |
|
|
74
|
+
| `portal({ target, render })` | Render into a different DOM location |
|
|
75
|
+
| `child({ def, key, props })` | Full component boundary (Level 2 composition) |
|
|
76
|
+
| `memo(accessor)` | Memoized derived value |
|
|
77
|
+
| `selector(field)` | O(1) one-of-N selection binding |
|
|
78
|
+
| `onMount(callback)` | Lifecycle hook (runs once after mount) |
|
|
79
|
+
| `errorBoundary(opts)` | Catch render errors |
|
|
80
|
+
| `foreign({ create, update })` | Integrate non-LLui libraries |
|
|
81
|
+
| `slice(h, selector)` | View over a sub-slice of state |
|
|
82
|
+
|
|
83
|
+
### Composition
|
|
84
|
+
|
|
85
|
+
| Export | Purpose |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `mergeHandlers(...handlers)` | Combine multiple update handlers |
|
|
88
|
+
| `sliceHandler({ get, set, narrow, sub })` | Route messages to a state slice |
|
|
89
|
+
|
|
90
|
+
### Context
|
|
91
|
+
|
|
92
|
+
| Export | Purpose |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `createContext(defaultValue)` | Create a context |
|
|
95
|
+
| `provide(ctx, accessor, children)` | Provide value to subtree |
|
|
96
|
+
| `useContext(ctx)` | Read context value |
|
|
97
|
+
|
|
98
|
+
### Element Helpers
|
|
99
|
+
|
|
100
|
+
50+ typed element constructors: `div`, `span`, `button`, `input`, `a`, `h1`-`h6`, `table`, `tr`, `td`, `ul`, `li`, `img`, `form`, `label`, `select`, `textarea`, `canvas`, `video`, `nav`, `header`, `footer`, `section`, `article`, `p`, `pre`, `code`, and more.
|
|
101
|
+
|
|
102
|
+
### SSR
|
|
103
|
+
|
|
104
|
+
| Export | Purpose |
|
|
105
|
+
|---|---|
|
|
106
|
+
| `renderToString(def)` | Render component to HTML string |
|
|
107
|
+
| `initSsrDom()` | Initialize jsdom for SSR (from `@llui/dom/ssr`) |
|
|
108
|
+
|
|
109
|
+
## Sub-path Exports
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { installDevTools } from '@llui/dom/devtools' // dev-only, tree-shaken
|
|
113
|
+
import { initSsrDom } from '@llui/dom/ssr' // server-only
|
|
114
|
+
import { replaceComponent } from '@llui/dom/hmr' // HMR support
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Performance
|
|
118
|
+
|
|
119
|
+
Competitive with Solid and Svelte on [js-framework-benchmark](https://github.com/krausest/js-framework-benchmark). 5.8 KB gzipped.
|
|
12
120
|
|
|
13
121
|
## License
|
|
14
122
|
|