@loadbare/app 0.9.0 → 0.11.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 +107 -90
- package/dist/build/assemble.js.map +1 -1
- package/dist/build/expand.d.ts +6 -1
- package/dist/build/expand.d.ts.map +1 -1
- package/dist/build/expand.js +112 -26
- 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 +27 -24
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +103 -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 +195 -199
- 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 +410 -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 +908 -585
- package/docs/comparison.md +243 -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 +291 -173
- package/docs/reference/data-binding.md +381 -374
- package/docs/reference/overview.md +12 -10
- package/docs/reference/page-files.md +164 -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/docs/what-does-loadbare-extend.md +124 -0
- package/package.json +1 -1
- package/skills/loadbare-app/SKILL.md +201 -123
- package/skills/loadbare-app/references/TECHREF-1.0.md +908 -585
- 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 +291 -173
- package/skills/loadbare-app/references/data-binding.md +381 -374
- package/skills/loadbare-app/references/overview.md +12 -10
- package/skills/loadbare-app/references/page-files.md +164 -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
package/docs/TECHREF-1.0.md
CHANGED
|
@@ -19,7 +19,8 @@ Loadbare/app enforces no data types on values between the database and the
|
|
|
19
19
|
browser. A uniform and useful approach is difficult to discern, and we do
|
|
20
20
|
not want to pollute 1.0 with a potentially sub-optimal solution. Until then
|
|
21
21
|
a value is rendered by browser coercion when it lands on an HTML text
|
|
22
|
-
element
|
|
22
|
+
element or a control, and by whatever the custom element does with
|
|
23
|
+
`lb-column-value` when it lands on one that is not a control.
|
|
23
24
|
|
|
24
25
|
We will then see if a useful solution emerges that Loadbare/app should
|
|
25
26
|
handle.
|
|
@@ -27,24 +28,21 @@ handle.
|
|
|
27
28
|
### Form controls
|
|
28
29
|
|
|
29
30
|
- **Checkboxes and radio buttons are not implemented.** A value does not
|
|
30
|
-
land on one and
|
|
31
|
+
land on one and the hub does not gather one. Landing needs a decision on
|
|
31
32
|
what counts as checked, which waits on [Data types](#data-types), and a
|
|
32
|
-
radio group is several elements answering to one
|
|
33
|
+
radio group is several elements answering to one column.
|
|
33
34
|
|
|
34
|
-
### Run-time
|
|
35
|
+
### Run-time stamps
|
|
35
36
|
|
|
36
|
-
The hub stamps `lb-row-count`, `lb-pending` and
|
|
37
|
-
stylesheet to read. Their consumer is CSS rather
|
|
37
|
+
The hub stamps `lb-query-row-count`, `lb-request-pending` and
|
|
38
|
+
`lb-request-error` for a stylesheet to read. Their consumer is CSS rather
|
|
39
|
+
than code.
|
|
38
40
|
|
|
39
|
-
- **Decide
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- **Say what `lb-row-count` holds.** It is stamped and never explained.
|
|
43
|
-
- **Decide whether an unarrived value needs a signal.** The roadmap proposes
|
|
44
|
-
deriving it from an absent `lb-value`. Recommend confirming that and
|
|
45
|
-
adding nothing.
|
|
41
|
+
- **Decide whether an unarrived value needs a signal.** An element whose
|
|
42
|
+
column has not landed carries no `lb-column-value`. Recommend confirming
|
|
43
|
+
that as the signal and adding nothing.
|
|
46
44
|
|
|
47
|
-
###
|
|
45
|
+
### Custom element hooks
|
|
48
46
|
|
|
49
47
|
Loadbare/app owns every method name beginning with `lb` on a custom element.
|
|
50
48
|
That decision is firm; what the set contains is not.
|
|
@@ -52,45 +50,43 @@ That decision is firm; what the set contains is not.
|
|
|
52
50
|
- **Decide what the build does with an unknown `lb*` method.** An element
|
|
53
51
|
carrying a method beginning with `lb` that Loadbare/app does not define may
|
|
54
52
|
be an error, a warning, or ignored.
|
|
55
|
-
- **Decide whether a
|
|
56
|
-
element at a time and offers no escape hatch. An optional
|
|
57
|
-
would sit beside `lbPlaceRow` and `lbRowsLanded`, and this is
|
|
58
|
-
likely first request from a
|
|
53
|
+
- **Decide whether a custom element may receive a whole row.** A column
|
|
54
|
+
lands one element at a time and offers no escape hatch. An optional
|
|
55
|
+
`lbAcceptRow` would sit beside `lbPlaceRow` and `lbRowsLanded`, and this is
|
|
56
|
+
the most likely first request from a custom element author.
|
|
59
57
|
|
|
60
|
-
###
|
|
58
|
+
### Nested queries
|
|
61
59
|
|
|
62
|
-
Imagine
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
Imagine an HTML `<select>` that shows a fixed set of choices on every row of
|
|
61
|
+
a table, such as `customer_type` for a table of customers. When every
|
|
62
|
+
customer may take every customer type, the system as written today is
|
|
63
|
+
fine, because every `<select>` receives the same rows.
|
|
66
64
|
|
|
67
|
-
But
|
|
68
|
-
in the row.
|
|
65
|
+
But the allowed values may depend on other values in the row.
|
|
69
66
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
nested list is for.
|
|
67
|
+
- **Decide how a new live row fills a nested query.** A row added to the
|
|
68
|
+
outer query starts with its nested query empty, and it fills only when the
|
|
69
|
+
nested query's name lands again. See [Master-detail](#master-detail) for
|
|
70
|
+
what a nested query is for.
|
|
75
71
|
|
|
76
72
|
```html
|
|
77
|
-
<tbody lb-
|
|
78
|
-
<template
|
|
79
|
-
<td lb-
|
|
80
|
-
<td><select lb-
|
|
73
|
+
<tbody lb-query="accounts">
|
|
74
|
+
<template><tr>
|
|
75
|
+
<td lb-column="name"></td>
|
|
76
|
+
<td><select lb-query="statuses" lb-column="status"><template><option lb-column="label"></option></template></select></td>
|
|
81
77
|
</tr></template>
|
|
82
78
|
</tbody>
|
|
83
79
|
```
|
|
84
80
|
|
|
85
81
|
### The server API
|
|
86
82
|
|
|
87
|
-
- **Give the chrome a way to state its own queries.** A
|
|
88
|
-
|
|
89
|
-
|
|
83
|
+
- **Give the chrome a way to state its own queries.** A custom element in
|
|
84
|
+
the chrome naming a query forces every page to declare that query, since
|
|
85
|
+
the page load covers one page's set. `lb-url` shows the pattern from the
|
|
90
86
|
framework side, and an application has no equivalent. Recommend a
|
|
91
87
|
chrome-level query set on `createHub`.
|
|
92
|
-
- **State that only `createHub` implements `Hub`.** Otherwise a
|
|
93
|
-
|
|
88
|
+
- **State that only `createHub` implements `Hub`.** Otherwise a third call
|
|
89
|
+
breaks anyone who wrote the interface by hand.
|
|
94
90
|
- **Decide the options-argument shape once.** Global hooks, transaction
|
|
95
91
|
wrapping, error handling and a CSRF token all want the same trailing
|
|
96
92
|
parameter on `createHub` and `hubRoutes`. Build none of them, but pick
|
|
@@ -110,9 +106,16 @@ in the row.
|
|
|
110
106
|
definition would let the builder ship only what survives expansion.
|
|
111
107
|
Recommend deferring the mechanism and reserving the configuration key, so
|
|
112
108
|
that it lands as an opt-in rather than as a silent drop.
|
|
113
|
-
- **Land the remaining validations.** One `lb-hub
|
|
114
|
-
|
|
115
|
-
|
|
109
|
+
- **Land the remaining validations.** One `lb-hub` and one empty `<main>`.
|
|
110
|
+
Recommend landing these now, because refusing markup that used to build is
|
|
111
|
+
the kind of change 1.0 gives up.
|
|
112
|
+
- **Decide whether the build validates HTML.** A page can be well formed and
|
|
113
|
+
still break HTML's rules for what an element may hold, such as a `<div>`
|
|
114
|
+
inside a `<p>`. The parser rearranges it silently, the same way at build
|
|
115
|
+
time and in the browser, so nothing reports it. Checking those rules is a
|
|
116
|
+
validator's job, and html-validate is one the builder could run on the
|
|
117
|
+
source or on the expanded output. Deciding it after 1.0 is free if it
|
|
118
|
+
lands as an opt-in, and it may need a configuration key.
|
|
116
119
|
- **Confirm the three output names.** `app.html`, `client.js` and `app.css`
|
|
117
120
|
are about to be fixed in `staticRoutes` as well as in the builder.
|
|
118
121
|
|
|
@@ -162,12 +165,12 @@ The builder bundles `client.js` from a `client-entry.ts` it generates into
|
|
|
162
165
|
`--out` and leaves there. Nothing reads it after the bundle, and an
|
|
163
166
|
application neither writes nor imports it.
|
|
164
167
|
|
|
165
|
-
Every `.css` file in every origin joins `app.css`. There is no
|
|
168
|
+
Every `.css` file in every origin joins `app.css`. There is no custom element, page
|
|
166
169
|
or chrome stylesheet: the builder concatenates them all, with nothing added,
|
|
167
170
|
removed or scoped, so what ships is what was authored. Within one origin
|
|
168
171
|
they are ordered by filename, the full path breaking a tie, and a
|
|
169
172
|
subdirectory never affects the order. Origins follow the cascade — this
|
|
170
|
-
package's own
|
|
173
|
+
package's own custom elements, then each package named in `imports.ts`, then `--src`
|
|
171
174
|
last — so an application's own stylesheet always lands after the ones it
|
|
172
175
|
imported. A file named `00-global.css` sorts first by saying so.
|
|
173
176
|
|
|
@@ -210,71 +213,84 @@ the whole of `--src` is one flat namespace, as stated in
|
|
|
210
213
|
|
|
211
214
|
Three kinds of file hold HTML.
|
|
212
215
|
|
|
213
|
-
| File | Holds
|
|
214
|
-
| ------------------ |
|
|
215
|
-
| `chrome.html` | The one HTML document
|
|
216
|
-
| `<stub>.page.html` | One page's markup, as a fragment
|
|
217
|
-
| `<tag-name>.html` | One
|
|
216
|
+
| File | Holds | Found in |
|
|
217
|
+
| ------------------ | ----------------------------------------- | ------------ |
|
|
218
|
+
| `chrome.html` | The one HTML document | `--src` |
|
|
219
|
+
| `<stub>.page.html` | One page's markup, as a fragment | `--src` |
|
|
220
|
+
| `<tag-name>.html` | One custom element's markup, as a fragment | Every origin |
|
|
218
221
|
|
|
219
222
|
As stated in [Chrome](#chrome), `chrome.html` is a required singleton.
|
|
220
223
|
|
|
221
224
|
As stated in [Pages](#pages), the `<stub>` of a `.page.html` is the path the
|
|
222
225
|
page maps to. The builder puts all pages into `dist/app.html` as HTML `<template>` objects
|
|
223
|
-
that carry `lb-page="<stub>"
|
|
226
|
+
that carry `lb-page="<stub>"`, and `lb-page-title` when the page file has a
|
|
227
|
+
`<title>`.
|
|
224
228
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
custom behavior, or both.
|
|
229
|
+
An element file is exactly `<kebab-case-name>.html`. A
|
|
230
|
+
[custom element](#custom-elements) is suitable for organizing large HTML
|
|
231
|
+
trees, or adding custom behavior, or both. An element file is named for the
|
|
232
|
+
tag it expands.
|
|
228
233
|
|
|
229
234
|
Any other HTML file, one that is not `chrome.html`, or `<stub>.page.html` or
|
|
230
235
|
`<kebab-case-name>.html` will either:
|
|
231
|
-
- be an error if it looks like
|
|
236
|
+
- be an error if it looks like an element file with capitalization
|
|
232
237
|
- be skipped
|
|
233
238
|
|
|
239
|
+
|
|
234
240
|
## HTML
|
|
235
241
|
|
|
236
242
|
In a Loadbare/app application, HTML is static after the build, and once
|
|
237
243
|
it is sent to the browser on initial page load, no HTML is ever sent
|
|
238
244
|
again.
|
|
239
245
|
|
|
240
|
-
|
|
246
|
+
The developer writes `lb-*` attributes in markup. Script never assigns
|
|
247
|
+
them. The hub and the builder write only their stamps. See
|
|
248
|
+
[The markup checks](#the-markup-checks).
|
|
249
|
+
|
|
250
|
+
### Expansion
|
|
241
251
|
|
|
242
252
|
The builder executes a process we call "expansion". When it finds
|
|
243
253
|
a custom element in the HTML it is processing, such as `<my-element>`,
|
|
244
|
-
it looks for the file
|
|
245
|
-
rules to place
|
|
254
|
+
it looks for the element file `my-element.html`, and follows the expansion
|
|
255
|
+
rules to place its contents.
|
|
246
256
|
|
|
247
|
-
A custom element with no
|
|
248
|
-
with neither
|
|
249
|
-
stated in [
|
|
257
|
+
A custom element with no element file is left alone. A custom element
|
|
258
|
+
with neither an element file nor a `.browser.ts` script is a build error, as
|
|
259
|
+
stated in [Custom elements](#custom-elements).
|
|
260
|
+
|
|
261
|
+
Each file is parsed on its own, and expansion joins them through the DOM,
|
|
262
|
+
which applies none of HTML's rules for what an element may hold. The browser
|
|
263
|
+
reads the shipped text by those rules, so a join it would rearrange, such as
|
|
264
|
+
a definition's `<dialog>` inside a page's `<p>`, is a build error naming the
|
|
265
|
+
file, the custom element and the path to what would move.
|
|
250
266
|
|
|
251
267
|
#### Build time parameters
|
|
252
268
|
|
|
253
269
|
A build time parameter is supplied as an attribute on a custom element,
|
|
254
270
|
and is converted to a fixed value within the HTML by the builder.
|
|
255
271
|
|
|
256
|
-
The attribute
|
|
257
|
-
the substitution locations use `{{label}}` (no
|
|
258
|
-
|
|
272
|
+
The attribute name carries an `exp-` prefix, as in `exp-label`, and
|
|
273
|
+
the substitution locations use `{{label}}` (no prefix) inside the
|
|
274
|
+
element file:
|
|
259
275
|
|
|
260
276
|
```html
|
|
261
|
-
<!--
|
|
277
|
+
<!-- note-field.html, the element file -->
|
|
262
278
|
<label>{{label}} <input readonly="{{readonly}}" /></label>
|
|
263
279
|
```
|
|
264
280
|
|
|
265
281
|
```html
|
|
266
282
|
<!-- a page -->
|
|
267
|
-
<
|
|
283
|
+
<note-field exp-label="Name"></note-field>
|
|
268
284
|
```
|
|
269
285
|
|
|
270
286
|
```html
|
|
271
287
|
<!-- dist/app.html -->
|
|
272
|
-
<
|
|
288
|
+
<note-field exp-label="Name"
|
|
273
289
|
><label>Name <input /></label
|
|
274
|
-
></
|
|
290
|
+
></note-field>
|
|
275
291
|
```
|
|
276
292
|
|
|
277
|
-
|
|
293
|
+
An element file states a default with `{{name|default}}`, as in
|
|
278
294
|
`{{button-label|OK}}`. Whitespace around the name and around the default is
|
|
279
295
|
discarded. The default is a literal.
|
|
280
296
|
|
|
@@ -297,48 +313,61 @@ A parameter value cannot become markup.
|
|
|
297
313
|
|
|
298
314
|
#### Slots and templates
|
|
299
315
|
|
|
300
|
-
|
|
316
|
+
An element file may contain any number of named templates and optionally
|
|
301
317
|
one slot.
|
|
302
318
|
|
|
303
|
-
|
|
319
|
+
| Attribute | The builder |
|
|
320
|
+
| ----------------- | ------------------------------------------------------- |
|
|
321
|
+
| `lb-exp-slot` | Puts in it the rest of the custom element's content |
|
|
322
|
+
| `lb-exp-template` | Puts in it the content of the template of the same name |
|
|
323
|
+
| `exp-<name>` | Replaces `{{name}}` in the element file with its value |
|
|
324
|
+
|
|
325
|
+
- `lb-exp-slot` goes on one element in an element file.
|
|
326
|
+
- `lb-exp-template` goes on an element in an element file, and on a
|
|
327
|
+
`<template>` in the custom element's content. Both name the same
|
|
328
|
+
template.
|
|
329
|
+
- `exp-<name>` goes on a custom element.
|
|
330
|
+
|
|
331
|
+
This simplified element file for `lb-table` names two templates, `head` and
|
|
304
332
|
`foot`, and marks `<tbody>` as the slot. A page supplies whichever templates
|
|
305
333
|
it wants, and everything else it writes goes to the slot.
|
|
306
334
|
|
|
307
335
|
```html
|
|
308
|
-
<!-- lb-table.html, the
|
|
336
|
+
<!-- lb-table.html, the element file -->
|
|
309
337
|
<table>
|
|
310
338
|
<caption>
|
|
311
339
|
{{caption}}
|
|
312
340
|
</caption>
|
|
313
|
-
<thead lb-template="head"></thead>
|
|
314
|
-
<tbody lb-slot></tbody>
|
|
315
|
-
<tfoot lb-template="foot"></tfoot>
|
|
341
|
+
<thead lb-exp-template="head"></thead>
|
|
342
|
+
<tbody lb-exp-slot></tbody>
|
|
343
|
+
<tfoot lb-exp-template="foot"></tfoot>
|
|
316
344
|
</table>
|
|
317
345
|
```
|
|
318
346
|
|
|
319
|
-
|
|
320
|
-
template
|
|
321
|
-
|
|
347
|
+
The page below supplies the `head` template and a row template. The row
|
|
348
|
+
template names no destination, so it goes to the slot. This page writes no
|
|
349
|
+
footer.
|
|
322
350
|
|
|
323
351
|
```html
|
|
324
352
|
<!-- a page -->
|
|
325
|
-
<lb-table lb-
|
|
326
|
-
<template lb-template="head">
|
|
353
|
+
<lb-table lb-query="staff" exp-caption="Everyone, by team">
|
|
354
|
+
<template lb-exp-template="head">
|
|
327
355
|
<tr><th>Name</th><th>Role</th></tr>
|
|
328
356
|
</template>
|
|
329
|
-
<template
|
|
330
|
-
<tr><td lb-
|
|
357
|
+
<template>
|
|
358
|
+
<tr><td lb-column="name"></td><td lb-column="role"></td></tr>
|
|
331
359
|
</template>
|
|
332
360
|
</lb-table>
|
|
333
361
|
```
|
|
334
362
|
|
|
335
363
|
When a named template is expanded, the `<template>` tag is discarded and its
|
|
336
|
-
contents are placed as children of the
|
|
337
|
-
|
|
364
|
+
contents are placed as children of the element that names it. Both
|
|
365
|
+
`lb-exp-slot` and `lb-exp-template` are gone from what ships. The built
|
|
366
|
+
document holds:
|
|
338
367
|
|
|
339
368
|
```html
|
|
340
369
|
<!-- dist/app.html -->
|
|
341
|
-
<lb-table lb-
|
|
370
|
+
<lb-table lb-query="staff" exp-caption="Everyone, by team"
|
|
342
371
|
><table>
|
|
343
372
|
<caption>
|
|
344
373
|
Everyone, by team
|
|
@@ -350,10 +379,10 @@ holds:
|
|
|
350
379
|
</tr>
|
|
351
380
|
</thead>
|
|
352
381
|
<tbody>
|
|
353
|
-
<template
|
|
382
|
+
<template>
|
|
354
383
|
<tr>
|
|
355
|
-
<td lb-
|
|
356
|
-
<td lb-
|
|
384
|
+
<td lb-column="name"></td>
|
|
385
|
+
<td lb-column="role"></td>
|
|
357
386
|
</tr>
|
|
358
387
|
</template>
|
|
359
388
|
</tbody>
|
|
@@ -363,75 +392,156 @@ holds:
|
|
|
363
392
|
|
|
364
393
|
These are build errors:
|
|
365
394
|
|
|
366
|
-
-
|
|
367
|
-
-
|
|
368
|
-
- A page naming a template the
|
|
395
|
+
- An element file with two destinations of one name
|
|
396
|
+
- An element file with more than one `lb-exp-slot`
|
|
397
|
+
- A page naming a template the element file does not have
|
|
369
398
|
- A page giving two templates for one name
|
|
370
|
-
- Content written in a
|
|
399
|
+
- Content written in a custom element whose element file has no slot
|
|
371
400
|
|
|
372
|
-
###
|
|
401
|
+
### Names
|
|
373
402
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
custom
|
|
403
|
+
Loadbare owns the `lb-` prefix in several namespaces: attribute names, the
|
|
404
|
+
values of its attributes, query names, the column names of its own queries,
|
|
405
|
+
request names, custom element names and custom event names. It also owns
|
|
406
|
+
custom element method names beginning with `lb`.
|
|
377
407
|
|
|
408
|
+
The application should never name anything with the `lb-` prefix anywhere.
|
|
378
409
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
| lb-list | Developer | Scopes DOM children to a named set of rows; a nested lb-list or lb-row begins a new scope |
|
|
382
|
-
| lb-row | Developer | Scopes DOM children to one named row; a nested lb-list or lb-row begins a new scope |
|
|
383
|
-
| lb-cell | Developer | This DOM node displays this column of the row in scope |
|
|
384
|
-
| lb-show | Developer | This DOM node is present when this column of the row in scope is neither null nor false |
|
|
385
|
-
| lb-key | Developer | Names the column that identifies a row, on the row template inside an lb-list |
|
|
386
|
-
| lb-key-value | Hub | Stamped on a live row: that row's value of `lb-key` |
|
|
387
|
-
| lb-value | Hub | The value that landed on a cell; a widget updates itself from it and a stylesheet selects on it |
|
|
410
|
+
`createHub` refuses, at startup, a page that declares a query or a handler
|
|
411
|
+
whose name begins with `lb-`.
|
|
388
412
|
|
|
389
|
-
|
|
413
|
+
### Landing
|
|
390
414
|
|
|
391
|
-
A
|
|
392
|
-
|
|
415
|
+
A query is a name for rows, of kind `row` or `rows`, with a key. The server
|
|
416
|
+
declares a page's queries in `<stub>.queries.ts`, and answers every request,
|
|
417
|
+
a page load included, with response items. A response item carries a query
|
|
418
|
+
name, its kind, its key's column name, and one of a row, all rows, or a
|
|
419
|
+
patch. See [The wire](#the-wire).
|
|
393
420
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
| A custom element, tag hyphenated | Its `lb-value` attribute |
|
|
397
|
-
| `<select>`, `<textarea>`, or `<input>` | Its `value`, and `lb-value` |
|
|
398
|
-
| Any other native element | Its `textContent`, and `lb-value` |
|
|
421
|
+
The hub lands a response item on every element whose `lb-query` names its
|
|
422
|
+
query.
|
|
399
423
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
424
|
+
| Attribute | Names | The hub |
|
|
425
|
+
| ----------- | -------- | -------------------------------------------------- |
|
|
426
|
+
| `lb-query` | a query | Puts its rows in the element's content |
|
|
427
|
+
| `lb-column` | a column | Sets the element from that column |
|
|
428
|
+
| `lb-show` | a column | Removes it while the named column is null or false |
|
|
404
429
|
|
|
405
|
-
|
|
406
|
-
|
|
430
|
+
| Stamp | The hub stamps it with |
|
|
431
|
+
| -------------------- | -------------------------------- |
|
|
432
|
+
| `lb-column-value` | The value it set |
|
|
433
|
+
| `lb-key-value` | The row's key |
|
|
434
|
+
| `lb-row-live` | Nothing; it marks a live row |
|
|
435
|
+
| `lb-query-row-count` | The number of live rows it holds |
|
|
407
436
|
|
|
408
|
-
|
|
409
|
-
|
|
437
|
+
The markup names a query and the columns it shows. The kind and the key are
|
|
438
|
+
the server's, and the markup states neither.
|
|
410
439
|
|
|
411
|
-
|
|
440
|
+
The row template is the first `<template>` among the descendants of an
|
|
441
|
+
element with `lb-query`, outside any nested `lb-query`. A live row is an
|
|
442
|
+
element the hub cloned from a row template for one row, and carries
|
|
443
|
+
`lb-row-live`.
|
|
444
|
+
|
|
445
|
+
| Kind | Row template | The hub |
|
|
446
|
+
| ------ | ------------ | ----------------------------------- |
|
|
447
|
+
| `row` | no | Lands the row on the element itself |
|
|
448
|
+
| `row` | yes | Lands one live row |
|
|
449
|
+
| `rows` | yes | Lands one live row per row |
|
|
450
|
+
| `rows` | no | Lands nothing |
|
|
451
|
+
|
|
452
|
+
A `rows` query with no row template names the query without showing it,
|
|
453
|
+
which is what an insert form naming the query it adds to is.
|
|
454
|
+
|
|
455
|
+
The nearest ancestor row of an element is the nearest of: the element
|
|
456
|
+
itself when it is a live row, an ancestor that is a live row, and an
|
|
457
|
+
ancestor that carries `lb-query` of kind `row`. The hub reads `lb-column`
|
|
458
|
+
and `lb-show` from the nearest ancestor row. A nested `lb-query` begins a
|
|
459
|
+
new query, and what is inside it is that query's.
|
|
412
460
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
461
|
+
An element's own `lb-query` names what the element holds, and its own
|
|
462
|
+
`lb-column` reads from the row around it. So a
|
|
463
|
+
`<select lb-query="statuses" lb-column="status">` in a live row of
|
|
464
|
+
`accounts` holds the statuses as its options and shows that account's
|
|
465
|
+
`status`. `lb-column` on an element with `lb-query` is undefined unless the
|
|
466
|
+
element is a control.
|
|
416
467
|
|
|
417
|
-
|
|
418
|
-
|
|
468
|
+
Name one query on more than one element to show it in more than one place.
|
|
469
|
+
Every one of them receives it.
|
|
470
|
+
|
|
471
|
+
A query that arrives with nowhere to land is reported to the console.
|
|
472
|
+
|
|
473
|
+
#### How a column lands
|
|
474
|
+
|
|
475
|
+
| Element | The hub sets |
|
|
476
|
+
| ------------------------------------------------ | ------------------------------ |
|
|
477
|
+
| A control | Its `value` |
|
|
478
|
+
| A custom element that is not a control | `lb-column-value` only |
|
|
479
|
+
| Any other element | Its text content |
|
|
480
|
+
|
|
481
|
+
A control is an `<input>`, `<select>` or `<textarea>`, or a form-associated
|
|
482
|
+
custom element with a `value` property that fires `change`. A
|
|
483
|
+
form-associated custom element declares `static formAssociated = true`.
|
|
484
|
+
|
|
485
|
+
The hub stamps `lb-column-value` with the value on every element it sets.
|
|
486
|
+
It never replaces the content of a custom element, which the builder placed
|
|
487
|
+
there from its element file. A custom element that is not a control
|
|
488
|
+
renders `lb-column-value` itself.
|
|
489
|
+
|
|
490
|
+
The hub gathers from the same controls it lands on, so a value read back is
|
|
491
|
+
the one that landed.
|
|
492
|
+
|
|
493
|
+
An `<input>` of type `checkbox`, `radio`, or `file` receives nothing, not
|
|
494
|
+
even `lb-column-value`, and the hub reports it to the console. The hub does
|
|
495
|
+
not gather one either.
|
|
496
|
+
|
|
497
|
+
A `<select>` with no option for the value shows no selection.
|
|
419
498
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
`lb-value` as "nothing landed here" and returns its control to its default,
|
|
423
|
-
the way a form control with no `value` attribute shows its default. A blank
|
|
424
|
-
`lb-value` is a value like any other.
|
|
499
|
+
An element with `lb-column` and no ancestor row receives nothing. The hub
|
|
500
|
+
still gathers from it.
|
|
425
501
|
|
|
426
502
|
The value arrives as the query produced it, with no conversion, so the
|
|
427
503
|
browser decides what a non-string looks like.
|
|
428
504
|
|
|
429
|
-
A
|
|
430
|
-
in a
|
|
505
|
+
A column holds one value. A set of values is a query of its own, never an
|
|
506
|
+
array in a column.
|
|
431
507
|
|
|
432
|
-
A
|
|
433
|
-
when it upgrades, so
|
|
434
|
-
and does not need to.
|
|
508
|
+
A custom element sees `attributeChangedCallback` for an `lb-column-value`
|
|
509
|
+
already present when it upgrades, so it cannot tell a first landing from a
|
|
510
|
+
refresh, and does not need to.
|
|
511
|
+
|
|
512
|
+
#### Rows
|
|
513
|
+
|
|
514
|
+
The hub matches each row to a live row by its key. It stamps
|
|
515
|
+
`lb-key-value` on each live row, and on an element a `row` lands on itself.
|
|
516
|
+
It stamps `lb-row-live` on each live row and nowhere else, so a `row`
|
|
517
|
+
landed inside another query, such as a total in a table's foot, is not one
|
|
518
|
+
of that query's rows. A stylesheet or a custom element selects live rows
|
|
519
|
+
with `[lb-row-live]`, and never with `[lb-key-value]`.
|
|
520
|
+
|
|
521
|
+
A key is unique within a query. Two rows with one key in the same answer,
|
|
522
|
+
or two live rows showing one key, are reported on the console.
|
|
523
|
+
|
|
524
|
+
All rows decide membership and order: every row is placed in the order
|
|
525
|
+
given, and a live row whose key did not arrive is removed. A patch touches
|
|
526
|
+
only the rows it names and leaves every other live row's contents and
|
|
527
|
+
position alone.
|
|
528
|
+
|
|
529
|
+
A new live row lands immediately before the row template, so rows
|
|
530
|
+
accumulate in the order they arrive. A custom element carrying `lb-query`
|
|
531
|
+
and a row template may place them itself — see [Row hooks](#row-hooks).
|
|
532
|
+
|
|
533
|
+
After every landing the hub stamps the element with `lb-query-row-count`,
|
|
534
|
+
the number of live rows it holds. The server answers with rows and says
|
|
535
|
+
nothing about how many survived, so this is the one fact a page cannot be
|
|
536
|
+
sent. It makes an empty query a stylesheet rule:
|
|
537
|
+
|
|
538
|
+
```css
|
|
539
|
+
[lb-query-row-count="0"] .roster-empty {
|
|
540
|
+
display: revert;
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
A row missing its key column is reported to the console and not landed.
|
|
435
545
|
|
|
436
546
|
#### Displaying by condition
|
|
437
547
|
|
|
@@ -440,166 +550,236 @@ and does not need to.
|
|
|
440
550
|
so `"false"` is on, and a query spells a condition as a boolean or a null.
|
|
441
551
|
|
|
442
552
|
```html
|
|
443
|
-
<template
|
|
553
|
+
<template>
|
|
444
554
|
<tr>
|
|
445
|
-
<td lb-
|
|
446
|
-
<td><button lb-
|
|
555
|
+
<td lb-column="name"></td>
|
|
556
|
+
<td><button lb-request="lb-row-delete" lb-show="removable">Remove</button></td>
|
|
447
557
|
</tr>
|
|
448
558
|
</template>
|
|
449
559
|
```
|
|
450
560
|
|
|
451
|
-
It
|
|
452
|
-
|
|
453
|
-
that does not carry the column leaves the element as it is.
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
561
|
+
It reads from the nearest ancestor row, as `lb-column` does, and on an
|
|
562
|
+
element that carries `lb-query` the column belongs to the row around it. A
|
|
563
|
+
row that does not carry the column leaves the element as it is.
|
|
564
|
+
`lb-show` with no ancestor row is undefined.
|
|
565
|
+
|
|
566
|
+
An element that is off is moved into a `<template lb-show="column">`
|
|
567
|
+
standing where it stood, and moved back out when the column turns on. It is
|
|
568
|
+
not rendered, focused, clicked, announced or gathered. It is moved and
|
|
569
|
+
never rebuilt, so a custom element keeps its instance and a control keeps
|
|
570
|
+
what was typed into it. Landing reaches into that template, so the element
|
|
571
|
+
and everything inside it return current. A custom element sees
|
|
572
|
+
`disconnectedCallback` then `adoptedCallback` going in, and
|
|
573
|
+
`adoptedCallback` then `connectedCallback` coming out, and still receives
|
|
574
|
+
`lb-column-value` while it is away.
|
|
463
575
|
|
|
464
576
|
The builder ships every `lb-show` element already inside its template, so
|
|
465
577
|
nothing conditional shows until its row lands. An absent element's template
|
|
466
578
|
keeps its place among its siblings, and a position selector counts it.
|
|
467
579
|
|
|
468
|
-
These are build errors: `lb-show` on a row template's root, with
|
|
469
|
-
|
|
470
|
-
scope outside its row template), or on a `<template>`.
|
|
580
|
+
These are build errors: `lb-show` on a row template's root, `lb-show` with
|
|
581
|
+
no `lb-query` around it, and `lb-show` on a `<template>`.
|
|
471
582
|
|
|
472
583
|
Hiding is presentation, and the server still refuses what a request may not
|
|
473
584
|
do.
|
|
474
585
|
|
|
475
586
|
#### Master-detail
|
|
476
587
|
|
|
477
|
-
A master is a row and its detail is a
|
|
478
|
-
side. A
|
|
588
|
+
A master is a `row` query and its detail is a `rows` query, as two names
|
|
589
|
+
answered side by side. A column never holds rows.
|
|
479
590
|
|
|
480
591
|
```html
|
|
481
|
-
<section lb-
|
|
482
|
-
<h2 lb-
|
|
483
|
-
<span lb-
|
|
592
|
+
<section lb-query="invoice">
|
|
593
|
+
<h2 lb-column="number"></h2>
|
|
594
|
+
<span lb-column="customer"></span>
|
|
484
595
|
</section>
|
|
485
596
|
|
|
486
597
|
<table>
|
|
487
|
-
<tbody lb-
|
|
488
|
-
<template
|
|
598
|
+
<tbody lb-query="invoiceLines">
|
|
599
|
+
<template><tr><td lb-column="item"></td><td lb-column="amount"></td></tr></template>
|
|
489
600
|
</tbody>
|
|
490
601
|
</table>
|
|
491
602
|
```
|
|
492
603
|
|
|
493
|
-
Many masters, each with its own detail, is one
|
|
494
|
-
detail row carries its master's columns. A
|
|
495
|
-
for display, as `lb-table` builds sections and
|
|
496
|
-
`<optgroup>`s.
|
|
604
|
+
Many masters, each with its own detail, is one `rows` query of joined rows:
|
|
605
|
+
each detail row carries its master's columns. A custom element's
|
|
606
|
+
`lbPlaceRow` groups them for display, as `lb-table` builds sections and
|
|
607
|
+
`lb-options` builds `<optgroup>`s.
|
|
497
608
|
|
|
498
|
-
A
|
|
499
|
-
row. That serves a picker offering the same choices on every
|
|
500
|
-
not a way to show a different detail per row.
|
|
609
|
+
A query nested in another query's row template receives the same rows in
|
|
610
|
+
every live row. That serves a picker offering the same choices on every
|
|
611
|
+
row, and is not a way to show a different detail per row.
|
|
501
612
|
|
|
502
613
|
Which master a page shows is a query parm, which a query reads off `ctx`
|
|
503
614
|
since it takes no argument from the browser — see [Query parms](#query-parms).
|
|
504
|
-
When a write creates or removes the master, the server response
|
|
505
|
-
|
|
506
|
-
[When a server response
|
|
615
|
+
When a write creates or removes the master, the server response moves the
|
|
616
|
+
URL — see
|
|
617
|
+
[When a server response moves the URL](#when-a-server-response-moves-the-url).
|
|
507
618
|
|
|
508
619
|
### Requests
|
|
509
620
|
|
|
510
621
|
---- UNEDITED ----
|
|
511
622
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
The hub
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
| lb-row-delete | anything inside a live row |
|
|
527
|
-
| lb-row-update | a `<form>`, a button in a live row, or a widget cell |
|
|
528
|
-
| anything else | must be a named routine in the page's server code |
|
|
529
|
-
|
|
530
|
-
The wire format is not visible to the user, but uses the same lb-*
|
|
531
|
-
attributes minus their prefix, so that it is intelligible when working on
|
|
532
|
-
Loadbare/app itself.
|
|
533
|
-
|
|
534
|
-
The hub scopes every request, whether a native element or a widget
|
|
535
|
-
dispatched it. It reads the scope from the dispatching element before any
|
|
536
|
-
ancestor sees the event, and never overwrites a field the request already
|
|
537
|
-
carries.
|
|
538
|
-
|
|
539
|
-
| `action` | Filled from scope | Required |
|
|
540
|
-
| ---------------- | ------------------------------ | ---------------------- |
|
|
541
|
-
| a declared name | `list` or `row`, `key`, `cell` | nothing |
|
|
542
|
-
| `lb-row-insert` | `list` | `list`, and `values` |
|
|
543
|
-
| `lb-row-delete` | `list`, `key` | both |
|
|
544
|
-
| `lb-row-update` | `list`, `key` | both, and `values` |
|
|
545
|
-
|
|
546
|
-
An element's own `lb-list` or `lb-row` names what it displays, never where
|
|
547
|
-
its request goes. A request belongs to the scope around the element, the
|
|
548
|
-
way a control belongs to the form around it. `list` or `row` comes from the
|
|
549
|
-
nearest ancestor scope, `key` from the nearest live row inside that scope,
|
|
550
|
-
and `cell` from the dispatching element's own `lb-cell`.
|
|
551
|
-
A request missing a required field is not sent. The hub never fills
|
|
552
|
-
`value`. An `lb-row-insert` or `lb-row-update` from an element carrying
|
|
553
|
-
`lb-cell` is a record of one cell, the way a control has a value and a form
|
|
554
|
-
has values: `values` holds that cell alone, taken from the `value` the widget
|
|
555
|
-
sent or else read from the control it is or wraps, and `value` is not sent
|
|
556
|
-
beside it. A widget that sends a `value` from an element carrying no
|
|
557
|
-
`lb-cell` is refused, since nothing names the column. Any other
|
|
558
|
-
`lb-row-insert` or `lb-row-update` gathers the row it belongs
|
|
559
|
-
to: `values` holds every `lb-cell` with a control to read in the nearest
|
|
560
|
-
`<form>`, `<tr>` or live row around the dispatching element, itself
|
|
561
|
-
included, inside its scope. The cells are found the way a row lands, so a
|
|
562
|
-
cell inside a scope nested in the row is that scope's and is not gathered,
|
|
563
|
-
while an element carrying a scope and `lb-cell` both is the row's cell.
|
|
564
|
-
This is a button's form owner: what else sits
|
|
565
|
-
beside the element never changes what is sent. A `<tr>` counts because a
|
|
566
|
-
form cannot go around a table row's controls, so a new row in a table is its
|
|
567
|
-
own form; a live row counts because it is the row the key names. The scope
|
|
568
|
-
itself is never the row, since its cells belong to other rows. A request
|
|
569
|
-
from an element in no form or row is not sent, and the hub reports that its
|
|
570
|
-
cells belong in a `<form>`. A request that already carries `values` keeps
|
|
571
|
-
them, and one whose row has no cell to read is not sent. A click on a
|
|
572
|
-
native element that carries either operation and is a cell or
|
|
573
|
-
holds cells is not sent, since clicking into one of its controls
|
|
574
|
-
would send the row: put the action on a form, on a button, or on a widget
|
|
575
|
-
that decides when its cell has changed.
|
|
576
|
-
|
|
577
|
-
A widget dispatches the action and, where it wraps a control, that control's
|
|
578
|
-
value.
|
|
623
|
+
| Attribute | Names | The hub |
|
|
624
|
+
| ------------ | --------- | -------------------------------------------- |
|
|
625
|
+
| `lb-request` | a request | Issues that request when the element commits |
|
|
626
|
+
|
|
627
|
+
| Stamp | The hub stamps it |
|
|
628
|
+
| -------------------- | ------------------------------------------ |
|
|
629
|
+
| `lb-request-pending` | While the round trip is in flight |
|
|
630
|
+
| `lb-request-error` | When the round trip fails, until the next |
|
|
631
|
+
|
|
632
|
+
The request name is the attribute value of `lb-request`. It names either a
|
|
633
|
+
request Loadbare provides or a declared request: a name the page declares
|
|
634
|
+
under `handlers`, which runs application code.
|
|
635
|
+
|
|
636
|
+
Every request has one shape, on the event and on the wire:
|
|
579
637
|
|
|
580
638
|
```
|
|
581
|
-
{
|
|
582
|
-
{
|
|
583
|
-
{
|
|
584
|
-
{
|
|
585
|
-
{ action: "selectTab", row: "prefs", cell: "active_tab", value: "two" }
|
|
639
|
+
{ name: "lb-row-insert", query: "roster", values: { name: "Ann" } }
|
|
640
|
+
{ name: "lb-row-update", query: "roster", key: "42", values: { name: "Ann" } }
|
|
641
|
+
{ name: "lb-row-delete", query: "roster", key: "42" }
|
|
642
|
+
{ name: "mailRoster", query: "roster", key: "42" }
|
|
586
643
|
```
|
|
587
644
|
|
|
645
|
+
The hub sends `query`, `key` and `values` as present.
|
|
646
|
+
|
|
647
|
+
#### Committing
|
|
648
|
+
|
|
649
|
+
An element commits on `submit` when it is a form, on `change` when it is a
|
|
650
|
+
control, and on `click` otherwise.
|
|
651
|
+
|
|
652
|
+
A submit button with a form owner never commits on `click`. On a form's
|
|
653
|
+
`submit`, the hub issues the submitter's `lb-request` when it carries one,
|
|
654
|
+
and the form's otherwise, the way `formaction` replaces `action`.
|
|
655
|
+
|
|
656
|
+
A click on a descendant of an element carrying `lb-request` commits that
|
|
657
|
+
element, as long as the element is inside `<lb-hub>` and no interactive
|
|
658
|
+
content lies between them. Interactive content is HTML's: `<a href>`,
|
|
659
|
+
`<button>`, `<input>` other than hidden, `<select>`, `<textarea>`, `<label>`,
|
|
660
|
+
`<details>`, `<iframe>`, `<embed>`, `<audio controls>`, `<video controls>`
|
|
661
|
+
and `<img usemap>`, plus any form-associated custom element. A click on
|
|
662
|
+
one of them belongs to it: a click into an input inside a deletable row
|
|
663
|
+
focuses the input and deletes nothing.
|
|
664
|
+
|
|
665
|
+
The hub ignores a commit on an element carrying `lb-request-pending`.
|
|
666
|
+
|
|
667
|
+
#### Gathering
|
|
668
|
+
|
|
669
|
+
The hub gathers values when it issues a request. It gathers each control
|
|
670
|
+
value under the column its `lb-column` names.
|
|
671
|
+
|
|
672
|
+
| Attribute | The hub |
|
|
673
|
+
| -------------- | ------------------------------------------- |
|
|
674
|
+
| `lb-query` | Issues the request for it |
|
|
675
|
+
| `lb-column` | Gathers the control value under that column |
|
|
676
|
+
| `lb-key-value` | Takes `key` from it |
|
|
677
|
+
|
|
678
|
+
- When the element carrying `lb-request` has `lb-column`, the hub gathers
|
|
679
|
+
its value alone, and sends `values` with one member.
|
|
680
|
+
- Otherwise, when the element is a `<form>`, the hub gathers every
|
|
681
|
+
`lb-column` whose form owner is the element.
|
|
682
|
+
- Otherwise, when the element is in a live row, the hub gathers every
|
|
683
|
+
`lb-column` in that live row.
|
|
684
|
+
- Otherwise the hub gathers every `lb-column` whose form owner is the
|
|
685
|
+
element's form owner.
|
|
686
|
+
|
|
687
|
+
The form owner includes a control that names the form with the HTML `form`
|
|
688
|
+
attribute from anywhere in the document, which is how a table row's
|
|
689
|
+
controls belong to a form that cannot wrap a `<tr>`:
|
|
690
|
+
|
|
691
|
+
```html
|
|
692
|
+
<form id="add-member" lb-request="lb-row-insert"></form>
|
|
693
|
+
<table lb-query="roster">
|
|
694
|
+
<tbody>
|
|
695
|
+
<template>
|
|
696
|
+
<tr><td lb-column="name"></td><td lb-column="role"></td></tr>
|
|
697
|
+
</template>
|
|
698
|
+
</tbody>
|
|
699
|
+
<tfoot>
|
|
700
|
+
<tr>
|
|
701
|
+
<td><input lb-column="name" form="add-member" /></td>
|
|
702
|
+
<td>
|
|
703
|
+
<input lb-column="role" form="add-member" />
|
|
704
|
+
<button form="add-member">Add</button>
|
|
705
|
+
</td>
|
|
706
|
+
</tr>
|
|
707
|
+
</tfoot>
|
|
708
|
+
</table>
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
The hub gathers from controls only. It skips a control whose nearest
|
|
712
|
+
ancestor `lb-query` is a descendant of the live row or form, since that
|
|
713
|
+
control belongs to a nested query. An element carrying `lb-query` and
|
|
714
|
+
`lb-column` both, such as a picker, belongs to the row around it and is
|
|
715
|
+
gathered.
|
|
716
|
+
|
|
717
|
+
The hub issues the request for the nearest ancestor `lb-query` of the
|
|
718
|
+
controls it gathered, and takes `key` from their nearest ancestor row when
|
|
719
|
+
that row is of the same query. When the gathered controls have different
|
|
720
|
+
nearest ancestor `lb-query`, the hub reports an error and sends nothing.
|
|
721
|
+
|
|
722
|
+
When it gathers nothing, the hub issues the request for the nearest
|
|
723
|
+
ancestor `lb-query` of the element carrying `lb-request`, and takes `key`
|
|
724
|
+
from that element's nearest ancestor row when that row is of the same
|
|
725
|
+
query.
|
|
726
|
+
|
|
727
|
+
A field the request already carries is its issuer's and is kept. A custom
|
|
728
|
+
element that dispatches its own request with `values` is not gathered over.
|
|
729
|
+
|
|
730
|
+
#### The request names
|
|
731
|
+
|
|
732
|
+
| Request name | Needs | Runs under `crud` |
|
|
733
|
+
| --------------- | ------------------------ | ----------------- |
|
|
734
|
+
| `lb-row-insert` | `query`, `values` | `rowInsert` |
|
|
735
|
+
| `lb-row-update` | `query`, `key`, `values` | `rowUpdate` |
|
|
736
|
+
| `lb-row-delete` | `query`, `key` | `rowDelete` |
|
|
737
|
+
|
|
738
|
+
The hub issues one of these only when it has what the table lists. An
|
|
739
|
+
`lb-row-insert` or `lb-row-update` that gathers nothing is not issued. A
|
|
740
|
+
request that is not issued is reported to the console.
|
|
741
|
+
|
|
742
|
+
Loadbare provides the handlers for these names: each runs the page's `crud`
|
|
743
|
+
entry for the request's query. A declared request runs the application's
|
|
744
|
+
handler under `handlers`, and the hub issues it with whatever it found.
|
|
745
|
+
|
|
746
|
+
A request name beginning with `lb-` that is none of these three is refused
|
|
747
|
+
by the builder in markup, and by the hub when a script dispatches it.
|
|
748
|
+
|
|
749
|
+
For a query the hub serves, the hub answers the request itself, with no
|
|
750
|
+
round trip. The hub serves `lb-url`, and answers `lb-row-update` for it —
|
|
751
|
+
see [Query parms](#query-parms).
|
|
752
|
+
|
|
588
753
|
#### The request event
|
|
589
754
|
|
|
590
|
-
The hub
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
listener on the hub sends it.
|
|
755
|
+
The hub dispatches every request as the bubbling `lb-request` event before
|
|
756
|
+
sending it, with the request as its `detail`. The event is dispatched from
|
|
757
|
+
the element that committed.
|
|
594
758
|
|
|
595
|
-
A
|
|
596
|
-
|
|
759
|
+
A custom element uses the same event to issue a request of its own, with at
|
|
760
|
+
least `name` in its `detail`. A native element and a custom element
|
|
597
761
|
therefore produce identical events.
|
|
598
762
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
763
|
+
The hub completes the request on the event's way down, so every ancestor
|
|
764
|
+
sees it whole on the way up. A request that cannot be issued is stopped
|
|
765
|
+
there. An ancestor may stop it too: the event is not cancelable, so an
|
|
766
|
+
interceptor calls `stopPropagation`, not `preventDefault`. This is what
|
|
767
|
+
makes a confirmation wrapper possible without the wrapped element knowing
|
|
768
|
+
about it.
|
|
769
|
+
|
|
770
|
+
#### Request state
|
|
771
|
+
|
|
772
|
+
The hub stamps `lb-request-pending` on the element that issued a request
|
|
773
|
+
while its round trip is in flight, together with `aria-busy="true"`, and
|
|
774
|
+
removes both when it settles. It stamps `lb-request-error` when the round
|
|
775
|
+
trip fails, and clears it when that element issues its next request.
|
|
776
|
+
|
|
777
|
+
A custom element observes them by naming them in `observedAttributes`. On
|
|
778
|
+
plain HTML a stylesheet is the only consumer: dim a pending button, mark a
|
|
779
|
+
failed one.
|
|
780
|
+
|
|
781
|
+
The hub ignores a commit on an element carrying `lb-request-pending`, so a
|
|
782
|
+
pending element is disabled in fact and a stylesheet only has to show it.
|
|
603
783
|
|
|
604
784
|
#### The round trip
|
|
605
785
|
|
|
@@ -608,77 +788,63 @@ aborts it and treats it as a failure, since `fetch` imposes no deadline of
|
|
|
608
788
|
its own. An application cannot change the deadline.
|
|
609
789
|
|
|
610
790
|
A round trip fails on a server error, on a network failure, or on that
|
|
611
|
-
deadline, and all three
|
|
612
|
-
request — see [Request state](#request-state).
|
|
613
|
-
|
|
614
|
-
After a write succeeds, the cells it gathered show what the server holds.
|
|
615
|
-
An `lb-row-update` does this by landing the row on them. An
|
|
616
|
-
`lb-row-insert` does it by resetting them, the way `form.reset()` resets a
|
|
617
|
-
form, because the row they held now lives in the list. The hub resets
|
|
618
|
-
after it lands the response, and only what it gathered:
|
|
619
|
-
|
|
620
|
-
- Each control returns to its default: an `<input>` or `<textarea>` to its
|
|
621
|
-
`defaultValue`, a `<select>` to the options marked `selected`. A page
|
|
622
|
-
that wants a prefilled insert writes a `value` attribute, as in plain
|
|
623
|
-
HTML.
|
|
624
|
-
- A control showing something other than the value the hub read holds an
|
|
625
|
-
edit made during the round trip. That edit was not sent, and is left.
|
|
626
|
-
- `lb-value` comes off each reset cell, and off its control, before the
|
|
627
|
-
control resets — see [How a value lands](#how-a-value-lands).
|
|
628
|
-
- Values a widget supplied in the request were not gathered, and are not
|
|
629
|
-
reset.
|
|
630
|
-
- Nothing is dispatched, as `form.reset()` fires no `change`.
|
|
631
|
-
|
|
632
|
-
A failed insert resets nothing, so the entry can be corrected.
|
|
633
|
-
|
|
634
|
-
A navigation that fails to load its data sets nothing. No element
|
|
635
|
-
dispatched it, so there is nothing to stamp, and the hub reports it to the
|
|
636
|
-
console. A load started by a control writing a query parm stamps that
|
|
637
|
-
control, as a request stamps its origin.
|
|
638
|
-
|
|
639
|
-
### Links
|
|
791
|
+
deadline, and all three stamp `lb-request-error` on the element that issued
|
|
792
|
+
the request — see [Request state](#request-state).
|
|
640
793
|
|
|
641
|
-
|
|
794
|
+
After a successful `lb-row-insert` gathered from a form, the hub resets the
|
|
795
|
+
form, after it lands the response. A failed insert resets nothing, so the
|
|
796
|
+
entry can be corrected. An `lb-row-update` resets nothing: the row it sent
|
|
797
|
+
lands back on its controls.
|
|
642
798
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
other link, so leaving the application is the default and staying in it is
|
|
647
|
-
the opt-in.
|
|
799
|
+
A reset of any form, whether the hub's, a reset button's or a script's,
|
|
800
|
+
returns its controls to their defaults, and the hub removes
|
|
801
|
+
`lb-column-value` from each of them. A cancelled reset keeps both.
|
|
648
802
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
803
|
+
A page load that fails lands nothing and is reported to the console. A load
|
|
804
|
+
started by a request for `lb-url` stamps the element that issued it, as any
|
|
805
|
+
request stamps its element.
|
|
652
806
|
|
|
653
|
-
|
|
654
|
-
link to determine the path.
|
|
807
|
+
### The URL
|
|
655
808
|
|
|
656
|
-
|
|
657
|
-
on the server.
|
|
809
|
+
---- UNEDITED ----
|
|
658
810
|
|
|
659
|
-
The query
|
|
660
|
-
|
|
661
|
-
already on loads that page again at the new URL without replacing its DOM —
|
|
662
|
-
see [Query parms](#query-parms).
|
|
811
|
+
The hub serves the query `lb-url`, of kind `row`, keyed by `lb-path`. It
|
|
812
|
+
lands wherever an element names it, like any server query.
|
|
663
813
|
|
|
664
|
-
The
|
|
814
|
+
| Column | The hub sets it to |
|
|
815
|
+
| ----------------- | ---------------------------------------------- |
|
|
816
|
+
| `lb-path` | The path. The row's key |
|
|
817
|
+
| `lb-page-label` | The page's title, or its stub when it has none |
|
|
818
|
+
| `lb-page-unknown` | Whether no page's stub matches the path |
|
|
819
|
+
| any other name | That query parm |
|
|
665
820
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
821
|
+
| Attribute | The hub |
|
|
822
|
+
| ---------------- | -------------------------------------------------------- |
|
|
823
|
+
| `lb-url-link` | On a plain primary click, sets `lb-path` from the `href` |
|
|
824
|
+
| `lb-url-push` | Pushes a history entry for this element's update |
|
|
825
|
+
| `lb-url-unknown` | Calls `showModal()` when `lb-page-unknown` is `true` |
|
|
826
|
+
|
|
827
|
+
- `lb-url-link` goes on an `<a>`.
|
|
828
|
+
- `lb-url-push` goes on an element with `lb-request`.
|
|
829
|
+
- `lb-url-unknown` goes on a `<dialog>` inside `<lb-hub>`.
|
|
830
|
+
|
|
831
|
+
The hub shows in `<main>` the page whose stub is `lb-path`. `/` is
|
|
832
|
+
`index`. When `lb-path` takes a new value, the hub loads the page: the
|
|
833
|
+
server runs its `onPageEnter`, then its queries. When a query parm takes a
|
|
834
|
+
new value, the hub reloads the page's queries, and keeps the page's DOM, so
|
|
835
|
+
live rows that come back keep their place.
|
|
836
|
+
|
|
837
|
+
The page's title is the text of the page file's `<title>`, which the builder
|
|
838
|
+
stamps on the page as `lb-page-title`. The hub sets the document title to
|
|
839
|
+
`lb-page-label` when it is not null.
|
|
671
840
|
|
|
672
841
|
```html
|
|
673
|
-
<
|
|
674
|
-
<
|
|
675
|
-
<
|
|
676
|
-
|
|
677
|
-
</nav>
|
|
842
|
+
<header lb-query="lb-url">
|
|
843
|
+
<h1>Membership Roster</h1>
|
|
844
|
+
<h2 lb-column="lb-page-label"></h2>
|
|
845
|
+
</header>
|
|
678
846
|
```
|
|
679
847
|
|
|
680
|
-
See also [Navigation Row lb-navigation](#lb-navigation).
|
|
681
|
-
|
|
682
848
|
#### What a URL names
|
|
683
849
|
|
|
684
850
|
The path names a page, a place in the application. It never names a
|
|
@@ -690,70 +856,115 @@ The query string describes what that page has on screen:
|
|
|
690
856
|
remembers nothing, so a URL is a reproducible view. Reload it, bookmark it,
|
|
691
857
|
or mail it to someone, and what they see is what the sender saw.
|
|
692
858
|
|
|
693
|
-
A page declares what it shows, its queries, and what it allows, its
|
|
694
|
-
Loading a page runs its queries. A request
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
859
|
+
A page declares what it shows, its queries, and what it allows, its
|
|
860
|
+
requests. Loading a page runs its queries. A request names a query and,
|
|
861
|
+
where it has one, a key, taken from where the element sits, which is how the
|
|
862
|
+
database already names a row. Nothing is fetched by URL, so an application
|
|
863
|
+
designs no endpoints.
|
|
698
864
|
|
|
699
865
|
That last is where query parms move Loadbare's position. A query still takes
|
|
700
866
|
no argument from the browser, and a parm arrives the way the session cookie
|
|
701
867
|
does, as part of the request the context is built from. But a user who
|
|
702
|
-
types `?acct=99999`
|
|
703
|
-
|
|
868
|
+
types `?acct=99999` influences what a query returns. A query parm is user
|
|
869
|
+
input, validated like any other where the application reads it, and a
|
|
704
870
|
per-session database role means an id outside the caller's reach finds
|
|
705
871
|
nothing.
|
|
706
872
|
|
|
707
873
|
Loadbare is for applications, not sites. Every route is answered with the
|
|
708
874
|
same document, and a path that names no page is found out in the browser —
|
|
709
|
-
see [
|
|
875
|
+
see [An unknown page](#an-unknown-page).
|
|
710
876
|
|
|
711
|
-
####
|
|
877
|
+
#### Links
|
|
712
878
|
|
|
713
|
-
|
|
714
|
-
|
|
879
|
+
An `<a>` carrying `lb-url-link` moves within the application. On a plain
|
|
880
|
+
primary click — the first button, no modifier key, not already handled — the
|
|
881
|
+
hub pushes a history entry for the `href`'s path and query string, and loads
|
|
882
|
+
the page there. Any other click, and any anchor without `lb-url-link`,
|
|
883
|
+
behaves like any other link, so leaving the application is the default and
|
|
884
|
+
staying in it is the opt-in.
|
|
715
885
|
|
|
716
886
|
```html
|
|
717
|
-
<
|
|
718
|
-
<
|
|
719
|
-
<
|
|
720
|
-
</
|
|
887
|
+
<nav>
|
|
888
|
+
<a href="/" lb-url-link>Home</a>
|
|
889
|
+
<a href="/members" lb-url-link>Members</a>
|
|
890
|
+
<a href="https://example.com/docs">Docs</a>
|
|
891
|
+
</nav>
|
|
721
892
|
```
|
|
722
893
|
|
|
723
|
-
|
|
724
|
-
| -------------------- | ---------- | --------------------------------------------------------------- |
|
|
725
|
-
| `lb-query-parm` | Developer | On a control: its `change` writes this parm and reloads the page |
|
|
726
|
-
| `lb-query-parm-push` | Developer | With `lb-query-parm`: the write pushes a history entry |
|
|
894
|
+
The attribute takes no value.
|
|
727
895
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
it may have arrived by link with no control on screen to say it again. An
|
|
731
|
-
empty value takes the parm out, so a URL is as long as the user has narrowed
|
|
732
|
-
the page. A value the URL already carries does nothing.
|
|
896
|
+
Path space is flat. A path such as `/members` links to the `members.*` files
|
|
897
|
+
on the server.
|
|
733
898
|
|
|
734
|
-
The
|
|
735
|
-
|
|
736
|
-
|
|
899
|
+
The query string is kept. `/transactions?date_begin=2026-09-01` opens the
|
|
900
|
+
transactions page narrowed to those dates.
|
|
901
|
+
|
|
902
|
+
The browser's Back and Forward load the page at the URL they arrive at, the
|
|
903
|
+
same way.
|
|
904
|
+
|
|
905
|
+
#### Query parms
|
|
906
|
+
|
|
907
|
+
A control that narrows what a page shows updates `lb-url` rather than
|
|
908
|
+
sending a request. Put it inside an element naming `lb-url`, with
|
|
909
|
+
`lb-column` naming the parm and `lb-request="lb-row-update"`:
|
|
910
|
+
|
|
911
|
+
```html
|
|
912
|
+
<div lb-query="lb-url">
|
|
913
|
+
<select lb-column="team" lb-request="lb-row-update">
|
|
914
|
+
<option value="">Every team</option>
|
|
915
|
+
<option value="Engines">Engines</option>
|
|
916
|
+
</select>
|
|
917
|
+
</div>
|
|
918
|
+
```
|
|
737
919
|
|
|
738
|
-
The
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
920
|
+
The hub answers the request itself. It sets the parms the request names in
|
|
921
|
+
the URL and leaves every other parm as it is, since one may have arrived by
|
|
922
|
+
link with no control on screen to say it again. An empty value takes the
|
|
923
|
+
parm out, so a URL is as long as the user has narrowed the page. A request
|
|
924
|
+
that leaves the URL as it was does nothing. `lb-page-label` and
|
|
925
|
+
`lb-page-unknown` are the hub's, and a request never sets them.
|
|
742
926
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
927
|
+
The update replaces the current history entry: changing what a page shows is
|
|
928
|
+
not going anywhere, so Back leaves the page rather than walking back through
|
|
929
|
+
every choice. `lb-url-push` on the element carrying `lb-request` makes the
|
|
930
|
+
update push an entry instead.
|
|
746
931
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
sent twice.
|
|
932
|
+
When `lb-path` takes a new value, the hub pushes a history entry, and the
|
|
933
|
+
new URL carries only the query parms the request names.
|
|
750
934
|
|
|
751
|
-
Every
|
|
752
|
-
|
|
935
|
+
Every load lands `lb-url`, and a parm the URL does not carry lands empty on
|
|
936
|
+
every element naming it as a column, so a control shows what the address
|
|
937
|
+
bar says after Back as well as after a reload. This is the one exception
|
|
938
|
+
to landing, where a row sets the columns it names and leaves the rest as
|
|
939
|
+
they were: the hub reads the column names under `lb-query="lb-url"` and
|
|
940
|
+
names each one in the row it lands.
|
|
941
|
+
|
|
942
|
+
The hub sends the query parms with every round trip, a page load and a
|
|
943
|
+
request alike. The server passes them to `contextFor` — see
|
|
753
944
|
[The Express server](#the-express-server). Nothing in Loadbare assigns a
|
|
754
945
|
parm a meaning.
|
|
755
946
|
|
|
756
|
-
####
|
|
947
|
+
#### An unknown page
|
|
948
|
+
|
|
949
|
+
When no page's stub matches the path, `lb-page-unknown` is `true` and
|
|
950
|
+
`lb-page-label` is null. The hub calls `showModal()` on the
|
|
951
|
+
`<dialog lb-url-unknown>`, where the chrome has one, and reports the path to
|
|
952
|
+
the console. A chrome with none shows nothing.
|
|
953
|
+
|
|
954
|
+
`lb-url` lands before the page is looked up, so the dialog shows it by
|
|
955
|
+
naming it like any other element:
|
|
956
|
+
|
|
957
|
+
```html
|
|
958
|
+
<dialog lb-url-unknown lb-query="lb-url">
|
|
959
|
+
The URL <span lb-column="lb-path"></span> is not in this app.
|
|
960
|
+
</dialog>
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
A path that names no page is detected in the browser, after a successful
|
|
964
|
+
200: every route gets the same document, so there is no server-delivered
|
|
965
|
+
404.
|
|
966
|
+
|
|
967
|
+
#### When a server response moves the URL
|
|
757
968
|
|
|
758
969
|
Some query parms can only be known once a write has run. After an insert,
|
|
759
970
|
the key of the new row exists only on the server. After a delete, only the
|
|
@@ -761,75 +972,66 @@ server knows that the row the page was showing is gone.
|
|
|
761
972
|
|
|
762
973
|
The usual web answer is Post/Redirect/Get: the server answers the write with
|
|
763
974
|
a redirect to a URL naming the result, and the browser makes a second request
|
|
764
|
-
to load it. Loadbare/app does the same work in one round trip
|
|
765
|
-
changes the path.
|
|
975
|
+
to load it. Loadbare/app does the same work in one round trip.
|
|
766
976
|
|
|
767
|
-
A
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
replacing the history entry, and then lands the load.
|
|
977
|
+
A handler returns `url()`, a new row for `lb-url`. The server loads the page
|
|
978
|
+
at the resulting URL, as a cold load of it would: `onPageEnter`, then every
|
|
979
|
+
query. The response carries the `lb-url` item followed by that load. The
|
|
980
|
+
hub writes the URL and then lands the load.
|
|
772
981
|
|
|
773
982
|
```ts
|
|
774
983
|
rowInsert: {
|
|
775
984
|
run: async (ctx, { values }) => {
|
|
776
985
|
const id = await ctx.db.addAccount(values);
|
|
777
|
-
return
|
|
986
|
+
return url({ acct: String(id) });
|
|
778
987
|
},
|
|
779
988
|
refresh: [],
|
|
780
989
|
},
|
|
781
990
|
```
|
|
782
991
|
|
|
783
|
-
-
|
|
784
|
-
|
|
785
|
-
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
- The
|
|
790
|
-
|
|
992
|
+
- A column other than `lb-path` is a query parm. Parms the row does not
|
|
993
|
+
name are kept, and an empty value removes its parm.
|
|
994
|
+
- A row carrying `lb-path` for another page enters that page with only the
|
|
995
|
+
parms the row names, and pushes a history entry. Otherwise the update
|
|
996
|
+
replaces the history entry, or pushes one when the element that issued the
|
|
997
|
+
request carries `lb-url-push`.
|
|
998
|
+
- The row names at least one column, and every value is a string. Otherwise
|
|
999
|
+
the server warns, ignores the row, and runs the refresh set as usual.
|
|
1000
|
+
- The refresh set does not run, and anything else the handler returned is
|
|
1001
|
+
dropped. Both were answers for the URL the page is leaving.
|
|
791
1002
|
- If loading the page fails, the write has still happened. The response
|
|
792
|
-
carries the
|
|
793
|
-
there is a page load's, and is not stamped on the element that
|
|
794
|
-
request.
|
|
1003
|
+
carries the `lb-url` item alone, and the hub loads the page itself. A
|
|
1004
|
+
failure there is a page load's, and is not stamped on the element that
|
|
1005
|
+
issued the request.
|
|
795
1006
|
- A user who has left the page by the time the response arrives keeps the
|
|
796
1007
|
URL they are on.
|
|
797
1008
|
|
|
798
1009
|
The page is loaded with a second context, built by `contextFor` from the new
|
|
799
1010
|
parms — see [The Express server](#the-express-server).
|
|
800
1011
|
|
|
801
|
-
###
|
|
802
|
-
|
|
803
|
-
---- UNEDITED ----
|
|
1012
|
+
### The markup checks
|
|
804
1013
|
|
|
805
|
-
|
|
1014
|
+
The builder checks the chrome and every page, after expansion, and refuses
|
|
1015
|
+
to build on any of these:
|
|
806
1016
|
|
|
807
|
-
|
|
808
|
-
|
|
1017
|
+
- An `lb-` attribute that is not a developer attribute: `lb-query`,
|
|
1018
|
+
`lb-column`, `lb-show`, `lb-request`, `lb-url-link`, `lb-url-push`,
|
|
1019
|
+
`lb-url-unknown`. Every other `lb-` attribute is a stamp.
|
|
1020
|
+
`lb-exp-slot` and `lb-exp-template` are consumed by expansion and never
|
|
1021
|
+
reach the check.
|
|
1022
|
+
- `lb-request` naming a value that begins with `lb-` and is not one of the
|
|
1023
|
+
three request names.
|
|
1024
|
+
- `lb-url-link` on anything but an `<a>`.
|
|
1025
|
+
- `lb-url-push` on an element with no `lb-request`.
|
|
1026
|
+
- `lb-url-unknown` on anything but a `<dialog>`, and in the chrome, outside
|
|
1027
|
+
`<lb-hub>`.
|
|
1028
|
+
- `lb-show` on a `<template>`, on a row template's root, or with no
|
|
1029
|
+
`lb-query` around it.
|
|
809
1030
|
|
|
810
|
-
|
|
811
|
-
| ------------ | ------------------------------------------------------------------- |
|
|
812
|
-
| `page-label` | The text of the link to the current page, empty if no link names it |
|
|
813
|
-
| `page-uri` | The path and query string as the browser has them, such as `/members?team=Engines` |
|
|
1031
|
+
`lb-column` with no ancestor row is allowed: the hub gathers from it.
|
|
814
1032
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
current path, so the query string does not change it.
|
|
818
|
-
|
|
819
|
-
The row lands before the page is looked up, so a path that names no page has
|
|
820
|
-
it too, and an `lb-unknown-page` dialog, where the chrome has one, displays
|
|
821
|
-
it by naming the row like any other subtree.
|
|
822
|
-
|
|
823
|
-
It lands only where a subtree names it. A chrome that displays no
|
|
824
|
-
navigation is not warned about a row with nowhere to go.
|
|
825
|
-
|
|
826
|
-
```html
|
|
827
|
-
<header lb-row="lb-navigation">
|
|
828
|
-
<h2 lb-cell="page-label"></h2>
|
|
829
|
-
</header>
|
|
830
|
-
```
|
|
831
|
-
|
|
832
|
-
See also [Links](#links).
|
|
1033
|
+
Script never assigns an `lb-` attribute, so the markup states every one
|
|
1034
|
+
that exists, and checking the markup is sound.
|
|
833
1035
|
|
|
834
1036
|
## Application files
|
|
835
1037
|
|
|
@@ -849,10 +1051,9 @@ The file is an HTML document, which must contain:
|
|
|
849
1051
|
|
|
850
1052
|
It may also contain:
|
|
851
1053
|
- `/app.css`, the stylesheet bundle
|
|
852
|
-
- A `<dialog
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
on; the builder rejects one placed elsewhere.
|
|
1054
|
+
- A `<dialog lb-url-unknown>`, which the hub opens when the path names no
|
|
1055
|
+
page. It goes inside `<lb-hub>`, like everything the hub acts on; the
|
|
1056
|
+
builder rejects one placed elsewhere.
|
|
856
1057
|
- The `hidden` attribute on `<body>`, which the hub removes once the first page has landed.
|
|
857
1058
|
|
|
858
1059
|
```html
|
|
@@ -867,52 +1068,66 @@ It may also contain:
|
|
|
867
1068
|
</head>
|
|
868
1069
|
<body hidden>
|
|
869
1070
|
<lb-hub>
|
|
870
|
-
<header lb-
|
|
1071
|
+
<header lb-query="lb-url">
|
|
871
1072
|
<h1>Membership Roster</h1>
|
|
872
|
-
<h2 lb-
|
|
1073
|
+
<h2 lb-column="lb-page-label"></h2>
|
|
873
1074
|
</header>
|
|
874
1075
|
<nav>
|
|
875
|
-
<a href="/" lb-
|
|
876
|
-
<a href="/members" lb-
|
|
1076
|
+
<a href="/" lb-url-link>Home</a>
|
|
1077
|
+
<a href="/members" lb-url-link>Members</a>
|
|
877
1078
|
<a href="https://example.org/">Our website</a>
|
|
878
1079
|
</nav>
|
|
879
1080
|
<main></main>
|
|
880
|
-
<dialog lb-unknown
|
|
881
|
-
The URL <span lb-
|
|
1081
|
+
<dialog lb-url-unknown lb-query="lb-url">
|
|
1082
|
+
The URL <span lb-column="lb-path"></span> is not in this app.
|
|
882
1083
|
</dialog>
|
|
883
1084
|
</lb-hub>
|
|
884
1085
|
</body>
|
|
885
1086
|
</html>
|
|
886
1087
|
```
|
|
887
1088
|
|
|
888
|
-
The
|
|
889
|
-
|
|
890
|
-
[Links](#links) and [lb-navigation](#lb-navigation).
|
|
1089
|
+
The chrome's `<title>` shows until the first page lands, and each page's
|
|
1090
|
+
title replaces it — see [The URL](#the-url).
|
|
891
1091
|
|
|
892
1092
|
### Pages
|
|
893
1093
|
|
|
894
1094
|
The page namespace is flat. Loadbare/app does not care where in the `--src`
|
|
895
1095
|
the page files are located, but they must be unique across the application.
|
|
896
|
-
A page `/deep/path/to/mypage.html` is routed to `/mypage`.
|
|
1096
|
+
A page `/deep/path/to/mypage.page.html` is routed to `/mypage`.
|
|
897
1097
|
|
|
898
1098
|
Pages are grouped as
|
|
899
|
-
-
|
|
1099
|
+
- `<stub>.page.html` is recognized as navigable and capable of
|
|
900
1100
|
having associated queries and requests
|
|
901
|
-
-
|
|
902
|
-
-
|
|
1101
|
+
- `<stub>.queries.ts` are the queries for a page
|
|
1102
|
+
- `<stub>.requests.ts` respond to the requests from the browser
|
|
903
1103
|
|
|
904
|
-
The
|
|
905
|
-
so that `<a href=
|
|
906
|
-
files `members.
|
|
1104
|
+
The stub of a page must match the path used in links,
|
|
1105
|
+
so that `<a href="/members" lb-url-link>Members</a>` has matching
|
|
1106
|
+
files `members.page.html` et al.
|
|
907
1107
|
|
|
908
1108
|
#### Page HTML
|
|
909
1109
|
|
|
910
1110
|
The markup in `<stub>.page.html` is the same HTML as everywhere else
|
|
911
1111
|
in a Loadbare/app application, see [HTML](#html).
|
|
912
1112
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1113
|
+
A page file may carry a `<title>`. The builder removes it and stamps its
|
|
1114
|
+
text content as `lb-page-title` on the page, and the hub shows it as
|
|
1115
|
+
`lb-page-label`.
|
|
1116
|
+
|
|
1117
|
+
```html
|
|
1118
|
+
<!-- src/pages/members.page.html -->
|
|
1119
|
+
<title>Members</title>
|
|
1120
|
+
<h1>Members</h1>
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
The builder wraps each expanded page in
|
|
1124
|
+
`<template lb-page="<stub>" lb-page-title="...">` and puts it in the built
|
|
1125
|
+
document. The hub reads `lb-page` to find the page a path names.
|
|
1126
|
+
|
|
1127
|
+
| Stamp | The builder stamps it with |
|
|
1128
|
+
| --------------- | --------------------------------------------- |
|
|
1129
|
+
| `lb-page` | The page's stub, which the hub reads |
|
|
1130
|
+
| `lb-page-title` | The text content of the page file's `<title>` |
|
|
916
1131
|
|
|
917
1132
|
## The server
|
|
918
1133
|
|
|
@@ -976,7 +1191,7 @@ Explaining Express is beyond the scope of this technical reference. The
|
|
|
976
1191
|
only real requirement is that the catch-all for app.html is at the end,
|
|
977
1192
|
so it does not catch any other files.
|
|
978
1193
|
|
|
979
|
-
`hubRoutes` answers `POST /lb/<
|
|
1194
|
+
`hubRoutes` answers `POST /lb/<stub>`, with the query string the browser is
|
|
980
1195
|
showing after it, verbatim. It hands `contextFor` that query string's parms
|
|
981
1196
|
as a second argument, and `contextFor` puts on the context whatever a query
|
|
982
1197
|
reads from them. They are user input:
|
|
@@ -988,7 +1203,7 @@ function contextFor(req: Request, parms: URLSearchParams): HubContext {
|
|
|
988
1203
|
```
|
|
989
1204
|
|
|
990
1205
|
Read the parms from that argument, not from `req.query`. After a request
|
|
991
|
-
whose
|
|
1206
|
+
whose handler returned `url()`, `contextFor` is called a second time for
|
|
992
1207
|
the same request, with the new parms, to load the page at them. So it must
|
|
993
1208
|
be safe to call twice, and a write must be visible to the second context by
|
|
994
1209
|
the time its `run` returns. A handle opened per request without a
|
|
@@ -1002,28 +1217,37 @@ the whole path space through unchanged.
|
|
|
1002
1217
|
### Page Queries
|
|
1003
1218
|
|
|
1004
1219
|
`<stub>.queries.ts` exports one object named `queries`, typed `Queries`.
|
|
1005
|
-
Each key is a query name,
|
|
1006
|
-
`lb-list` or `lb-row`.
|
|
1220
|
+
Each key is a query name, which the markup names with `lb-query`.
|
|
1007
1221
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
Queries names cannot begin with `lb-`, that namespace is reserved for
|
|
1013
|
-
Loadbare/app queries the hub makes available in the browser, such
|
|
1014
|
-
as [lb-navigation](#lb-navigation).
|
|
1222
|
+
A query's kind and key belong to its name. Declare each query with
|
|
1223
|
+
`row(key, run)` or `rows(key, run)`, naming the key column first. A query
|
|
1224
|
+
cannot be built without one of these functions. Every query has a key; an
|
|
1225
|
+
aggregate row answers with a constant one.
|
|
1015
1226
|
|
|
1016
1227
|
```ts
|
|
1017
1228
|
// members.queries.ts
|
|
1018
|
-
import {
|
|
1229
|
+
import { row, rows, type Queries } from "@loadbare/app/server";
|
|
1019
1230
|
|
|
1020
1231
|
export const queries: Queries = {
|
|
1021
|
-
roster:
|
|
1022
|
-
summary: row(async (ctx) => ({
|
|
1232
|
+
roster: rows("id", (ctx) => ctx.db.members()),
|
|
1233
|
+
summary: row("id", async (ctx) => ({
|
|
1234
|
+
id: "all",
|
|
1235
|
+
count: String(await ctx.db.memberCount()),
|
|
1236
|
+
})),
|
|
1023
1237
|
};
|
|
1024
1238
|
```
|
|
1025
1239
|
|
|
1026
|
-
|
|
1240
|
+
A `row` query returns one object, and a `rows` query returns an array of
|
|
1241
|
+
objects in the order the page shows them. An answer that disagrees with
|
|
1242
|
+
its declared kind is left out of the response, with a warning naming the
|
|
1243
|
+
query.
|
|
1244
|
+
|
|
1245
|
+
Query names cannot begin with `lb-`, that namespace is reserved for
|
|
1246
|
+
Loadbare/app queries the hub serves in the browser, such as
|
|
1247
|
+
[`lb-url`](#the-url). `createHub` refuses at startup a page that declares
|
|
1248
|
+
such a name as a query, a handler or a `crud` entry.
|
|
1249
|
+
|
|
1250
|
+
Every query and every handler receives `ctx`, the application's own request
|
|
1027
1251
|
context. The application builds it once per request and hands it to the hub,
|
|
1028
1252
|
see [The Express server](#the-express-server). Loadbare/app declares it empty
|
|
1029
1253
|
and never reads it, so a query finds exactly what the application put there,
|
|
@@ -1039,36 +1263,32 @@ It has three optional keys.
|
|
|
1039
1263
|
|
|
1040
1264
|
| Key | Keyed by | Answers |
|
|
1041
1265
|
| ------------- | --------------------- | -------------------------------------------------- |
|
|
1042
|
-
| `
|
|
1043
|
-
| `crud` | A query name | The three
|
|
1266
|
+
| `handlers` | The request name | The page's declared requests |
|
|
1267
|
+
| `crud` | A query name | The three requests Loadbare provides |
|
|
1044
1268
|
| `onPageEnter` | Nothing | Runs once on entering the page, before its queries |
|
|
1045
1269
|
|
|
1270
|
+
The server resolves a declared request against `handlers`, and a request
|
|
1271
|
+
Loadbare provides against `crud` under the request's query. A name with no
|
|
1272
|
+
entry there logs a server console warning naming the page and the missing
|
|
1273
|
+
entry, and answers 200 with no response items, so nothing lands and
|
|
1274
|
+
`lb-request-pending` clears from the element that issued the request. A
|
|
1275
|
+
request carrying no name answers 400, and a `run` that throws answers 500.
|
|
1046
1276
|
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
there logs a server console warning naming the page and the missing entry,
|
|
1050
|
-
and answers 200 with `{}`. The browser applies `{}`, so no cell is set and
|
|
1051
|
-
no row is added or removed, and `lb-pending` clears from the element that
|
|
1052
|
-
sent the request. A request carrying no action answers 400, and a `run`
|
|
1053
|
-
that throws answers 500.
|
|
1054
|
-
|
|
1055
|
-
Every entry under `actions` and `crud` has the same two members. `run`
|
|
1056
|
-
performs the work, and `refresh` names the queries to re-run once the action
|
|
1057
|
-
is complete.
|
|
1277
|
+
Every handler under `handlers` and `crud` has the same two members. `run`
|
|
1278
|
+
performs the work, and `refresh` names the queries to re-run once it has.
|
|
1058
1279
|
|
|
1059
|
-
`run` receives the same `ctx` and
|
|
1060
|
-
|
|
1061
|
-
`crud` the `where` carries only what that operation is typed to carry.
|
|
1280
|
+
`run` receives the same `ctx` and the request less its name: `query`, `key`
|
|
1281
|
+
and `values`, as present.
|
|
1062
1282
|
|
|
1063
|
-
`run` may also return
|
|
1064
|
-
ones. That is how a delta reaches the browser: wrap it in
|
|
1065
|
-
the rows that arrived or changed and the keys that went.
|
|
1283
|
+
`run` may also return answers of its own, by query name, which are laid over
|
|
1284
|
+
the refreshed ones. That is how a delta reaches the browser: wrap it in
|
|
1285
|
+
`patch()`, naming the rows that arrived or changed and the keys that went.
|
|
1066
1286
|
|
|
1067
|
-
`run` may instead return `
|
|
1068
|
-
can know, such as the key of a row it inserted. The page
|
|
1069
|
-
in the same round trip, in place of the refresh set — see
|
|
1070
|
-
[When a server response
|
|
1071
|
-
A `rowDelete` that removes the row on screen returns `
|
|
1287
|
+
`run` may instead return `url()`, a new row for `lb-url`, naming query parms
|
|
1288
|
+
only the write can know, such as the key of a row it inserted. The page
|
|
1289
|
+
then loads at them in the same round trip, in place of the refresh set — see
|
|
1290
|
+
[When a server response moves the URL](#when-a-server-response-moves-the-url).
|
|
1291
|
+
A `rowDelete` that removes the row on screen returns `url({ acct: "" })`
|
|
1072
1292
|
and leaves any other delete to its refresh set.
|
|
1073
1293
|
|
|
1074
1294
|
```ts
|
|
@@ -1076,7 +1296,7 @@ and leaves any other delete to its refresh set.
|
|
|
1076
1296
|
import { patch, type Requests } from "@loadbare/app/server";
|
|
1077
1297
|
|
|
1078
1298
|
export const requests: Requests = {
|
|
1079
|
-
|
|
1299
|
+
handlers: {
|
|
1080
1300
|
resetRoster: {
|
|
1081
1301
|
run: (ctx) => ctx.db.resetMembers(),
|
|
1082
1302
|
refresh: ["roster"],
|
|
@@ -1085,9 +1305,9 @@ export const requests: Requests = {
|
|
|
1085
1305
|
crud: {
|
|
1086
1306
|
roster: {
|
|
1087
1307
|
rowDelete: {
|
|
1088
|
-
run: async (ctx,
|
|
1089
|
-
await ctx.db.deleteMember(
|
|
1090
|
-
return { roster: patch({ drop: [
|
|
1308
|
+
run: async (ctx, { key }) => {
|
|
1309
|
+
await ctx.db.deleteMember(key);
|
|
1310
|
+
return { roster: patch({ drop: [key] }) };
|
|
1091
1311
|
},
|
|
1092
1312
|
refresh: [],
|
|
1093
1313
|
},
|
|
@@ -1096,86 +1316,142 @@ export const requests: Requests = {
|
|
|
1096
1316
|
};
|
|
1097
1317
|
```
|
|
1098
1318
|
|
|
1099
|
-
Each key under a `crud` entry is a
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
a key exists only on a live row.
|
|
1319
|
+
Each key under a `crud` entry is a request name with its prefix stripped and
|
|
1320
|
+
the rest camel-cased, so the attribute, the wire and this key are one
|
|
1321
|
+
vocabulary. A query with no `crud` entry permits none of them.
|
|
1103
1322
|
|
|
1104
|
-
|
|
|
1105
|
-
|
|
|
1106
|
-
| `lb-row-
|
|
1107
|
-
| `lb-row-
|
|
1108
|
-
| `lb-row-
|
|
1323
|
+
| Request name | Key under `crud` | `run` receives |
|
|
1324
|
+
| --------------- | ---------------- | --------------- |
|
|
1325
|
+
| `lb-row-insert` | `rowInsert` | `values` |
|
|
1326
|
+
| `lb-row-update` | `rowUpdate` | `key`, `values` |
|
|
1327
|
+
| `lb-row-delete` | `rowDelete` | `key` |
|
|
1109
1328
|
|
|
1110
1329
|
A `rowUpdate` sets the columns `values` names and leaves the rest as they
|
|
1111
|
-
are, as an SQL `UPDATE` does. A form sends the
|
|
1112
|
-
|
|
1330
|
+
are, as an SQL `UPDATE` does. A form sends the controls it holds and a
|
|
1331
|
+
control carrying `lb-request` sends itself, so one handler answers both.
|
|
1113
1332
|
|
|
1114
|
-
|
|
1333
|
+
### The wire
|
|
1115
1334
|
|
|
1116
1335
|
---- UNEDITED ----
|
|
1117
1336
|
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1337
|
+
One path, one method. The hub sends every round trip as
|
|
1338
|
+
`POST /lb/<stub>?<query string>`, with the query string the browser is
|
|
1339
|
+
showing. An empty body asks for the page load. Any other body is a
|
|
1340
|
+
request:
|
|
1341
|
+
|
|
1342
|
+
```ts
|
|
1343
|
+
interface HubRequest {
|
|
1344
|
+
name: string;
|
|
1345
|
+
query?: string;
|
|
1346
|
+
key?: string;
|
|
1347
|
+
values?: Record<string, string>;
|
|
1348
|
+
}
|
|
1349
|
+
```
|
|
1350
|
+
|
|
1351
|
+
Every response is an array of response items:
|
|
1352
|
+
|
|
1353
|
+
```ts
|
|
1354
|
+
interface ResponseItem {
|
|
1355
|
+
query: string;
|
|
1356
|
+
kind: "row" | "rows";
|
|
1357
|
+
key: string;
|
|
1358
|
+
row?: Row;
|
|
1359
|
+
rows?: Row[];
|
|
1360
|
+
patch?: { rows?: Row[]; drop?: unknown[] };
|
|
1361
|
+
}
|
|
1362
|
+
```
|
|
1363
|
+
|
|
1364
|
+
A response item carries exactly one of `row`, `rows` or `patch`. A patch
|
|
1365
|
+
answers a `rows` query only: rows named in `rows` are added or updated, keys
|
|
1366
|
+
in `drop` are removed, and every other row is left alone.
|
|
1367
|
+
|
|
1368
|
+
`createHub(pages)` builds the `Hub`, which has two calls, both answering
|
|
1369
|
+
with response items: `dataForPage(page, ctx)` for a page load, and
|
|
1370
|
+
`runRequest(page, request, ctx)` for a request.
|
|
1371
|
+
|
|
1372
|
+
## Custom elements
|
|
1373
|
+
|
|
1374
|
+
---- UNEDITED ----
|
|
1375
|
+
|
|
1376
|
+
A custom element is an element defined by `customElements.define`, and
|
|
1377
|
+
follows these fixed rules in a Loadbare/app application:
|
|
1378
|
+
- Its name contains a hyphen, as HTML requires, like `<my-custom-element>`.
|
|
1379
|
+
- Its element file, if present, is `my-custom-element.html`.
|
|
1380
|
+
- Its script, if present, is `my-custom-element.browser.ts`. The
|
|
1381
|
+
`browser` segment is a safety feature, requiring the file to be
|
|
1123
1382
|
explicitly named as a browser file, to help prevent unfortunate
|
|
1124
1383
|
naming collisions where a server file happens to have the name of
|
|
1125
|
-
a
|
|
1384
|
+
a custom element and gets built into the browser bundle.
|
|
1126
1385
|
|
|
1127
|
-
A
|
|
1128
|
-
it may have both. If it has neither, the builder reports an
|
|
1386
|
+
A custom element must have one or the other of an element file and a
|
|
1387
|
+
script, and it may have both. If it has neither, the builder reports an
|
|
1388
|
+
error.
|
|
1129
1389
|
|
|
1130
|
-
To use
|
|
1131
|
-
in the
|
|
1390
|
+
To use custom elements from a widget library, add the library to
|
|
1391
|
+
`imports.ts` anywhere in the builder's `--src`:
|
|
1132
1392
|
|
|
1133
1393
|
```ts
|
|
1134
1394
|
export default ["@scope/library-name"];
|
|
1135
1395
|
```
|
|
1136
1396
|
|
|
1137
|
-
|
|
1397
|
+
Import every attribute name from `@loadbare/app/constants` — see
|
|
1398
|
+
[Constants](#constants). Never write one as a string literal.
|
|
1138
1399
|
|
|
1139
|
-
|
|
1400
|
+
### Controls
|
|
1140
1401
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1402
|
+
A form-associated custom element with a `value` property that fires
|
|
1403
|
+
`change` is a control. The hub sets its `value` when a column lands, and
|
|
1404
|
+
gathers its `value` when a request is issued, exactly as for a native
|
|
1405
|
+
`<input>`. It commits on `change`, and belongs to its form owner, which the
|
|
1406
|
+
browser tracks and the `form` attribute names.
|
|
1144
1407
|
|
|
1145
|
-
|
|
1408
|
+
```ts
|
|
1409
|
+
class NoteField extends HTMLElement {
|
|
1410
|
+
static formAssociated = true;
|
|
1411
|
+
// a `value` property, and a `change` event when the edit commits
|
|
1412
|
+
}
|
|
1413
|
+
```
|
|
1414
|
+
|
|
1415
|
+
A control that is not yet upgraded when its column lands receives
|
|
1416
|
+
`lb-column-value` alone, since the hub sets `value` only on a control. That
|
|
1417
|
+
happens to an element the builder shipped absent, and to one whose
|
|
1418
|
+
definition loads after the hub lands. A control takes the stamp up the
|
|
1419
|
+
first time it connects, and never again.
|
|
1146
1420
|
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
must name them in `observedAttributes` to see them change.
|
|
1421
|
+
A custom element that is not a control receives `lb-column-value` and
|
|
1422
|
+
renders it; its content is never replaced.
|
|
1150
1423
|
|
|
1151
|
-
|
|
1152
|
-
| -------------- | ----------------------- | --------------------------------------------------------------------------------------- |
|
|
1153
|
-
| `lb-pending` | The dispatching element | The round trip is in flight |
|
|
1154
|
-
| `lb-error` | The dispatching element | The last round trip failed, cleared on the next — see [The round trip](#the-round-trip) |
|
|
1155
|
-
| `lb-row-count` | A list scope | How many rows the scope is showing |
|
|
1424
|
+
### Lifecycle
|
|
1156
1425
|
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1426
|
+
| The hub | A custom element sees |
|
|
1427
|
+
| ---------------------------------------- | -------------------------------------------- |
|
|
1428
|
+
| Shows a page | `constructor`, `connectedCallback` |
|
|
1429
|
+
| Creates a live row, before placing it | `constructor` |
|
|
1430
|
+
| Places a live row the first time | `connectedCallback` |
|
|
1431
|
+
| Places it again, on every set of all rows | `disconnectedCallback`, `connectedCallback` |
|
|
1432
|
+
| Removes a live row | `disconnectedCallback` |
|
|
1433
|
+
| Turns `lb-show` off | `disconnectedCallback`, `adoptedCallback` |
|
|
1434
|
+
| Turns `lb-show` on | `adoptedCallback`, `connectedCallback` |
|
|
1435
|
+
| Turns on an element shipped absent | `constructor`, `connectedCallback` |
|
|
1160
1436
|
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
not held back this way, because a widget that sends on change must have its
|
|
1166
|
-
latest value sent rather than dropped.
|
|
1437
|
+
A listener on the element itself goes in the constructor, which reads no
|
|
1438
|
+
attribute and no child. A child is looked up when it is needed.
|
|
1439
|
+
`connectedCallback` runs on every move and is written to run again; work
|
|
1440
|
+
done once per instance goes behind a flag.
|
|
1167
1441
|
|
|
1168
|
-
|
|
1169
|
-
removes it when the round trip settles, so assistive technology hears the
|
|
1170
|
-
state a stylesheet shows.
|
|
1442
|
+
### Row hooks
|
|
1171
1443
|
|
|
1172
|
-
|
|
1444
|
+
| Method | Implemented By | The hub calls it |
|
|
1445
|
+
| ------------------------------- | -------------- | -------------------------------------------------- |
|
|
1446
|
+
| `lbPlaceRow(el, row, template)` | Developer | To place a live row, detached, on its first arrival and whenever all rows decide the order |
|
|
1447
|
+
| `lbRowsLanded()` | Developer | After the rows land |
|
|
1173
1448
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1449
|
+
The hub calls both on a custom element with `lb-query` and a row template.
|
|
1450
|
+
Both are optional. The `RowsHost` interface in `@loadbare/app/types`
|
|
1451
|
+
declares them.
|
|
1452
|
+
|
|
1453
|
+
`applyRow(root, row)`, exported by `@loadbare/app`, fills `root` from one
|
|
1454
|
+
row, the same operation that fills a live row.
|
|
1179
1455
|
|
|
1180
1456
|
## Internal linkage
|
|
1181
1457
|
|
|
@@ -1209,16 +1485,16 @@ Loadbare/app owns every file name and pattern in this table. A file so
|
|
|
1209
1485
|
named carries its meaning wherever it sits in the `--src` tree, so an
|
|
1210
1486
|
application must not use one of these names for anything else.
|
|
1211
1487
|
|
|
1212
|
-
| File | How many
|
|
1213
|
-
| ----------------------- |
|
|
1214
|
-
| `chrome.html` | Exactly one
|
|
1215
|
-
| `imports.ts` | Zero or one
|
|
1216
|
-
| `<stub>.page.html` | One per page
|
|
1217
|
-
| `<stub>.queries.ts` | Zero or one per page
|
|
1218
|
-
| `<stub>.requests.ts` | Zero or one per page
|
|
1219
|
-
| `<tag-name>.html` | Zero or one per
|
|
1220
|
-
| `<tag-name>.browser.ts` | Zero or one per
|
|
1221
|
-
| `*.css` | Any number
|
|
1488
|
+
| File | How many | Holds | Defined in |
|
|
1489
|
+
| ----------------------- | ------------------------------ | -------------------------------------------------------- | ------------------------------------------------------- |
|
|
1490
|
+
| `chrome.html` | Exactly one | The application's one HTML document | [Chrome](#chrome) |
|
|
1491
|
+
| `imports.ts` | Zero or one | Default-exports an array of widget library package names | [Imported widget libraries](#imported-widget-libraries) |
|
|
1492
|
+
| `<stub>.page.html` | One per page | One page's markup, as a fragment | [Pages](#pages) |
|
|
1493
|
+
| `<stub>.queries.ts` | Zero or one per page | That page's queries | [Page Queries](#page-queries) |
|
|
1494
|
+
| `<stub>.requests.ts` | Zero or one per page | That page's requests | [Page Requests](#page-requests) |
|
|
1495
|
+
| `<tag-name>.html` | Zero or one per custom element | One element file, as a fragment | [Custom elements](#custom-elements) |
|
|
1496
|
+
| `<tag-name>.browser.ts` | Zero or one per custom element | One custom element's script, `.js` also accepted | [Custom elements](#custom-elements) |
|
|
1497
|
+
| `*.css` | Any number | A stylesheet, concatenated into `app.css` | [Running the builder](#running-the-builder) |
|
|
1222
1498
|
|
|
1223
1499
|
### Build outputs
|
|
1224
1500
|
|
|
@@ -1230,7 +1506,7 @@ only when some origin has a stylesheet, `pages.ts` only when some page has a
|
|
|
1230
1506
|
| Path | Written | Holds | Read by | Defined in |
|
|
1231
1507
|
| ------------------ | ----------------- | ------------------------------------------------------------------ | ---------- | ------------------------------------------- |
|
|
1232
1508
|
| `/app.html` | Always | The chrome, built | The server | [Chrome](#chrome) |
|
|
1233
|
-
| `/client.js` | Always | `<lb-hub>` and every
|
|
1509
|
+
| `/client.js` | Always | `<lb-hub>` and every custom element the application uses | The chrome | [Chrome](#chrome) |
|
|
1234
1510
|
| `/client-entry.ts` | Always | The generated entry `client.js` is bundled from | Nothing | [Running the builder](#running-the-builder) |
|
|
1235
1511
|
| `/app.css` | With a stylesheet | Every stylesheet in the cascade, concatenated | The chrome | [Chrome](#chrome) |
|
|
1236
1512
|
| `/pages.ts` | With page data | The one `createHub()` call, over every page's queries and requests | The server | [The Express server](#the-express-server) |
|
|
@@ -1240,57 +1516,65 @@ only when some origin has a stylesheet, `pages.ts` only when some page has a
|
|
|
1240
1516
|
Loadbare/app owns every key in this table. A widget library declares them.
|
|
1241
1517
|
An application never does.
|
|
1242
1518
|
|
|
1243
|
-
| Key | Value | Means
|
|
1244
|
-
| ------------------ | ------------------------- |
|
|
1245
|
-
| `loadbare.widgets` | A path inside the package | Where this package's
|
|
1519
|
+
| Key | Value | Means | Defined in |
|
|
1520
|
+
| ------------------ | ------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------- |
|
|
1521
|
+
| `loadbare.widgets` | A path inside the package | Where this package's custom elements are. Absent means the whole installed directory | [Imported widget libraries](#imported-widget-libraries) |
|
|
1246
1522
|
|
|
1247
1523
|
### Reserved namespaces
|
|
1248
1524
|
|
|
1249
1525
|
Loadbare/app owns every namespace in this table. Ownership of a name and
|
|
1250
1526
|
ownership of its behavior are stated separately, because they differ.
|
|
1251
1527
|
|
|
1252
|
-
| Namespace | Applies to
|
|
1253
|
-
| --------- |
|
|
1254
|
-
| `lb-*` |
|
|
1255
|
-
| `exp-*` |
|
|
1256
|
-
| `lb*` | Methods on custom elements
|
|
1528
|
+
| Namespace | Applies to | Loadbare owns | Defined in |
|
|
1529
|
+
| --------- | ----------------------------------------------------------- | --------------------------------------------------- | ----------------------------------------------- |
|
|
1530
|
+
| `lb-*` | Attributes, their values, queries, columns of `lb-` queries, requests, custom elements, events | The names and their behavior | [The lb-* namespace](#the-lb--namespace) |
|
|
1531
|
+
| `exp-*` | Attributes on a custom element | The behavior; element file authors pick the names | [Build time parameters](#build-time-parameters) |
|
|
1532
|
+
| `lb*` | Methods on custom elements | The names and their behavior | [Row hooks](#row-hooks) |
|
|
1257
1533
|
|
|
1258
1534
|
#### The lb-* namespace
|
|
1259
1535
|
|
|
1260
|
-
Every
|
|
1261
|
-
application writes the
|
|
1262
|
-
because a name Loadbare has not defined today
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
|
1271
|
-
|
|
|
1272
|
-
| `lb-
|
|
1273
|
-
| `lb-
|
|
1274
|
-
| `lb-
|
|
1275
|
-
| `lb-
|
|
1276
|
-
| `lb-
|
|
1277
|
-
| `lb-key-value` | Hub | [
|
|
1278
|
-
| `lb-
|
|
1279
|
-
| `lb-
|
|
1280
|
-
| `lb-
|
|
1281
|
-
| `lb-
|
|
1282
|
-
| `lb-
|
|
1283
|
-
| `lb-
|
|
1284
|
-
| `lb-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
[The
|
|
1536
|
+
Every name beginning with `lb-` belongs to Loadbare/app — see
|
|
1537
|
+
[Names](#names). An application writes the attributes this section names,
|
|
1538
|
+
and invents none of its own, because a name Loadbare has not defined today
|
|
1539
|
+
it may define tomorrow.
|
|
1540
|
+
|
|
1541
|
+
| Attribute | Written by | On | Defined in |
|
|
1542
|
+
| ----------------- | ------------- | ----------------------------------- | --------------------------------------------- |
|
|
1543
|
+
| `lb-query` | Developer | Any element | [Landing](#landing) |
|
|
1544
|
+
| `lb-column` | Developer | Any element | [Landing](#landing) |
|
|
1545
|
+
| `lb-show` | Developer | Any element but a `<template>` | [Displaying by condition](#displaying-by-condition) |
|
|
1546
|
+
| `lb-request` | Developer | Any element | [Requests](#requests) |
|
|
1547
|
+
| `lb-url-link` | Developer | An `<a>` | [Links](#links) |
|
|
1548
|
+
| `lb-url-push` | Developer | An element with `lb-request` | [Query parms](#query-parms) |
|
|
1549
|
+
| `lb-url-unknown` | Developer | A `<dialog>` inside `<lb-hub>` | [An unknown page](#an-unknown-page) |
|
|
1550
|
+
| `lb-exp-slot` | Developer | One element in an element file | [Slots and templates](#slots-and-templates) |
|
|
1551
|
+
| `lb-exp-template` | Developer | An element file, and a `<template>` | [Slots and templates](#slots-and-templates) |
|
|
1552
|
+
| `lb-column-value` | Hub | Every element set from a column | [How a column lands](#how-a-column-lands) |
|
|
1553
|
+
| `lb-key-value` | Hub | A live row, and an element a `row` lands on | [Rows](#rows) |
|
|
1554
|
+
| `lb-row-live` | Hub | A live row | [Rows](#rows) |
|
|
1555
|
+
| `lb-query-row-count` | Hub | An element with a row template | [Rows](#rows) |
|
|
1556
|
+
| `lb-request-pending` | Hub | The element that issued a request | [Request state](#request-state) |
|
|
1557
|
+
| `lb-request-error` | Hub | The element that issued a request | [Request state](#request-state) |
|
|
1558
|
+
| `lb-show` on a `<template>` | Hub and builder | Where an absent element stands | [Displaying by condition](#displaying-by-condition) |
|
|
1559
|
+
| `lb-page` | Builder | A page's `<template>` | [Page HTML](#page-html) |
|
|
1560
|
+
| `lb-page-title` | Builder | A page's `<template>` | [Page HTML](#page-html) |
|
|
1561
|
+
|
|
1562
|
+
The builder refuses any other `lb-` attribute in markup — see
|
|
1563
|
+
[The markup checks](#the-markup-checks).
|
|
1564
|
+
|
|
1565
|
+
### Reserved queries
|
|
1566
|
+
|
|
1567
|
+
| Query | Kind | Key | Columns | Defined in |
|
|
1568
|
+
| -------- | ----- | --------- | --------------------------------------------------------------- | ------------------- |
|
|
1569
|
+
| `lb-url` | `row` | `lb-path` | `lb-path`, `lb-page-label`, `lb-page-unknown`, every query parm | [The URL](#the-url) |
|
|
1570
|
+
|
|
1571
|
+
### Reserved request names
|
|
1572
|
+
|
|
1573
|
+
| Request name | Runs under `crud` | Defined in |
|
|
1574
|
+
| --------------- | ----------------- | ----------------------------------- |
|
|
1575
|
+
| `lb-row-insert` | `rowInsert` | [The request names](#the-request-names) |
|
|
1576
|
+
| `lb-row-update` | `rowUpdate` | [The request names](#the-request-names) |
|
|
1577
|
+
| `lb-row-delete` | `rowDelete` | [The request names](#the-request-names) |
|
|
1294
1578
|
|
|
1295
1579
|
### Reserved tags
|
|
1296
1580
|
|
|
@@ -1306,16 +1590,55 @@ never defines them.
|
|
|
1306
1590
|
Loadbare/app owns every DOM event in this table, both the name and what its
|
|
1307
1591
|
`detail` carries.
|
|
1308
1592
|
|
|
1309
|
-
| Event | Dispatched from
|
|
1310
|
-
| ------------ |
|
|
1311
|
-
| `lb-request` | The element that
|
|
1312
|
-
|
|
1313
|
-
### Reserved
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1593
|
+
| Event | Dispatched from | Bubbles | Cancelable | Defined in |
|
|
1594
|
+
| ------------ | -------------------------------- | ------- | ---------- | --------------------------------------- |
|
|
1595
|
+
| `lb-request` | The element that committed | Yes | No | [The request event](#the-request-event) |
|
|
1596
|
+
|
|
1597
|
+
### Reserved methods
|
|
1598
|
+
|
|
1599
|
+
| Method | On | Defined in |
|
|
1600
|
+
| -------------- | -------------------------------------------------- | ----------------------- |
|
|
1601
|
+
| `lbPlaceRow` | A custom element with `lb-query` and a row template | [Row hooks](#row-hooks) |
|
|
1602
|
+
| `lbRowsLanded` | A custom element with `lb-query` and a row template | [Row hooks](#row-hooks) |
|
|
1603
|
+
|
|
1604
|
+
### Constants
|
|
1605
|
+
|
|
1606
|
+
`@loadbare/app/constants` exports every name above. Custom element code
|
|
1607
|
+
imports them rather than writing a string.
|
|
1608
|
+
|
|
1609
|
+
| Constant | Value |
|
|
1610
|
+
| ------------------------- | --------------------------------------- |
|
|
1611
|
+
| `ATTR_QUERY` | `lb-query` |
|
|
1612
|
+
| `ATTR_COLUMN` | `lb-column` |
|
|
1613
|
+
| `ATTR_SHOW` | `lb-show` |
|
|
1614
|
+
| `ATTR_COLUMN_VALUE` | `lb-column-value` |
|
|
1615
|
+
| `ATTR_KEY_VALUE` | `lb-key-value` |
|
|
1616
|
+
| `ATTR_ROW_LIVE` | `lb-row-live` |
|
|
1617
|
+
| `LIVE_ROW` | `[lb-row-live]`, the selector for a live row |
|
|
1618
|
+
| `ATTR_QUERY_ROW_COUNT` | `lb-query-row-count` |
|
|
1619
|
+
| `ATTR_REQUEST` | `lb-request` |
|
|
1620
|
+
| `ATTR_REQUEST_PENDING` | `lb-request-pending` |
|
|
1621
|
+
| `ATTR_REQUEST_ERROR` | `lb-request-error` |
|
|
1622
|
+
| `ATTR_URL_LINK` | `lb-url-link` |
|
|
1623
|
+
| `ATTR_URL_PUSH` | `lb-url-push` |
|
|
1624
|
+
| `ATTR_URL_UNKNOWN` | `lb-url-unknown` |
|
|
1625
|
+
| `ATTR_EXP_SLOT` | `lb-exp-slot` |
|
|
1626
|
+
| `ATTR_EXP_TEMPLATE` | `lb-exp-template` |
|
|
1627
|
+
| `ATTR_PAGE` | `lb-page` |
|
|
1628
|
+
| `ATTR_PAGE_TITLE` | `lb-page-title` |
|
|
1629
|
+
| `DEVELOPER_ATTRIBUTES` | The seven attributes a developer writes |
|
|
1630
|
+
| `LB_EVENT_NAME` | `lb-request` |
|
|
1631
|
+
| `LB_RESERVED_PREFIX` | `lb-` |
|
|
1632
|
+
| `REQUEST_ROW_INSERT` | `lb-row-insert` |
|
|
1633
|
+
| `REQUEST_ROW_UPDATE` | `lb-row-update` |
|
|
1634
|
+
| `REQUEST_ROW_DELETE` | `lb-row-delete` |
|
|
1635
|
+
| `LB_ROW_REQUESTS` | All three request names |
|
|
1636
|
+
| `KIND_ROW` | `row` |
|
|
1637
|
+
| `KIND_ROWS` | `rows` |
|
|
1638
|
+
| `URL_QUERY` | `lb-url` |
|
|
1639
|
+
| `URL_COLUMN_PATH` | `lb-path` |
|
|
1640
|
+
| `URL_COLUMN_PAGE_LABEL` | `lb-page-label` |
|
|
1641
|
+
| `URL_COLUMN_PAGE_UNKNOWN` | `lb-page-unknown` |
|
|
1642
|
+
| `HUB_TAG_NAME` | `lb-hub` |
|
|
1643
|
+
| `LB_ENDPOINT` | `/lb` |
|
|
1644
|
+
| `LB_REQUEST_TIMEOUT_MS` | `10000` |
|