@loadbare/app 0.25.0 → 0.26.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.
Files changed (55) hide show
  1. package/dist/build/assemble.d.ts +1 -2
  2. package/dist/build/assemble.d.ts.map +1 -1
  3. package/dist/build/assemble.js +11 -53
  4. package/dist/build/assemble.js.map +1 -1
  5. package/dist/build/cli.d.ts +4 -10
  6. package/dist/build/cli.d.ts.map +1 -1
  7. package/dist/build/cli.js +11 -33
  8. package/dist/build/cli.js.map +1 -1
  9. package/dist/build/declared.js +1 -1
  10. package/dist/build/declared.js.map +1 -1
  11. package/dist/core/lb-constants.d.ts +8 -18
  12. package/dist/core/lb-constants.d.ts.map +1 -1
  13. package/dist/core/lb-constants.js +32 -72
  14. package/dist/core/lb-constants.js.map +1 -1
  15. package/dist/core/lb-types.d.ts +12 -11
  16. package/dist/core/lb-types.d.ts.map +1 -1
  17. package/dist/core/lb-types.js +7 -0
  18. package/dist/core/lb-types.js.map +1 -1
  19. package/dist/hub/lb-apply.d.ts +10 -37
  20. package/dist/hub/lb-apply.d.ts.map +1 -1
  21. package/dist/hub/lb-apply.js +139 -522
  22. package/dist/hub/lb-apply.js.map +1 -1
  23. package/dist/hub/lb-hub.browser.d.ts.map +1 -1
  24. package/dist/hub/lb-hub.browser.js +115 -126
  25. package/dist/hub/lb-hub.browser.js.map +1 -1
  26. package/dist/hub/lb-lifecycle.d.ts +2 -2
  27. package/dist/hub/lb-lifecycle.d.ts.map +1 -1
  28. package/dist/hub/lb-lifecycle.js +9 -17
  29. package/dist/hub/lb-lifecycle.js.map +1 -1
  30. package/dist/server/lb-express.d.ts.map +1 -1
  31. package/dist/server/lb-express.js +3 -3
  32. package/dist/server/lb-express.js.map +1 -1
  33. package/dist/server/lb-server.d.ts +35 -19
  34. package/dist/server/lb-server.d.ts.map +1 -1
  35. package/dist/server/lb-server.js +141 -88
  36. package/dist/server/lb-server.js.map +1 -1
  37. package/docs/binding-cases.md +150 -0
  38. package/docs/comparison.md +18 -29
  39. package/docs/miswarts.md +2 -2
  40. package/docs/reference/css.md +2 -15
  41. package/docs/reference/custom-elements.md +6 -8
  42. package/docs/reference/data-binding.md +37 -180
  43. package/docs/reference/names.md +10 -26
  44. package/docs/reference/page-files.md +1 -12
  45. package/docs/reference/server.md +0 -1
  46. package/docs/testing.md +2 -5
  47. package/package.json +1 -1
  48. package/skills/loadbare-app/SKILL.md +52 -54
  49. package/skills/loadbare-app/references/css.md +2 -15
  50. package/skills/loadbare-app/references/custom-elements.md +6 -8
  51. package/skills/loadbare-app/references/data-binding.md +37 -180
  52. package/skills/loadbare-app/references/miswarts.md +2 -2
  53. package/skills/loadbare-app/references/names.md +10 -26
  54. package/skills/loadbare-app/references/page-files.md +1 -12
  55. package/skills/loadbare-app/references/server.md +0 -1
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: loadbare-app
3
- description: Build server-bound web applications with Loadbare/app. Covers the file conventions the builder finds by name (`chrome.html`, `*.page.html`, `*.queries.ts`, `*.requests.ts`, `imports.ts`, `<tag>.html`, `<tag>.browser.ts`); the `lb-*` attribute vocabulary that lands query rows on HTML and issues requests (`lb-query`, `lb-column`, `lb-show`, `lb-request`); queries declared with `row(key, run)` and `rows(key, run)`; the URL as the hub's own query `lb-url`, with `lb-url-link`, `lb-url-parm`, `lb-url-push` and `lb-url-unknown`; the user's order and filters as the hub's query `lb-choices`, read by `lb-order` and `lb-filter`; whether the page loaded and the server answers as the hub's query `lb-hub`; one form pointed at a row by the `--lb-edit`, `--lb-new` and `--lb-release` commands; rows that hold still under `lb-manual` and are placed again with `--lb-place`; build-time expansion with `exp-*` parameters, `lb-exp-slot` and `lb-exp-template`; custom elements as form-associated controls and row hosts; the Express wiring with `hubRoutes`; and the `@loadbare/widgets` library. Use whenever a task involves `@loadbare/app`, `@loadbare/widgets`, `loadbare-app-build`, an `lb-` attribute, or a page, query, request or custom element file in a Loadbare application. The model is not React, htmx or REST, and cannot be inferred from them.
3
+ description: Build server-bound web applications with Loadbare/app. Covers the file conventions the builder finds by name (`chrome.html`, `*.page.html`, `*.queries.ts`, `*.requests.ts`, `imports.ts`, `<tag>.html`, `<tag>.browser.ts`); the `lb-*` attribute vocabulary that lands query rows on HTML and issues requests (`lb-query`, `lb-column`, `lb-show`, `lb-request`); queries declared with `row(key, run)` and `rows(key, run)`; the URL as the hub's own query `lb-url`, with `lb-url-link`, `lb-url-parm`, `lb-url-push` and `lb-url-unknown`; the user's order and filters as the hub's queries `lb-sort` and `lb-filters`, read by `lb-filter`; whether the page loaded and the server answers as the hub's query `lb-hub-status`; one form pointed at a row by the `--lb-edit`, `--lb-new` and `--lb-release` commands; rows that hold still under `lb-manual` and are placed again with `--lb-place`; build-time expansion with `exp-*` parameters, `lb-exp-slot` and `lb-exp-template`; custom elements as form-associated controls and row hosts; the Express wiring with `hubRoutes`; and the `@loadbare/widgets` library. Use whenever a task involves `@loadbare/app`, `@loadbare/widgets`, `loadbare-app-build`, an `lb-` attribute, or a page, query, request or custom element file in a Loadbare application. The model is not React, htmx or REST, and cannot be inferred from them.
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  package: "@loadbare/app"
@@ -75,7 +75,7 @@ server.ts
75
75
  The chrome carries `<lb-hub>` inside `<body>`, an empty `<main>` inside the
