@loadbare/app 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/build/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +74 -66
- package/dist/build/assemble.js.map +1 -1
- package/dist/build/expand.d.ts.map +1 -1
- package/dist/build/expand.js +20 -19
- package/dist/build/expand.js.map +1 -1
- package/dist/build/locations.d.ts +2 -3
- package/dist/build/locations.d.ts.map +1 -1
- package/dist/build/locations.js +2 -3
- package/dist/build/locations.js.map +1 -1
- package/dist/build/pages.d.ts +3 -4
- package/dist/build/pages.d.ts.map +1 -1
- package/dist/build/pages.js +3 -4
- package/dist/build/pages.js.map +1 -1
- package/dist/core/lb-constants.d.ts +25 -24
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +95 -168
- package/dist/core/lb-constants.js.map +1 -1
- package/dist/core/lb-types.d.ts +64 -77
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js +40 -7
- package/dist/core/lb-types.js.map +1 -1
- package/dist/hub/lb-apply.d.ts +47 -37
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +174 -193
- package/dist/hub/lb-apply.js.map +1 -1
- package/dist/hub/lb-hub.browser.d.ts +1 -1
- package/dist/hub/lb-hub.browser.d.ts.map +1 -1
- package/dist/hub/lb-hub.browser.js +411 -449
- package/dist/hub/lb-hub.browser.js.map +1 -1
- package/dist/server/lb-express.d.ts +5 -5
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +35 -66
- package/dist/server/lb-express.js.map +1 -1
- package/dist/server/lb-server.d.ts +77 -135
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +132 -79
- package/dist/server/lb-server.js.map +1 -1
- package/docs/TECHREF-1.0.md +861 -587
- package/docs/comparison.md +222 -185
- package/docs/prior-art.md +15 -14
- package/docs/reference/builder.md +9 -3
- package/docs/reference/chrome.md +107 -56
- package/docs/reference/custom-elements.md +199 -173
- package/docs/reference/data-binding.md +374 -374
- package/docs/reference/overview.md +12 -10
- package/docs/reference/page-files.md +135 -99
- package/docs/reference/server.md +2 -2
- package/docs/reference/widgets.md +104 -110
- package/docs/roadmap.md +32 -39
- package/docs/terms-of-art.md +57 -0
- package/docs/testing.md +97 -68
- package/docs/theory.md +92 -58
- package/docs/tutorials/010-pages-and-navigation.md +20 -12
- package/docs/tutorials/020-css.md +6 -3
- package/docs/tutorials/030-html-decomposition.md +9 -7
- package/docs/tutorials/040-displaying-data.md +30 -13
- package/docs/tutorials/{050-actions.md → 050-requests.md} +25 -15
- package/docs/tutorials/060-custom-element-code.md +17 -16
- package/docs/tutorials/065-conditional-rendering.md +34 -23
- package/docs/tutorials/070-displaying-a-list.md +29 -21
- package/docs/tutorials/072-inserting-into-a-list.md +24 -16
- package/docs/tutorials/074-deleting-from-a-list.md +9 -7
- package/docs/tutorials/076-updating-a-list-item.md +11 -10
- package/docs/tutorials/080-widget-requests.md +71 -43
- package/docs/tutorials/090-using-widget-libraries.md +22 -22
- package/package.json +1 -1
- package/skills/loadbare-app/SKILL.md +178 -122
- package/skills/loadbare-app/references/TECHREF-1.0.md +861 -587
- package/skills/loadbare-app/references/builder.md +9 -3
- package/skills/loadbare-app/references/chrome.md +107 -56
- package/skills/loadbare-app/references/custom-elements.md +199 -173
- package/skills/loadbare-app/references/data-binding.md +374 -374
- package/skills/loadbare-app/references/overview.md +12 -10
- package/skills/loadbare-app/references/page-files.md +135 -99
- package/skills/loadbare-app/references/server.md +2 -2
- package/skills/loadbare-app/references/widgets.md +104 -110
- package/docs/analysis-accidental-complexity.md +0 -149
- package/docs/analysis-closed-set.md +0 -210
|
@@ -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.
|
|
445
|
+
|
|
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.
|
|
412
452
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
that the hub leaves a widget's own clicks alone — see [Requests](#requests).
|
|
453
|
+
Name one query on more than one element to show it in more than one place.
|
|
454
|
+
Every one of them receives it.
|
|
416
455
|
|
|
417
|
-
|
|
418
|
-
a widget or a stylesheet reads it.
|
|
456
|
+
A query that arrives with nowhere to land is reported to the console.
|
|
419
457
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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.
|
|
483
|
+
|
|
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.
|
|
492
|
+
|
|
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.
|
|
431
496
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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,166 +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).
|
|
504
|
-
When a write creates or removes the master, the server response
|
|
505
|
-
|
|
506
|
-
[When a server response
|
|
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).
|
|
507
596
|
|
|
508
597
|
### Requests
|
|
509
598
|
|
|
510
599
|
---- UNEDITED ----
|
|
511
600
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
The hub
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
| lb-row-delete | anything inside a live row |
|
|
527
|
-
| lb-row-update | a `<form>`, a button in a live row, or a widget cell |
|
|
528
|
-
| anything else | must be a named routine in the page's server code |
|
|
529
|
-
|
|
530
|
-
The wire format is not visible to the user, but uses the same lb-*
|
|
531
|
-
attributes minus their prefix, so that it is intelligible when working on
|
|
532
|
-
Loadbare/app itself.
|
|
533
|
-
|
|
534
|
-
The hub scopes every request, whether a native element or a widget
|
|
535
|
-
dispatched it. It reads the scope from the dispatching element before any
|
|
536
|
-
ancestor sees the event, and never overwrites a field the request already
|
|
537
|
-
carries.
|
|
538
|
-
|
|
539
|
-
| `action` | Filled from scope | Required |
|
|
540
|
-
| ---------------- | ------------------------------ | ---------------------- |
|
|
541
|
-
| a declared name | `list` or `row`, `key`, `cell` | nothing |
|
|
542
|
-
| `lb-row-insert` | `list` | `list`, and `values` |
|
|
543
|
-
| `lb-row-delete` | `list`, `key` | both |
|
|
544
|
-
| `lb-row-update` | `list`, `key` | both, and `values` |
|
|
545
|
-
|
|
546
|
-
An element's own `lb-list` or `lb-row` names what it displays, never where
|
|
547
|
-
its request goes. A request belongs to the scope around the element, the
|
|
548
|
-
way a control belongs to the form around it. `list` or `row` comes from the
|
|
549
|
-
nearest ancestor scope, `key` from the nearest live row inside that scope,
|
|
550
|
-
and `cell` from the dispatching element's own `lb-cell`.
|
|
551
|
-
A request missing a required field is not sent. The hub never fills
|
|
552
|
-
`value`. An `lb-row-insert` or `lb-row-update` from an element carrying
|
|
553
|
-
`lb-cell` is a record of one cell, the way a control has a value and a form
|
|
554
|
-
has values: `values` holds that cell alone, taken from the `value` the widget
|
|
555
|
-
sent or else read from the control it is or wraps, and `value` is not sent
|
|
556
|
-
beside it. A widget that sends a `value` from an element carrying no
|
|
557
|
-
`lb-cell` is refused, since nothing names the column. Any other
|
|
558
|
-
`lb-row-insert` or `lb-row-update` gathers the row it belongs
|
|
559
|
-
to: `values` holds every `lb-cell` with a control to read in the nearest
|
|
560
|
-
`<form>`, `<tr>` or live row around the dispatching element, itself
|
|
561
|
-
included, inside its scope. The cells are found the way a row lands, so a
|
|
562
|
-
cell inside a scope nested in the row is that scope's and is not gathered,
|
|
563
|
-
while an element carrying a scope and `lb-cell` both is the row's cell.
|
|
564
|
-
This is a button's form owner: what else sits
|
|
565
|
-
beside the element never changes what is sent. A `<tr>` counts because a
|
|
566
|
-
form cannot go around a table row's controls, so a new row in a table is its
|
|
567
|
-
own form; a live row counts because it is the row the key names. The scope
|
|
568
|
-
itself is never the row, since its cells belong to other rows. A request
|
|
569
|
-
from an element in no form or row is not sent, and the hub reports that its
|
|
570
|
-
cells belong in a `<form>`. A request that already carries `values` keeps
|
|
571
|
-
them, and one whose row has no cell to read is not sent. A click on a
|
|
572
|
-
native element that carries either operation and is a cell or
|
|
573
|
-
holds cells is not sent, since clicking into one of its controls
|
|
574
|
-
would send the row: put the action on a form, on a button, or on a widget
|
|
575
|
-
that decides when its cell has changed.
|
|
576
|
-
|
|
577
|
-
A widget dispatches the action and, where it wraps a control, that control's
|
|
578
|
-
value.
|
|
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:
|
|
579
615
|
|
|
580
616
|
```
|
|
581
|
-
{
|
|
582
|
-
{
|
|
583
|
-
{
|
|
584
|
-
{
|
|
585
|
-
|
|
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" }
|
|
621
|
+
```
|
|
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>
|
|
586
687
|
```
|
|
587
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
|
+
|
|
588
731
|
#### The request event
|
|
589
732
|
|
|
590
|
-
The hub
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
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.
|
|
594
736
|
|
|
595
|
-
A
|
|
596
|
-
|
|
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
|
|
597
739
|
therefore produce identical events.
|
|
598
740
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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.
|
|
603
761
|
|
|
604
762
|
#### The round trip
|
|
605
763
|
|
|
@@ -608,77 +766,63 @@ aborts it and treats it as a failure, since `fetch` imposes no deadline of
|
|
|
608
766
|
its own. An application cannot change the deadline.
|
|
609
767
|
|
|
610
768
|
A round trip fails on a server error, on a network failure, or on that
|
|
611
|
-
deadline, and all three
|
|
612
|
-
request — see [Request state](#request-state).
|
|
613
|
-
|
|
614
|
-
After a write succeeds, the cells it gathered show what the server holds.
|
|
615
|
-
An `lb-row-update` does this by landing the row on them. An
|
|
616
|
-
`lb-row-insert` does it by resetting them, the way `form.reset()` resets a
|
|
617
|
-
form, because the row they held now lives in the list. The hub resets
|
|
618
|
-
after it lands the response, and only what it gathered:
|
|
619
|
-
|
|
620
|
-
- Each control returns to its default: an `<input>` or `<textarea>` to its
|
|
621
|
-
`defaultValue`, a `<select>` to the options marked `selected`. A page
|
|
622
|
-
that wants a prefilled insert writes a `value` attribute, as in plain
|
|
623
|
-
HTML.
|
|
624
|
-
- A control showing something other than the value the hub read holds an
|
|
625
|
-
edit made during the round trip. That edit was not sent, and is left.
|
|
626
|
-
- `lb-value` comes off each reset cell, and off its control, before the
|
|
627
|
-
control resets — see [How a value lands](#how-a-value-lands).
|
|
628
|
-
- Values a widget supplied in the request were not gathered, and are not
|
|
629
|
-
reset.
|
|
630
|
-
- Nothing is dispatched, as `form.reset()` fires no `change`.
|
|
631
|
-
|
|
632
|
-
A failed insert resets nothing, so the entry can be corrected.
|
|
633
|
-
|
|
634
|
-
A navigation that fails to load its data sets nothing. No element
|
|
635
|
-
dispatched it, so there is nothing to stamp, and the hub reports it to the
|
|
636
|
-
console. A load started by a control writing a query parm stamps that
|
|
637
|
-
control, as a request stamps its origin.
|
|
638
|
-
|
|
639
|
-
### Links
|
|
769
|
+
deadline, and all three stamp `lb-request-error` on the element that issued
|
|
770
|
+
the request — see [Request state](#request-state).
|
|
640
771
|
|
|
641
|
-
|
|
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.
|
|
642
776
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
other link, so leaving the application is the default and staying in it is
|
|
647
|
-
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.
|
|
648
780
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
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.
|
|
652
784
|
|
|
653
|
-
|
|
654
|
-
link to determine the path.
|
|
785
|
+
### The URL
|
|
655
786
|
|
|
656
|
-
|
|
657
|
-
on the server.
|
|
787
|
+
---- UNEDITED ----
|
|
658
788
|
|
|
659
|
-
The query
|
|
660
|
-
|
|
661
|
-
already on loads that page again at the new URL without replacing its DOM —
|
|
662
|
-
see [Query parms](#query-parms).
|
|
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.
|
|
663
791
|
|
|
664
|
-
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 |
|
|
665
798
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
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.
|
|
671
818
|
|
|
672
819
|
```html
|
|
673
|
-
<
|
|
674
|
-
<
|
|
675
|
-
<
|
|
676
|
-
|
|
677
|
-
</nav>
|
|
820
|
+
<header lb-query="lb-url">
|
|
821
|
+
<h1>Membership Roster</h1>
|
|
822
|
+
<h2 lb-column="lb-page-label"></h2>
|
|
823
|
+
</header>
|
|
678
824
|
```
|
|
679
825
|
|
|
680
|
-
See also [Navigation Row lb-navigation](#lb-navigation).
|
|
681
|
-
|
|
682
826
|
#### What a URL names
|
|
683
827
|
|
|
684
828
|
The path names a page, a place in the application. It never names a
|
|
@@ -690,70 +834,115 @@ The query string describes what that page has on screen:
|
|
|
690
834
|
remembers nothing, so a URL is a reproducible view. Reload it, bookmark it,
|
|
691
835
|
or mail it to someone, and what they see is what the sender saw.
|
|
692
836
|
|
|
693
|
-
A page declares what it shows, its queries, and what it allows, its
|
|
694
|
-
Loading a page runs its queries. A request
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
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.
|
|
698
842
|
|
|
699
843
|
That last is where query parms move Loadbare's position. A query still takes
|
|
700
844
|
no argument from the browser, and a parm arrives the way the session cookie
|
|
701
845
|
does, as part of the request the context is built from. But a user who
|
|
702
|
-
types `?acct=99999`
|
|
703
|
-
|
|
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
|
|
704
848
|
per-session database role means an id outside the caller's reach finds
|
|
705
849
|
nothing.
|
|
706
850
|
|
|
707
851
|
Loadbare is for applications, not sites. Every route is answered with the
|
|
708
852
|
same document, and a path that names no page is found out in the browser —
|
|
709
|
-
see [
|
|
853
|
+
see [An unknown page](#an-unknown-page).
|
|
710
854
|
|
|
711
|
-
####
|
|
855
|
+
#### Links
|
|
712
856
|
|
|
713
|
-
|
|
714
|
-
|
|
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.
|
|
715
863
|
|
|
716
864
|
```html
|
|
717
|
-
<
|
|
718
|
-
<
|
|
719
|
-
<
|
|
720
|
-
</
|
|
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>
|
|
721
870
|
```
|
|
722
871
|
|
|
723
|
-
|
|
724
|
-
| -------------------- | ---------- | --------------------------------------------------------------- |
|
|
725
|
-
| `lb-query-parm` | Developer | On a control: its `change` writes this parm and reloads the page |
|
|
726
|
-
| `lb-query-parm-push` | Developer | With `lb-query-parm`: the write pushes a history entry |
|
|
872
|
+
The attribute takes no value.
|
|
727
873
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
it may have arrived by link with no control on screen to say it again. An
|
|
731
|
-
empty value takes the parm out, so a URL is as long as the user has narrowed
|
|
732
|
-
the page. A value the URL already carries does nothing.
|
|
874
|
+
Path space is flat. A path such as `/members` links to the `members.*` files
|
|
875
|
+
on the server.
|
|
733
876
|
|
|
734
|
-
The
|
|
735
|
-
|
|
736
|
-
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.
|
|
737
879
|
|
|
738
|
-
The
|
|
739
|
-
|
|
740
|
-
lands by key, so a list that gets its rows back keeps them and its scroll
|
|
741
|
-
position.
|
|
880
|
+
The browser's Back and Forward load the page at the URL they arrive at, the
|
|
881
|
+
same way.
|
|
742
882
|
|
|
743
|
-
|
|
744
|
-
parm on the control that writes it, and an absent parm lands empty. A
|
|
745
|
-
control therefore shows what the address bar says.
|
|
883
|
+
#### Query parms
|
|
746
884
|
|
|
747
|
-
A control that
|
|
748
|
-
|
|
749
|
-
|
|
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"`:
|
|
750
888
|
|
|
751
|
-
|
|
752
|
-
|
|
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
|
+
```
|
|
897
|
+
|
|
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.
|
|
904
|
+
|
|
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.
|
|
909
|
+
|
|
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.
|
|
912
|
+
|
|
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.
|
|
919
|
+
|
|
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
|
|
753
922
|
[The Express server](#the-express-server). Nothing in Loadbare assigns a
|
|
754
923
|
parm a meaning.
|
|
755
924
|
|
|
756
|
-
####
|
|
925
|
+
#### An unknown page
|
|
926
|
+
|
|
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.
|
|
931
|
+
|
|
932
|
+
`lb-url` lands before the page is looked up, so the dialog shows it by
|
|
933
|
+
naming it like any other element:
|
|
934
|
+
|
|
935
|
+
```html
|
|
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
|
|
757
946
|
|
|
758
947
|
Some query parms can only be known once a write has run. After an insert,
|
|
759
948
|
the key of the new row exists only on the server. After a delete, only the
|
|
@@ -761,75 +950,66 @@ server knows that the row the page was showing is gone.
|
|
|
761
950
|
|
|
762
951
|
The usual web answer is Post/Redirect/Get: the server answers the write with
|
|
763
952
|
a redirect to a URL naming the result, and the browser makes a second request
|
|
764
|
-
to load it. Loadbare/app does the same work in one round trip
|
|
765
|
-
changes the path.
|
|
953
|
+
to load it. Loadbare/app does the same work in one round trip.
|
|
766
954
|
|
|
767
|
-
A
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
replacing the history entry, and then lands the load.
|
|
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.
|
|
772
959
|
|
|
773
960
|
```ts
|
|
774
961
|
rowInsert: {
|
|
775
962
|
run: async (ctx, { values }) => {
|
|
776
963
|
const id = await ctx.db.addAccount(values);
|
|
777
|
-
return
|
|
964
|
+
return url({ acct: String(id) });
|
|
778
965
|
},
|
|
779
966
|
refresh: [],
|
|
780
967
|
},
|
|
781
968
|
```
|
|
782
969
|
|
|
783
|
-
-
|
|
784
|
-
|
|
785
|
-
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
- The
|
|
790
|
-
|
|
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.
|
|
791
980
|
- If loading the page fails, the write has still happened. The response
|
|
792
|
-
carries the
|
|
793
|
-
there is a page load's, and is not stamped on the element that
|
|
794
|
-
request.
|
|
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.
|
|
795
984
|
- A user who has left the page by the time the response arrives keeps the
|
|
796
985
|
URL they are on.
|
|
797
986
|
|
|
798
987
|
The page is loaded with a second context, built by `contextFor` from the new
|
|
799
988
|
parms — see [The Express server](#the-express-server).
|
|
800
989
|
|
|
801
|
-
###
|
|
802
|
-
|
|
803
|
-
---- UNEDITED ----
|
|
804
|
-
|
|
805
|
-
Status: 1.0-RC.
|
|
806
|
-
|
|
807
|
-
Current page, published by the hub as a row. Can be bound anywhere just
|
|
808
|
-
like a server-produced result.
|
|
809
|
-
|
|
810
|
-
| Cell | Holds |
|
|
811
|
-
| ------------ | ------------------------------------------------------------------- |
|
|
812
|
-
| `page-label` | The text of the link to the current page, empty if no link names it |
|
|
813
|
-
| `page-uri` | The path and query string as the browser has them, such as `/members?team=Engines` |
|
|
814
|
-
|
|
815
|
-
The `page-uri` is taken from the current URL. The `page-label` is taken from the first
|
|
816
|
-
`lb-nav-link` link in the document (presumably in a nav bar) whose path matches the
|
|
817
|
-
current path, so the query string does not change it.
|
|
990
|
+
### The markup checks
|
|
818
991
|
|
|
819
|
-
The
|
|
820
|
-
|
|
821
|
-
it by naming the row like any other subtree.
|
|
992
|
+
The builder checks the chrome and every page, after expansion, and refuses
|
|
993
|
+
to build on any of these:
|
|
822
994
|
|
|
823
|
-
|
|
824
|
-
|
|
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.
|
|
825
1008
|
|
|
826
|
-
|
|
827
|
-
<header lb-row="lb-navigation">
|
|
828
|
-
<h2 lb-cell="page-label"></h2>
|
|
829
|
-
</header>
|
|
830
|
-
```
|
|
1009
|
+
`lb-column` with no ancestor row is allowed: the hub gathers from it.
|
|
831
1010
|
|
|
832
|
-
|
|
1011
|
+
Script never assigns an `lb-` attribute, so the markup states every one
|
|
1012
|
+
that exists, and checking the markup is sound.
|
|
833
1013
|
|
|
834
1014
|
## Application files
|
|
835
1015
|
|
|
@@ -849,10 +1029,9 @@ The file is an HTML document, which must contain:
|
|
|
849
1029
|
|
|
850
1030
|
It may also contain:
|
|
851
1031
|
- `/app.css`, the stylesheet bundle
|
|
852
|
-
- A `<dialog
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
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.
|
|
856
1035
|
- The `hidden` attribute on `<body>`, which the hub removes once the first page has landed.
|
|
857
1036
|
|
|
858
1037
|
```html
|
|
@@ -867,52 +1046,66 @@ It may also contain:
|
|
|
867
1046
|
</head>
|
|
868
1047
|
<body hidden>
|
|
869
1048
|
<lb-hub>
|
|
870
|
-
<header lb-
|
|
1049
|
+
<header lb-query="lb-url">
|
|
871
1050
|
<h1>Membership Roster</h1>
|
|
872
|
-
<h2 lb-
|
|
1051
|
+
<h2 lb-column="lb-page-label"></h2>
|
|
873
1052
|
</header>
|
|
874
1053
|
<nav>
|
|
875
|
-
<a href="/" lb-
|
|
876
|
-
<a href="/members" lb-
|
|
1054
|
+
<a href="/" lb-url-link>Home</a>
|
|
1055
|
+
<a href="/members" lb-url-link>Members</a>
|
|
877
1056
|
<a href="https://example.org/">Our website</a>
|
|
878
1057
|
</nav>
|
|
879
1058
|
<main></main>
|
|
880
|
-
<dialog lb-unknown
|
|
881
|
-
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.
|
|
882
1061
|
</dialog>
|
|
883
1062
|
</lb-hub>
|
|
884
1063
|
</body>
|
|
885
1064
|
</html>
|
|
886
1065
|
```
|
|
887
1066
|
|
|
888
|
-
The
|
|
889
|
-
|
|
890
|
-
[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).
|
|
891
1069
|
|
|
892
1070
|
### Pages
|
|
893
1071
|
|
|
894
1072
|
The page namespace is flat. Loadbare/app does not care where in the `--src`
|
|
895
1073
|
the page files are located, but they must be unique across the application.
|
|
896
|
-
A page `/deep/path/to/mypage.html` is routed to `/mypage`.
|
|
1074
|
+
A page `/deep/path/to/mypage.page.html` is routed to `/mypage`.
|
|
897
1075
|
|
|
898
1076
|
Pages are grouped as
|
|
899
|
-
-
|
|
1077
|
+
- `<stub>.page.html` is recognized as navigable and capable of
|
|
900
1078
|
having associated queries and requests
|
|
901
|
-
-
|
|
902
|
-
-
|
|
1079
|
+
- `<stub>.queries.ts` are the queries for a page
|
|
1080
|
+
- `<stub>.requests.ts` respond to the requests from the browser
|
|
903
1081
|
|
|
904
|
-
The
|
|
905
|
-
so that `<a href=
|
|
906
|
-
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.
|
|
907
1085
|
|
|
908
1086
|
#### Page HTML
|
|
909
1087
|
|
|
910
1088
|
The markup in `<stub>.page.html` is the same HTML as everywhere else
|
|
911
1089
|
in a Loadbare/app application, see [HTML](#html).
|
|
912
1090
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
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>` |
|
|
916
1109
|
|
|
917
1110
|
## The server
|
|
918
1111
|
|
|
@@ -976,7 +1169,7 @@ Explaining Express is beyond the scope of this technical reference. The
|
|
|
976
1169
|
only real requirement is that the catch-all for app.html is at the end,
|
|
977
1170
|
so it does not catch any other files.
|
|
978
1171
|
|
|
979
|
-
`hubRoutes` answers `POST /lb/<
|
|
1172
|
+
`hubRoutes` answers `POST /lb/<stub>`, with the query string the browser is
|
|
980
1173
|
showing after it, verbatim. It hands `contextFor` that query string's parms
|
|
981
1174
|
as a second argument, and `contextFor` puts on the context whatever a query
|
|
982
1175
|
reads from them. They are user input:
|
|
@@ -988,7 +1181,7 @@ function contextFor(req: Request, parms: URLSearchParams): HubContext {
|
|
|
988
1181
|
```
|
|
989
1182
|
|
|
990
1183
|
Read the parms from that argument, not from `req.query`. After a request
|
|
991
|
-
whose
|
|
1184
|
+
whose handler returned `url()`, `contextFor` is called a second time for
|
|
992
1185
|
the same request, with the new parms, to load the page at them. So it must
|
|
993
1186
|
be safe to call twice, and a write must be visible to the second context by
|
|
994
1187
|
the time its `run` returns. A handle opened per request without a
|
|
@@ -1002,28 +1195,37 @@ the whole path space through unchanged.
|
|
|
1002
1195
|
### Page Queries
|
|
1003
1196
|
|
|
1004
1197
|
`<stub>.queries.ts` exports one object named `queries`, typed `Queries`.
|
|
1005
|
-
Each key is a query name,
|
|
1006
|
-
`lb-list` or `lb-row`.
|
|
1198
|
+
Each key is a query name, which the markup names with `lb-query`.
|
|
1007
1199
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
Queries names cannot begin with `lb-`, that namespace is reserved for
|
|
1013
|
-
Loadbare/app queries the hub makes available in the browser, such
|
|
1014
|
-
as [lb-navigation](#lb-navigation).
|
|
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.
|
|
1015
1204
|
|
|
1016
1205
|
```ts
|
|
1017
1206
|
// members.queries.ts
|
|
1018
|
-
import {
|
|
1207
|
+
import { row, rows, type Queries } from "@loadbare/app/server";
|
|
1019
1208
|
|
|
1020
1209
|
export const queries: Queries = {
|
|
1021
|
-
roster:
|
|
1022
|
-
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
|
+
})),
|
|
1023
1215
|
};
|
|
1024
1216
|
```
|
|
1025
1217
|
|
|
1026
|
-
|
|
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
|
|
1027
1229
|
context. The application builds it once per request and hands it to the hub,
|
|
1028
1230
|
see [The Express server](#the-express-server). Loadbare/app declares it empty
|
|
1029
1231
|
and never reads it, so a query finds exactly what the application put there,
|
|
@@ -1039,36 +1241,32 @@ It has three optional keys.
|
|
|
1039
1241
|
|
|
1040
1242
|
| Key | Keyed by | Answers |
|
|
1041
1243
|
| ------------- | --------------------- | -------------------------------------------------- |
|
|
1042
|
-
| `
|
|
1043
|
-
| `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 |
|
|
1044
1246
|
| `onPageEnter` | Nothing | Runs once on entering the page, before its queries |
|
|
1045
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.
|
|
1046
1254
|
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
there logs a server console warning naming the page and the missing entry,
|
|
1050
|
-
and answers 200 with `{}`. The browser applies `{}`, so no cell is set and
|
|
1051
|
-
no row is added or removed, and `lb-pending` clears from the element that
|
|
1052
|
-
sent the request. A request carrying no action answers 400, and a `run`
|
|
1053
|
-
that throws answers 500.
|
|
1054
|
-
|
|
1055
|
-
Every entry under `actions` and `crud` has the same two members. `run`
|
|
1056
|
-
performs the work, and `refresh` names the queries to re-run once the action
|
|
1057
|
-
is complete.
|
|
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.
|
|
1058
1257
|
|
|
1059
|
-
`run` receives the same `ctx` and
|
|
1060
|
-
|
|
1061
|
-
`crud` the `where` carries only what that operation is typed to carry.
|
|
1258
|
+
`run` receives the same `ctx` and the request less its name: `query`, `key`
|
|
1259
|
+
and `values`, as present.
|
|
1062
1260
|
|
|
1063
|
-
`run` may also return
|
|
1064
|
-
ones. That is how a delta reaches the browser: wrap it in
|
|
1065
|
-
the rows that arrived or changed and the keys that went.
|
|
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.
|
|
1066
1264
|
|
|
1067
|
-
`run` may instead return `
|
|
1068
|
-
can know, such as the key of a row it inserted. The page
|
|
1069
|
-
in the same round trip, in place of the refresh set — see
|
|
1070
|
-
[When a server response
|
|
1071
|
-
A `rowDelete` that removes the row on screen returns `
|
|
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: "" })`
|
|
1072
1270
|
and leaves any other delete to its refresh set.
|
|
1073
1271
|
|
|
1074
1272
|
```ts
|
|
@@ -1076,7 +1274,7 @@ and leaves any other delete to its refresh set.
|
|
|
1076
1274
|
import { patch, type Requests } from "@loadbare/app/server";
|
|
1077
1275
|
|
|
1078
1276
|
export const requests: Requests = {
|
|
1079
|
-
|
|
1277
|
+
handlers: {
|
|
1080
1278
|
resetRoster: {
|
|
1081
1279
|
run: (ctx) => ctx.db.resetMembers(),
|
|
1082
1280
|
refresh: ["roster"],
|
|
@@ -1085,9 +1283,9 @@ export const requests: Requests = {
|
|
|
1085
1283
|
crud: {
|
|
1086
1284
|
roster: {
|
|
1087
1285
|
rowDelete: {
|
|
1088
|
-
run: async (ctx,
|
|
1089
|
-
await ctx.db.deleteMember(
|
|
1090
|
-
return { roster: patch({ drop: [
|
|
1286
|
+
run: async (ctx, { key }) => {
|
|
1287
|
+
await ctx.db.deleteMember(key);
|
|
1288
|
+
return { roster: patch({ drop: [key] }) };
|
|
1091
1289
|
},
|
|
1092
1290
|
refresh: [],
|
|
1093
1291
|
},
|
|
@@ -1096,86 +1294,118 @@ export const requests: Requests = {
|
|
|
1096
1294
|
};
|
|
1097
1295
|
```
|
|
1098
1296
|
|
|
1099
|
-
Each key under a `crud` entry is a
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
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.
|
|
1103
1300
|
|
|
1104
|
-
|
|
|
1105
|
-
|
|
|
1106
|
-
| `lb-row-
|
|
1107
|
-
| `lb-row-
|
|
1108
|
-
| `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` |
|
|
1109
1306
|
|
|
1110
1307
|
A `rowUpdate` sets the columns `values` names and leaves the rest as they
|
|
1111
|
-
are, as an SQL `UPDATE` does. A form sends the
|
|
1112
|
-
|
|
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.
|
|
1113
1310
|
|
|
1114
|
-
|
|
1311
|
+
### The wire
|
|
1115
1312
|
|
|
1116
1313
|
---- UNEDITED ----
|
|
1117
1314
|
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
'browser' segment is a safety feature, requiring the file to be
|
|
1123
|
-
explicitly named as a browser file, to help prevent unfortunate
|
|
1124
|
-
naming collisions where a server file happens to have the name of
|
|
1125
|
-
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:
|
|
1126
1319
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1320
|
+
```ts
|
|
1321
|
+
interface HubRequest {
|
|
1322
|
+
name: string;
|
|
1323
|
+
query?: string;
|
|
1324
|
+
key?: string;
|
|
1325
|
+
values?: Record<string, string>;
|
|
1326
|
+
}
|
|
1327
|
+
```
|
|
1129
1328
|
|
|
1130
|
-
|
|
1131
|
-
in the builders `--src`:
|
|
1329
|
+
Every response is an array of response items:
|
|
1132
1330
|
|
|
1133
1331
|
```ts
|
|
1134
|
-
|
|
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
|
+
}
|
|
1135
1340
|
```
|
|
1136
1341
|
|
|
1137
|
-
|
|
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
|
|
1138
1351
|
|
|
1139
1352
|
---- UNEDITED ----
|
|
1140
1353
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
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.
|
|
1144
1363
|
|
|
1145
|
-
|
|
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.
|
|
1367
|
+
|
|
1368
|
+
To use custom elements from a widget library, add the library to
|
|
1369
|
+
`imports.ts` anywhere in the builder's `--src`:
|
|
1370
|
+
|
|
1371
|
+
```ts
|
|
1372
|
+
export default ["@scope/library-name"];
|
|
1373
|
+
```
|
|
1374
|
+
|
|
1375
|
+
Import every attribute name from `@loadbare/app/constants` — see
|
|
1376
|
+
[Constants](#constants). Never write one as a string literal.
|
|
1146
1377
|
|
|
1147
|
-
|
|
1148
|
-
widget itself when a widget fired it. A widget observes them and reacts; it
|
|
1149
|
-
must name them in `observedAttributes` to see them change.
|
|
1378
|
+
### Controls
|
|
1150
1379
|
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
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.
|
|
1156
1385
|
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
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
|
+
```
|
|
1160
1392
|
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
click or submit is cancelled and nothing is sent. A pending native action is
|
|
1164
|
-
therefore disabled in fact, and a stylesheet only has to show it. A widget is
|
|
1165
|
-
not held back this way, because a widget that sends on change must have its
|
|
1166
|
-
latest value sent rather than dropped.
|
|
1393
|
+
A custom element that is not a control receives `lb-column-value` and
|
|
1394
|
+
renders it; its content is never replaced.
|
|
1167
1395
|
|
|
1168
|
-
|
|
1169
|
-
removes it when the round trip settles, so assistive technology hears the
|
|
1170
|
-
state a stylesheet shows.
|
|
1396
|
+
### Row hooks
|
|
1171
1397
|
|
|
1172
|
-
|
|
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 |
|
|
1173
1402
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
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.
|
|
1179
1409
|
|
|
1180
1410
|
## Internal linkage
|
|
1181
1411
|
|
|
@@ -1209,16 +1439,16 @@ Loadbare/app owns every file name and pattern in this table. A file so
|
|
|
1209
1439
|
named carries its meaning wherever it sits in the `--src` tree, so an
|
|
1210
1440
|
application must not use one of these names for anything else.
|
|
1211
1441
|
|
|
1212
|
-
| File | How many
|
|
1213
|
-
| ----------------------- |
|
|
1214
|
-
| `chrome.html` | Exactly one
|
|
1215
|
-
| `imports.ts` | Zero or one
|
|
1216
|
-
| `<stub>.page.html` | One per page
|
|
1217
|
-
| `<stub>.queries.ts` | Zero or one per page
|
|
1218
|
-
| `<stub>.requests.ts` | Zero or one per page
|
|
1219
|
-
| `<tag-name>.html` | Zero or one per
|
|
1220
|
-
| `<tag-name>.browser.ts` | Zero or one per
|
|
1221
|
-
| `*.css` | Any number
|
|
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) |
|
|
1222
1452
|
|
|
1223
1453
|
### Build outputs
|
|
1224
1454
|
|
|
@@ -1230,7 +1460,7 @@ only when some origin has a stylesheet, `pages.ts` only when some page has a
|
|
|
1230
1460
|
| Path | Written | Holds | Read by | Defined in |
|
|
1231
1461
|
| ------------------ | ----------------- | ------------------------------------------------------------------ | ---------- | ------------------------------------------- |
|
|
1232
1462
|
| `/app.html` | Always | The chrome, built | The server | [Chrome](#chrome) |
|
|
1233
|
-
| `/client.js` | Always | `<lb-hub>` and every
|
|
1463
|
+
| `/client.js` | Always | `<lb-hub>` and every custom element the application uses | The chrome | [Chrome](#chrome) |
|
|
1234
1464
|
| `/client-entry.ts` | Always | The generated entry `client.js` is bundled from | Nothing | [Running the builder](#running-the-builder) |
|
|
1235
1465
|
| `/app.css` | With a stylesheet | Every stylesheet in the cascade, concatenated | The chrome | [Chrome](#chrome) |
|
|
1236
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) |
|
|
@@ -1240,57 +1470,64 @@ only when some origin has a stylesheet, `pages.ts` only when some page has a
|
|
|
1240
1470
|
Loadbare/app owns every key in this table. A widget library declares them.
|
|
1241
1471
|
An application never does.
|
|
1242
1472
|
|
|
1243
|
-
| Key | Value | Means
|
|
1244
|
-
| ------------------ | ------------------------- |
|
|
1245
|
-
| `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) |
|
|
1246
1476
|
|
|
1247
1477
|
### Reserved namespaces
|
|
1248
1478
|
|
|
1249
1479
|
Loadbare/app owns every namespace in this table. Ownership of a name and
|
|
1250
1480
|
ownership of its behavior are stated separately, because they differ.
|
|
1251
1481
|
|
|
1252
|
-
| Namespace | Applies to
|
|
1253
|
-
| --------- |
|
|
1254
|
-
| `lb-*` |
|
|
1255
|
-
| `exp-*` |
|
|
1256
|
-
| `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) |
|
|
1257
1487
|
|
|
1258
1488
|
#### The lb-* namespace
|
|
1259
1489
|
|
|
1260
|
-
Every
|
|
1261
|
-
application writes the
|
|
1262
|
-
because a name Loadbare has not defined today
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
|
1271
|
-
|
|
|
1272
|
-
| `lb-
|
|
1273
|
-
| `lb-
|
|
1274
|
-
| `lb-
|
|
1275
|
-
| `lb-
|
|
1276
|
-
| `lb-
|
|
1277
|
-
| `lb-key-value` | Hub | [
|
|
1278
|
-
| `lb-
|
|
1279
|
-
| `lb-
|
|
1280
|
-
| `lb-
|
|
1281
|
-
| `lb-
|
|
1282
|
-
| `lb-
|
|
1283
|
-
| `lb-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
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) |
|
|
1294
1531
|
|
|
1295
1532
|
### Reserved tags
|
|
1296
1533
|
|
|
@@ -1306,16 +1543,53 @@ never defines them.
|
|
|
1306
1543
|
Loadbare/app owns every DOM event in this table, both the name and what its
|
|
1307
1544
|
`detail` carries.
|
|
1308
1545
|
|
|
1309
|
-
| Event | Dispatched from
|
|
1310
|
-
| ------------ |
|
|
1311
|
-
| `lb-request` | The element that
|
|
1312
|
-
|
|
1313
|
-
### Reserved
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
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` |
|