@pfern/elements 0.1.3 → 0.1.6

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,6 +1,7 @@
1
1
  # Elements.js
2
2
 
3
- A minimalist declarative UI toolkit designed around purity, immutability, and HTML semantics.
3
+ Elements.js is a minimalist declarative UI toolkit designed around purity,
4
+ immutability, and HTML semantics.
4
5
 
5
6
  ## Features
6
7
 
@@ -14,7 +15,8 @@ A minimalist declarative UI toolkit designed around purity, immutability, and HT
14
15
 
15
16
  ## Why Elements.js?
16
17
 
17
- Modern frameworks introduced declarative UI—but buried it beneath lifecycle hooks, mutable state, and complex diffing algorithms.
18
+ Modern frameworks introduced declarative UI—but buried it beneath lifecycle
19
+ hooks, mutable state, and complex diffing algorithms.
18
20
 
19
21
  **Elements.js goes further:**
20
22
 
@@ -36,7 +38,8 @@ Yes. Elements.js proves it.
36
38
  * No lifecycle methods or effects
37
39
  * Every component is a function
38
40
 
39
- To update a view: just **call the function again** with new arguments. The DOM subtree is replaced in place.
41
+ To update a view: just **call the function again** with new arguments. The DOM
42
+ subtree is replaced in place.
40
43
 
41
44
  ### State lives in the DOM
42
45
 
@@ -49,23 +52,22 @@ The DOM node *is the history*. Input state is passed as an argument.
49
52
  * No transpilation step
50
53
  * No reactive graph to debug
51
54
 
52
- Elements.js embraces the full truth of each function call as the only valid state.
55
+ Elements.js embraces the full truth of each function call as the only valid
56
+ state.
53
57
 
54
58
  ---
55
59
 
56
60
  ## Example: Counter
57
61
 
58
62
  ```js
59
- import { div, pre, button, component, render } from './elements.js';
63
+ import { button, component, div, output } from '@pfern/elements'
60
64
 
61
- const counter = component((count = 0) =>
65
+ export const counter = component((count = 0) =>
62
66
  div(
63
- pre(count),
64
- button({ onclick: () => counter(count + 1) }, 'Increment')
65
- )
66
- )
67
-
68
- render(counter(), document.body);
67
+ output(count),
68
+ button(
69
+ { onclick: () => counter(count + 1) },
70
+ 'Increment')))
69
71
  ```
70
72
 
71
73
  * Each click returns a new call to `counter(count + 1)`
@@ -77,30 +79,36 @@ render(counter(), document.body);
77
79
  ## Form Example: Todos App
78
80
 
79
81
  ```js
80
- import { button, div, component, form, input, li, span, ul } from './elements.js';
81
82
 
82
- export const todos = component((items = []) => {
83
- const add = ({ todo: { value } }) =>
84
- value && todos([...items, { value, done: false }])
83
+ import { button, component, div,
84
+ form, input, li, span, ul } from '@pfern/elements'
85
+
86
+ export const todos = component(
87
+ (items = [{ value: 'Add my first todo', done: true }]) => {
88
+
89
+ const add = ({ todo: { value } }) =>
90
+ value && todos([...items, { value, done: false }])
91
+
92
+ const remove = item =>
93
+ todos(items.filter(i => i !== item))
94
+
95
+ const toggle = item =>
96
+ todos(items.map(i => i === item ? { ...i, done: !item.done } : i))
97
+
98
+ return (
99
+ div({ class: 'todos' },
100
+
101
+ form({ onsubmit: add },
102
+ input({ name: 'todo', placeholder: 'What needs doing?' }),
103
+ button({ type: 'submit' }, 'Add')),
104
+
105
+ ul(...items.map(item =>
106
+ li(
107
+ { style:
108
+ { 'text-decoration': item.done ? 'line-through' : 'none' } },
109
+ span({ onclick: () => toggle(item) }, item.value),
110
+ button({ onclick: () => remove(item) }, '✕'))))))})
85
111
 
86
- const remove = item =>
87
- todos(items.filter(i => i !== item))
88
-
89
- const toggle = item =>
90
- todos(items.map(i => i === item ? { ...i, done: !item.done } : i))
91
-
92
- return div({ class: 'todos' },
93
- form({ onsubmit: add },
94
- input({ name: 'todo', placeholder: 'What needs doing?' }),
95
- button({ type: 'submit' }, 'Add')),
96
- ul(...items.map(item =>
97
- li({ style: { 'text-decoration': item.done ? 'line-through' : 'none' } },
98
- span({ onclick: () => toggle(item) }, item.value),
99
- button({ onclick: () => remove(item) }, '✕')
100
- ))
101
- )
102
- )
103
- })
104
112
  ```
105
113
 
106
114
  This is a complete MVC-style app:
@@ -109,11 +117,17 @@ This is a complete MVC-style app:
109
117
  * Immutable
110
118
  * Pure
111
119
 