76
76
  hub, and `<script src="/client.js" defer>`. Everything a user touches sits
77
77
  inside `<lb-hub>`. The chrome belongs to no page, so it names only the
78
- hub's queries, `lb-url`, `lb-choices` and `lb-hub`, and sends no declared
78
+ hub's queries, `lb-url`, `lb-sort`, `lb-filters` and `lb-hub-status`, and sends no declared
79
79
  request.
80
80
 
81
81
  A page is an HTML fragment that lands in `<main>`:
@@ -183,7 +183,7 @@ from it.
183
183
 
184
184
  **A column never holds rows.** Master-detail is a `row` query and a `rows`
185
185
  query under two names. Many masters with their details is one `rows` query
186
- of joined rows, ordered by the master first and shown in groups. A
186
+ of joined rows, ordered by the master first. A
187
187
  query nested inside another query's row template receives the same rows in
188
188
  every outer row; use it for a picker, never for per-row detail. Inside a
189
189
  form, the live rows of a query nested in the form's own are gathered as
@@ -195,7 +195,10 @@ is added by a button with `commandfor` naming the nested list and
195
195
  the form's submit carries it as `{ key: "", values }`, the handler inserts
196
196
  each row with no key, and it leaves when the submit succeeds. So a master
197
197
  and its details are one save, before or after the master is on record; never
198
- gate the details behind `lb-show` on the master being saved. The form needs
198
+ gate the details behind `lb-show` on the master being saved. A form bound
199
+ by Edit or New reads its nested lists again (`lb-read`) with the bound key
200
+ as the parm named for its key column, so the details query reads that parm
201
+ and follows the row the form is bound to; New sends it empty. The form needs
199
202
  a submit. A new outer
200
203
  row is filled from the nested query's last answer, whatever order they land
201
204
  in.
@@ -220,21 +223,19 @@ JSON number or is declared one: `types: { amount: "number" }` beside
220
223
  hold a place.
221
224
 
222
225
  **The user's order and filters are choices, not query parms.** The hub
223
- serves a second query, `lb-choices`, keyed by `lb-path`, one column per
224
- choice, named by the application. A control inside
225
- `lb-query="lb-choices"` with `lb-request="lb-row-update"` makes a choice.
226
- `lb-order="<choice>"` on the element carrying `lb-query` places its rows by
227
- the order that choice holds, in place of the server's while it is not
228
- empty. `lb-filter="col, col"` there hides each row that does not pass the
229
- choice of the same name: `^=Amazon`, `>=2026-01-01 <=2026-03-31`, or plain
230
- text for contains. A choice shows with no round trip, then the server's
231
- `onChoices(ctx, choices)` hears every choice, to keep them;
232
- `onPageEnter` returns `choices()` to restore them. Choices hold until the
233
- page changes. A filter that decides which rows the server sends is a query
234
- parm.
226
+ serves them as two queries, `lb-sort` and `lb-filters`, one row per query
227
+ keyed by the query's name. `lb-sort` holds each query's order as the
228
+ server resolved it; `lb-filters` holds each query's filter values.
229
+ `lb-filter="col, col"` on the element carrying `lb-query` hides each row
230
+ that does not pass its query's filter of the same name: `^=Amazon`,
231
+ `>=2026-01-01 <=2026-03-31`, or plain text for contains. The server's
232
+ `onChoices(ctx, { query, sort, filters })` hears each change, to keep it;
233
+ `onPageEnter` returns `sort()` and `filters()` to restore them. Choices
234
+ hold until the page changes. A filter that decides which rows the server
235
+ sends is a query parm.
235
236
 
236
237
  **A page that failed to load says so.** The hub serves a third query,
237
- `lb-hub`, and stamps its columns on `<lb-hub>` too: `lb-load-state`
238
+ `lb-hub-status`, and stamps its columns on `<lb-hub>` too: `lb-load-state`
238
239
  (`loading`, `loaded`, `failed`), `lb-server-state` (`reachable`, `failing`,
239
240
  `unreachable`), `lb-error` (`network`, `timeout` or the HTTP status),
240
241
  `lb-pending` and `lb-as-of`. Show a server that is down from the chrome's
@@ -242,19 +243,10 @@ stylesheet, `lb-hub[lb-load-state="failed"] .load-failed`, rather than
242
243
  leaving an empty page; see
