@loadbare/app 0.9.0 → 0.10.0
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 +3 -3
- package/dist/build/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +74 -66
- package/dist/build/assemble.js.map +1 -1
- package/dist/build/expand.d.ts.map +1 -1
- package/dist/build/expand.js +20 -19
- package/dist/build/expand.js.map +1 -1
- package/dist/build/locations.d.ts +2 -3
- package/dist/build/locations.d.ts.map +1 -1
- package/dist/build/locations.js +2 -3
- package/dist/build/locations.js.map +1 -1
- package/dist/build/pages.d.ts +3 -4
- package/dist/build/pages.d.ts.map +1 -1
- package/dist/build/pages.js +3 -4
- package/dist/build/pages.js.map +1 -1
- package/dist/core/lb-constants.d.ts +25 -24
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +95 -168
- package/dist/core/lb-constants.js.map +1 -1
- package/dist/core/lb-types.d.ts +64 -77
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js +40 -7
- package/dist/core/lb-types.js.map +1 -1
- package/dist/hub/lb-apply.d.ts +47 -37
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +174 -193
- package/dist/hub/lb-apply.js.map +1 -1
- 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 +411 -449
- package/dist/hub/lb-hub.browser.js.map +1 -1
- package/dist/server/lb-express.d.ts +5 -5
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +35 -66
- package/dist/server/lb-express.js.map +1 -1
- package/dist/server/lb-server.d.ts +77 -135
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +132 -79
- package/dist/server/lb-server.js.map +1 -1
- package/docs/TECHREF-1.0.md +861 -587
- package/docs/comparison.md +222 -185
- package/docs/prior-art.md +15 -14
- package/docs/reference/builder.md +9 -3
- package/docs/reference/chrome.md +107 -56
- package/docs/reference/custom-elements.md +199 -173
- package/docs/reference/data-binding.md +374 -374
- package/docs/reference/overview.md +12 -10
- package/docs/reference/page-files.md +135 -99
- package/docs/reference/server.md +2 -2
- package/docs/reference/widgets.md +104 -110
- package/docs/roadmap.md +32 -39
- package/docs/terms-of-art.md +57 -0
- package/docs/testing.md +97 -68
- package/docs/theory.md +92 -58
- package/docs/tutorials/010-pages-and-navigation.md +20 -12
- package/docs/tutorials/020-css.md +6 -3
- package/docs/tutorials/030-html-decomposition.md +9 -7
- package/docs/tutorials/040-displaying-data.md +30 -13
- package/docs/tutorials/{050-actions.md → 050-requests.md} +25 -15
- package/docs/tutorials/060-custom-element-code.md +17 -16
- package/docs/tutorials/065-conditional-rendering.md +34 -23
- package/docs/tutorials/070-displaying-a-list.md +29 -21
- package/docs/tutorials/072-inserting-into-a-list.md +24 -16
- package/docs/tutorials/074-deleting-from-a-list.md +9 -7
- package/docs/tutorials/076-updating-a-list-item.md +11 -10
- package/docs/tutorials/080-widget-requests.md +71 -43
- package/docs/tutorials/090-using-widget-libraries.md +22 -22
- package/package.json +1 -1
- package/skills/loadbare-app/SKILL.md +178 -122
- package/skills/loadbare-app/references/TECHREF-1.0.md +861 -587
- package/skills/loadbare-app/references/builder.md +9 -3
- package/skills/loadbare-app/references/chrome.md +107 -56
- package/skills/loadbare-app/references/custom-elements.md +199 -173
- package/skills/loadbare-app/references/data-binding.md +374 -374
- package/skills/loadbare-app/references/overview.md +12 -10
- package/skills/loadbare-app/references/page-files.md +135 -99
- package/skills/loadbare-app/references/server.md +2 -2
- package/skills/loadbare-app/references/widgets.md +104 -110
- package/docs/analysis-accidental-complexity.md +0 -149
- package/docs/analysis-closed-set.md +0 -210
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Custom Elements
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
A custom element is written as a set of files sharing one tag name. It
|
|
4
|
+
serves two purposes, and an application uses it for either or for both at
|
|
5
|
+
once.
|
|
6
6
|
|
|
7
7
|
| File | Holds |
|
|
8
8
|
|--------------------------|----------------------------------|
|
|
9
|
-
| `<tag-name>.html` | The markup the tag expands into
|
|
9
|
+
| `<tag-name>.html` | The element file: the markup the tag expands into |
|
|
10
10
|
| `<tag-name>.browser.ts` | The class the tag registers |
|
|
11
11
|
|
|
12
12
|
Write one of the two, or both, but write at least one. A tag with neither is
|
|
@@ -26,13 +26,13 @@ either.
|
|
|
26
26
|
Put the files anywhere under `src/`. The builder finds them by name, the
|
|
27
27
|
same way it finds pages — see [The Builder](./builder.md).
|
|
28
28
|
|
|
29
|
-
Loadbare uses light DOM throughout. A
|
|
29
|
+
Loadbare uses light DOM throughout. A custom element's markup is ordinary markup in
|
|
30
30
|
the document, visible in view-source, reachable by `closest()` and by the
|
|
31
31
|
application's stylesheets.
|
|
32
32
|
|
|
33
33
|
## HTML
|
|
34
34
|
|
|
35
|
-
An `.html` file named for a tag defines that tag. When the builder finds the
|
|
35
|
+
An element file is an `.html` file named for a tag, and defines that tag. When the builder finds the
|
|
36
36
|
tag in the chrome, in a page, or in another definition, it inserts the
|
|
37
37
|
definition's content as children of the tag. We call this process
|
|
38
38
|
'HTML expansion'.
|
|
@@ -44,13 +44,13 @@ application's.
|
|
|
44
44
|
### Parameters
|
|
45
45
|
|
|
46
46
|
A definition takes parameters. Three namespaces share the attributes of a
|
|
47
|
-
|
|
47
|
+
custom element's tag, and the prefix says which namespace an attribute is in:
|
|
48
48
|
|
|
49
|
-
| Prefix | Belongs to | Read
|
|
50
|
-
|
|
51
|
-
| `exp-` |
|
|
52
|
-
| `lb-` |
|
|
53
|
-
| No prefix | HTML | By the browser, as HTML says
|
|
49
|
+
| Prefix | Belongs to | Read |
|
|
50
|
+
|-------------|------------|-------------------------------|
|
|
51
|
+
| `exp-` | The definition | By the builder, at build time |
|
|
52
|
+
| `lb-` | Loadbare | By the builder and the hub |
|
|
53
|
+
| No prefix | HTML | By the browser, as HTML says |
|
|
54
54
|
|
|
55
55
|
Pass a parameter by writing `exp-<name>` on the tag. Read it in the
|
|
56
56
|
definition by writing `{{<name>}}`. The prefix marks the attribute as
|
|
@@ -64,11 +64,11 @@ supplies `{{label}}`:
|
|
|
64
64
|
|
|
65
65
|
```html
|
|
66
66
|
<!-- authored -->
|
|
67
|
-
<note-field
|
|
67
|
+
<note-field exp-label="Name"></note-field>
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
Write `class`, `title`, or any other unprefixed attribute on a
|
|
71
|
-
freely. They are HTML's, and never collide with a definition's parameters.
|
|
70
|
+
Write `class`, `title`, or any other unprefixed attribute on a custom
|
|
71
|
+
element freely. They are HTML's, and never collide with a definition's parameters.
|
|
72
72
|
|
|
73
73
|
Write a placeholder as an entire attribute value or an entire text node.
|
|
74
74
|
Nothing inside one is evaluated, and expansion has no data to branch on —
|
|
@@ -102,12 +102,12 @@ escaping.
|
|
|
102
102
|
|
|
103
103
|
### The slot
|
|
104
104
|
|
|
105
|
-
`lb-slot` marks the one element in a definition whose children receive
|
|
105
|
+
`lb-exp-slot` marks the one element in a definition whose children receive
|
|
106
106
|
whatever the author wrote inside the tag:
|
|
107
107
|
|
|
108
108
|
```html
|
|
109
109
|
<!-- definition: src/note-card.html -->
|
|
110
|
-
<article class="card"><div lb-slot></div></article>
|
|
110
|
+
<article class="card"><div lb-exp-slot></div></article>
|
|
111
111
|
```
|
|
112
112
|
|
|
113
113
|
```html
|
|
@@ -130,35 +130,35 @@ whatever the author wrote inside the tag:
|
|
|
130
130
|
</note-card>
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
The tag stays and the definition becomes its children.
|
|
134
|
-
|
|
133
|
+
The tag stays and the definition becomes its children. The builder removes
|
|
134
|
+
`lb-exp-slot` from what ships.
|
|
135
135
|
|
|
136
|
-
Give a definition at most one `lb-slot`. A definition with a slot and
|
|
136
|
+
Give a definition at most one `lb-exp-slot`. A definition with a slot and
|
|
137
137
|
nothing written inside it leaves the slot empty.
|
|
138
138
|
|
|
139
|
-
`lb-slot` is an attribute rather than an element because HTML's content
|
|
139
|
+
`lb-exp-slot` is an attribute rather than an element because HTML's content
|
|
140
140
|
model discards foreign elements inside `<select>` and `<table>`. A slot
|
|
141
141
|
marker has to survive on an element the surrounding tag already permits.
|
|
142
142
|
|
|
143
143
|
### Destinations
|
|
144
144
|
|
|
145
|
-
`lb-template` names a destination in a definition. On an authored
|
|
146
|
-
`<template lb-template="name">`, it names the destination that template
|
|
145
|
+
`lb-exp-template` names a destination in a definition. On an authored
|
|
146
|
+
`<template lb-exp-template="name">`, it names the destination that template
|
|
147
147
|
fills:
|
|
148
148
|
|
|
149
149
|
```html
|
|
150
150
|
<!-- definition: src/ledger-table.html -->
|
|
151
151
|
<table>
|
|
152
152
|
<caption>{{caption}}</caption>
|
|
153
|
-
<thead lb-template="head"></thead>
|
|
154
|
-
<tbody lb-slot></tbody>
|
|
153
|
+
<thead lb-exp-template="head"></thead>
|
|
154
|
+
<tbody lb-exp-slot></tbody>
|
|
155
155
|
</table>
|
|
156
156
|
```
|
|
157
157
|
|
|
158
158
|
```html
|
|
159
159
|
<!-- authored -->
|
|
160
160
|
<ledger-table exp-caption="Ledger">
|
|
161
|
-
<template lb-template="head">
|
|
161
|
+
<template lb-exp-template="head">
|
|
162
162
|
<tr><th>Date</th><th>Amount</th></tr>
|
|
163
163
|
</template>
|
|
164
164
|
<tbody>
|
|
@@ -180,15 +180,15 @@ an ordinary `<thead>` in view-source — not the template element.
|
|
|
180
180
|
|
|
181
181
|
### Recursion
|
|
182
182
|
|
|
183
|
-
A definition may use other
|
|
184
|
-
appears, so a
|
|
185
|
-
it.
|
|
183
|
+
A definition may use other custom elements. Expansion repeats until nothing
|
|
184
|
+
new appears, so a custom element built out of others needs nothing declared
|
|
185
|
+
for it.
|
|
186
186
|
|
|
187
187
|
Keep the definition graph acyclic. A cycle is a build error, reported as the
|
|
188
188
|
path that closes it.
|
|
189
189
|
|
|
190
|
-
Expansion runs over `<template>` contents wherever they occur, so a
|
|
191
|
-
row template ships already expanded, `{{placeholder}}` included.
|
|
190
|
+
Expansion runs over `<template>` contents wherever they occur, so a
|
|
191
|
+
definition's row template ships already expanded, `{{placeholder}}` included.
|
|
192
192
|
|
|
193
193
|
### What fails at build time
|
|
194
194
|
|
|
@@ -199,9 +199,9 @@ shipped:
|
|
|
199
199
|
- a cycle in the definition graph
|
|
200
200
|
- an `exp-` attribute the definition never declared
|
|
201
201
|
- a `{{placeholder}}` spelled so that no `exp-` attribute could supply it
|
|
202
|
-
- content written inside a tag whose definition has no `lb-slot`
|
|
203
|
-
- more than one `lb-slot` in one definition
|
|
204
|
-
- a `<template lb-template="name">` naming a destination the definition
|
|
202
|
+
- content written inside a tag whose definition has no `lb-exp-slot`
|
|
203
|
+
- more than one `lb-exp-slot` in one definition
|
|
204
|
+
- a `<template lb-exp-template="name">` naming a destination the definition
|
|
205
205
|
does not have
|
|
206
206
|
- two templates for the same destination
|
|
207
207
|
|
|
@@ -209,44 +209,109 @@ An unfilled `{{placeholder}}` is not among these. That is presence
|
|
|
209
209
|
propagation working as intended, indistinguishable from an author who left
|
|
210
210
|
an optional parameter out.
|
|
211
211
|
|
|
212
|
+
|
|
212
213
|
## Code
|
|
213
214
|
|
|
214
|
-
A `<tag>.browser.ts` file registers that tag's class.
|
|
215
|
-
ordinary custom element
|
|
216
|
-
in data binding
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
Import every
|
|
220
|
-
as a string literal
|
|
221
|
-
|
|
222
|
-
| Constant
|
|
223
|
-
|
|
224
|
-
| `
|
|
225
|
-
| `
|
|
226
|
-
| `ATTR_SHOW`
|
|
227
|
-
| `
|
|
228
|
-
| `
|
|
229
|
-
| `
|
|
230
|
-
| `
|
|
231
|
-
| `
|
|
232
|
-
| `
|
|
233
|
-
| `
|
|
234
|
-
| `
|
|
235
|
-
| `
|
|
236
|
-
| `
|
|
215
|
+
A `<tag>.browser.ts` file registers that tag's class. The class is an
|
|
216
|
+
ordinary custom element, and Loadbare imposes no base class. A custom element
|
|
217
|
+
takes part in data binding in one of four ways: it is a control, it renders
|
|
218
|
+
a value, it sends a request, or it holds rows.
|
|
219
|
+
|
|
220
|
+
Import every Loadbare name from `@loadbare/app/constants`, and never write
|
|
221
|
+
one as a string literal:
|
|
222
|
+
|
|
223
|
+
| Constant | Value |
|
|
224
|
+
|------------------------|----------------------|
|
|
225
|
+
| `ATTR_QUERY` | `lb-query` |
|
|
226
|
+
| `ATTR_COLUMN` | `lb-column` |
|
|
227
|
+
| `ATTR_SHOW` | `lb-show` |
|
|
228
|
+
| `ATTR_REQUEST` | `lb-request` |
|
|
229
|
+
| `ATTR_COLUMN_VALUE` | `lb-column-value` |
|
|
230
|
+
| `ATTR_KEY_VALUE` | `lb-key-value` |
|
|
231
|
+
| `ATTR_QUERY_ROW_COUNT` | `lb-query-row-count` |
|
|
232
|
+
| `ATTR_REQUEST_PENDING` | `lb-request-pending` |
|
|
233
|
+
| `ATTR_REQUEST_ERROR` | `lb-request-error` |
|
|
234
|
+
| `REQUEST_ROW_INSERT`, `REQUEST_ROW_UPDATE`, `REQUEST_ROW_DELETE` | The requests Loadbare provides |
|
|
235
|
+
| `LB_ROW_REQUESTS` | All three, in one array |
|
|
236
|
+
| `LB_EVENT_NAME` | `lb-request`, the event every request travels as |
|
|
237
|
+
| `URL_QUERY` | `lb-url` |
|
|
238
|
+
| `URL_COLUMN_PATH`, `URL_COLUMN_PAGE_LABEL`, `URL_COLUMN_PAGE_UNKNOWN` | `lb-path`, `lb-page-label`, `lb-page-unknown` |
|
|
239
|
+
| `LB_RESERVED_PREFIX` | `lb-` |
|
|
240
|
+
|
|
241
|
+
A custom element reads `lb-` attributes and never assigns one. The developer
|
|
242
|
+
writes them in markup, and the hub and the builder write their stamps.
|
|
243
|
+
|
|
244
|
+
Loadbare reserves method names beginning with `lb` on a custom element, for
|
|
245
|
+
the methods it calls; see [Holding rows](#holding-rows).
|
|
246
|
+
|
|
247
|
+
### Being a control
|
|
248
|
+
|
|
249
|
+
A form-associated custom element with a `value` property that fires `change`
|
|
250
|
+
is a control, the same as an `<input>`. The hub sets its `value` from the
|
|
251
|
+
column its `lb-column` names, gathers its `value` back under that column,
|
|
252
|
+
and commits its `lb-request` on `change`:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
// src/star-rating.browser.ts
|
|
256
|
+
class StarRating extends HTMLElement {
|
|
257
|
+
static formAssociated = true;
|
|
258
|
+
#internals = this.attachInternals();
|
|
259
|
+
#value = "";
|
|
260
|
+
|
|
261
|
+
get value(): string {
|
|
262
|
+
return this.#value;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
set value(value: string) {
|
|
266
|
+
this.#value = value ?? "";
|
|
267
|
+
this.#internals.setFormValue(this.#value);
|
|
268
|
+
this.render();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
formResetCallback() {
|
|
272
|
+
this.value = "";
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
pick(stars: number) {
|
|
276
|
+
this.value = String(stars);
|
|
277
|
+
this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
render() {
|
|
281
|
+
// Draw this.#value stars.
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
customElements.define("star-rating", StarRating);
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
```html
|
|
289
|
+
<star-rating lb-column="rating" lb-request="lb-row-update"></star-rating>
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Dispatch `change` with `bubbles: true`, as a native control's does, when the
|
|
293
|
+
user commits an edit. Setting `value` from the hub fires nothing.
|
|
294
|
+
|
|
295
|
+
A form owns a form-associated custom element the way it owns an `<input>`,
|
|
296
|
+
including through the HTML `form` attribute, so a form gathers it on submit.
|
|
297
|
+
Write `formResetCallback` to restore the default value: the hub resets a form
|
|
298
|
+
after a successful insert.
|
|
299
|
+
|
|
300
|
+
The `<lb-input>`, `<lb-select>`, `<lb-options>` and `<lb-picker>` widgets
|
|
301
|
+
are controls; see [The Basic Widget Library](./widgets.md).
|
|
237
302
|
|
|
238
303
|
### Receiving a value
|
|
239
304
|
|
|
240
|
-
A
|
|
241
|
-
|
|
242
|
-
|
|
305
|
+
A custom element that is not a control keeps the content the builder placed
|
|
306
|
+
in it, and receives a column as its `lb-column-value` attribute. Observe it,
|
|
307
|
+
and render the value:
|
|
243
308
|
|
|
244
309
|
```ts
|
|
245
310
|
// src/visit-count.browser.ts
|
|
246
|
-
import {
|
|
311
|
+
import { ATTR_COLUMN_VALUE } from "@loadbare/app/constants";
|
|
247
312
|
|
|
248
313
|
class VisitCount extends HTMLElement {
|
|
249
|
-
static observedAttributes = [
|
|
314
|
+
static observedAttributes = [ATTR_COLUMN_VALUE];
|
|
250
315
|
|
|
251
316
|
attributeChangedCallback(_name: string, _old: string, value: string) {
|
|
252
317
|
this.textContent = `visited ${value} times`;
|
|
@@ -256,142 +321,103 @@ class VisitCount extends HTMLElement {
|
|
|
256
321
|
customElements.define("visit-count", VisitCount);
|
|
257
322
|
```
|
|
258
323
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
first render and every later refresh go through the one callback. There is
|
|
277
|
-
no separate hydration path to write.
|
|
324
|
+
```html
|
|
325
|
+
<p lb-query="visits"><visit-count lb-column="count"></visit-count></p>
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
List `ATTR_COLUMN_VALUE` in `observedAttributes`. The browser calls
|
|
329
|
+
`attributeChangedCallback` for an attribute already present when an element
|
|
330
|
+
upgrades, as well as for one that changes afterward, so the first render and
|
|
331
|
+
every later one go through the one callback.
|
|
332
|
+
|
|
333
|
+
A custom element carrying `lb-show`, or inside an element that does, is
|
|
334
|
+
moved into a template while its column is off and back out when it turns on
|
|
335
|
+
— see [Conditional rendering](./data-binding.md#conditional-rendering).
|
|
336
|
+
Going in, it sees `disconnectedCallback` and then `adoptedCallback`; coming
|
|
337
|
+
out, `adoptedCallback` and then `connectedCallback`. It is the same instance
|
|
338
|
+
throughout, and it still receives its column while it is away, so write
|
|
339
|
+
`connectedCallback` to run more than once, as a live row the hub reorders
|
|
340
|
+
already requires.
|
|
278
341
|
|
|
279
342
|
### Sending a request
|
|
280
343
|
|
|
281
|
-
A
|
|
282
|
-
click
|
|
283
|
-
|
|
284
|
-
|
|
344
|
+
A custom element that carries `lb-request` and is not a control commits on
|
|
345
|
+
`click`, as a button does. To send a request at a moment of its own, a
|
|
346
|
+
custom element dispatches the `lb-request` event itself, a bubbling
|
|
347
|
+
`CustomEvent` whose `detail` is the request:
|
|
285
348
|
|
|
286
349
|
```ts
|
|
287
350
|
import { LB_EVENT_NAME } from "@loadbare/app/constants";
|
|
288
351
|
import type { HubRequest } from "@loadbare/app/types";
|
|
289
352
|
|
|
290
|
-
const detail: HubRequest = {
|
|
353
|
+
const detail: HubRequest = { name: "archiveOld" };
|
|
291
354
|
this.dispatchEvent(new CustomEvent(LB_EVENT_NAME, { bubbles: true, detail }));
|
|
292
355
|
```
|
|
293
356
|
|
|
294
|
-
The hub
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
sends that one cell. The hub builds `values` from the cell's name and the
|
|
301
|
-
`value` the widget sent, or reads the control the widget wraps when it sent
|
|
302
|
-
none.
|
|
303
|
-
|
|
304
|
-
A widget that carries no `lb-cell` doesn't read its own controls. It
|
|
305
|
-
dispatches the bare action from the row, or from anything inside it, and the
|
|
306
|
-
hub gathers `values` from the row the event came from:
|
|
307
|
-
the nearest `<form>`, `<tr>` or live row around the dispatching element,
|
|
308
|
-
itself included, inside its scope. Every readable `lb-cell` in that row is
|
|
309
|
-
gathered, wherever in the row it sits, except the cells of a scope nested in
|
|
310
|
-
it: a picker carrying `lb-list` and `lb-cell` gives its own value, not its
|
|
311
|
-
options'. A request from an element in no such
|
|
312
|
-
row is not sent, and the console says to use a `<form>`.
|
|
313
|
-
|
|
314
|
-
```ts
|
|
315
|
-
row.dispatchEvent(
|
|
316
|
-
new CustomEvent(LB_EVENT_NAME, {
|
|
317
|
-
bubbles: true,
|
|
318
|
-
detail: { action: "lb-row-insert" } as HubRequest,
|
|
319
|
-
}),
|
|
320
|
-
);
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
Values the widget supplies itself are kept, and nothing is gathered over
|
|
324
|
-
them.
|
|
325
|
-
|
|
326
|
-
When an insert succeeds, the hub resets the controls it gathered to their
|
|
327
|
-
defaults, so a blank row for entering a new one is blank again without the
|
|
328
|
-
widget watching the request settle. A hidden input the widget added keeps
|
|
329
|
-
its value, since a hidden input's default is its value. Values the widget
|
|
330
|
-
supplied in the request were not gathered, and are not reset.
|
|
357
|
+
The hub completes the request from where the element sits before any
|
|
358
|
+
ancestor sees the event, gathering as it does for an element carrying
|
|
359
|
+
`lb-request`; see [Gathering](./data-binding.md#gathering). A field the
|
|
360
|
+
element supplies in `detail` is kept. The hub does not send a request
|
|
361
|
+
missing a field its name needs, and stamps the dispatching element with
|
|
362
|
+
`lb-request-pending` and `lb-request-error` as it would any other.
|
|
331
363
|
|
|
332
|
-
Let the event bubble, so an ancestor
|
|
333
|
-
the hub sees it. A hand-written widget and a native element carrying
|
|
334
|
-
`lb-action` produce the same event.
|
|
364
|
+
Let the event bubble, so an ancestor can stop it before the hub sends it.
|
|
335
365
|
|
|
336
|
-
###
|
|
366
|
+
### Holding rows
|
|
337
367
|
|
|
338
|
-
The hub
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
only it can decide.
|
|
368
|
+
The hub lands every row template itself. A plain element carrying `lb-query`
|
|
369
|
+
with a row template inside it needs no code, so a custom element holds rows
|
|
370
|
+
only when they need placement or scaffolding that only it can decide.
|
|
342
371
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
not own, so a widget's own methods can never collide with a later one:
|
|
372
|
+
A custom element carrying `lb-query` and a row template may implement two
|
|
373
|
+
optional methods, which the hub calls:
|
|
346
374
|
|
|
347
375
|
```ts
|
|
348
|
-
import type {
|
|
376
|
+
import type { RowsHost, Row } from "@loadbare/app/types";
|
|
349
377
|
|
|
350
|
-
class SortedList extends HTMLElement implements
|
|
378
|
+
class SortedList extends HTMLElement implements RowsHost {
|
|
351
379
|
lbPlaceRow(el: Element, row: Row, template: HTMLTemplateElement) {
|
|
352
|
-
// Where this row goes. Called with the
|
|
353
|
-
// appearance and again whenever
|
|
380
|
+
// Where this live row goes. Called with the live row detached, on its
|
|
381
|
+
// first appearance and again whenever all rows decide the order.
|
|
354
382
|
}
|
|
355
383
|
|
|
356
384
|
lbRowsLanded() {
|
|
357
|
-
// Once, after the
|
|
385
|
+
// Once, after the rows have landed. For scaffolding derived from the
|
|
358
386
|
// rows: a section heading, an <optgroup>, anything that goes when its
|
|
359
387
|
// last row does.
|
|
360
388
|
}
|
|
361
389
|
}
|
|
362
390
|
```
|
|
363
391
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
`
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
Style an empty
|
|
387
|
-
|
|
388
|
-
[
|
|
389
|
-
|
|
390
|
-
### Filling a
|
|
391
|
-
|
|
392
|
-
`@loadbare/app`
|
|
393
|
-
|
|
394
|
-
own rather than one the hub
|
|
395
|
-
|
|
396
|
-
descendant.
|
|
397
|
-
</content>
|
|
392
|
+
| Method | The hub calls it |
|
|
393
|
+
|----------------|---------------------|
|
|
394
|
+
| `lbPlaceRow` | To place a live row |
|
|
395
|
+
| `lbRowsLanded` | After the rows land |
|
|
396
|
+
|
|
397
|
+
Everything else is the hub's:
|
|
398
|
+
|
|
399
|
+
| Concern | The hub |
|
|
400
|
+
|-------------|----------------------------------------------------------|
|
|
401
|
+
| Cloning | Clones the row template once per new key |
|
|
402
|
+
| Matching | Fills the live row already showing that key |
|
|
403
|
+
| Removing | Removes the live rows the response says are gone |
|
|
404
|
+
| Stamping | Stamps each live row with `lb-key-value` |
|
|
405
|
+
| Counting | Stamps `lb-query-row-count` with the number of live rows |
|
|
406
|
+
|
|
407
|
+
`lbPlaceRow` receives the live row already filled and not yet in the
|
|
408
|
+
document, so a custom element that reads a column to decide where the row
|
|
409
|
+
goes can. Without it, the hub places a live row immediately before the row
|
|
410
|
+
template, in arrival order. `lb-options.browser.ts` and
|
|
411
|
+
`lb-table.browser.ts` in [`@loadbare/widgets`](./widgets.md) are two
|
|
412
|
+
different placements over the same landing.
|
|
413
|
+
|
|
414
|
+
Style an empty query against `lb-query-row-count` rather than carrying an
|
|
415
|
+
empty state in the custom element — see
|
|
416
|
+
[Counting rows](./data-binding.md#counting-rows).
|
|
417
|
+
|
|
418
|
+
### Filling a subtree by hand
|
|
419
|
+
|
|
420
|
+
`@loadbare/app` exports `applyRow(root, row)`, the operation that lands a
|
|
421
|
+
`row` on an element. Call it in a custom element that fills a subtree of its
|
|
422
|
+
own rather than one the hub lands on. It sets every element carrying a
|
|
423
|
+
matching `lb-column` inside `root`, outside any nested `lb-query`.
|