@loadbare/app 0.14.0 → 0.15.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 (35) hide show
  1. package/dist/build/elements.d.ts +12 -2
  2. package/dist/build/elements.d.ts.map +1 -1
  3. package/dist/build/elements.js +44 -10
  4. package/dist/build/elements.js.map +1 -1
  5. package/dist/core/lb-constants.d.ts +2 -1
  6. package/dist/core/lb-constants.d.ts.map +1 -1
  7. package/dist/core/lb-constants.js +16 -3
  8. package/dist/core/lb-constants.js.map +1 -1
  9. package/dist/core/lb-types.d.ts +25 -8
  10. package/dist/core/lb-types.d.ts.map +1 -1
  11. package/dist/core/lb-types.js.map +1 -1
  12. package/dist/hub/lb-apply.d.ts +2 -0
  13. package/dist/hub/lb-apply.d.ts.map +1 -1
  14. package/dist/hub/lb-apply.js +18 -29
  15. package/dist/hub/lb-apply.js.map +1 -1
  16. package/dist/hub/lb-hub.browser.d.ts.map +1 -1
  17. package/dist/hub/lb-hub.browser.js +41 -5
  18. package/dist/hub/lb-hub.browser.js.map +1 -1
  19. package/dist/hub/lb-lifecycle.d.ts +40 -0
  20. package/dist/hub/lb-lifecycle.d.ts.map +1 -0
  21. package/dist/hub/lb-lifecycle.js +140 -0
  22. package/dist/hub/lb-lifecycle.js.map +1 -0
  23. package/docs/TECHREF-1.0.md +49 -47
  24. package/docs/comparison.md +7 -5
  25. package/docs/possible-ideas.md +6 -0
  26. package/docs/reference/custom-elements.md +106 -87
  27. package/docs/reference/data-binding.md +7 -7
  28. package/docs/reference/widgets.md +2 -2
  29. package/docs/testing.md +2 -2
  30. package/package.json +1 -1
  31. package/skills/loadbare-app/SKILL.md +20 -16
  32. package/skills/loadbare-app/references/TECHREF-1.0.md +49 -47
  33. package/skills/loadbare-app/references/custom-elements.md +106 -87
  34. package/skills/loadbare-app/references/data-binding.md +7 -7
  35. package/skills/loadbare-app/references/widgets.md +2 -2
@@ -278,10 +278,10 @@ A custom element reads `lb-` attributes and never assigns one. The developer
278
278
  writes them in markup, and the hub and the builder write their stamps.
279
279
 
280
280
  Loadbare reserves method names beginning with `lb` on a custom element, for
