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