120
+ You can view these examples live on [Github
121
+ Pages](https://pfernandez.github.io/elements/) or by running them locally with
122
+ `npm run dev`.
123
+
112
124
  ---
113
125
 
114
126
  ## Root Rendering Shortcut
115
127
 
116
- If you use `html`, `head`, or `body` as the top-level tag, `render()` will automatically mount into the corresponding document element—no need to pass a container.
128
+ If you use `html`, `head`, or `body` as the top-level tag, `render()` will
129
+ automatically mount into the corresponding document element—no need to pass a
130
+ container.
117
131
 
118
132
  ```js
119
133
  import {
@@ -126,7 +140,8 @@ render(
126
140
  html(
127
141
  head(
128
142
  title('Elements.js'),
129
- meta({ name: 'viewport', content: 'width=device-width, initial-scale=1.0' }),
143
+ meta({ name: 'viewport',
144
+ content: 'width=device-width, initial-scale=1.0' }),
130
145
  link({ rel: 'stylesheet', href: 'css/style.css' })
131
146
  ),
132
147
  body(
@@ -134,29 +149,29 @@ render(
134
149
  main(
135
150
  section(
136
151
  h2('Todos'),
137
- todos()
138
- )
139
- )
140
- )
141
- )
142
- )
152
+ todos())))))
143
153
  ```
144
154
 
145
155
  ---
146
156
 
147
157
  ## Declarative Events
148
158
 
149
- All event listeners in Elements.js are pure functions. You can return a vnode from a listener to declaratively update the component tree—no mutation or imperative logic required.
159
+ All event listeners in Elements.js are pure functions. You can return a vnode
160
+ from a listener to declaratively update the component tree—- no mutation or
161
+ imperative logic required.
150
162
 
151
163
  ### General Behavior
152
164
 
153
- * Any event handler (e.g. `onclick`, `onsubmit`, `oninput`) may return a new vnode to trigger a subtree replacement.
154
- * If the handler returns `undefined`, the event is treated as passive (no update occurs).
165
+ * Any event handler (e.g. `onclick`, `onsubmit`, `oninput`) may return a new
166
+ vnode to trigger a subtree replacement.
167
+ * If the handler returns `undefined`, the event is treated as passive (no update
168
+ occurs).
155
169
  * Returned vnodes are passed to `component()` to re-render declaratively.
156
170
 
157
171
  ### Form Events
158
172
 
159
- For `onsubmit`, `oninput`, and `onchange`, Elements.js provides a special signature:
173
+ For `onsubmit`, `oninput`, and `onchange`, Elements.js provides a special
174
+ signature:
160
175
 
161
176
  ```js
162
177
  (event.target.elements, event)
@@ -167,7 +182,8 @@ That is, your handler receives:
167
182
  1. `elements`: the HTML form’s named inputs
168
183
  2. `event`: the original DOM event object
169
184
 
170
- Elements.js will automatically call `event.preventDefault()` *only if* your handler returns a vnode.
185
+ Elements.js will automatically call `event.preventDefault()` *only if* your
186
+ handler returns a vnode.
171
187
 
172
188
  ```js
173
189
  form({
@@ -176,7 +192,8 @@ form({
176
192
  })
177
193
  ```
178
194
 
179
- If the handler returns nothing, `preventDefault()` is skipped and the form submits natively.
195
+ If the handler returns nothing, `preventDefault()` is skipped and the form
196
+ submits natively.
180
197
 
181
198
  ---
182
199
 
@@ -188,7 +205,8 @@ Wrap a recursive pure function that returns a vnode.
188
205
 
189
206
  ### `render(vnode[, container])`
190
207
 
191
- Render a vnode into the DOM. If `vnode[0]` is `html`, `head`, or `body`, no `container` is required.
208
+ Render a vnode into the DOM. If `vnode[0]` is `html`, `head`, or `body`, no
209
+ `container` is required.
192
210
 
193
211
  ### DOM Elements
194
212
 
@@ -201,17 +219,23 @@ svg({ width: 100 }, circle({ r: 10 }))
201
219
 
202
220
  ### TypeScript & JSDoc
203
221
 
204
- Each tag function (e.g. `div`, `button`, `svg`) includes a `@typedef` and MDN-sourced description to:
222
+ Each tag function (e.g. `div`, `button`, `svg`) includes a `@typedef` and
223
+ MDN-sourced description to:
205
224
 
206
225
  * Provide editor hints
207
226
  * Encourage accessibility and semantic markup
208
227
  * Enable intelligent autocomplete
209
228
 
229
+ ### Testing Philosophy
230
+
231
+ Elements are data-in, data-out only, so mocking and headless browsers like
232
+ `jsdom` are unnecessary out of the box. See the tests [in this
233
+ repository](test/README.md) for some examples.
234
+
210
235
  ---
211
236
 
212
237
  ## Status
213
238
 
214
- * ✅ Production-ready core
215
239
  * 🧪 Fully tested (data-in/data-out behavior)
216
240
  * ⚡ Under 2kB min+gzip
217
241
  * ✅ Node and browser compatible