281
- the methods it calls; see [Holding rows](#holding-rows). The build refuses
282
- a script that declares a method, accessor or field whose name is `lb`, a
283
- capital, and anything else Loadbare does not call, so a misspelled
284
- `lbRowLanded` fails the build rather than never being called.
281
+ the methods it calls; see [When its code runs](#when-its-code-runs). The
282
+ build refuses a script that declares a method, accessor or field whose name
283
+ is `lb`, a capital, and anything else Loadbare does not call, so a
284
+ misspelled `lbRowLanded` fails the build rather than never being called.
285
285
 
286
286
  ### What the hub and an element say to each other
287
287
 
@@ -291,39 +291,80 @@ Each direction has one mechanism for each kind of message:
291
291
  |----------------|--------------------------------------------|-----------------------------|-------|
292
292
  | 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
293
  | Hub to element | State the platform already names | A property | A control's `value` |
294
- | Hub to element | Work the hub needs done now, while landing | An optional `lb` method | `lbRowsLanded` |
294
+ | Hub to element | What the hub did | An optional `lb` method | `lbAttached`, `lbRowsLanded`, `lbRequestDone`, `lbDetached` |
295
295
  | Hub to element | A moment an element started | A bubbling event | `lb-request-done` |
296
296
  | Element to hub | A moment | A bubbling event | `lb-request`, and `change`, `click` and `submit` |
297
297
 
298
- State is an attribute: a stylesheet can select on it, and an element that
299
- upgrades late still finds it. A method is for work the hub cannot go on
300
- without, done by one element at one point in landing. A moment is an event,
301
- because the element that cares is often an ancestor of the one it concerns,
302
- and an event reaches it knowing neither.
298
+ State is an attribute, because a stylesheet can select on it. A method is
299
+ how the hub tells one element what it just did to that element. A moment is
300
+ an event, because the element that cares is often an ancestor of the one it
301
+ concerns, and an event reaches it knowing neither.
303
302
 
304
303
  ### When its code runs
305
304
 
306
- The hub creates, places and parks elements, and each of those reaches a
307
- custom element as the browser's lifecycle callbacks. It is the same instance
308
- from its constructor on, however often it moves:
305
+ The hub tells a custom element what it did through four optional methods,
306
+ named for the hub's own work. The `HubLifecycle` interface in
307
+ `@loadbare/app/types` declares them. Here is an element that closes its
308
+ popup on Escape, listening on `document` only while the hub has it on the
309
+ page:
309
310
 
310
- | What the hub does | Callbacks, in order |
311
- |----------------------------------------------------|---------------------------------------------|
312
- | Shows a page | `constructor`, `connectedCallback` |
313
- | Creates a live row, filled before it is placed | `constructor` |
314
- | Places a live row for the first time | `connectedCallback` |
315
- | Places it again, as it does on every set of all rows | `disconnectedCallback`, `connectedCallback` |
316
- | Removes a live row | `disconnectedCallback` |
317
- | Turns `lb-show` off | `disconnectedCallback`, `adoptedCallback` |
318
- | Turns `lb-show` on | `adoptedCallback`, `connectedCallback` |
319
- | Turns on an element the builder shipped absent | `constructor`, `connectedCallback` |
311
+ ```ts
312
+ // src/note-popup.browser.ts
313
+ import type { HubLifecycle } from "@loadbare/app/types";
314
+
315
+ class NotePopup extends HTMLElement implements HubLifecycle {
316
+ #onKey = (e: KeyboardEvent) => {
317
+ if (e.key === "Escape") this.querySelector("dialog")?.close();
318
+ };
320
319
 
321
- The builder ships every `lb-show` element inside its template, and nothing
322
- in a template is upgraded, so such an element has no instance until its
323
- column first turns on. See
324
- [Conditional rendering](./data-binding.md#conditional-rendering).
320
+ lbAttached() {
321
+ document.addEventListener("keydown", this.#onKey);
322
+ }
325
323
 
326
- So each kind of work has one place:
324
+ lbDetached() {
325
+ document.removeEventListener("keydown", this.#onKey);
326
+ }
327
+ }
328
+
329
+ customElements.define("note-popup", NotePopup);
330
+ ```
331
+
332
+ | Method | The hub calls it |
333
+ |---------------------------------|-------------------------------------------------------------------|
334
+ | `lbAttached()` | Once, after it places the element and every column has landed on it |
335
+ | `lbRowsLanded()` | After rows land on the element, with every row placed and stamped |
336
+ | `lbRequestDone(detail, origin)` | After a request committed by the element, or inside it, has finished |
337
+ | `lbDetached()` | Once, after it removes the element |
338
+
339
+ The hub places an element when it starts up the chrome, shows a page,
340
+ creates a live row, or builds a group. It places the chrome and a page as
341
+ the page's first answer lands, or once that round trip fails, so an element
342
+ in either hears `lbAttached` with the page's data in place. It removes an
343
+ element when a live row or a group has finished leaving, and when it shows
344
+ another page in place of the one holding it. Write setup in `lbAttached` and its undoing in
345
+ `lbDetached`; the two pair exactly, so neither guards against running
346
+ twice.
347
+
348
+ Moving a row and turning `lb-show` off and on are neither placing nor
349
+ removing. The element keeps its instance and hears nothing. An element
350
+ under an `lb-show` that is off is placed inside its template, so its
351
+ `lbAttached` runs whether or not it shows. Measure and focus when the user
352
+ acts, never in `lbAttached`, where the element may not be rendered.
353
+
354
+ The hub calls `lbAttached` on an outer custom element before the ones
355
+ inside it, and `lbDetached` on the inner ones first. A table that sets the
356
+ `form` attribute on its controls in `lbAttached` does so before each control
357
+ hears its own.
358
+
359
+ An element a script creates hears none of the four, and neither does the
360
+ copy of a moved row the hub leaves behind while it fades. The hub calls
361
+ them only on what it places.
362
+
363
+ Never write `connectedCallback`, `disconnectedCallback`, `adoptedCallback`
364
+ or `connectedMoveCallback`. The browser calls those on every move, and the
365
+ build refuses a script that declares one.
366
+
367
+ Each kind of work then has one place:
327
368
 
328
369
  1. **A listener on the element itself goes in the constructor.** It is
329
370
  attached once and survives every move, and a live row gets its own
@@ -331,15 +372,11 @@ So each kind of work has one place:
331
372
  element, so the listener needs no child to exist yet. The constructor
332
373
  reads no attribute and no child: an element made with
333
374
  `document.createElement` has neither when it runs.
334
- 2. **A child is looked up when it is needed**, in a handler, a getter,
335
- or `lbRowsLanded`, and never held from setup.
336
- 3. **`connectedCallback` runs on every move**, so it is written to run
337
- again. It rearranges the element's own children into a state it checks
338
- for first, or adds a listener to `document` or `window`, which
339
- `disconnectedCallback` removes. Work done once per instance that needs
340
- the element's attributes, such as a control taking up a value that landed
341
- before it upgraded, goes behind a flag there; see
342
- [Being a control](#being-a-control).
375
+ 2. **A child is looked up when it is needed**, in a handler, a getter, or
376
+ one of the four methods, and never held from setup.
377
+ 3. **Work done once per instance goes in `lbAttached`**, with its undoing
378
+ in `lbDetached`: rearranging the element's own children, or a listener on
379
+ `document` or `window`.
343
380
 
344
381
  ### Being a control
345
382
 
@@ -350,20 +387,10 @@ and commits its `lb-request` on `change`:
350
387
 
351
388
  ```ts
352
389
  // src/star-rating.browser.ts
353
- import { ATTR_COLUMN_VALUE } from "@loadbare/app/constants";
354
-
355
390
  class StarRating extends HTMLElement {
356
391
  static formAssociated = true;
357
392
  #internals = this.attachInternals();
358
393
  #value = "";
359
- #connected = false;
360
-
361
- connectedCallback() {
362
- if (this.#connected) return;
363
- this.#connected = true;
364
- const stamped = this.getAttribute(ATTR_COLUMN_VALUE);
365
- if (stamped !== null) this.value = stamped;
366
- }
367
394
 
368
395
  get value(): string {
369
396
  return this.#value;
@@ -399,14 +426,6 @@ customElements.define("star-rating", StarRating);
399
426
  Dispatch `change` with `bubbles: true`, as a native control's does, when the
400
427
  user commits an edit. Setting `value` from the hub fires nothing.
401
428
 
402
- A control can receive its value before it is a control. An element the
403
- builder shipped absent, or one whose definition loads after the hub has
404
- landed, is not upgraded when its column lands, so the hub cannot set its
405
- `value` and the value arrives as `lb-column-value` alone. Take the stamp up
406
- the first time the control connects, as `connectedCallback` does above, and
407
- never again: after that the hub sets `value` itself, and a later move must
408
- not undo an edit.
409
-
410
429
  A form owns a form-associated custom element the way it owns an `<input>`,
411
430
  including through the HTML `form` attribute, so a form gathers it on submit.
412
431
  Write `formResetCallback` to restore the default value: the hub resets a form
@@ -571,23 +590,28 @@ Let the event bubble, so an ancestor can stop it before the hub sends it.
571
590
  ### Hearing how it turned out
572
591
 
573
592
  Once what a request brought back has landed, or its round trip has failed,
574
- the hub dispatches `lb-request-done` from the element that committed. It
575
- bubbles, so the element that cares need not be the one that committed: a
576
- dialog hears it from the form inside it.
593
+ the hub calls `lbRequestDone(detail, origin)` on the custom element that
594
+ committed and on every custom element around it, innermost first. `origin`
595
+ is the element that committed, so the element that cares need not be the
596
+ one that committed: a dialog hears it from the form inside it.
577
597
 
578
598
  ```ts
579
- import { LB_DONE_EVENT_NAME } from "@loadbare/app/constants";
580
- import type { HubRequestDone } from "@loadbare/app/types";
581
-
582
- this.addEventListener(LB_DONE_EVENT_NAME, (e) => {
583
- const { request, items, error } = (e as CustomEvent<HubRequestDone>).detail;
584
- if (error) return;
585
- const item = items.find((i) => i.query === request.query);
586
- const created = item?.patch?.rows?.[0];
587
- if (created) this.choose(String(created[item!.key]));
588
- });
599
+ import type { HubLifecycle, HubRequestDone } from "@loadbare/app/types";
600
+
601
+ class NewRowDialog extends HTMLElement implements HubLifecycle {
602
+ lbRequestDone({ request, items, error }: HubRequestDone) {
603
+ if (error) return;
604
+ const item = items.find((i) => i.query === request.query);
605
+ const created = item?.patch?.rows?.[0];
606
+ if (created) this.choose(String(created[item!.key]));
607
+ }
608
+ }
589
609
  ```
590
610
 
611
+ The hub then dispatches `lb-request-done` from the element that committed,
612
+ carrying the same `detail` as a bubbling `CustomEvent`, for any other
613
+ listener. Import its name as `LB_DONE_EVENT_NAME`.
614
+
591
615
  `items` is every response item that landed because of the request, in
592
616
  order. `refusal` is set beside `error` when the handler refused the
593
617
  request, holding its `message` and `columns`. An answer that moved the URL contributes its `lb-url` item, which
@@ -596,11 +620,12 @@ the round trip failed, and then nothing landed. A new row's key is in the
596
620
  answer only when the handler returned it in a patch: a refresh sends all
597
621
  rows, and nothing marks which one is new.
598
622
 
599
- By the time the event is dispatched, `lb-request-pending` is gone and the
600
- page is as the answer left it. A request that was never sent, because the
601
- hub or an ancestor stopped it, gets no `lb-request-done`. An element the
602
- answer removed, such as the row a delete took away, dispatches the event
603
- outside the document, and no ancestor it had there hears it.
623
+ By the time either is called, `lb-request-pending` is gone and the page is
624
+ as the answer left it. A request that was never sent, because the hub or an
625
+ ancestor stopped it, gets neither. An element the answer removed, such as
626
+ the row a delete took away, is outside the document by then: no ancestor it
627
+ had there hears the event, and no custom element the hub has removed hears
628
+ `lbRequestDone`.
604
629
 
605
630
  ### Holding rows
606
631
 
@@ -611,26 +636,22 @@ plain element carrying `lb-query` with a row template inside it needs no
611
636
  code, so a custom element holds rows only to do something with them once
612
637
  they land.
613
638
 
614
- A custom element carrying `lb-query` and a row template may implement one
615
- optional method, which the hub calls:
639
+ A custom element carrying `lb-query` and a row template implements
640
+ `lbRowsLanded`, which the hub calls once the rows have landed, with every
641
+ row placed and stamped:
616
642
 
617
643
  ```ts
618
- import type { RowsHost } from "@loadbare/app/types";
644
+ import type { HubLifecycle } from "@loadbare/app/types";
619
645
  import { ATTR_ROW_REQUESTED, REQUESTED_CREATED } from "@loadbare/app/constants";
620
646
 
621
- class ScrollingList extends HTMLElement implements RowsHost {
647
+ class ScrollingList extends HTMLElement implements HubLifecycle {
622
648
  lbRowsLanded() {
623
- // Once, after the rows have landed, every row placed and stamped.
624
649
  this.querySelector(`[${ATTR_ROW_REQUESTED}="${REQUESTED_CREATED}"]`)
625
650
  ?.scrollIntoView({ block: "nearest" });
626
651
  }
627
652
  }
628
653
  ```
629
654
 
630
- | Method | The hub calls it |
631
- |----------------|-------------------------------------------------------|
632
- | `lbRowsLanded` | After the rows land, with every row placed and stamped |
633
-
634
655
  Everything else is the hub's:
635
656
 
636
657
  | Concern | The hub |
@@ -649,11 +670,9 @@ gives each option and `<optgroup>` what landing does not, a value and a
649
670
  label, and the other scrolls to the row the page's request created or
650
671
  moved.
651
672
 
652
- A custom element the builder shipped absent under `lb-show` has not
653
- upgraded while its column is off, and the hub places the rows that land on
654
- it then as it places any. When the column first turns on and the element
655
- upgrades, the hub lands the query's last answer on it again, as all rows,
656
- so `lbRowsLanded` runs.
673
+ A custom element under an `lb-show` that is off still holds its rows. The
674
+ hub places the rows that land on it then as it places any, and calls its
675
+ `lbRowsLanded`, so it returns current.
657
676
 
658
677
  A custom element that walks its own rows finds them with `LIVE_ROW` from
659
678
  `@loadbare/app/constants`, never by `lb-key-value`: an element a `row` lands
@@ -284,10 +284,9 @@ scaffolding around rows, such as a group's heading or a ghost row. So
284
284
  a query nested in another query's rows need not land again, or land after
285
285
  the outer query, for a new outer row to show its choices.
286
286
 
287
- A custom element the builder shipped absent has not upgraded while its
288
- `lb-show` column is off. When the column first turns on and the element
289
- upgrades, the hub lands the kept answer on it again, so its `lbRowsLanded`
290
- runs. See [Holding rows](./custom-elements.md#holding-rows).
287
+ A custom element under an `lb-show` that is off is filled like any other,
288
+ and its `lbRowsLanded` runs as its rows land. See
289
+ [Holding rows](./custom-elements.md#holding-rows).
291
290
 
292
291
  Nothing is answered from what the hub kept: a request always goes to the
293
292
  server, and a query re-runs only when a response names it. A patch that
@@ -877,9 +876,10 @@ An ancestor may stop the event, and the request is not sent. A custom
877
876
  element may dispatch the event itself; see
878
877
  [Sending a request](./custom-elements.md#sending-a-request).
879
878
 
880
- Once the answer has landed, or the round trip has failed, the hub dispatches
881
- a bubbling `lb-request-done` event from the same element, carrying the
882
- request and what landed because of it, or the error; see
879
+ Once the answer has landed, or the round trip has failed, the hub calls
880
+ `lbRequestDone` on the custom elements from the committing element outward,
881
+ then dispatches a bubbling `lb-request-done` event from the same element.
882
+ Both carry the request and what landed because of it, or the error; see
883
883
  [Hearing how it turned out](./custom-elements.md#hearing-how-it-turned-out).
884
884
 
885
885
  ## The URL
@@ -182,8 +182,8 @@ the hub until the user says yes:
182
182
  | `exp-request` | The confirm button's `lb-request` |
183
183
  | `exp-confirm` | The confirm button's text, `Confirm` when absent |
184
184
 
185
- The widget closes the dialog once `lb-request-done` says the request
186
- succeeded, and leaves it open on a refusal.
185
+ The widget closes the dialog once the request succeeds, and leaves it open
186
+ on a refusal.
187
187
 
188
188
  ## `lb-form`
189
189
 
package/docs/testing.md CHANGED
@@ -263,8 +263,8 @@ One window per file, which is what running each file in its own process gives
263
263
  for free — registration is global and permanent, so sharing a process across
264
264
  widget files would mean sharing a registry.
265
265
 
266
- What that buys is real upgrades: the constructor, `connectedCallback`, and
267
- `attributeChangedCallback` for attributes already present. That last one is
266
+ What that buys is real upgrades: the constructor, the hub's `lb` methods,
267
+ and `attributeChangedCallback` for attributes already present. That last one is
268
268
  the mechanism by which hydration and refresh are one operation, and it
269
269
  behaves the same in jsdom as in a browser. What it does not buy is layout,
270
270
  painting, or navigation.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@loadbare/app",
3
3
  "description": "High performance web app framework for server-bound applications",
4
- "version": "0.14.0",
4
+ "version": "0.15.0",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",
@@ -238,7 +238,7 @@ and are not gathered.
238
238
  row.** A form cannot sit inside another form or in a table row, so a dialog
239
239
  that creates an account while a posting's picker is choosing one goes at the
240
240
  end of the page, and each row opens it with `commandfor`. The dialog learns
241
- the new key from `lb-request-done`.
241
+ the new key in `lbRequestDone`.
242
242
 
243
243
  **A set the user edits has one form, pointed at a row.** Never put a form
244
244
  in every row. Write one form naming the set's query, outside every row,
@@ -387,25 +387,29 @@ element (`static formAssociated = true`) with a `value` property that fires
387
387
  on `change`. Any other custom element keeps its content and receives the
388
388
  column as its `lb-column-value` attribute.
389
389
 
390
- **A custom element connects many times.** It is moved, never rebuilt: the
391
- hub moves a live row the order puts elsewhere, and parks an `lb-show`
392
- element in a template while it is off, so `connectedCallback` runs on every
393
- move. Listen on the element itself in the constructor, look a
394
- child up when it is needed, and in a control take up `lb-column-value` once,
395
- on first connect, since a value can land before the element upgrades.
390
+ **A custom element hears the hub's lifecycle, never the browser's.** The
391
+ hub calls `lbAttached()` once when it places the element, already filled,
392
+ and `lbDetached()` once when it removes it. Moving a row and turning
393
+ `lb-show` off and on call neither, and the instance is kept. Never write
394
+ `connectedCallback`, `disconnectedCallback`, `adoptedCallback` or
395
+ `connectedMoveCallback`; the build refuses them. Listen on the element
396
+ itself in the constructor, look a child up when it is needed, and put
397
+ once-per-instance setup in `lbAttached` with its undoing in `lbDetached`.
396
398
 
397
399
  **The hub and a custom element talk in four ways, one per kind of
398
400
  message.** State the hub gives an element is an attribute stamp, or `value`
399
- on a control. Work the hub needs done while landing is an optional `lb`
400
- method: `lbRowsLanded`. A moment is a bubbling event: an
401
- element sends `lb-request`, and the hub answers on the same element with
402
- `lb-request-done`, carrying the request and the response items that landed,
403
- or the error. Listen for `lb-request-done` on whichever ancestor needs the
404
- outcome, such as a dialog choosing the row its form just created.
401
+ on a control. What the hub did to an element is an optional `lb` method:
402
+ `lbAttached`, `lbRowsLanded`, `lbRequestDone`, `lbDetached`, declared by
403
+ `HubLifecycle`. A moment is a bubbling event: an element sends
404
+ `lb-request`, and the hub answers by calling `lbRequestDone(detail,
405
+ origin)` on the custom elements from the committing element outward, then
406
+ dispatching `lb-request-done` from it, each carrying the request and the
407
+ response items that landed, or the error. A dialog choosing the row its
408
+ form just created implements `lbRequestDone`.
405
409
 
406
410
  **A widget that holds rows may sit under `lb-show`.** Rows that land while
407
- it is away are placed by the hub, and its `lbRowsLanded` runs once it first
408
- appears, so hide a table with `lb-show`, not with a stylesheet rule.
411
+ it is away are placed by the hub and its `lbRowsLanded` runs, so hide a
412
+ table with `lb-show`, not with a stylesheet rule.
409
413
 
410
414
  **Name a custom element script `<tag>.browser.ts`.** A plain `<tag>.ts`
411
415
  stays on the server and the tag goes unregistered.
@@ -434,7 +438,7 @@ parser loses the tag before the build can report it.
434
438
  Reach for one only when plain HTML cannot do the job. A set of rows, a form
435
439
  and a condition need none. A custom element exists to be a control of its
436
440
  own, or to act on rows once they land through the hook the hub calls,
437
- `lbRowsLanded` (the `RowsHost` interface).
441
+ `lbRowsLanded` (the `HubLifecycle` interface).
438
442
 
439
443
  Before writing one, check `@loadbare/widgets`: `lb-input`, `lb-select`,
440
444
  `lb-options`, `lb-picker`, `lb-table`, `lb-unknown-page`, `lb-confirm`,
@@ -715,10 +715,9 @@ standing where it stood, and moved back out when the column turns on. It is
715
715
  not rendered, focused, clicked, announced or gathered. It is moved and
716
716
  never rebuilt, so a custom element keeps its instance and a control keeps
717
717
  what was typed into it. Landing reaches into that template, so the element
718
- and everything inside it return current. A custom element sees
719
- `disconnectedCallback` then `adoptedCallback` going in, and
720
- `adoptedCallback` then `connectedCallback` coming out, and still receives
721
- `lb-column-value` while it is away.
718
+ and everything inside it return current. A custom element hears nothing
719
+ going in or coming out, and still receives `lb-column-value` while it is
720
+ away.
722
721
 
723
722
  The builder ships every `lb-show` element already inside its template, so
724
723
  nothing conditional shows until its row lands. An absent element's template
@@ -1098,8 +1097,10 @@ makes a confirmation wrapper possible without the wrapped element knowing
1098
1097
  about it.
1099
1098
 
1100
1099
  Once what the request brought back has landed, or its round trip has failed,
1101
- the hub dispatches the bubbling `lb-request-done` event from the same
1102
- element. Its `detail`, `HubRequestDone` in `@loadbare/app/types`, holds
1100
+ the hub calls `lbRequestDone(detail, origin)` on the committing element and
1101
+ every custom element around it, innermost first (see
1102
+ [Lifecycle](#lifecycle)), then dispatches the bubbling `lb-request-done`
1103
+ event from the same element. Its `detail`, `HubRequestDone` in `@loadbare/app/types`, holds
1103
1104
  the request as the hub sent it and `items`, every response item that landed
1104
1105
  because of it, in order. An answer that moved the URL contributes its
1105
1106
  `lb-url` item and the page load. `error` is set instead when the round trip
@@ -1884,14 +1885,12 @@ Each direction has one mechanism for each kind of message:
1884
1885
  | -------------- | ------------------------------------------- | ---------------------------- | ------------------------------------------------ |
1885
1886
  | 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` |
1886
1887
  | Hub to element | State the platform already names | A property | A control's `value` |
1887
- | Hub to element | Work the hub needs done now, while landing | An optional `lb` method | `lbRowsLanded` |
1888
+ | Hub to element | What the hub did | An optional `lb` method | `lbAttached`, `lbRowsLanded`, `lbRequestDone`, `lbDetached` |
1888
1889
  | Hub to element | A moment an element started | A bubbling event | `lb-request-done` |
1889
1890
  | Element to hub | A moment | A bubbling event | `lb-request`, and `change`, `click` and `submit` |
1890
1891
 
1891
- State is an attribute, because a stylesheet can select on it and an element
1892
- that upgrades late still finds it. A method is for work the hub cannot go
1893
- on without, since it acts on one element at one point in landing and nothing
1894
- else can do it. A moment is an event, because the element that cares is
1892
+ State is an attribute, because a stylesheet can select on it. A method is
1893
+ how the hub tells one element what it just did to that element. A moment is an event, because the element that cares is
1895
1894
  often an ancestor of the one it concerns, and an event reaches it with no
1896
1895
  knowledge of either.
1897
1896
 
@@ -1910,45 +1909,47 @@ class NoteField extends HTMLElement {
1910
1909
  }
1911
1910
  ```
1912
1911
 
1913
- A control that is not yet upgraded when its column lands receives
1914
- `lb-column-value` alone, since the hub sets `value` only on a control. That
1915
- happens to an element the builder shipped absent, and to one whose
1916
- definition loads after the hub lands. A control takes the stamp up the
1917
- first time it connects, and never again.
1918
-
1919
1912
  A custom element that is not a control receives `lb-column-value` and
1920
1913
  renders it; its content is never replaced.
1921
1914
 
1922
1915
  ### Lifecycle
1923
1916
 
1924
- | The hub | A custom element sees |
1925
- | ---------------------------------------- | -------------------------------------------- |
1926
- | Shows a page | `constructor`, `connectedCallback` |
1927
- | Creates a live row, before placing it | `constructor` |
1928
- | Places a live row the first time | `connectedCallback` |
1929
- | Moves a live row the order put elsewhere | `connectedMoveCallback` where the browser moves in place, otherwise `disconnectedCallback`, `connectedCallback` |
1930
- | Leaves a copy of a moved row behind | The copy's `constructor`, `connectedCallback`, and later `disconnectedCallback` |
1931
- | Removes a live row | `disconnectedCallback`, once its animations end |
1932
- | Turns `lb-show` off | `disconnectedCallback`, `adoptedCallback` |
1933
- | Turns `lb-show` on | `adoptedCallback`, `connectedCallback` |
1934
- | Turns on an element shipped absent | `constructor`, `connectedCallback` |
1917
+ A custom element hears the hub's lifecycle, never the browser's. The hub
1918
+ calls four optional methods, which the `HubLifecycle` interface in
1919
+ `@loadbare/app/types` declares:
1920
+
1921
+ | The hub | A custom element hears |
1922
+ | ------------------------------------------------ | ------------------------------- |
1923
+ | Places it: chrome at startup, a page, a new live row, a new group | `lbAttached()`, once, after every column has landed on it |
1924
+ | Lands rows on it | `lbRowsLanded()` |
1925
+ | Finishes a request committed by it or inside it | `lbRequestDone(detail, origin)` |
1926
+ | Removes it: a row or group done leaving, the page replaced | `lbDetached()`, once |
1927
+ | Moves a live row the order put elsewhere | Nothing |
1928
+ | Turns `lb-show` off or on | Nothing |
1929
+ | Leaves a copy of a moved row behind | Nothing, on the copy |
1930
+
1931
+ The chrome and a page are placed as the page's first answer lands, or once
1932
+ that round trip fails. An element under an `lb-show` that is off is placed
1933
+ inside its template, so `lbAttached` runs whether it shows or not. `lbAttached` reaches an outer
1934
+ custom element before the ones inside it, and `lbDetached` the inner ones
1935
+ first. `lbRequestDone` reaches the committing element and every custom
1936
+ element around it, innermost first, before the `lb-request-done` event.
1937
+ An element a script creates hears none of them.
1935
1938
 
1936
1939
  A listener on the element itself goes in the constructor, which reads no
1937
- attribute and no child. A child is looked up when it is needed.
1938
- `connectedCallback` runs on every move and is written to run again; work
1939
- done once per instance goes behind a flag.
1940
+ attribute and no child. A child is looked up when it is needed. Work done
1941
+ once per instance goes in `lbAttached`, and its undoing in `lbDetached`.
1940
1942
 
1941
- ### Row hooks
1943
+ The build refuses a script that declares `connectedCallback`,
1944
+ `disconnectedCallback`, `adoptedCallback` or `connectedMoveCallback`.
1942
1945
 
1943
- | Method | Implemented By | The hub calls it |
1944
- | ---------------- | -------------- | ------------------------------------------------------ |
1945
- | `lbRowsLanded()` | Developer | After the rows land, with every row placed and stamped |
1946
+ ### Row hooks
1946
1947
 
1947
- The hub calls it on a custom element with `lb-query` and a row template.
1948
- It is optional. The `RowsHost` interface in `@loadbare/app/types` declares
1949
- it. The hub places every row and builds every group itself, so a custom
1950
- element reacts to rows and never places them. Scrolling to the row the
1951
- page's request created is the case:
1948
+ The hub calls `lbRowsLanded()` on a custom element with `lb-query` and a row
1949
+ template, after the rows land, with every row placed and stamped. The hub
1950
+ places every row and builds every group itself, so a custom element reacts
1951
+ to rows and never places them. Scrolling to the row the page's request
1952
+ created is the case:
1952
1953
 
1953
1954
  ```ts
1954
1955
  lbRowsLanded() {
@@ -1957,10 +1958,8 @@ lbRowsLanded() {
1957
1958
  }
1958
1959
  ```
1959
1960
 
1960
- A custom element the builder shipped absent under `lb-show` has not
1961
- upgraded while its column is off. When the column first turns on and it
1962
- upgrades, the hub lands the query's last answer on it again, as all rows,
1963
- so the hook runs.
1961
+ A custom element under an `lb-show` that is off holds its rows all the
1962
+ same, and the hook runs as they land.
1964
1963
 
1965
1964
  `applyRow(root, row)`, exported by `@loadbare/app`, fills `root` from one
1966
1965
  row, the same operation that fills a live row.
@@ -2136,9 +2135,12 @@ Loadbare/app owns every DOM event in this table, both the name and what its
2136
2135
 
2137
2136
  ### Reserved methods
2138
2137
 
2139
- | Method | On | Defined in |
2140
- | -------------- | -------------------------------------------------- | ----------------------- |
2141
- | `lbRowsLanded` | A custom element with `lb-query` and a row template | [Row hooks](#row-hooks) |
2138
+ | Method | On | Defined in |
2139
+ | --------------- | --------------------------------------------------- | ----------------------- |
2140
+ | `lbAttached` | Any custom element the hub places | [Lifecycle](#lifecycle) |
2141
+ | `lbRowsLanded` | A custom element with `lb-query` and a row template | [Row hooks](#row-hooks) |
2142
+ | `lbRequestDone` | A custom element that committed, or holds one that did | [Lifecycle](#lifecycle) |
2143
+ | `lbDetached` | Any custom element the hub removes | [Lifecycle](#lifecycle) |
2142
2144
 
2143
2145
  ### Constants
2144
2146