@loadbare/app 0.18.1 → 0.19.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 +17 -17
- package/dist/build/assemble.d.ts +7 -1
- package/dist/build/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +81 -11
- package/dist/build/assemble.js.map +1 -1
- package/dist/build/cli.d.ts +3 -0
- package/dist/build/cli.d.ts.map +1 -1
- package/dist/build/cli.js +6 -1
- package/dist/build/cli.js.map +1 -1
- package/dist/build/declared.d.ts +25 -0
- package/dist/build/declared.d.ts.map +1 -0
- package/dist/build/declared.js +62 -0
- package/dist/build/declared.js.map +1 -0
- package/dist/build/pages.d.ts +6 -0
- package/dist/build/pages.d.ts.map +1 -1
- package/dist/build/pages.js +32 -12
- package/dist/build/pages.js.map +1 -1
- package/dist/core/lb-types.d.ts +6 -3
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js.map +1 -1
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +10 -3
- package/dist/hub/lb-apply.js.map +1 -1
- package/dist/server/lb-server.d.ts +5 -2
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +12 -3
- package/dist/server/lb-server.js.map +1 -1
- package/docs/TECHREF-1.0.md +33 -85
- package/docs/comparison.md +695 -482
- package/docs/departures.md +63 -0
- package/docs/miswarts.md +140 -0
- package/docs/opinions.md +155 -0
- package/docs/reference/builder.md +9 -2
- package/docs/reference/chrome.md +2 -1
- package/docs/reference/css.md +163 -0
- package/docs/reference/custom-elements.md +1 -1
- package/docs/reference/data-binding.md +44 -10
- package/docs/reference/page-files.md +38 -5
- package/docs/reference/widgets.md +24 -2
- package/docs/roadmap.md +55 -43
- package/docs/testing.md +62 -28
- package/docs/theory.md +348 -108
- package/package.json +1 -1
- package/skills/loadbare-app/SKILL.md +16 -9
- package/skills/loadbare-app/references/TECHREF-1.0.md +33 -85
- package/skills/loadbare-app/references/builder.md +9 -2
- package/skills/loadbare-app/references/chrome.md +2 -1
- package/skills/loadbare-app/references/css.md +163 -0
- package/skills/loadbare-app/references/custom-elements.md +1 -1
- package/skills/loadbare-app/references/data-binding.md +44 -10
- package/skills/loadbare-app/references/miswarts.md +140 -0
- package/skills/loadbare-app/references/page-files.md +38 -5
- package/skills/loadbare-app/references/widgets.md +24 -2
- package/docs/possible-ideas.md +0 -314
- package/docs/theory-old.md +0 -839
- package/docs/what-does-loadbare-extend.md +0 -124
|
@@ -5,79 +5,6 @@ This is the complete technical reference to the upcoming Release 1.0.
|
|
|
5
5
|
Method-of-work is to continually revise this document to what we want
|
|
6
6
|
to be true, then revise the code, docs, and tests to ensure it is true.
|
|
7
7
|
|
|
8
|
-
## Blockers
|
|
9
|
-
|
|
10
|
-
We cannot declare 1.0 until we determine if these blockers can be
|
|
11
|
-
added later w/o breaking changes. If we are reasonably confident
|
|
12
|
-
that they can be decided later w/o breaking changes, then we rename
|
|
13
|
-
them to "Out of Scope" or move them to the roadmap. Otherwise,
|
|
14
|
-
they must be resolved before we declare 1.0.
|
|
15
|
-
|
|
16
|
-
### Data types
|
|
17
|
-
|
|
18
|
-
A value renders by browser coercion when it lands on an HTML text element or
|
|
19
|
-
a control, and by whatever the custom element does with `lb-column-value`
|
|
20
|
-
when it lands on one that is not a control. A `rows` query may declare a
|
|
21
|
-
column's type, which the [order](#order), a [filter](#filters), `lb-min` and
|
|
22
|
-
`lb-max` read.
|
|
23
|
-
|
|
24
|
-
- **Decide who formats a value.** The hub could own formatting, or take it
|
|
25
|
-
from the application as configuration or as code.
|
|
26
|
-
|
|
27
|
-
### Nested queries
|
|
28
|
-
|
|
29
|
-
Imagine an HTML `<select>` that shows a fixed set of choices on every row of
|
|
30
|
-
a table, such as `customer_type` for a table of customers. When every
|
|
31
|
-
customer may take every customer type, the system as written today is
|
|
32
|
-
fine, because every `<select>` receives the same rows.
|
|
33
|
-
|
|
34
|
-
But the allowed values may depend on other values in the row.
|
|
35
|
-
|
|
36
|
-
- **Decide whether a nested query's rows may depend on its row.** A nested
|
|
37
|
-
query has one answer, which every live row receives. See
|
|
38
|
-
[Master-detail](#master-detail) for what a nested query is for.
|
|
39
|
-
|
|
40
|
-
```html
|
|
41
|
-
<tbody lb-query="accounts">
|
|
42
|
-
<template><tr>
|
|
43
|
-
<td lb-column="name"></td>
|
|
44
|
-
<td><select lb-query="statuses" lb-column="status"><template><option lb-column="label"></option></template></select></td>
|
|
45
|
-
</tr></template>
|
|
46
|
-
</tbody>
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### The server API
|
|
50
|
-
|
|
51
|
-
- **Land `staticRoutes`.** It closes the last place where an
|
|
52
|
-
application hand-writes facts the builder owns.
|
|
53
|
-
|
|
54
|
-
### The builder
|
|
55
|
-
|
|
56
|
-
- **Adopt a configuration file and fold `imports.ts` into it.** It holds
|
|
57
|
-
`out`, `minify` and `imports`, and lives where `imports.ts` lives now, so a
|
|
58
|
-
repository with several applications gets one per application. `--src`
|
|
59
|
-
stays a flag, since it is what locates the file. State the precedence
|
|
60
|
-
between a flag and a key once. Its key names join the permanent surface,
|
|
61
|
-
so this lands before 1.0 or not at all. It reserves a key for the
|
|
62
|
-
roadmap's tree-shaking CSS by tag name.
|
|
63
|
-
- **Confirm the three output names.** `app.html`, `client.js` and `app.css`
|
|
64
|
-
are about to be fixed in `staticRoutes` as well as in the builder.
|
|
65
|
-
|
|
66
|
-
### The internal surface
|
|
67
|
-
|
|
68
|
-
None of this is surface. It is listed here because trimming it is free
|
|
69
|
-
today and breaking after 1.0.
|
|
70
|
-
|
|
71
|
-
- **Audit what the hub does.** Every routine it runs answers to an `lb-*`
|
|
72
|
-
attribute, a [reserved query](#reserved-queries), or a
|
|
73
|
-
[reserved event](#reserved-events) or [method](#reserved-methods). Trim
|
|
74
|
-
whatever does not, and leave what stays clean: no dead code, no routine
|
|
75
|
-
written twice, no comment the code contradicts.
|
|
76
|
-
- **Keep the rest as constants.** The endpoint, `index` and the timeout are
|
|
77
|
-
linkage between the builder and the hub rather than facts an application
|
|
78
|
-
varies. Adding a knob for any of them buys a second shape of application
|
|
79
|
-
to reason about.
|
|
80
|
-
|
|
81
8
|
## The build
|
|
82
9
|
|
|
83
10
|
### Running the builder
|
|
@@ -184,8 +111,8 @@ it is sent to the browser on initial page load, no HTML is ever sent
|
|
|
184
111
|
again.
|
|
185
112
|
|
|
186
113
|
The developer writes `lb-*` attributes in markup. Script never assigns
|
|
187
|
-
them. The hub and the builder write only their stamps,
|
|
188
|
-
|
|
114
|
+
them. The hub and the builder write only their stamps, apart from the
|
|
115
|
+
few exceptions listed in [Miswarts](./miswarts.md). See
|
|
189
116
|
[The markup checks](#the-markup-checks).
|
|
190
117
|
|
|
191
118
|
### Expansion
|
|
@@ -483,6 +410,10 @@ landed inside another query, such as a total in a table's foot, is not one
|
|
|
483
410
|
of that query's rows. A stylesheet or a custom element selects live rows
|
|
484
411
|
with `[lb-row-live]`, and never with `[lb-key-value]`.
|
|
485
412
|
|
|
413
|
+
A `row` query answering `null` lands every column its element names as
|
|
414
|
+
null, and the element loses `lb-key-value`. Under a row template, its live
|
|
415
|
+
row leaves.
|
|
416
|
+
|
|
486
417
|
A key is unique within a query. Two rows with one key in the same answer,
|
|
487
418
|
or two live rows showing one key, are reported on the console.
|
|
488
419
|
|
|
@@ -1373,10 +1304,10 @@ new URL carries only the query parms the request names.
|
|
|
1373
1304
|
|
|
1374
1305
|
Every load lands `lb-url`, and a parm the URL does not carry lands empty on
|
|
1375
1306
|
every element naming it as a column, so a control shows what the address
|
|
1376
|
-
bar says after Back as well as after a reload
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1307
|
+
bar says after Back as well as after a reload
|
|
1308
|
+
([miswart](./miswarts.md#lb-url-lands-every-parm-it-names)): the hub reads the
|
|
1309
|
+
column names under `lb-query="lb-url"` and names each one in the row it
|
|
1310
|
+
lands.
|
|
1380
1311
|
|
|
1381
1312
|
The hub sends the query parms with every round trip, a page load and a
|
|
1382
1313
|
request alike. The server passes them to `contextFor` — see
|
|
@@ -1525,6 +1456,15 @@ to build on any of these:
|
|
|
1525
1456
|
reach the check.
|
|
1526
1457
|
- `lb-request` naming a value that begins with `lb-` and is not one of the
|
|
1527
1458
|
three request names.
|
|
1459
|
+
- `lb-query` naming a query the page does not declare. `lb-url` and
|
|
1460
|
+
`lb-choices` are the hub's, and every page and the chrome may name them.
|
|
1461
|
+
The chrome belongs to no page, and names no other query.
|
|
1462
|
+
- `lb-request` naming a request the page does not declare under `handlers`.
|
|
1463
|
+
The chrome sends none of them.
|
|
1464
|
+
- One of the three request names for a query whose `crud` entry does not
|
|
1465
|
+
declare it, and any but `lb-row-update` for `lb-url` or `lb-choices`. The
|
|
1466
|
+
query is a form's own when the form carries `lb-query`, and otherwise the
|
|
1467
|
+
nearest `lb-query` around the element.
|
|
1528
1468
|
- `lb-url-link` on anything but an `<a>`.
|
|
1529
1469
|
- `lb-url-push` on an element with no `lb-request`.
|
|
1530
1470
|
- `lb-url-unknown` on anything but a `<dialog>`, and in the chrome, outside
|
|
@@ -1554,6 +1494,10 @@ whose name begins with `lb` and a capital and is not in
|
|
|
1554
1494
|
|
|
1555
1495
|
`lb-column` with no ancestor row is allowed: the hub gathers from it.
|
|
1556
1496
|
|
|
1497
|
+
To know what each page declares, the builder loads the page's
|
|
1498
|
+
`.queries.ts` and `.requests.ts` modules, so their import-time code runs
|
|
1499
|
+
during the build, as it does when the server starts.
|
|
1500
|
+
|
|
1557
1501
|
Script never assigns an `lb-` attribute, so the markup states every one
|
|
1558
1502
|
that exists, and checking the markup is sound.
|
|
1559
1503
|
|
|
@@ -1566,7 +1510,8 @@ uncommon, and Loadbare/app does not shape itself around them.
|
|
|
1566
1510
|
by `name`, and a row template repeats one `name` in every live row. The
|
|
1567
1511
|
hub stamps each radio button's `name` with the `name` written, or its
|
|
1568
1512
|
column when none is, followed by `-` and the live row's key, so each live
|
|
1569
|
-
row holds a group of its own
|
|
1513
|
+
row holds a group of its own
|
|
1514
|
+
([miswart](./miswarts.md#radio-button-names-in-a-row-template)).
|
|
1570
1515
|
- **Several rows not yet entered, each holding radio buttons.** A
|
|
1571
1516
|
[row not yet entered](#a-row-not-yet-entered) has an empty key, so the
|
|
1572
1517
|
radio buttons of every such row share one `name` and form one group.
|
|
@@ -1795,8 +1740,8 @@ export const queries: Queries = {
|
|
|
1795
1740
|
};
|
|
1796
1741
|
```
|
|
1797
1742
|
|
|
1798
|
-
A `row` query returns one object,
|
|
1799
|
-
objects, which the page shows in that order when the query declares none. An answer that disagrees with
|
|
1743
|
+
A `row` query returns one object, or `null` when it finds no row, and a
|
|
1744
|
+
`rows` query returns an array of objects, which the page shows in that order when the query declares none. An answer that disagrees with
|
|
1800
1745
|
its declared kind is left out of the response, with a warning naming the
|
|
1801
1746
|
query.
|
|
1802
1747
|
|
|
@@ -1828,9 +1773,12 @@ It has four optional keys.
|
|
|
1828
1773
|
| `onChoices` | Nothing | Runs with every choice made on the page, each time the user makes one |
|
|
1829
1774
|
|
|
1830
1775
|
The server resolves a declared request against `handlers`, and a request
|
|
1831
|
-
Loadbare provides against `crud` under the request's query.
|
|
1832
|
-
|
|
1833
|
-
|
|
1776
|
+
Loadbare provides against `crud` under the request's query. The builder
|
|
1777
|
+
refuses markup naming a request the page does not declare; see
|
|
1778
|
+
[The markup checks](#the-markup-checks). A name with no entry there can
|
|
1779
|
+
still arrive, from a widget's script or from outside the page, and it
|
|
1780
|
+
logs a server console warning naming the page and the missing entry, and
|
|
1781
|
+
answers 200 with no response items, so nothing lands and
|
|
1834
1782
|
`lb-request-pending` clears from the element that issued the request. A
|
|
1835
1783
|
request carrying no name answers 400, and a `run` that throws answers 500.
|
|
1836
1784
|
|
|
@@ -128,11 +128,18 @@ substitution rules — wraps each expanded page in
|
|
|
128
128
|
`<template lb-page="<stub>">`, and splices them into the chrome's `<body>`.
|
|
129
129
|
It removes a page file's `<title>` and stamps its text on the page's
|
|
130
130
|
template as `lb-page-title`; see [The page title](./page-files.md#the-page-title).
|
|
131
|
-
It formats the result with Prettier
|
|
131
|
+
It formats the result with Prettier, which ships with `@loadbare/app`, and
|
|
132
|
+
keeps the unformatted result if Prettier fails.
|
|
132
133
|
|
|
133
134
|
The builder checks the `lb-` attributes of the chrome and every page after
|
|
134
135
|
expansion, and stops on the first file with a problem; see
|
|
135
|
-
[The markup checks](./data-binding.md#the-markup-checks).
|
|
136
|
+
[The markup checks](./data-binding.md#the-markup-checks). The names
|
|
137
|
+
`lb-query` and `lb-request` carry are checked against what each page
|
|
138
|
+
declares, which the builder reads by loading the page's `.queries.ts` and
|
|
139
|
+
`.requests.ts` modules. Their import-time code runs during the build, as it
|
|
140
|
+
does when the server starts, so a module that connects to a database at
|
|
141
|
+
import connects during the build too; open connections in `contextFor`
|
|
142
|
+
instead.
|
|
136
143
|
|
|
137
144
|
The builder writes `app.css` only when it finds a stylesheet, and `pages.ts`
|
|
138
145
|
only when some page has a `.requests.ts` or a `.queries.ts` file. Import `hub`
|
|
@@ -224,7 +224,8 @@ being visibly empty — so it's the whole document that needs to wait, not
|
|
|
224
224
|
just the piece that was empty.
|
|
225
225
|
|
|
226
226
|
The `<noscript>` block is the escape hatch for a visitor with JavaScript
|
|
227
|
-
disabled. `<lb-hub>` is what removes `hidden` from `<body
|
|
227
|
+
disabled. `<lb-hub>` is what removes `hidden` from `<body>`
|
|
228
|
+
([miswart](./miswarts.md#the-hub-unhides-the-body)), so a browser
|
|
228
229
|
that never runs `client.js` would otherwise be stuck looking at a
|
|
229
230
|
permanently blank page. `<noscript>` content is only rendered when scripting
|
|
230
231
|
is off, so the fallback rule only ever applies in exactly that case — it
|
|
@@ -42,3 +42,166 @@ See [`chrome.html`](./chrome.md) for the link, [The Express
|
|
|
42
42
|
server](./server.md) for the route, and
|
|
43
43
|
[`loadbare-app-build`](./builder.md#running-the-builder) for `--minify`.
|
|
44
44
|
</content>
|
|
45
|
+
|
|
46
|
+
## Selecting what the hub stamps
|
|
47
|
+
|
|
48
|
+
The hub tells a stylesheet everything it knows through attributes, so an
|
|
49
|
+
application styles state with selectors and writes no script for it. Each
|
|
50
|
+
stamp is an `lb-` attribute the hub writes; never write one in markup.
|
|
51
|
+
|
|
52
|
+
### A value
|
|
53
|
+
|
|
54
|
+
Every element a column lands on carries the value as `lb-column-value`,
|
|
55
|
+
exactly as the query sent it, and loses it when the value is null. Select
|
|
56
|
+
on the column and the value:
|
|
57
|
+
|
|
58
|
+
```css
|
|
59
|
+
[lb-column="status"][lb-column-value="locked"] { opacity: 0.5; }
|
|
60
|
+
[lb-column="balance"][lb-column-value^="-"] { color: var(--negative); }
|
|
61
|
+
[lb-column="note"]:not([lb-column-value]) { display: none; }
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
A widget that formats its value, such as `lb-number`, keeps the raw value
|
|
65
|
+
in the stamp, so the selector reads the number and not its text. See
|
|
66
|
+
[Where a column lands](./data-binding.md#where-a-column-lands).
|
|
67
|
+
|
|
68
|
+
### A row
|
|
69
|
+
|
|
70
|
+
| Select | For |
|
|
71
|
+
|----------------------|-------------------------------------------------|
|
|
72
|
+
| `[lb-row-live]` | Every live row of a list |
|
|
73
|
+
| `[lb-row-selected]` | A row its `lb-select` checkbox ticks |
|
|
74
|
+
| `[lb-row-editing]` | The row a pointed form is editing |
|
|
75
|
+
| `[lb-key-value="…"]` | One row, by its key |
|
|
76
|
+
| `[hidden]` | A row a filter hides; the browser hides it |
|
|
77
|
+
|
|
78
|
+
An element a `row` query lands on carries `lb-key-value` while the query has
|
|
79
|
+
a row, and loses it when the query answers `null`:
|
|
80
|
+
|
|
81
|
+
```css
|
|
82
|
+
[lb-query="member"]:not([lb-key-value]) .member-missing { display: revert; }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Select live rows with `[lb-row-live]`, never with `[lb-key-value]`: a `row`
|
|
86
|
+
landed inside a list, such as a total in a table's foot, carries a key and
|
|
87
|
+
is not one of the list's rows.
|
|
88
|
+
|
|
89
|
+
```css
|
|
90
|
+
[lb-row-selected] { background: var(--selected); }
|
|
91
|
+
[lb-row-editing] { outline: 2px solid var(--accent); }
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
See [Rows](./data-binding.md#rows), [Selecting rows](./data-binding.md#selecting-rows)
|
|
95
|
+
and [Editing a row in one form](./data-binding.md#editing-a-row-in-one-form).
|
|
96
|
+
|
|
97
|
+
### A group
|
|
98
|
+
|
|
99
|
+
Each top-level element of a group carries `lb-group-live`, the column it
|
|
100
|
+
breaks on as `lb-group-column`, and its value there as `lb-group-value`:
|
|
101
|
+
|
|
102
|
+
```css
|
|
103
|
+
[lb-group-column="category"][lb-group-value="Income"] { color: var(--income); }
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
See [Groups](./data-binding.md#groups).
|
|
107
|
+
|
|
108
|
+
### What just happened to a row
|
|
109
|
+
|
|
110
|
+
The hub stamps what its last landing did, so a stylesheet animates with
|
|
111
|
+
nothing else:
|
|
112
|
+
|
|
113
|
+
| Select | For |
|
|
114
|
+
|--------------------------------|------------------------------------------------|
|
|
115
|
+
| `[lb-row-created]` | A row that arrived |
|
|
116
|
+
| `[lb-row-changed]` | A row whose values changed |
|
|
117
|
+
| `[lb-row-moved]` | A row whose values changed and now stands elsewhere; it carries `lb-row-changed` too |
|
|
118
|
+
| `[lb-row-requested]` | Any of the three, done by this page's request, as `created`, `changed` or `moved` |
|
|
119
|
+
| `[lb-row-leaving]`, `[lb-group-leaving]` | A row or group on its way out; the hub removes it when its animations end |
|
|
120
|
+
|
|
121
|
+
```css
|
|
122
|
+
[lb-row-created] { animation: arrive 200ms; }
|
|
123
|
+
[lb-row-requested="changed"] { animation: flash 600ms; }
|
|
124
|
+
[lb-row-leaving] { animation: depart 200ms forwards; }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
A row stamped again plays its animation again. See
|
|
128
|
+
[What happened to a row](./data-binding.md#what-happened-to-a-row).
|
|
129
|
+
|
|
130
|
+
### How many rows show
|
|
131
|
+
|
|
132
|
+
An element carrying `lb-query` and a row template carries
|
|
133
|
+
`lb-query-row-count`, the number of live rows showing. A row a filter hides
|
|
134
|
+
is not counted.
|
|
135
|
+
|
|
136
|
+
```css
|
|
137
|
+
.roster-empty { display: none; }
|
|
138
|
+
[lb-query-row-count="0"] .roster-empty { display: revert; }
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
See [Counting rows](./data-binding.md#counting-rows).
|
|
142
|
+
|
|
143
|
+
### A request
|
|
144
|
+
|
|
145
|
+
| Select | For |
|
|
146
|
+
|------------------------|---------------------------------------------------------|
|
|
147
|
+
| `[lb-request-pending]` | The element that committed, while its round trip is out; `aria-busy="true"` rides with it |
|
|
148
|
+
| `[lb-request-error]` | The element whose round trip failed, until it commits again |
|
|
149
|
+
| `[lb-request-refused]` | The element whose request the handler refused, holding the message, empty when there is none |
|
|
150
|
+
| `:invalid` | A control a refusal named a column for |
|
|
151
|
+
|
|
152
|
+
The hub ignores a commit on a pending element, so the stylesheet only has to
|
|
153
|
+
show it:
|
|
154
|
+
|
|
155
|
+
```css
|
|
156
|
+
[lb-request-pending] { cursor: progress; opacity: 0.6; }
|
|
157
|
+
[lb-request-error] { outline: 2px solid var(--error); }
|
|
158
|
+
[lb-request-refused]:not([lb-request-refused=""])::after {
|
|
159
|
+
content: attr(lb-request-refused);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
See [Request state](./data-binding.md#request-state) and
|
|
164
|
+
[Refusals](./data-binding.md#refusals).
|
|
165
|
+
|
|
166
|
+
### A form pointed at a row
|
|
167
|
+
|
|
168
|
+
A pointed form's `lb-key-value` says what it is doing:
|
|
169
|
+
|
|
170
|
+
| Select | The form is |
|
|
171
|
+
|------------------------------|--------------------|
|
|
172
|
+
| `form[lb-key-value]:not([lb-key-value=""])` | Editing a row |
|
|
173
|
+
| `form[lb-key-value=""]` | Entering a new row |
|
|
174
|
+
| `form:not([lb-key-value])` | Not in use |
|
|
175
|
+
|
|
176
|
+
```css
|
|
177
|
+
form[lb-key-value=""] .save-existing,
|
|
178
|
+
form:not([lb-key-value=""]) .save-new { display: none; }
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
See [Editing a row in one form](./data-binding.md#editing-a-row-in-one-form).
|
|
182
|
+
|
|
183
|
+
### The user's choices
|
|
184
|
+
|
|
185
|
+
An order or a filter the user chose is a column of `lb-choices`, so the
|
|
186
|
+
element showing it carries it as `lb-column-value`. Style anything else by
|
|
187
|
+
it with `:has()`:
|
|
188
|
+
|
|
189
|
+
```css
|
|
190
|
+
table:has([lb-column="accounts_order"][lb-column-value="-balance"])
|
|
191
|
+
th.balance::after { content: "▼"; }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
See [Choices](./data-binding.md#choices).
|
|
195
|
+
|
|
196
|
+
### Content shown by condition
|
|
197
|
+
|
|
198
|
+
An element `lb-show` turns off is moved into a `<template>`, so no selector
|
|
199
|
+
reaches it and nothing about it needs styling. The `<template>` keeps its
|
|
200
|
+
place, so `:nth-child()` counts it; prefer `:nth-child(… of …)` or a class
|
|
201
|
+
over position. See [Where an absent element is](./data-binding.md#where-an-absent-element-is).
|
|
202
|
+
|
|
203
|
+
### The first load
|
|
204
|
+
|
|
205
|
+
Write `hidden` on the chrome's `<body>`, and the hub removes it once the
|
|
206
|
+
first page has landed. See
|
|
207
|
+
[Preventing the first-load blink](./chrome.md#preventing-the-first-load-blink).
|
|
@@ -290,7 +290,7 @@ Each direction has one mechanism for each kind of message:
|
|
|
290
290
|
|
|
291
291
|
| Direction | What | How | Today |
|
|
292
292
|
|----------------|--------------------------------------------|-----------------------------|-------|
|
|
293
|
-
| Hub to element | State that lasts | An attribute the hub stamps | `lb-column-value`, `lb-key-value`, `lb-query-row-count`, `lb-group-*`, `lb-row-*`, `lb-request-pending`, `lb-request-error` |
|
|
293
|
+
| Hub to element | State that lasts | An attribute the hub stamps | `lb-column-value`, `lb-key-value`, `lb-query-row-count`, `lb-group-*`, `lb-row-*`, `lb-request-pending`, `lb-request-error`, `lb-request-refused` |
|
|
294
294
|
| Hub to element | State the platform already names | A property | A control's `value` |
|
|
295
295
|
| Hub to element | What the hub did | An optional `lb` method | `lbAttached`, `lbRowsLanded`, `lbRequestDone`, `lbDetached` |
|
|
296
296
|
| Hub to element | A moment an element started | A bubbling event | `lb-request-done` |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A page shows server data by naming queries and columns in its HTML, and asks
|
|
4
4
|
the server to change that data by naming requests. The developer writes
|
|
5
|
-
|
|
5
|
+
sixteen `lb-` attributes. The server declares every query a page may show and
|
|
6
6
|
every request it may send, and refuses anything it has not declared.
|
|
7
7
|
|
|
8
8
|
Everything is a query. A query is a name for rows, of kind `row` or `rows`,
|
|
@@ -48,6 +48,10 @@ each with its key, and permits the three requests on `roster`; see
|
|
|
48
48
|
| `lb-show` | Any element but `<template>` | The column that decides whether it is present |
|
|
49
49
|
| `lb-order` | An element with `lb-query` | The choice holding the user's order; see [Order](#order) |
|
|
50
50
|
| `lb-filter` | An element with `lb-query` | The columns a choice filters; see [Filters](#filters) |
|
|
51
|
+
| `lb-group` | A `<template>` | Nothing: it marks a group template; see [Groups](#groups) |
|
|
52
|
+
| `lb-count` | Any element | Nothing: it shows a count of rows; see [Aggregates](#aggregates) |
|
|
53
|
+
| `lb-sum`, `lb-avg`, `lb-min`, `lb-max` | Any element | The column aggregated; see [Aggregates](#aggregates) |
|
|
54
|
+
| `lb-select` | A checkbox in a row template | Nothing: it selects its live row; see [Selecting rows](#selecting-rows) |
|
|
51
55
|
| `lb-request` | Any element | The request it sends when it commits |
|
|
52
56
|
| `lb-url-link` | An `<a>` | A link between pages; see [chrome](./chrome.md#links) |
|
|
53
57
|
| `lb-url-push` | An element with `lb-request` | A URL change that pushes a history entry |
|
|
@@ -62,8 +66,14 @@ builder writes a stamp, and a stylesheet or a custom element reads it.
|
|
|
62
66
|
| `lb-key-value` | The key of a live row, or of a `row` landed on an element |
|
|
63
67
|
| `lb-row-live` | The element is a live row |
|
|
64
68
|
| `lb-query-row-count` | The number of live rows an element shows |
|
|
69
|
+
| `lb-group-live`, `lb-group-column`, `lb-group-value` | A group, the column it breaks on, and its value; see [Groups](#groups) |
|
|
70
|
+
| `lb-row-created`, `lb-row-changed`, `lb-row-moved`, `lb-row-requested` | What happened to a row; see [What happened to a row](#what-happened-to-a-row) |
|
|
71
|
+
| `lb-row-leaving`, `lb-group-leaving` | A row or group on its way out |
|
|
72
|
+
| `lb-row-editing` | The row a form is pointed at; see [Editing a row in one form](#editing-a-row-in-one-form) |
|
|
73
|
+
| `lb-row-selected` | A row whose `lb-select` is ticked |
|
|
65
74
|
| `lb-request-pending` | The element's request is in flight |
|
|
66
75
|
| `lb-request-error` | The element's last request failed |
|
|
76
|
+
| `lb-request-refused` | The handler refused it; see [Refusals](#refusals) |
|
|
67
77
|
| `lb-page` | The stub of a page, on its `<template>` |
|
|
68
78
|
| `lb-page-title` | The text of a page file's `<title>` |
|
|
69
79
|
|
|
@@ -95,6 +105,10 @@ not `lb-row-live`, so a `row` shown inside another query, such as a total in
|
|
|
95
105
|
a table's foot, is not one of that query's rows. Select live rows with
|
|
96
106
|
`[lb-row-live]`, never with `[lb-key-value]`.
|
|
97
107
|
|
|
108
|
+
A `row` query answering `null` found no row. The hub lands every column the
|
|
109
|
+
element names as null and removes its `lb-key-value`; under a row template,
|
|
110
|
+
the live row leaves.
|
|
111
|
+
|
|
98
112
|
Write `lb-column` on each element that shows a column. The element reads
|
|
99
113
|
from its nearest ancestor row: the live row it is in, or the element a
|
|
100
114
|
`row` landed on. A nested `lb-query` begins a new query, and what is inside
|
|
@@ -139,12 +153,16 @@ from its element file, and renders `lb-column-value` itself; see
|
|
|
139
153
|
A file input receives nothing, and the hub reports it to the console.
|
|
140
154
|
|
|
141
155
|
The hub hands every value to the browser as the server sent it. What a
|
|
142
|
-
number, a date or a null looks like is decided in the query
|
|
156
|
+
number, a date or a null looks like is decided in the query, or by a custom
|
|
157
|
+
element that formats it, such as
|
|
158
|
+
[`lb-number` and `lb-date`](./widgets.md#lb-number-and-lb-date). Send a
|
|
159
|
+
column the user sorts or filters raw, and format it in the element.
|
|
143
160
|
|
|
144
161
|
### Checkboxes
|
|
145
162
|
|
|
146
163
|
A checkbox is a control. It is checked when its column is anything but
|
|
147
|
-
`false` or null, and it sends `"true"` or `"false"
|
|
164
|
+
`false` or null, and it sends `"true"` or `"false"`
|
|
165
|
+
([miswart](./miswarts.md#checkboxes-and-radio-groups-always-gather-a-value)):
|
|
148
166
|
|
|
149
167
|
```html
|
|
150
168
|
<input type="checkbox" lb-column="active" lb-request="lb-row-update" />
|
|
@@ -169,8 +187,9 @@ when none is:
|
|
|
169
187
|
|
|
170
188
|
In a row template, the hub stamps each radio button's `name` with the
|
|
171
189
|
`name` written, or its column when none is, followed by `-` and the live
|
|
172
|
-
row's key, so each live row holds a group of its own
|
|
173
|
-
|
|
190
|
+
row's key, so each live row holds a group of its own
|
|
191
|
+
([miswart](./miswarts.md#radio-button-names-in-a-row-template)). Rows not yet
|
|
192
|
+
entered share the empty key, and so share one group.
|
|
174
193
|
|
|
175
194
|
A radio button in a query's row template answers to that query's row, as
|
|
176
195
|
any element there does. A choice among a query's rows is better made with
|
|
@@ -273,7 +292,8 @@ its rows the user may filter, separated by commas. The column of
|
|
|
273
292
|
```
|
|
274
293
|
|
|
275
294
|
A row that does not pass every filter is `hidden`, and a ticked `lb-select`
|
|
276
|
-
in it is cleared. A group whose rows are all hidden is hidden too
|
|
295
|
+
in it is cleared. A group whose rows are all hidden is hidden too
|
|
296
|
+
([miswart](./miswarts.md#rows-hidden-by-a-filter)). An empty
|
|
277
297
|
choice filters nothing.
|
|
278
298
|
|
|
279
299
|
A filter is one or more terms, each an operator followed by a value:
|
|
@@ -364,8 +384,9 @@ Every change to a list is one a stylesheet can see:
|
|
|
364
384
|
| `lb-row-leaving` | A row on its way out |
|
|
365
385
|
| `lb-group-leaving` | A group on its way out |
|
|
366
386
|
|
|
367
|
-
A row that leaves loses `lb-row-live`, takes `inert
|
|
368
|
-
|
|
387
|
+
A row that leaves loses `lb-row-live`, takes `inert`
|
|
388
|
+
([miswart](./miswarts.md#leaving-rows-are-inert)), and is removed once its
|
|
389
|
+
animations finish, at once when there are none. `lb-row-requested` is
|
|
369
390
|
cleared at every landing; the others when the hub next touches the row.
|
|
370
391
|
|
|
371
392
|
A row is moved when its values changed and the rows whose values did not
|
|
@@ -920,7 +941,8 @@ The hub stamps the element that committed:
|
|
|
920
941
|
|
|
921
942
|
`lb-request-error` covers a non-2xx response, a network failure and a
|
|
922
943
|
timeout alike. The hub aborts a round trip after ten seconds, and sets
|
|
923
|
-
`aria-busy="true"` for as long as `lb-request-pending` is present
|
|
944
|
+
`aria-busy="true"` for as long as `lb-request-pending` is present
|
|
945
|
+
([miswart](./miswarts.md#busy-elements-carry-aria-busy)).
|
|
924
946
|
|
|
925
947
|
The hub ignores a commit on an element carrying `lb-request-pending`, so a
|
|
926
948
|
pending button is already disabled and a stylesheet only shows it:
|
|
@@ -1034,8 +1056,16 @@ The developer writes `lb-` attributes in markup, and script never assigns
|
|
|
1034
1056
|
them. The builder checks the markup of the chrome and of every page, after
|
|
1035
1057
|
expansion, and refuses to build:
|
|
1036
1058
|
|
|
1037
|
-
- an `lb-` attribute that is not one of the
|
|
1059
|
+
- an `lb-` attribute that is not one of the sixteen the developer writes
|
|
1038
1060
|
- `lb-request` naming an `lb-` request other than the three Loadbare provides
|
|
1061
|
+
- `lb-query` naming a query the page does not declare; every page and the
|
|
1062
|
+
chrome may name `lb-url` and `lb-choices`, and the chrome names no other
|
|
1063
|
+
- `lb-request` naming a request the page does not declare under `handlers`;
|
|
1064
|
+
the chrome sends none
|
|
1065
|
+
- one of the three requests for a query whose `crud` entry does not declare
|
|
1066
|
+
it, or any but `lb-row-update` for `lb-url` or `lb-choices`; the query is
|
|
1067
|
+
a form's own when the form carries `lb-query`, and otherwise the nearest
|
|
1068
|
+
`lb-query` around the element
|
|
1039
1069
|
- `lb-url-link` on anything but an `<a>`
|
|
1040
1070
|
- `lb-url-push` on an element without `lb-request`
|
|
1041
1071
|
- `lb-url-unknown` on anything but a `<dialog>`, or outside `<lb-hub>`
|
|
@@ -1048,3 +1078,7 @@ expansion, and refuses to build:
|
|
|
1048
1078
|
template, or beside `lb-column` or `lb-request`
|
|
1049
1079
|
- a `command` beginning `--lb-` that is not one of the three commands
|
|
1050
1080
|
- one of the three commands with no `commandfor`
|
|
1081
|
+
|
|
1082
|
+
To know what each page declares, the builder loads its `.queries.ts` and
|
|
1083
|
+
`.requests.ts` modules, so their import-time code runs during the build, as
|
|
1084
|
+
it does when the server starts.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Miswarts
|
|
2
|
+
|
|
3
|
+
A miswart is a place where Loadbare/app breaks one of its own rules.
|
|
4
|
+
Anybody paying attention will see it as a violation, and they will be
|
|
5
|
+
right. Yet in context it turns out to be the RightThing(tm), and doing it
|
|
6
|
+
any other way would be worse for the user or the developer.
|
|
7
|
+
|
|
8
|
+
The reference describes each miswart's behavior where it applies, and
|
|
9
|
+
links here. This document is the one place that admits they are
|
|
10
|
+
exceptions, and says why each one is allowed to stay.
|
|
11
|
+
|
|
12
|
+
## The test
|
|
13
|
+
|
|
14
|
+
A miswart must be:
|
|
15
|
+
|
|
16
|
+
- **Small.** It can be stated in a sentence or two.
|
|
17
|
+
- **Orthogonal.** It has no interaction with any other miswart.
|
|
18
|
+
- **Terminating.** It does not, by logical extension, require another
|
|
19
|
+
miswart. If fixing the edges of one exception calls for a second, we
|
|
20
|
+
probably should be looking for a weakness in earlier implementation
|
|
21
|
+
decisions.
|
|
22
|
+
|
|
23
|
+
Each entry also names two things:
|
|
24
|
+
|
|
25
|
+
- **The rule it breaks.** If we cannot name the rule, it is not a
|
|
26
|
+
miswart, it is undocumented behavior.
|
|
27
|
+
- **The motivation that justifies it**: performance, correctness or
|
|
28
|
+
ergonomics, as set out in the theory document. That is what makes it
|
|
29
|
+
the RightThing(tm) rather than a convenience.
|
|
30
|
+
|
|
31
|
+
## Radio button names in a row template
|
|
32
|
+
|
|
33
|
+
**What.** The hub stamps the `name` of each radio button in a row
|
|
34
|
+
template with the `name` written, or its column when none is, followed by
|
|
35
|
+
`-` and the live row's key.
|
|
36
|
+
|
|
37
|
+
**Rule broken.** The hub writes only its own stamps, which always
|
|
38
|
+
begin with `lb-`.
|
|
39
|
+
|
|
40
|
+
**Why it is the RightThing(tm).** Correctness. The browser groups radio
|
|
41
|
+
buttons by `name`, and a row template repeats one `name` in every live
|
|
42
|
+
row, so without the stamp every row's buttons would form a single group,
|
|
43
|
+
and checking one row would uncheck all the others.
|
|
44
|
+
|
|
45
|
+
**Why it terminates.** It applies only to radio buttons inside a row
|
|
46
|
+
template, and only the `name`.
|
|
47
|
+
|
|
48
|
+
## Checkboxes and radio groups always gather a value
|
|
49
|
+
|
|
50
|
+
**What.** An unchecked checkbox gathers `"false"`, a checked one
|
|
51
|
+
`"true"`, and its `value` attribute means nothing to the hub. A radio
|
|
52
|
+
group with none checked gathers `""`.
|
|
53
|
+
|
|
54
|
+
**Rule broken.** Where HTML already has an opinion, Loadbare/app
|
|
55
|
+
follows it. HTML sends a checkbox's `value` when it
|
|
56
|
+
is checked, and sends nothing for an unchecked checkbox or an empty radio
|
|
57
|
+
group.
|
|
58
|
+
|
|
59
|
+
**Why it is the RightThing(tm).** Correctness. A write must be able to
|
|
60
|
+
set a flag back to false, or clear a choice, and nothing on the wire
|
|
61
|
+
cannot say either.
|
|
62
|
+
|
|
63
|
+
**Why it terminates.** It applies only to gathering from these two
|
|
64
|
+
controls. Landing still follows HTML's own idea of checked.
|
|
65
|
+
|
|
66
|
+
## Rows hidden by a filter
|
|
67
|
+
|
|
68
|
+
**What.** A row that does not pass the user's filter gets the `hidden`
|
|
69
|
+
attribute, and so does a group whose rows are all hidden.
|
|
70
|
+
|
|
71
|
+
**Rule broken.** The hub writes only its own stamps, which always
|
|
72
|
+
begin with `lb-`.
|
|
73
|
+
|
|
74
|
+
**Why it is the RightThing(tm).** Performance and ergonomics. A filter
|
|
75
|
+
over rows already on the page needs no round trip, and `hidden` is HTML's
|
|
76
|
+
own word for an element that is present but not shown. A stamp of our
|
|
77
|
+
own would make every stylesheet restate what the browser already does.
|
|
78
|
+
|
|
79
|
+
**Why it terminates.** It applies only to live rows and groups under a
|
|
80
|
+
filter.
|
|
81
|
+
|
|
82
|
+
## Leaving rows are inert
|
|
83
|
+
|
|
84
|
+
**What.** A row on its way out takes `inert` while its animation runs,
|
|
85
|
+
and is removed when the animation finishes.
|
|
86
|
+
|
|
87
|
+
**Rule broken.** The hub writes only its own stamps, which always
|
|
88
|
+
begin with `lb-`.
|
|
89
|
+
|
|
90
|
+
**Why it is the RightThing(tm).** Correctness. A row that is gone from
|
|
91
|
+
the database must not take a click or the focus during its exit
|
|
92
|
+
animation, and `inert` is exactly HTML's word for that.
|
|
93
|
+
|
|
94
|
+
**Why it terminates.** It applies only to a row already stamped
|
|
95
|
+
`lb-row-leaving`, which never comes back.
|
|
96
|
+
|
|
97
|
+
## Busy elements carry aria-busy
|
|
98
|
+
|
|
99
|
+
**What.** While an element's request is in flight, it carries
|
|
100
|
+
`aria-busy="true"` alongside `lb-request-pending`.
|
|
101
|
+
|
|
102
|
+
**Rule broken.** The hub writes only its own stamps, which always
|
|
103
|
+
begin with `lb-`.
|
|
104
|
+
|
|
105
|
+
**Why it is the RightThing(tm).** Correctness, for a user of assistive
|
|
106
|
+
technology. `lb-request-pending` tells a stylesheet, and `aria-busy` tells
|
|
107
|
+
a screen reader, the same fact.
|
|
108
|
+
|
|
109
|
+
**Why it terminates.** It rides with `lb-request-pending` and nothing
|
|
110
|
+
else.
|
|
111
|
+
|
|
112
|
+
## The hub unhides the body
|
|
113
|
+
|
|
114
|
+
**What.** When the chrome writes `<body hidden>`, the hub removes
|
|
115
|
+
`hidden` once the first page has landed.
|
|
116
|
+
|
|
117
|
+
**Rule broken.** The hub writes only its own stamps, which always
|
|
118
|
+
begin with `lb-`.
|
|
119
|
+
|
|
120
|
+
**Why it is the RightThing(tm).** Performance, as the user perceives it.
|
|
121
|
+
Without it, the first load blinks an empty page before the data lands.
|
|
122
|
+
Only the hub knows when the first page has landed.
|
|
123
|
+
|
|
124
|
+
**Why it terminates.** It happens once, to `<body>`, and only when the
|
|
125
|
+
application opted in by writing `hidden`.
|
|
126
|
+
|
|
127
|
+
## `lb-url` lands every parm it names
|
|
128
|
+
|
|
129
|
+
**What.** When `lb-url` lands, a query parm the URL does not carry lands
|
|
130
|
+
empty on every element naming it as a column.
|
|
131
|
+
|
|
132
|
+
**Rule broken.** Landing a row sets the columns the row carries, and
|
|
133
|
+
leaves the rest as they were.
|
|
134
|
+
|
|
135
|
+
**Why it is the RightThing(tm).** Correctness. A control must show what
|
|
136
|
+
the address bar says, after Back as well as after a reload, and a parm
|
|
137
|
+
that is absent from the URL is a parm whose value is empty.
|
|
138
|
+
|
|
139
|
+
**Why it terminates.** It applies only to `lb-url`, the one query whose
|
|
140
|
+
columns are the URL itself.
|