@loadbare/app 0.6.0 → 0.7.1
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/dist/build/assemble.js +1 -0
- package/dist/build/assemble.js.map +1 -0
- package/dist/build/cli.js +1 -0
- package/dist/build/cli.js.map +1 -0
- package/dist/build/elements.js +1 -0
- package/dist/build/elements.js.map +1 -0
- package/dist/build/expand.js +1 -0
- package/dist/build/expand.js.map +1 -0
- package/dist/build/format.js +1 -0
- package/dist/build/format.js.map +1 -0
- package/dist/build/locations.d.ts +2 -2
- package/dist/build/locations.d.ts.map +1 -1
- package/dist/build/locations.js +3 -4
- package/dist/build/locations.js.map +1 -0
- package/dist/build/origins.js +1 -0
- package/dist/build/origins.js.map +1 -0
- package/dist/build/package-root.js +1 -0
- package/dist/build/package-root.js.map +1 -0
- package/dist/build/pages.d.ts +4 -0
- package/dist/build/pages.d.ts.map +1 -1
- package/dist/build/pages.js +5 -0
- package/dist/build/pages.js.map +1 -0
- package/dist/build/styles.js +1 -0
- package/dist/build/styles.js.map +1 -0
- package/dist/core/lb-constants.js +1 -0
- package/dist/core/lb-constants.js.map +1 -0
- package/dist/core/lb-types.d.ts +1 -1
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js +2 -1
- package/dist/core/lb-types.js.map +1 -0
- package/dist/hub/lb-apply.d.ts +11 -1
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +50 -13
- package/dist/hub/lb-apply.js.map +1 -0
- package/dist/hub/lb-hub.browser.d.ts +1 -1
- package/dist/hub/lb-hub.browser.d.ts.map +1 -1
- package/dist/hub/lb-hub.browser.js +143 -112
- package/dist/hub/lb-hub.browser.js.map +1 -0
- package/dist/server/lb-express.d.ts +1 -1
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +1 -0
- package/dist/server/lb-express.js.map +1 -0
- package/dist/server/lb-server.d.ts +1 -1
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +2 -1
- package/dist/server/lb-server.js.map +1 -0
- package/docs/TECHREF-1.0.md +158 -43
- package/docs/analysis-accidental-complexity.md +149 -0
- package/docs/reference/custom-elements.md +27 -17
- package/docs/reference/data-binding.md +88 -29
- package/docs/reference/server.md +18 -0
- package/docs/reference/widgets.md +3 -3
- package/docs/theory.md +114 -1
- package/docs/tutorials/072-inserting-into-a-list.md +14 -11
- package/docs/tutorials/080-widget-requests.md +9 -26
- package/package.json +2 -2
- package/dist/tests/assemble.test.d.ts +0 -8
- package/dist/tests/assemble.test.d.ts.map +0 -1
- package/dist/tests/assemble.test.js +0 -210
- package/dist/tests/elements.test.d.ts +0 -8
- package/dist/tests/elements.test.d.ts.map +0 -1
- package/dist/tests/elements.test.js +0 -118
- package/dist/tests/expand.test.d.ts +0 -10
- package/dist/tests/expand.test.d.ts.map +0 -1
- package/dist/tests/expand.test.js +0 -253
- package/dist/tests/fixtures/elements/collision/imports.d.ts +0 -3
- package/dist/tests/fixtures/elements/collision/imports.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/collision/imports.js +0 -1
- package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.d.ts +0 -2
- package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.js +0 -1
- package/dist/tests/fixtures/elements/local/widgets/app-box.browser.d.ts +0 -2
- package/dist/tests/fixtures/elements/local/widgets/app-box.browser.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/local/widgets/app-box.browser.js +0 -1
- package/dist/tests/fixtures/elements/manifest/imports.d.ts +0 -3
- package/dist/tests/fixtures/elements/manifest/imports.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/manifest/imports.js +0 -1
- package/dist/tests/fixtures/elements/manifest-bad-entry/imports.d.ts +0 -3
- package/dist/tests/fixtures/elements/manifest-bad-entry/imports.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/manifest-bad-entry/imports.js +0 -1
- package/dist/tests/fixtures/elements/manifest-not-array/imports.d.ts +0 -5
- package/dist/tests/fixtures/elements/manifest-not-array/imports.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/manifest-not-array/imports.js +0 -1
- package/dist/tests/fixtures/elements/pkg/acme-widget.browser.d.ts +0 -2
- package/dist/tests/fixtures/elements/pkg/acme-widget.browser.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/pkg/acme-widget.browser.js +0 -1
- package/dist/tests/fixtures/elements/unmarked/widgets/app-box.d.ts +0 -6
- package/dist/tests/fixtures/elements/unmarked/widgets/app-box.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/unmarked/widgets/app-box.js +0 -1
- package/dist/tests/helpers/console.d.ts +0 -20
- package/dist/tests/helpers/console.d.ts.map +0 -1
- package/dist/tests/helpers/console.js +0 -28
- package/dist/tests/helpers/dom.d.ts +0 -18
- package/dist/tests/helpers/dom.d.ts.map +0 -1
- package/dist/tests/helpers/dom.js +0 -22
- package/dist/tests/helpers/hub.d.ts +0 -73
- package/dist/tests/helpers/hub.d.ts.map +0 -1
- package/dist/tests/helpers/hub.js +0 -151
- package/dist/tests/lb-apply.test.d.ts +0 -8
- package/dist/tests/lb-apply.test.d.ts.map +0 -1
- package/dist/tests/lb-apply.test.js +0 -177
- package/dist/tests/lb-express.test.d.ts +0 -14
- package/dist/tests/lb-express.test.d.ts.map +0 -1
- package/dist/tests/lb-express.test.js +0 -243
- package/dist/tests/lb-hub.test.d.ts +0 -14
- package/dist/tests/lb-hub.test.d.ts.map +0 -1
- package/dist/tests/lb-hub.test.js +0 -319
- package/dist/tests/lb-list.test.d.ts +0 -12
- package/dist/tests/lb-list.test.d.ts.map +0 -1
- package/dist/tests/lb-list.test.js +0 -339
- package/dist/tests/lb-server.test.d.ts +0 -9
- package/dist/tests/lb-server.test.d.ts.map +0 -1
- package/dist/tests/lb-server.test.js +0 -546
- package/dist/tests/origins.test.d.ts +0 -10
- package/dist/tests/origins.test.d.ts.map +0 -1
- package/dist/tests/origins.test.js +0 -387
- package/dist/tests/pages.test.d.ts +0 -6
- package/dist/tests/pages.test.d.ts.map +0 -1
- package/dist/tests/pages.test.js +0 -148
- package/dist/tests/styles.test.d.ts +0 -7
- package/dist/tests/styles.test.d.ts.map +0 -1
- package/dist/tests/styles.test.js +0 -76
|
@@ -19,19 +19,21 @@ Here is a page that binds a scalar, a list, and three requests:
|
|
|
19
19
|
|
|
20
20
|
<p lb-row="dues">Dues collected this year: <span lb-cell="total"></span></p>
|
|
21
21
|
|
|
22
|
-
<
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
</
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
</
|
|
22
|
+
<section lb-list="roster">
|
|
23
|
+
<form lb-action="lb-row-insert">
|
|
24
|
+
<input lb-cell="name" placeholder="Name" />
|
|
25
|
+
<button type="submit">Add member</button>
|
|
26
|
+
</form>
|
|
27
|
+
|
|
28
|
+
<ul>
|
|
29
|
+
<template lb-key="id">
|
|
30
|
+
<li>
|
|
31
|
+
<lb-input lb-cell="name"></lb-input>
|
|
32
|
+
<button lb-action="lb-row-delete">Remove</button>
|
|
33
|
+
</li>
|
|
34
|
+
</template>
|
|
35
|
+
</ul>
|
|
36
|
+
</section>
|
|
35
37
|
```
|
|
36
38
|
|
|
37
39
|
The queries named here — `dues` and `roster` — and the operations the page
|
|
@@ -46,7 +48,7 @@ asks for are declared on the server; see [page files](./page-files.md).
|
|
|
46
48
|
| `lb-key` | a developer | The column that identifies a row |
|
|
47
49
|
| `lb-cell` | a developer | The column an element displays |
|
|
48
50
|
| `lb-key-value` | the hub | A live row's own key |
|
|
49
|
-
| `lb-value` | the hub |
|
|
51
|
+
| `lb-value` | the hub | The value that landed on a cell |
|
|
50
52
|
|
|
51
53
|
A binding is scoped by ancestry. Either scope attribute scopes its DOM
|
|
52
54
|
children, and a nested one of either kind begins a new scope, so an element
|
|
@@ -85,20 +87,23 @@ value of it. A developer writes the first and never the second.
|
|
|
85
87
|
|
|
86
88
|
### Where a bound value lands
|
|
87
89
|
|
|
88
|
-
A value lands on a bound element one of
|
|
90
|
+
A value lands on a bound element one of three ways.
|
|
89
91
|
|
|
90
|
-
| Element
|
|
91
|
-
|
|
92
|
-
| A
|
|
93
|
-
|
|
|
92
|
+
| Element | Receives the value as |
|
|
93
|
+
|----------------------------------------|-----------------------------------|
|
|
94
|
+
| A custom element | Its `lb-value` attribute |
|
|
95
|
+
| `<select>`, `<textarea>`, or `<input>` | Its `value`, and `lb-value` |
|
|
96
|
+
| Any other native element | Its `textContent`, and `lb-value` |
|
|
94
97
|
|
|
95
|
-
A
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
A widget owns whatever control it wraps, so it is handed the value and
|
|
99
|
+
renders it itself — see [Custom Elements](./custom-elements.md#code) for
|
|
100
|
+
observing `lb-value`. A form control shows its state as its `value`, so a
|
|
101
|
+
`<select>` keeps its options. Any other native element has no behavior of its
|
|
102
|
+
own, so its value is its text. Checkboxes, radio buttons and file inputs
|
|
103
|
+
receive nothing, not even `lb-value`, and the hub reports it to the console.
|
|
99
104
|
|
|
100
105
|
Nothing an application writes ever sets `lb-value`. It is written by
|
|
101
|
-
Loadbare and read by
|
|
106
|
+
Loadbare and read by a widget or a stylesheet.
|
|
102
107
|
|
|
103
108
|
### Lists
|
|
104
109
|
|
|
@@ -154,7 +159,9 @@ the wire discriminant: a value beginning with `lb-` is an operation, and
|
|
|
154
159
|
anything else is a name the page declared.
|
|
155
160
|
|
|
156
161
|
The hub sends the first four from a native element on the element's own
|
|
157
|
-
event: a form on submit, anything else on click.
|
|
162
|
+
event: a form on submit, anything else on click. An insert or update clicked
|
|
163
|
+
from a button reads the cells around the button (see [Forms](#forms)). A
|
|
164
|
+
cell change needs a
|
|
158
165
|
widget to say what a change is, so only a widget sends it: the shipped
|
|
159
166
|
`<lb-input>` does when it carries `lb-action="lb-cell-change"`, and stays
|
|
160
167
|
quiet otherwise — an input inside an `lb-row-insert` or `lb-row-update` form is read
|
|
@@ -222,7 +229,35 @@ from the same ancestor a delete button reads:
|
|
|
222
229
|
|
|
223
230
|
Give every `lb-cell` in a form a control to read. A cell that is neither an
|
|
224
231
|
`<input>`, `<select>`, or `<textarea>` nor wraps one is left out of the
|
|
225
|
-
values map.
|
|
232
|
+
values map, and a form with no cell to read sends nothing.
|
|
233
|
+
|
|
234
|
+
Where the cells cannot sit in a form, as in a table row, put the action on a
|
|
235
|
+
button beside them. The hub gathers from the nearest element around the
|
|
236
|
+
button that holds a cell to read, so a button in a row for a new entry
|
|
237
|
+
inserts that row's cells:
|
|
238
|
+
|
|
239
|
+
```html
|
|
240
|
+
<table lb-list="roster">
|
|
241
|
+
<tbody>
|
|
242
|
+
<tr>
|
|
243
|
+
<td><input lb-cell="name" /></td>
|
|
244
|
+
<td><input lb-cell="role" /></td>
|
|
245
|
+
<td><button lb-action="lb-row-insert">Add</button></td>
|
|
246
|
+
</tr>
|
|
247
|
+
</tbody>
|
|
248
|
+
</table>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
The search stops at the live row the button is in, so an `lb-row-update`
|
|
252
|
+
button in a row saves only that row, and it never takes in the list scope
|
|
253
|
+
itself. Keep the button's own cell apart from the others: a button sharing
|
|
254
|
+
a `<td>` with one input gathers only that input.
|
|
255
|
+
|
|
256
|
+
Put these two actions on a form or a button, not on an element that holds
|
|
257
|
+
the cells. A click into one of its inputs would send the row, so the hub
|
|
258
|
+
refuses it. A widget may dispatch either from any element, and the hub
|
|
259
|
+
gathers the same way; see
|
|
260
|
+
[Sending a request](./custom-elements.md#sending-a-request).
|
|
226
261
|
|
|
227
262
|
## Conditional rendering
|
|
228
263
|
|
|
@@ -230,9 +265,33 @@ Loadbare ships static HTML and hydrates elements that are already in the
|
|
|
230
265
|
document. There is no `if`, and none is needed: write every possibility into
|
|
231
266
|
the page, and control which of them is showing.
|
|
232
267
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
268
|
+
Every cell carries the value that landed on it as `lb-value`, so a
|
|
269
|
+
stylesheet can show or hide part of a page from a value the server sent. Bind
|
|
270
|
+
a column the page does not display to a hidden element:
|
|
271
|
+
|
|
272
|
+
```html
|
|
273
|
+
<template lb-key="id">
|
|
274
|
+
<tr>
|
|
275
|
+
<td lb-cell="name"></td>
|
|
276
|
+
<td lb-cell="locked" hidden></td>
|
|
277
|
+
<td><button lb-action="lb-row-delete">Remove</button></td>
|
|
278
|
+
</tr>
|
|
279
|
+
</template>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
```css
|
|
283
|
+
tr:has([lb-cell="locked"][lb-value="true"]) button {
|
|
284
|
+
display: none;
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
`lb-value` holds the string the query sent, so the query decides the spelling
|
|
289
|
+
the selector matches. A stylesheet can hide a control but cannot disable one,
|
|
290
|
+
and hiding is presentation: the server still refuses what a request may not
|
|
291
|
+
do.
|
|
292
|
+
|
|
293
|
+
Where showing a case takes more than a selector, a widget receives the value
|
|
294
|
+
in `lb-value` and decides what to show:
|
|
236
295
|
|
|
237
296
|
```ts
|
|
238
297
|
for (const step of steps) step.hidden = step.dataset.step !== value;
|
package/docs/reference/server.md
CHANGED
|
@@ -86,6 +86,24 @@ Run the server under a TypeScript-capable runner. The builder writes
|
|
|
86
86
|
}
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
Node 22.18 and later also runs it with no runner at all, since Node strips
|
|
90
|
+
TypeScript types itself. `dist/pages.ts` imports each `.requests.ts` and
|
|
91
|
+
`.queries.ts` file by its full name, extension included, because Node looks
|
|
92
|
+
for exactly the path an import names.
|
|
93
|
+
|
|
94
|
+
Enable `allowImportingTsExtensions` in the application's `tsconfig.json`
|
|
95
|
+
when `tsc` type-checks the server. Without it, `tsc` rejects the `.ts`
|
|
96
|
+
extensions in `dist/pages.ts`:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"compilerOptions": {
|
|
101
|
+
"noEmit": true,
|
|
102
|
+
"allowImportingTsExtensions": true
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
89
107
|
Restart the server after adding or changing a `.requests.ts` or `.queries.ts`
|
|
90
108
|
file.
|
|
91
109
|
|
|
@@ -27,9 +27,9 @@ for where a listed package sits in the cascade.
|
|
|
27
27
|
|
|
28
28
|
Wraps an `<input>`. `lb-value` sets the input's `.value`. The widget sends
|
|
29
29
|
nothing on its own: `lb-action` names what the input's `change` sends. The
|
|
30
|
-
reserved `lb-cell-change` sends `lb-cell-change
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
reserved `lb-cell-change` sends `lb-cell-change`; any other name sends that
|
|
31
|
+
action. Either carries the input's value, and the hub adds the scope the
|
|
32
|
+
input sits in.
|
|
33
33
|
|
|
34
34
|
An input inside an `lb-row-insert` or `lb-row-update` form leaves the attribute off.
|
|
35
35
|
The form reads every `lb-cell` in it on submit and sends one request for all
|
package/docs/theory.md
CHANGED
|
@@ -344,4 +344,117 @@ I wrote Loadbare originally in 2003, as "Andromeda", before Node existed
|
|
|
344
344
|
and even 4 years before we had JQuery. Andromeda was not nearly as
|
|
345
345
|
optimized as Loadbare is now, but the principles were the same then as
|
|
346
346
|
they are now. It has always worked for me, and I hope that it will work
|
|
347
|
-
for you.
|
|
347
|
+
for you.
|
|
348
|
+
|
|
349
|
+
## A Consistency Model
|
|
350
|
+
|
|
351
|
+
> **LLM-authored, not yet revised by a person.** Drafted by Claude on
|
|
352
|
+
> 2026-09-13, from a design session against `@loadbare/app` 0.6.0. Treat
|
|
353
|
+
> it as a proposal for this essay, not as the author's statement.
|
|
354
|
+
|
|
355
|
+
### From a subjective claim to a checkable one
|
|
356
|
+
|
|
357
|
+
Whether Loadbare carries less accidental complexity than another tool will
|
|
358
|
+
always be disputable. A developer fluent in React has muscle memory for its
|
|
359
|
+
rules and will see Loadbare's rules as foreign, and foreign work feels
|
|
360
|
+
accidental. That objection is about familiarity, and familiarity is a cost
|
|
361
|
+
paid once, in the same way this essay treats the Express boilerplate.
|
|
362
|
+
|
|
363
|
+
A stronger claim can be checked, and a single counterexample in markup
|
|
364
|
+
would refute it. The model for applying data to HTML is:
|
|
365
|
+
|
|
366
|
+
1. **Internally consistent.** A few rules, each applied the same way
|
|
367
|
+
everywhere, with no exceptions.
|
|
368
|
+
2. **Consistent with HTML.** Each rule has a counterpart in how HTML
|
|
369
|
+
already behaves, and none contradicts it.
|
|
370
|
+
3. **Consistent with relational data.** What the server sends is the shape
|
|
371
|
+
a SQL query already returns, so nothing is translated on the way.
|
|
372
|
+
|
|
373
|
+
### Where the vocabulary comes from
|
|
374
|
+
|
|
375
|
+
This essay says the vocabulary is small because the operations a database
|
|
376
|
+
affords are small and coherent. A sharper statement is that the vocabulary
|
|
377
|
+
sits where two systems that are already coherent meet: the relational model
|
|
378
|
+
and HTML's containment model. Each attribute is a relational idea placed on
|
|
379
|
+
an element.
|
|
380
|
+
|
|
381
|
+
| Loadbare | Relational | HTML precedent |
|
|
382
|
+
| ---------------------------------------- | -------------------------------------------- | ------------------------------------------------------------ |
|
|
383
|
+
| `lb-list` | a relation, a set of rows | `<select>`, `<ul>`: a container whose contents are its items |
|
|
384
|
+
| `lb-row` | a single row | `<form>`: one record of fields |
|
|
385
|
+
| `lb-cell` | a column | `name` on a control: which field this is |
|
|
386
|
+
| `lb-key`, `lb-key-value` | the primary key | `value` on `<option>`: identity apart from the label |
|
|
387
|
+
| `lb-action` | a closed set of writes, and named procedures | `action` on `<form>`: where a submission goes |
|
|
388
|
+
| scope from ancestors | | form ownership, `lang`, `<fieldset disabled>` |
|
|
389
|
+
| a nested scope begins a new one | | a nested element owns its own contents |
|
|
390
|
+
| `lb-row-count`, `lb-pending`, `lb-error` | | state attributes such as `open` on `<details>` |
|
|
391
|
+
|
|
392
|
+
### The rules
|
|
393
|
+
|
|
394
|
+
1. **An element's own attributes describe what it displays. Its ancestors
|
|
395
|
+
describe where it belongs.** A picker carrying `lb-list` displays that
|
|
396
|
+
list, and its choice is addressed to the row it sits in, the way a
|
|
397
|
+
`<select>` displays its options and submits to the form around it.
|
|
398
|
+
2. **A name answers with one shape, a row or a set of rows, and a cell holds
|
|
399
|
+
one value.** Master-detail is a row and a list under two names. Many
|
|
400
|
+
masters with their details is one list of joined rows, grouped for
|
|
401
|
+
display.
|
|
402
|
+
3. **A value lands where the element shows its state, and a form gathers
|
|
403
|
+
from the same places.** A widget receives `lb-value`, a form control its
|
|
404
|
+
`value`, and any other element its text.
|
|
405
|
+
4. **A request is an action and a position.** The hub supplies the position
|
|
406
|
+
from the document. Only an interaction supplies a value.
|
|
407
|
+
5. **The hub owns position and reconciliation. A widget owns interaction and
|
|
408
|
+
placement.**
|
|
409
|
+
|
|
410
|
+
### Evidence
|
|
411
|
+
|
|
412
|
+
The following decisions each removed an exception or a conflict, and none
|
|
413
|
+
added an attribute. Over the same span the model gained pickers inside rows,
|
|
414
|
+
native selects that show their own state, and nested lists that do not
|
|
415
|
+
destroy each other.
|
|
416
|
+
|
|
417
|
+
| Decision | Removed |
|
|
418
|
+
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
419
|
+
| The hub scopes every request | Native requests were scoped and widget requests were not |
|
|
420
|
+
| Scope comes from ancestors, never the element's own `lb-list` | `lb-list` meant "display" in one place and "address" in another |
|
|
421
|
+
| A form control receives its value as `value` | A `<select>` was treated as text, against HTML, and landing did not mirror gathering |
|
|
422
|
+
| Row and template lookups stop at a nested scope | Cells respected nesting while rows and templates did not |
|
|
423
|
+
| Master-detail is a row and a list | An open question born of document-shaped data |
|
|
424
|
+
| `lb-value` on every cell | Only a widget's landed value was visible to a stylesheet |
|
|
425
|
+
|
|
426
|
+
Two signs suggest these are rules rather than patches. Fixing where a
|
|
427
|
+
request's scope comes from also fixed an unrelated console error about insert
|
|
428
|
+
forms, which was never worked on directly. Making rows respect nested scopes
|
|
429
|
+
needed no change to the reference, which already described that behavior.
|
|
430
|
+
|
|
431
|
+
### Where the claim is not yet proven
|
|
432
|
+
|
|
433
|
+
- **Parameters and view state.** A query takes no argument from the
|
|
434
|
+
browser, so there is nowhere to put which record is selected, a filter, a
|
|
435
|
+
sort, or a collapsed section. The model is incomplete here rather than
|
|
436
|
+
inconsistent. If the answer is HTML's own, the URL, in the way
|
|
437
|
+
`<form method="get">` puts its fields in the query string, the claim grows
|
|
438
|
+
stronger. If it needs a state mechanism with no HTML precedent, that will
|
|
439
|
+
be the model's first real exception.
|
|
440
|
+
- **Checkboxes and radio buttons.** HTML's boolean convention is presence or
|
|
441
|
+
absence, as with `checked`, `hidden` and `disabled`. A rule following it
|
|
442
|
+
would avoid inventing truthiness, though it still touches data types.
|
|
443
|
+
- **Data types.** HTML attributes are strings, so comparing values as strings
|
|
444
|
+
in a stylesheet is consistent with HTML. What is not yet reconciled is
|
|
445
|
+
that a database's typed values arrive untyped.
|
|
446
|
+
- **A nested list shows the same rows in every outer row.** That follows
|
|
447
|
+
from the second rule: one name is one relation. Only a new outer row
|
|
448
|
+
starting with an empty nested list is a mechanical gap.
|
|
449
|
+
|
|
450
|
+
### A test for every change
|
|
451
|
+
|
|
452
|
+
Before a change is made, ask:
|
|
453
|
+
|
|
454
|
+
- Does it remove an exception, or add one?
|
|
455
|
+
- Does it have an HTML precedent?
|
|
456
|
+
- Does it have a relational counterpart?
|
|
457
|
+
|
|
458
|
+
Stamping every column of a row onto the row element as `data-*` fails the
|
|
459
|
+
first two: attributes nobody wrote, with no precedent in HTML. Writing
|
|
460
|
+
`lb-value` on every cell passes all three.
|
|
@@ -8,20 +8,23 @@ that allows a user to add a note.
|
|
|
8
8
|
```html
|
|
9
9
|
<!-- src/pages/about.page.html -->
|
|
10
10
|
<h2>Notes</h2>
|
|
11
|
-
<
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
</
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
<
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
</
|
|
11
|
+
<section lb-list="notes">
|
|
12
|
+
<form lb-action="lb-row-insert">
|
|
13
|
+
<input lb-cell="text" placeholder="Write a note" />
|
|
14
|
+
<button type="submit">Add</button>
|
|
15
|
+
</form>
|
|
16
|
+
|
|
17
|
+
<ul>
|
|
18
|
+
<template lb-key="id">
|
|
19
|
+
<li lb-cell="text"></li>
|
|
20
|
+
</template>
|
|
21
|
+
</ul>
|
|
22
|
+
</section>
|
|
21
23
|
```
|
|
22
24
|
|
|
23
25
|
`lb-action="lb-row-insert"` on a `<form>` gathers its `lb-cell`s into a values
|
|
24
|
-
map on submit and sends them against `lb-list
|
|
26
|
+
map on submit and sends them against the `lb-list` around it, the way an
|
|
27
|
+
`<input>` belongs to the `<form>` around it. The name is one of
|
|
25
28
|
Loadbare's reserved ones — every value beginning with `lb-` is — so it
|
|
26
29
|
needs no declaring, and nothing of yours may be called that. There's no `lb-key-value` — there's no row
|
|
27
30
|
yet.
|
|
@@ -9,11 +9,9 @@ changes.
|
|
|
9
9
|
|
|
10
10
|
## Writing the widget
|
|
11
11
|
|
|
12
|
-
Our new custom input will no longer rely on Loadbare's form handling
|
|
13
|
-
and
|
|
14
|
-
|
|
15
|
-
discouraged. This tutorial shows only that it is possible, and exposes
|
|
16
|
-
how Loadbare internals work.
|
|
12
|
+
Our new custom input will no longer rely on Loadbare's form handling. It
|
|
13
|
+
says what happened and what its value is. Where it sits, the list, the row
|
|
14
|
+
and the cell, is added by the hub.
|
|
17
15
|
|
|
18
16
|
```html
|
|
19
17
|
<!-- src/note-input.html -->
|
|
@@ -22,13 +20,7 @@ how Loadbare internals work.
|
|
|
22
20
|
|
|
23
21
|
```ts
|
|
24
22
|
// src/note-input.browser.ts
|
|
25
|
-
import {
|
|
26
|
-
ATTR_CELL,
|
|
27
|
-
ATTR_KEY_VALUE,
|
|
28
|
-
ATTR_LIST,
|
|
29
|
-
ATTR_VALUE,
|
|
30
|
-
LB_EVENT_NAME,
|
|
31
|
-
} from "@loadbare/app/constants";
|
|
23
|
+
import { ATTR_VALUE, LB_EVENT_NAME } from "@loadbare/app/constants";
|
|
32
24
|
|
|
33
25
|
class NoteInput extends HTMLElement {
|
|
34
26
|
static observedAttributes = [ATTR_VALUE];
|
|
@@ -42,20 +34,10 @@ class NoteInput extends HTMLElement {
|
|
|
42
34
|
this.addEventListener("change", () => {
|
|
43
35
|
const input = this.querySelector("input");
|
|
44
36
|
if (!input) return;
|
|
45
|
-
const list = this.closest(`[${ATTR_LIST}]`)?.getAttribute(ATTR_LIST);
|
|
46
|
-
const key = this.closest(`[${ATTR_KEY_VALUE}]`)?.getAttribute(ATTR_KEY_VALUE);
|
|
47
|
-
const cell = this.getAttribute(ATTR_CELL);
|
|
48
|
-
if (!list || !key || !cell) return;
|
|
49
37
|
this.dispatchEvent(
|
|
50
38
|
new CustomEvent(LB_EVENT_NAME, {
|
|
51
39
|
bubbles: true,
|
|
52
|
-
detail: {
|
|
53
|
-
action: "lb-cell-change",
|
|
54
|
-
list,
|
|
55
|
-
key,
|
|
56
|
-
cell,
|
|
57
|
-
value: input.value,
|
|
58
|
-
},
|
|
40
|
+
detail: { action: "lb-cell-change", value: input.value },
|
|
59
41
|
}),
|
|
60
42
|
);
|
|
61
43
|
});
|
|
@@ -66,9 +48,10 @@ customElements.define("note-input", NoteInput);
|
|
|
66
48
|
```
|
|
67
49
|
|
|
68
50
|
A widget with both a `.html` and a `.browser.ts` file shares one tag name; the
|
|
69
|
-
build finds each half independently. On change, it
|
|
70
|
-
`lb-
|
|
71
|
-
|
|
51
|
+
build finds each half independently. On change, it dispatches a
|
|
52
|
+
`lb-cell-change` carrying the input's value. The hub adds the list, the key
|
|
53
|
+
of the row it is in and its own `lb-cell`, the same way it does for a
|
|
54
|
+
`lb-row-update` form.
|
|
72
55
|
|
|
73
56
|
## Using it
|
|
74
57
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loadbare/app",
|
|
3
3
|
"description": "High performance web app framework for server-bound applications",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"prebuild": "node --eval \"fs.rmSync('dist',{recursive:true,force:true})\" --input-type=module",
|
|
33
33
|
"build:server": "tsc --project tsconfig.build.json",
|
|
34
34
|
"build": "npm run build:server",
|
|
35
|
-
"postbuild": "node --eval \"fs.chmodSync('dist/build/cli.js', 0o755)\" --input-type=module",
|
|
35
|
+
"postbuild": "node --eval \"fs.chmodSync('dist/build/cli.js', 0o755)\" --input-type=module && node ../../scripts/check-dist-imports.mjs",
|
|
36
36
|
"test": "tsx --test \"tests/**/*.test.ts\"",
|
|
37
37
|
"typecheck": "tsc --noEmit",
|
|
38
38
|
"format": "prettier --write .",
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Assembling a chrome file and its pages — see build/assemble.ts.
|
|
3
|
-
*
|
|
4
|
-
* The chrome and pages together are one write to a temp tree per test,
|
|
5
|
-
* since assemble() reads from disk rather than taking sources in memory.
|
|
6
|
-
*/
|
|
7
|
-
export {};
|
|
8
|
-
//# sourceMappingURL=assemble.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"assemble.test.d.ts","sourceRoot":"","sources":["../../tests/assemble.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|