243
244
  [`references/chrome.md`](references/chrome.md#the-hubs-state).
244
245
 
245
- **Groups are templates.** A `<template lb-group>` holds a heading and one
246
- nested `<template>`, the next level or the row template; the nth breaks on
247
- the order's nth term, to any depth. Contents land before the nested
248
- template, so put it inside the heading's element for a container and beside
249
- the heading for a heading row. `<tbody>` and `<optgroup>` do not nest. The
250
- heading is filled from the group's first row. `lb-count`, `lb-sum="col"`,
251
- `lb-avg`, `lb-min` and `lb-max` total the rows showing in the nearest group,
252
- or in the whole list outside any.
253
-
254
246
  **Animate with CSS.** The hub stamps `lb-row-created`, `lb-row-changed`,
255
247
  `lb-row-moved` (its values changed and it now stands elsewhere), and
256
248
  `lb-row-requested` for this page's request, and marks a
257
- leaving row or group `lb-row-leaving` or `lb-group-leaving`, removing it once
249
+ leaving row `lb-row-leaving`, removing it once
258
250
  its animations end. Scrolling to a row is script, in `lbRowsLanded`.
259
251
 
260
252
  **Every request has one shape.** `lb-request` names the request, and the
@@ -327,16 +319,6 @@ update or delete at all, silently. So a control with
327
319
  `lb-request="lb-row-update"` saves on a row on record and only holds its
328
320
  value on a new one. Never write script to stop such an update.
329
321
 
330
- **Select rows with `lb-select`, never with a column.** An
331
- `<input type="checkbox" lb-select>` in a row template selects its row; the
332
- hub stamps `lb-row-selected`. A request for that query naming no one row,
333
- such as a form outside the rows, carries the ticked rows as `keys`.
334
- `rowUpdate` and `rowDelete` then run once per key with no new code; a
335
- declared request gets `keys` itself. With none ticked an update or delete
336
- is not sent. Success clears the selection, and so does `lb-show` hiding a
337
- selector. A selecting mode kept in the URL reaches the rows as a column the
338
- query computes from the parm.
339
-
340
322
  **Refuse with `refusal()`, never with a plain throw.** `throw
341
323
  refusal("message")`, `throw refusal({ column: "message" })` or both, from
342
324
  `@loadbare/app/server`. The whole-request message lands as the
@@ -439,9 +421,9 @@ whole data channel.
439
421
 
440
422
  **The developer writes `lb-*` attributes in markup, and script never
441
423
  assigns them.** The builder refuses any `lb-` attribute other than the
442
- eighteen in [Attributes](references/names.md#attributes): `lb-query`,
443
- `lb-column`, `lb-show`, `lb-order`, `lb-filter`, `lb-manual`, `lb-group`, `lb-count`,
444
- `lb-sum`, `lb-avg`, `lb-min`, `lb-max`, `lb-select`, `lb-request`,
424
+ ten in [Attributes](references/names.md#attributes): `lb-query`,
425
+ `lb-column`, `lb-show`, `lb-filter`, `lb-manual`,
426
+ `lb-request`,
445
427
  `lb-url-link`, `lb-url-parm`, `lb-url-push` and `lb-url-unknown`. `lb-exp-slot` and
446
428
  `lb-exp-template` are expansion's, and never reach the page. Everything
447
429
  else with the prefix is a stamp the hub or the builder writes.
@@ -527,28 +509,44 @@ The application's own definition of a tag overrides a package's.
527
509
  Read these when the summary above does not settle the question. They ship
528
510
  with the package, so no network is needed.
529
511
 
530
- - [`references/overview.md`](references/overview.md) — the map of the
531
- reference, by part of the application.
512
+ [`references/overview.md`](references/overview.md) is the map of the
513
+ reference, in the groups below.
514
+
515
+ The application shell:
516
+
517
+ - [`references/chrome.md`](references/chrome.md) — the chrome, the URL as
518
+ the query `lb-url`, and the hub's state.
519
+ - [`references/server.md`](references/server.md) — the Express server, the
520
+ request context, and where the database layer fits.
521
+
522
+ Building the app:
523
+
524
+ - [`references/builder.md`](references/builder.md) — the builder's options,
525
+ what it reads, where it looks, and what it writes.
526
+ - [`references/css.md`](references/css.md) — how stylesheets are ordered, and
527
+ the stamps a stylesheet selects on.
528
+ - [`references/dev-server.md`](references/dev-server.md) — `devServer` from
529
+ `@loadbare/app/dev`, and the parts to assemble another from.
530
+
531
+ Application pages:
532
+
533
+ - [`references/page-files.md`](references/page-files.md) — what page
534
+ files are, queries, `onPageEnter`, `onChoices`, handlers, `crud`, refresh
535
+ and patch, `url()`, `sort()` and `filters()`.
532
536
  - [`references/data-binding.md`](references/data-binding.md) — landing,
533
537
  gathering, requests, conditions and request state. Start here for
534
538
  anything in a page.
535
- - [`references/page-files.md`](references/page-files.md) — what page
536
- files are, queries, `onPageEnter`, `onChoices`, handlers, `crud`, refresh
537
- and patch, `url()` and `choices()`.
539
+
540
+ Custom elements:
541
+
538
542
  - [`references/custom-elements.md`](references/custom-elements.md) —
539
543
  expansion, parameters, slots, destinations, and custom element code and
540
544
  when it runs.
541
- - [`references/chrome.md`](references/chrome.md) — the chrome, and the URL
542
- as the query `lb-url`.
543
- - [`references/server.md`](references/server.md) — the Express server and
544
- the request context.
545
- - [`references/builder.md`](references/builder.md) — the builder's options,
546
- what it reads, and where it looks.
547
- - [`references/dev-server.md`](references/dev-server.md) — `devServer` from
548
- `@loadbare/app/dev`, and the parts to assemble another from.
549
- - [`references/css.md`](references/css.md) — how stylesheets are ordered.
550
545
  - [`references/widgets.md`](references/widgets.md) — the basic widget
551
546
  library as a package; each widget is in the loadbare-widgets skill.
547
+
548
+ Names:
549
+
552
550
  - [`references/names.md`](references/names.md) — every name Loadbare
553
551
  owns, the constants, and the section defining each. Check a name here
554
552
  before designing around something the other references do not mention.
@@ -69,7 +69,6 @@ in the stamp, so the selector reads the number and not its text. See
69
69
  | Select | For |
70
70
  |----------------------|-------------------------------------------------|
71
71
  | `[lb-row-live]` | Every live row of a list |
72
- | `[lb-row-selected]` | A row its `lb-select` checkbox ticks |
73
72
  | `[lb-row-editing]` | The row a pointed form is editing |
74
73
  | `[lb-key-value="…"]` | One row, by its key |
75
74
  | `[hidden]` | A row a filter hides; the browser hides it |
@@ -86,24 +85,12 @@ landed inside a list, such as a total in a table's foot, carries a key and
86
85
  is not one of the list's rows.
87
86
 
88
87
  ```css
89
- [lb-row-selected] { background: var(--selected); }
90
88
  [lb-row-editing] { outline: 2px solid var(--accent); }
91
89
  ```
92
90
 
93
- See [Rows](./data-binding.md#rows), [Selecting rows](./data-binding.md#selecting-rows)
91
+ See [Rows](./data-binding.md#rows)
94
92
  and [Editing a row in one form](./data-binding.md#editing-a-row-in-one-form).
95
93
 
96
- ### A group
97
-
98
- Each top-level element of a group carries `lb-group-live`, the column it
99
- breaks on as `lb-group-column`, and its value there as `lb-group-value`:
100
-
101
- ```css
102
- [lb-group-column="category"][lb-group-value="Income"] { color: var(--income); }
103
- ```
104
-
105
- See [Groups](./data-binding.md#groups).
106
-
107
94
  ### What just happened to a row
108
95
 
109
96
  The hub stamps what its last landing did, so a stylesheet animates with
@@ -115,7 +102,7 @@ nothing else:
115
102
  | `[lb-row-changed]` | A row whose values changed |
116
103
  | `[lb-row-moved]` | A row whose values changed and now stands elsewhere; it carries `lb-row-changed` too |
117
104
  | `[lb-row-requested]` | Any of the three, done by this page's request, as `created`, `changed` or `moved` |
118
- | `[lb-row-leaving]`, `[lb-group-leaving]` | A row or group on its way out; the hub removes it when its animations end |
105
+ | `[lb-row-leaving]` | A row on its way out; the hub removes it when its animations end |
119
106
 
120
107
  ```css
121
108
  [lb-row-created] { animation: arrive 200ms; }
@@ -268,7 +268,7 @@ Each direction has one mechanism for each kind of message:
268
268
 
269
269
  | Direction | What | How | Today |
270
270
  |----------------|--------------------------------------------|-----------------------------|-------|
271
- | Hub to element | State that lasts | An attribute the hub stamps | `lb-column-value`, `lb-key-value`, `lb-query-row-count`, `lb-query-unplaced-count`, `lb-query-order`, `lb-group-*`, `lb-row-*`, `lb-request-pending`, `lb-request-error`, `lb-request-refused` |
271
+ | Hub to element | State that lasts | An attribute the hub stamps | `lb-column-value`, `lb-key-value`, `lb-query-row-count`, `lb-query-unplaced-count`, `lb-query-order`, `lb-row-*`, `lb-request-pending`, `lb-request-error`, `lb-request-refused` |
272
272
  | Hub to element | State the platform already names | A property | A control's `value` |
273
273
  | Hub to element | What the hub did | An optional `lb` method | `lbAttached`, `lbRowsLanded`, `lbRequestDone`, `lbDetached` |
274
274
  | Hub to element | A moment an element started | A bubbling event | `lb-request-done` |
@@ -315,11 +315,11 @@ customElements.define("note-popup", NotePopup);
315
315
  | `lbRequestDone(detail, origin)` | After a request committed by the element, or inside it, has finished |
316
316
  | `lbDetached()` | Once, after it removes the element |
317
317
 
318
- The hub places an element when it starts up the chrome, shows a page,
319
- creates a live row, or builds a group. It places the chrome and a page as
318
+ The hub places an element when it starts up the chrome, shows a page, or
319
+ creates a live row. It places the chrome and a page as
320
320
  the page's first answer lands, or once that round trip fails, so an element
321
321
  in either hears `lbAttached` with the page's data in place. It removes an
322
- element when a live row or a group has finished leaving, and when it shows
322
+ element when a live row has finished leaving, and when it shows
323
323
  another page in place of the one holding it. Write setup in `lbAttached` and its undoing in
324
324
  `lbDetached`; the two pair exactly, so neither guards against running
325
325
  twice.
@@ -633,8 +633,7 @@ had there hears the event, and no custom element the hub has removed hears
633
633
 
634
634
  The hub lands every row template itself, places every row by the query's
635
635
  order (see [Placing by hand](./data-binding.md#placing-by-hand)
636
- for when it does not), and builds every group the markup's group templates describe; see
637
- [Order](./data-binding.md#order) and [Groups](./data-binding.md#groups). A
636
+ for when it does not). A
638
637
  plain element carrying `lb-query` with a row template inside it needs no
639
638
  code, so a custom element holds rows only to do something with them once
640
639
  they land.
@@ -662,8 +661,7 @@ Everything else is the hub's:
662
661
  | Cloning | Clones the row template once per new key |
663
662
  | Matching | Fills the live row already showing that key, when its values changed |
664
663
  | Placing | Places every live row by the user's order or the query's, and leaves them where they stand for an answer under `lb-manual` |
665
- | Filtering | Hides a row the user's filter does not pass, and a group whose rows are all hidden |
666
- | Grouping | Builds a group where the order breaks, keeps it while its run goes on, and removes it with its last row |
664
+ | Filtering | Hides a row the user's filter does not pass |
667
665
  | Removing | Marks the live rows the response says are gone as leaving, and removes them |
668
666
  | Stamping | Stamps each live row with `lb-row-live`, `lb-key-value`, and what happened to it |
669
667
  | Counting | Stamps `lb-query-row-count` with the number of live rows showing, `lb-query-unplaced-count` with the number out of place, and `lb-query-order` with the order placing them |
@@ -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
- eighteen `lb-` attributes. The server declares every query a page may show and
5
+ fourteen `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,10 +48,6 @@ 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) |
55
51
  | `lb-request` | Any element | The request it sends when it commits |
56
52
  | `lb-url-link` | An `<a>` | A link between pages; see [chrome](./chrome.md#links) |
57
53
  | `lb-url-parm` | An `<a lb-url-link>` in a row | Columns of the row its `href` carries as parms; see [chrome](./chrome.md#a-link-from-a-row) |
@@ -69,11 +65,9 @@ builder writes a stamp, and a stylesheet or a custom element reads it.
69
65
  | `lb-query-row-count` | The number of live rows an element shows |
70
66
  | `lb-query-unplaced-count` | The number of them out of place; see [Placing by hand](#placing-by-hand) |
71
67
  | `lb-query-order` | The order placing them; see [Order](#order) |
72
- | `lb-group-live`, `lb-group-column`, `lb-group-value` | A group, the column it breaks on, and its value; see [Groups](#groups) |
73
68
  | `lb-row-created`, `lb-row-changed`, `lb-row-moved`, `lb-row-requested`, `lb-row-unplaced` | What happened to a row; see [What happened to a row](#what-happened-to-a-row) |
74
- | `lb-row-leaving`, `lb-group-leaving` | A row or group on its way out |
69
+ | `lb-row-leaving` | A row on its way out |
75
70
  | `lb-row-editing` | The row a form is pointed at; see [Editing a row in one form](#editing-a-row-in-one-form) |
76
- | `lb-row-selected` | A row whose `lb-select` is ticked |
77
71
  | `lb-request-pending` | The element's request is in flight |
78
72
  | `lb-request-error` | The element's last request failed |
79
73
  | `lb-request-refused` | The handler refused it; see [Refusals](#refusals) |
@@ -92,8 +86,7 @@ elements as the page needs.
92
86
 
93
87
  What lands depends on the query's kind and on whether the element holds a
94
88
  row template, which is the first `<template>` among its descendants outside
95
- any nested `lb-query`, or, when that template is a [group
96
- template](#groups), the innermost template inside it:
89
+ any nested `lb-query`:
97
90
 
98
91
  | Kind | Row template | The hub |
99
92
  |--------|--------------|----------------------------------------|
@@ -332,8 +325,7 @@ form `--lb-edit` pointed at a row, or a form elsewhere on the page.
332
325
  | The answer | Under `lb-manual`, the row | Side |
333
326
  |----------------------------------------|---------------------------------------------------|----------|
334
327
  | Changes a column the order reads | Stays where it stands | `order` |
335
- | Creates a row | Goes after the last row of its group, or last of all | `order` |
336
- | Changes the column its group breaks on | Stays under its group's heading | `order` |
328
+ | Creates a row | Goes last | `order` |
337
329
  | Changes a row so the filter hides it | Stays showing | `filter` |
338
330
  | Drops a row | Leaves, under either side | |
339
331
 
@@ -394,11 +386,9 @@ its rows the user may filter, separated by commas. The column of
394
386
  </ul>
395
387
  ```
396
388
 
397
- A row that does not pass every filter is `hidden`, and a ticked `lb-select`
398
- in it is cleared. Under `lb-manual="filter"`, a row an answer changed stays
389
+ A row that does not pass every filter is `hidden`. Under `lb-manual="filter"`, a row an answer changed stays
399
390
  showing until the rows are placed again; see
400
- [Placing by hand](#placing-by-hand). A group whose rows are all hidden is hidden too
401
- ([miswart](./miswarts.md#rows-hidden-by-a-filter)). An empty
391
+ [Placing by hand](#placing-by-hand). An empty
402
392
  choice filters nothing.
403
393
 
404
394
  A filter is one or more terms, each an operator followed by a value:
@@ -431,96 +421,6 @@ column that is null or missing, pass only `!=`.
431
421
  `parseFilter`, `passes` and `passesTerm` in `@loadbare/app/types` are the
432
422
  rule the hub applies.
433
423
 
434
- ### Groups
435
-
436
- A group is a run of rows sharing the order's leading term. Write a
437
- `<template lb-group>` where the row template goes, holding the group's
438
- heading and one nested `<template>`: the next group template, or the row
439
- template. The nth group template breaks on the order's nth term.
440
-
441
- ```html
442
- <ul lb-query="roster">
443
- <template lb-group>
444
- <li>
445
- <h3 lb-column="team"></h3>
446
- <ul>
447
- <template><li lb-column="name"></li></template>
448
- </ul>
449
- </li>
450
- </template>
451
- </ul>
452
- ```
453
-
454
- The hub clones the group template where the order breaks, and the group's
455
- contents land immediately before the clone of its nested template, so the
456
- markup's nesting decides the shape:
457
-
458
- | The nested template is | The group's contents land | Example |
459
- |------------------------------|------------------------------------|-----------------------------------|
460
- | Inside the heading's element | Inside it | `<tbody>`, `<li>`, `<details>` |
461
- | Beside the heading | After the heading, as its siblings | A heading `<tr>` in one `<tbody>` |
462
-
463
- `<tbody>` and `<optgroup>` do not nest, so a table's levels below the first
464
- are heading rows beside their rows:
465
-
466
- ```html
467
- <table lb-query="accounts">
468
- <thead><tr><th>Account</th></tr></thead>
469
- <template lb-group>
470
- <tbody>
471
- <tr><th scope="rowgroup" lb-column="category"></th></tr>
472
- <template><tr><td lb-column="account_name"></td></tr></template>
473
- </tbody>
474
- </template>
475
- </table>
476
- ```
477
-
478
- Content after the nested template lands after the group's rows, which is
479
- where a group's footer goes.
480
-
481
- A heading is filled from its group's first row as an element in a row is,
482
- `href`s from `lb-url-parm` included, so it may show a column other than the
483
- one the group breaks on. Under `lb-manual="order"`, a row an answer
484
- moved to another group stays under its old heading, and the heading shows
485
- the row as it stood, until the rows are placed again. The hub stamps each top-level
486
- element of a group with `lb-group-live`, `lb-group-column` and
487
- `lb-group-value`. A row creates its group, and a group leaves with its last
488
- row. When the order changes, a group whose run goes on is kept and moved,
489
- and a group whose run ended leaves.
490
-
491
- A group template with no nested template is reported to the console. More
492
- group templates than the order has terms show each extra level as one
493
- group, and are reported once.
494
-
495
- ### Aggregates
496
-
497
- `lb-count`, `lb-sum="col"`, `lb-avg="col"`, `lb-min="col"` and
498
- `lb-max="col"` set an element from the rows of the nearest group around it,
499
- or of the whole list outside any group:
500
-
501
- ```html
502
- <tr><td><span lb-count></span> people</td><td lb-sum="salary"></td></tr>
503
- ```
504
-
505
- | Attribute | Sets the element to |
506
- |----------------|-----------------------------------------------------------|
507
- | `lb-count` | The number of rows |
508
- | `lb-sum="col"` | The sum of `col` |
509
- | `lb-avg="col"` | The average of `col` |
510
- | `lb-min="col"` | The least value of `col`, by the [order](#order)'s comparison |
511
- | `lb-max="col"` | The greatest, likewise |
512
-
513
- An aggregate lands as a column does, with `lb-column-value`. A sum and an
514
- average are exact decimals, read from JSON numbers and from strings holding
515
- a plain decimal, as Postgres sends `numeric`. A sum has as many places as
516
- the most precise value, and an average up to two more. `null` and empty
517
- values are left out, as SQL leaves them out, and any other value is left
518
- out and reported once. The result lands unformatted.
519
-
520
- An aggregate covers the rows showing, so a row a filter hides is left out.
521
- Where the true total matters beyond what the page shows, have the server
522
- answer it as a row.
523
-
524
424
  ### What happened to a row
525
425
 
526
426
  Every change to a list is one a stylesheet can see:
@@ -533,7 +433,6 @@ Every change to a list is one a stylesheet can see:
533
433
  | `lb-row-requested` | `created`, `changed` or `moved` by this page's request |
534
434
  | `lb-row-unplaced` | Stands where the order or the filter would not leave it |
535
435
  | `lb-row-leaving` | A row on its way out |
536
- | `lb-group-leaving` | A group on its way out |
537
436
 
538
437
  The hub touches a row when an answer names it. `lb-row-created`,
539
438
  `lb-row-changed` and `lb-row-moved` are cleared when it next does, and one
@@ -544,8 +443,8 @@ query, and set only by a landing that answers a request from the page.
544
443
  placed again; see
545
444
  [Placing by hand](#placing-by-hand).
546
445
 
547
- A row or a group that leaves loses `lb-row-live` or `lb-group-live`, takes
548
- `lb-row-leaving` or `lb-group-leaving` and `inert`
446
+ A row that leaves loses `lb-row-live`, takes
447
+ `lb-row-leaving` and `inert`
549
448
  ([miswart](./miswarts.md#leaving-rows-are-inert)), and is removed once its
550
449
  animations finish, at once when there are none.
551
450
 
@@ -569,7 +468,7 @@ The hub keeps each query's last answer until the page changes, with any
569
468
  patch since applied to it. An element that names a query and arrives after
570
469
  that query's answer is filled from what was kept, at the end of the landing
571
470
  that follows its arrival. Such an element is a picker in a new live row, or
572
- scaffolding around rows, such as a group's heading or a ghost row. So
471
+ scaffolding around rows, such as a ghost row. So
573
472
  a query nested in another query's rows need not land again, or land after
574
473
  the outer query, for a new outer row to show its choices.
575
474
 
@@ -637,8 +536,8 @@ answered side by side. A column never holds rows.
637
536
  ```
638
537
 
639
538
  Many masters, each with its own detail, is one `rows` query of joined rows:
640
- each detail row carries its master's columns. A [group template](#groups)
641
- shows each master as a group, ordered by the master's key first.
539
+ each detail row carries its master's columns, ordered by the master's key
540
+ first.
642
541
 
643
542
  A query nested in another query's row template receives the same rows in
644
543
  every live row. That serves a picker offering the same choices on every
@@ -753,8 +652,7 @@ Every request has one shape:
753
652
  ```
754
653
 
755
654
  The hub takes `query`, `key` and `values` from where the element sits, and
756
- sends each when it has one. A request acting on several rows carries `keys`
757
- in place of `key`; see [Selecting rows](#selecting-rows). The server runs the handler for the name and
655
+ sends each when it has one. The server runs the handler for the name and
758
656
  answers with response items, which land like any others.
759
657
 
760
658
  ### Committing
@@ -929,7 +827,7 @@ it, and sends nothing:
929
827
  Its controls start at their HTML defaults, and its `lb-show` branches are
930
828
  as for a row whose every column is null.
931
829
  - It stands after every row of the list, in the order added, and is not
932
- ordered, filtered or aggregated, since it holds no data. It counts in
830
+ ordered or filtered, since it holds no data. It counts in
933
831
  `lb-query-row-count`.
934
832
  - The form's submit carries it in `rows`, with `key: ""`, beside the rows on
935
833
  record: an insert while the master is not on record, an update once it
@@ -1012,7 +910,7 @@ nothing is not sent.
1012
910
  For a query the hub serves, `lb-url` or `lb-choices`, the hub answers
1013
911
  `lb-row-update` itself, with no round trip; see
1014
912
  [Query parms](./chrome.md#query-parms) and [Choices](#choices). The third,
1015
- `lb-hub`, takes no request.
913
+ `lb-hub-status`, takes no request.
1016
914
 
1017
915
  A key comes from a live row, or from an element a `row` landed on, so all
1018
916
  three work against either kind:
@@ -1118,6 +1016,27 @@ The rows a form's answer lands are placed by the list's `lb-manual`, as any
1118
1016
  answer's are, whether the form was pointed by Edit or by New; see
1119
1017
  [Placing by hand](#placing-by-hand).
1120
1018
 
1019
+ A list nested in the form follows the row the form is bound to. When Edit
1020
+ or New binds the form, the hub drops the list's rows not yet entered and
1021
+ asks the server for the list's query again, as the request `lb-read`, with
1022
+ the bound row's key added to the query string as the parm named for the
1023
+ form's key column. `contextFor` receives it among the parms, and the query
1024
+ reads it as it reads any parm, so one query serves a master the URL names
1025
+ and a master Edit binds. New sends the parm empty, and the query answers
1026
+ with no rows:
1027
+
1028
+ ```ts
1029
+ function contextFor(req: Request, parms: URLSearchParams): HubContext {
1030
+ return { db: openDb(), paycheck: parms.get("paycheck_id") ?? "" };
1031
+ }
1032
+
1033
+ deductions: rows("id", (ctx) => ctx.db.deductionsFor(ctx.paycheck)),
1034
+ ```
1035
+
1036
+ A form with no list inside it sends nothing when it is bound. An answer
1037
+ that returns after the form is bound to another row, or after the page
1038
+ moved on, lands nothing.
1039
+
1121
1040
  A successful request from the form lets it go, as Cancel does: the form
1122
1041
  empties, its controls return to their defaults, and the stamps go. A failed
1123
1042
  one leaves the form as the user had it, to correct.
@@ -1145,63 +1064,6 @@ outside a live row of the form's query, Edit for a key the hub holds no row
1145
1064
  for, and any of these three commands aimed at an element that is not a form
1146
1065
  carrying `lb-query`.
1147
1066
 
1148
- ### Selecting rows
1149
-
1150
- A checkbox carrying `lb-select` in a row template selects the live row it
1151
- sits in. It is not a column: it carries no `lb-column`, lands nothing and
1152
- is not gathered. The hub stamps the row `lb-row-selected` while it is
1153
- ticked.
1154
-
1155
- A request for the query that names no one row then acts on the rows
1156
- selected, and carries their keys as `keys`:
1157
-
1158
- ```html
1159
- <tbody lb-query="transactions">
1160
- <template>
1161
- <tr>
1162
- <td><input type="checkbox" lb-select lb-show="selecting" /></td>
1163
- <td lb-column="description"></td>
1164
- <td lb-column="amount"></td>
1165
- </tr>
1166
- </template>
1167
- </tbody>
1168
-
1169
- <form lb-query="transactions" lb-request="lb-row-update">
1170
- <lb-picker lb-query="accounts" lb-column="acct" exp-label="Account" exp-column="name"></lb-picker>
1171
- <button type="submit">Apply</button>
1172
- <button type="button" lb-request="lb-row-delete">Delete</button>
1173
- </form>
1174
- ```
1175
-
1176
- ```json
1177
- { "name": "lb-row-update", "query": "transactions", "keys": ["4", "9"], "values": { "acct": "12" } }
1178
- ```
1179
-
1180
- | Request | With rows selected | With none ticked |
1181
- |------------------|----------------------------------------|----------------------|
1182
- | `lb-row-update` | Sent with `keys` | Not sent |
1183
- | `lb-row-delete` | Sent with `keys` | Not sent |
1184
- | A declared one | Sent with `keys` | Sent with `keys: []` |
1185
- | `lb-row-insert` | Sent as ever; the selection is not its | Sent as ever |
1186
-
1187
- An update or delete with nothing ticked has nothing to act on, and nothing
1188
- is reported. A request from inside a row still names that row alone. The
1189
- server runs the page's `rowUpdate` or `rowDelete` once for each key, as for
1190
- each row alone, and refreshes once; see [crud](./page-files.md#crud).
1191
-
1192
- After the request succeeds, the hub clears the selection. A failed one
1193
- leaves it, to try again.
1194
-
1195
- Only a checkbox that is showing counts. Turning a selector off with
1196
- `lb-show` also clears its tick, so leaving a selecting mode abandons the
1197
- selection rather than keeping it out of sight. With every selector off, a
1198
- request for the query is an ordinary one again.
1199
-
1200
- Which rows are selected, and whether the page is in a selecting mode, is
1201
- interaction state. A mode the URL keeps, such as `?selecting=1`, reaches
1202
- the rows as a column the query computes from the parm, which `lb-show`
1203
- reads.
1204
-
1205
1067
  ### Declared requests
1206
1068
 
1207
1069
  A request name the page declares under `handlers` runs the application's
@@ -1331,7 +1193,7 @@ returns its controls to their defaults, and the hub removes
1331
1193
  `lb-column-value` from each of them. A cancelled reset keeps both.
1332
1194
 
1333
1195
  A page load that fails lands nothing and is reported to the console, and
1334
- the query `lb-hub` says so; see [The hub's state](./chrome.md#the-hubs-state).
1196
+ the query `lb-hub-status` says so; see [The hub's state](./chrome.md#the-hubs-state).
1335
1197
  A load
1336
1198
  started by a request for `lb-url` stamps the element that sent it, as any
1337
1199
  request stamps its element.
@@ -1392,7 +1254,7 @@ The developer writes `lb-` attributes in markup, and script never assigns
1392
1254
  them. The builder checks the markup of the chrome and of every page, after
1393
1255
  expansion, and refuses to build:
1394
1256
 
1395
- - an `lb-` attribute that is not one of the eighteen the developer writes;
1257
+ - an `lb-` attribute that is not one of the fourteen the developer writes;
1396
1258
  `lb-exp-slot` and `lb-exp-template` are consumed by expansion and never
1397
1259
  reach the check
1398
1260
  - `lb-request` naming an `lb-` request other than the three Loadbare provides
@@ -1420,16 +1282,11 @@ expansion, and refuses to build:
1420
1282
  no column
1421
1283
  - `lb-manual` on an element not carrying `lb-query`, or naming anything but
1422
1284
  `order`, `filter` or both
1423
- - `lb-select` on anything but `<input type="checkbox">`, outside a row
1424
- template, or beside `lb-column` or `lb-request`
1425
1285
  - a `command` beginning `--lb-` that is not one of the four commands
1426
1286
  - one of the four commands with no `commandfor`
1427
1287
  - `--lb-new` aimed at an element the markup holds that is neither a
1428
1288
  `<form>` nor a list carrying `lb-query` nested directly in a form's own
1429
1289
  query
1430
- - `lb-group` on anything but a `<template>`, or on one holding no
1431
- `<template>` for its contents
1432
- - an aggregate on a `<template>`, or any but `lb-count` naming no column
1433
1290
  - a chrome without exactly one `<lb-hub>` and exactly one `<main>`, a
1434
1291
  `<main>` outside `<lb-hub>`, or a `<main>` holding anything but whitespace
1435
1292
  - a page carrying an `<lb-hub>` or a `<main>`
@@ -68,7 +68,7 @@ gathered.
68
68
  ## Rows hidden by a filter
69
69
 
70
70
  **What.** A row that does not pass the user's filter gets the `hidden`
71
- attribute, and so does a group whose rows are all hidden.
71
+ attribute.
72
72
 
73
73
  **Rule broken.** The hub writes only its own stamps, which always
74
74
  begin with `lb-`.
@@ -78,7 +78,7 @@ over rows already on the page needs no round trip, and `hidden` is HTML's
78
78
  own word for an element that is present but not shown. A stamp of our
79
79
  own would make every stylesheet restate what the browser already does.
80
80
 
81
- **Why it terminates.** It applies only to live rows and groups under a
81
+ **Why it terminates.** It applies only to live rows under a
82
82
  filter.
83
83
 
84
84
  ## Leaving rows are inert