@loadbare/app 0.10.0 → 0.12.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 (38) hide show
  1. package/dist/build/assemble.d.ts.map +1 -1
  2. package/dist/build/assemble.js +51 -38
  3. package/dist/build/assemble.js.map +1 -1
  4. package/dist/build/expand.d.ts +6 -1
  5. package/dist/build/expand.d.ts.map +1 -1
  6. package/dist/build/expand.js +92 -7
  7. package/dist/build/expand.js.map +1 -1
  8. package/dist/core/lb-constants.d.ts +4 -0
  9. package/dist/core/lb-constants.d.ts.map +1 -1
  10. package/dist/core/lb-constants.js +16 -0
  11. package/dist/core/lb-constants.js.map +1 -1
  12. package/dist/core/lb-types.d.ts +11 -0
  13. package/dist/core/lb-types.d.ts.map +1 -1
  14. package/dist/core/lb-types.js.map +1 -1
  15. package/dist/hub/lb-apply.d.ts +7 -1
  16. package/dist/hub/lb-apply.d.ts.map +1 -1
  17. package/dist/hub/lb-apply.js +136 -18
  18. package/dist/hub/lb-apply.js.map +1 -1
  19. package/dist/hub/lb-hub.browser.d.ts.map +1 -1
  20. package/dist/hub/lb-hub.browser.js +121 -37
  21. package/dist/hub/lb-hub.browser.js.map +1 -1
  22. package/dist/server/lb-server.d.ts.map +1 -1
  23. package/dist/server/lb-server.js +17 -0
  24. package/dist/server/lb-server.js.map +1 -1
  25. package/docs/TECHREF-1.0.md +151 -10
  26. package/docs/comparison.md +30 -5
  27. package/docs/reference/chrome.md +15 -0
  28. package/docs/reference/custom-elements.md +163 -9
  29. package/docs/reference/data-binding.md +45 -2
  30. package/docs/reference/page-files.md +59 -2
  31. package/docs/what-does-loadbare-extend.md +124 -0
  32. package/package.json +1 -1
  33. package/skills/loadbare-app/SKILL.md +86 -10
  34. package/skills/loadbare-app/references/TECHREF-1.0.md +151 -10
  35. package/skills/loadbare-app/references/chrome.md +15 -0
  36. package/skills/loadbare-app/references/custom-elements.md +163 -9
  37. package/skills/loadbare-app/references/data-binding.md +45 -2
  38. package/skills/loadbare-app/references/page-files.md +59 -2
@@ -159,7 +159,9 @@ row template lands on the element itself. A `rows` query with no row
159
159
  template lands nothing, which is what an insert form naming its query is. A
160
160
  condition is `lb-show="<column>"`: write every possibility into the page and
161
161
  let a column decide which is present. The column is a boolean or null; the
162
- string `"false"` counts as present.
162
+ string `"false"` counts as present. `lb-show="!<column>"` is present when
163
+ the column is not, so one column decides both sides and the query never
164
+ sends a flag and its opposite.
163
165
 
164
166
  **`{{placeholder}}` is build time only.** It reads an `exp-` attribute on
165
167
  the tag, never data. Write it as an entire attribute value or an entire
@@ -176,7 +178,9 @@ from it.
176
178
  query under two names. Many masters with their details is one `rows` query
177
179
  of joined rows, grouped for display by a custom element's `lbPlaceRow`. A
178
180
  query nested inside another query's row template receives the same rows in
179
- every outer row; use it for a picker, never for per-row detail.
181
+ every outer row; use it for a picker, never for per-row detail. A new outer
182
+ row is filled from the nested query's last answer, whatever order they land
183
+ in.
180
184
 
181
185
  **Every request has one shape.** `lb-request` names the request, and the
182
186
  hub sends `{ name, query, key, values }` as present: `query` from the
@@ -201,6 +205,12 @@ elsewhere with the HTML `form` attribute. Checkboxes, radio buttons and
201
205
  file inputs are not controls here: they receive no value and are not
202
206
  gathered.
203
207
 
208
+ **A form that adds to a set chosen elsewhere is written once, outside every
209
+ row.** A form cannot sit inside another form or in a table row, so a dialog
210
+ that creates an account while a posting's picker is choosing one goes at the
211
+ end of the page, and each row opens it with `commandfor`. The dialog learns
212
+ the new key from `lb-request-done`.
213
+
204
214
  **Three requests are Loadbare's.** `lb-row-insert` needs `query` and
205
215
  `values`, `lb-row-update` needs `query`, `key` and `values`, and
206
216
  `lb-row-delete` needs `query` and `key`. The page permits each under
@@ -216,11 +226,42 @@ page may edit.
216
226
 
217
227
  **Return a patch when the change has a known extent.** `patch({ rows })`
218
228
  for rows added or edited, `patch({ drop })` for keys removed, with
219
- `refresh: []`. List a query in `refresh` only when its membership or order
220
- changed in a way the handler cannot name.
221
-
222
- **Format values in the query.** The hub does no type conversion. What a
223
- number, date or null looks like is decided on the server.
229
+ `refresh: []`. A refreshed `rows` query sends every row, and the hub places
230
+ every row again, which moves each element and takes focus from the control
231
+ the user is in. List a query in `refresh` only when its membership or order
232
+ changed in a way the handler cannot name. Never list one only to fill the
233
+ picker in a row the request adds: its answer did not change, and the hub
234
+ fills a new row from the last one.
235
+
236
+ **The usual reasons for a refresh each have a patch.**
237
+ - A placeholder row standing in for an empty set or section: drop its key
238
+ in the patch that adds the first real row, and add it back in the patch
239
+ that drops the last.
240
+ - A row whose position changes: the rows host places it, as `lb-table` does
241
+ by `data-sort`.
242
+ - A row whose other columns change with the edit: re-read the row and patch
243
+ it.
244
+ - Rows the database removes with a deleted one: select their keys before
245
+ the delete and drop them too.
246
+
247
+ An update or a delete names its row by key, and removing a row never
248
+ reorders the rest, so each always has a patch: `createHub` refuses at
249
+ startup a `rowUpdate` or `rowDelete` whose `refresh` names its own `rows`
250
+ query. An insert may refresh its own query, and should only when the new
251
+ row's place matters and nothing places it: a patched row lands where its
252
+ host puts it, in order under `lb-table` with `data-sort`, last in plain
253
+ markup and in `lb-options`.
254
+ Each handler's `refresh` is its own, and every query in it needs its own
255
+ reason. A list shared by several handlers is a warning sign.
256
+
257
+ **A page's queries are its view model, not a data layer.** Each answers
258
+ one page's elements in the form they show: formatted dates and amounts, and
259
+ the words a reader sees, such as "12 postings". The hub does no type
260
+ conversion, so what a number, date or null looks like is decided on the
261
+ server. A value the page reads back stays data: a column on a control, the
262
+ key, an `lb-show` condition, a value a stylesheet selects on. What pages
263
+ share goes beneath them, in a database view or a helper module, never in a
264
+ layer between the page and its queries.
224
265
 
225
266
  **A URL's path names a page, never a resource.** Do not design
226
267
  `/accounts/42`. A row is addressed by `query` and `key`, taken from where
@@ -241,6 +282,12 @@ puts parms onto `ctx`, and a query reads them there. Read them from
241
282
  request. A parm is user input, so validate it where it is read. Do not
242
283
  keep a selection in server state set by a handler.
243
284
 
285
+ **Where focus starts is the hub's.** On entering a page, once its queries
286
+ have landed, the hub focuses the first control in `<main>` the user can
287
+ operate; a change of query parm leaves focus where it is. Order the page so
288
+ that control is the one to start in, and write no `autofocus` and no script
289
+ that focuses on load.
290
+
244
291
  **A handler moves the URL with `url()`.** After an insert, only the server
245
292
  knows the new key; after a delete, only the server knows the parm should
246
293
  go. Have `run` return `url({ acct: String(id) })`, or `url({ acct: "" })`
@@ -279,6 +326,26 @@ element (`static formAssociated = true`) with a `value` property that fires
279
326
  on `change`. Any other custom element keeps its content and receives the
280
327
  column as its `lb-column-value` attribute.
281
328
 
329
+ **A custom element connects many times.** It is moved, never rebuilt: the
330
+ hub places every live row again on each set of all rows, and parks an
331
+ `lb-show` element in a template while it is off, so `connectedCallback` runs
332
+ on every move. Listen on the element itself in the constructor, look a
333
+ child up when it is needed, and in a control take up `lb-column-value` once,
334
+ on first connect, since a value can land before the element upgrades.
335
+
336
+ **The hub and a custom element talk in four ways, one per kind of
337
+ message.** State the hub gives an element is an attribute stamp, or `value`
338
+ on a control. Work the hub needs done while landing is an optional `lb`
339
+ method: `lbPlaceRow`, `lbRowsLanded`. A moment is a bubbling event: an
340
+ element sends `lb-request`, and the hub answers on the same element with
341
+ `lb-request-done`, carrying the request and the response items that landed,
342
+ or the error. Listen for `lb-request-done` on whichever ancestor needs the
343
+ outcome, such as a dialog choosing the row its form just created.
344
+
345
+ **A widget that holds rows may sit under `lb-show`.** Rows that land while
346
+ it is away are placed by its `lbPlaceRow` once it first appears, so hide a
347
+ table with `lb-show`, not with a stylesheet rule.
348
+
282
349
  **Name a custom element script `<tag>.browser.ts`.** A plain `<tag>.ts`
283
350
  stays on the server and the tag goes unregistered.
284
351
 
@@ -296,6 +363,13 @@ A custom element is `<tag>.html`, `<tag>.browser.ts`, or both, and a tag
296
363
  with neither is a build error. The HTML is expanded at build time into the
297
364
  tag's children; the class is an ordinary custom element with no base class.
298
365
 
366
+ Place a custom element where HTML allows what its definition holds. One
367
+ whose definition has a `<div>` or a `<dialog>` cannot sit inside a `<p>`, and
368
+ the build refuses it; see
369
+ [Where a custom element can go](references/custom-elements.md#where-a-custom-element-can-go).
370
+ Never write one inside `<select>`, `<option>` or `<textarea>`, where the
371
+ parser loses the tag before the build can report it.
372
+
299
373
  Reach for one only when plain HTML cannot do the job. A set of rows, a form
300
374
  and a condition need none. A custom element exists to be a control of its
301
375
  own, or to place and scaffold rows through the two hooks the hub calls,
@@ -322,10 +396,12 @@ with the package, so no network is needed.
322
396
  - [`references/data-binding.md`](references/data-binding.md) — landing,
323
397
  gathering, requests, conditions and request state. Start here for
324
398
  anything in a page.
325
- - [`references/page-files.md`](references/page-files.md) — queries,
326
- `onPageEnter`, handlers, `crud`, refresh and patch, and `url()`.
399
+ - [`references/page-files.md`](references/page-files.md) — what page
400
+ files are, queries, `onPageEnter`, handlers, `crud`, refresh and patch,
401
+ and `url()`.
327
402
  - [`references/custom-elements.md`](references/custom-elements.md) —
328
- expansion, parameters, slots, destinations, and custom element code.
403
+ expansion, parameters, slots, destinations, and custom element code and
404
+ when it runs.
329
405
  - [`references/chrome.md`](references/chrome.md) — the chrome, and the URL
330
406
  as the query `lb-url`.
331
407
  - [`references/server.md`](references/server.md) — the Express server and
@@ -64,10 +64,9 @@ fine, because every `<select>` receives the same rows.
64
64
 
65
65
  But the allowed values may depend on other values in the row.
66
66
 
67
- - **Decide how a new live row fills a nested query.** A row added to the
68
- outer query starts with its nested query empty, and it fills only when the
69
- nested query's name lands again. See [Master-detail](#master-detail) for
70
- what a nested query is for.
67
+ - **Decide whether a nested query's rows may depend on its row.** A nested
68
+ query has one answer, which every live row receives. See
69
+ [Master-detail](#master-detail) for what a nested query is for.
71
70
 
72
71
  ```html
73
72
  <tbody lb-query="accounts">
@@ -78,6 +77,24 @@ But the allowed values may depend on other values in the row.
78
77
  </tbody>
79
78
  ```
80
79
 
80
+ ### Who creates and orders rows
81
+
82
+ The hub creates every live row from a row template and places it in the
83
+ query's order, unless the element holding the rows has `lbPlaceRow`, in
84
+ which case the custom element decides where each row goes. Custom elements
85
+ also create rows of their own: `lb-table` clones a ghost row per section and
86
+ builds each section's heading. Neither side owns creation or order.
87
+
88
+ - **Decide who creates rows and who orders them.** A patch cannot say where
89
+ a new row goes, so it lands where the host puts it: in order under
90
+ `lb-table` with `data-sort`, last in plain markup and in `lb-options`.
91
+ Whether an insert may answer with a patch therefore depends on markup the
92
+ server cannot see, and the server cannot refuse an insert that refreshes
93
+ its own query the way it refuses an update or a delete.
94
+ - **Decide whether landing all rows moves rows already in place.** It moves
95
+ every row today, which takes focus from the control the user is in, and
96
+ `lbPlaceRow` does the same.
97
+
81
98
  ### The server API
82
99
 
83
100
  - **Give the chrome a way to state its own queries.** A custom element in
@@ -109,6 +126,13 @@ But the allowed values may depend on other values in the row.
109
126
  - **Land the remaining validations.** One `lb-hub` and one empty `<main>`.
110
127
  Recommend landing these now, because refusing markup that used to build is
111
128
  the kind of change 1.0 gives up.
129
+ - **Decide whether the build validates HTML.** A page can be well formed and
130
+ still break HTML's rules for what an element may hold, such as a `<div>`
131
+ inside a `<p>`. The parser rearranges it silently, the same way at build
132
+ time and in the browser, so nothing reports it. Checking those rules is a
133
+ validator's job, and html-validate is one the builder could run on the
134
+ source or on the expanded output. Deciding it after 1.0 is free if it
135
+ lands as an opt-in, and it may need a configuration key.
112
136
  - **Confirm the three output names.** `app.html`, `client.js` and `app.css`
113
137
  are about to be fixed in `staticRoutes` as well as in the builder.
114
138
 
@@ -251,6 +275,12 @@ A custom element with no element file is left alone. A custom element
251
275
  with neither an element file nor a `.browser.ts` script is a build error, as
252
276
  stated in [Custom elements](#custom-elements).
253
277
 
278
+ Each file is parsed on its own, and expansion joins them through the DOM,
279
+ which applies none of HTML's rules for what an element may hold. The browser
280
+ reads the shipped text by those rules, so a join it would rearrange, such as
281
+ a definition's `<dialog>` inside a page's `<p>`, is a build error naming the
282
+ file, the custom element and the path to what would move.
283
+
254
284
  #### Build time parameters
255
285
 
256
286
  A build time parameter is supplied as an attribute on a custom element,
@@ -412,12 +442,13 @@ query.
412
442
  | ----------- | -------- | -------------------------------------------------- |
413
443
  | `lb-query` | a query | Puts its rows in the element's content |
414
444
  | `lb-column` | a column | Sets the element from that column |
415
- | `lb-show` | a column | Removes it while the named column is null or false |
445
+ | `lb-show` | a column | Removes it while the named column is null or false; `!column` reverses it |
416
446
 
417
447
  | Stamp | The hub stamps it with |
418
448
  | -------------------- | -------------------------------- |
419
449
  | `lb-column-value` | The value it set |
420
450
  | `lb-key-value` | The row's key |
451
+ | `lb-row-live` | Nothing; it marks a live row |
421
452
  | `lb-query-row-count` | The number of live rows it holds |
422
453
 
423
454
  The markup names a query and the columns it shows. The kind and the key are
@@ -425,7 +456,8 @@ the server's, and the markup states neither.
425
456
 
426
457
  The row template is the first `<template>` among the descendants of an
427
458
  element with `lb-query`, outside any nested `lb-query`. A live row is an
428
- element the hub cloned from a row template for one row.
459
+ element the hub cloned from a row template for one row, and carries
460
+ `lb-row-live`.
429
461
 
430
462
  | Kind | Row template | The hub |
431
463
  | ------ | ------------ | ----------------------------------- |
@@ -498,6 +530,13 @@ refresh, and does not need to.
498
530
 
499
531
  The hub matches each row to a live row by its key. It stamps
500
532
  `lb-key-value` on each live row, and on an element a `row` lands on itself.
533
+ It stamps `lb-row-live` on each live row and nowhere else, so a `row`
534
+ landed inside another query, such as a total in a table's foot, is not one
535
+ of that query's rows. A stylesheet or a custom element selects live rows
536
+ with `[lb-row-live]`, and never with `[lb-key-value]`.
537
+
538
+ A key is unique within a query. Two rows with one key in the same answer,
539
+ or two live rows showing one key, are reported on the console.
501
540
 
502
541
  All rows decide membership and order: every row is placed in the order
503
542
  given, and a live row whose key did not arrive is removed. A patch touches
@@ -536,6 +575,10 @@ so `"false"` is on, and a query spells a condition as a boolean or a null.
536
575
  </template>
537
576
  ```
538
577
 
578
+ Write `!` before the column to reverse it: `lb-show="!chosen"` is present
579
+ while `chosen` is null or false. One column then decides both of two
580
+ elements, rather than a column and its opposite, which could disagree.
581
+
539
582
  It reads from the nearest ancestor row, as `lb-column` does, and on an
540
583
  element that carries `lb-query` the column belongs to the row around it. A
541
584
  row that does not carry the column leaves the element as it is.
@@ -556,7 +599,8 @@ nothing conditional shows until its row lands. An absent element's template
556
599
  keeps its place among its siblings, and a position selector counts it.
557
600
 
558
601
  These are build errors: `lb-show` on a row template's root, `lb-show` with
559
- no `lb-query` around it, and `lb-show` on a `<template>`.
602
+ no `lb-query` around it, `lb-show` on a `<template>`, and `lb-show="!"` or
603
+ `lb-show="!!column"`.
560
604
 
561
605
  Hiding is presentation, and the server still refuses what a request may not
562
606
  do.
@@ -586,7 +630,9 @@ each detail row carries its master's columns. A custom element's
586
630
 
587
631
  A query nested in another query's row template receives the same rows in
588
632
  every live row. That serves a picker offering the same choices on every
589
- row, and is not a way to show a different detail per row.
633
+ row, and is not a way to show a different detail per row. A live row added
634
+ later is filled from the nested query's last answer, so a request that adds
635
+ one does not refresh the nested query to fill it.
590
636
 
591
637
  Which master a page shows is a query parm, which a query reads off `ctx`
592
638
  since it takes no argument from the browser — see [Query parms](#query-parms).
@@ -745,6 +791,18 @@ interceptor calls `stopPropagation`, not `preventDefault`. This is what
745
791
  makes a confirmation wrapper possible without the wrapped element knowing
746
792
  about it.
747
793
 
794
+ Once what the request brought back has landed, or its round trip has failed,
795
+ the hub dispatches the bubbling `lb-request-done` event from the same
796
+ element. Its `detail`, `HubRequestDone` in `@loadbare/app/types`, holds
797
+ the request as the hub sent it and `items`, every response item that landed
798
+ because of it, in order. An answer that moved the URL contributes its
799
+ `lb-url` item and the page load. `error` is set instead when the round trip
800
+ failed. By then `lb-request-pending` is gone, and a listener sees the page
801
+ as the answer left it. A request the hub or an ancestor stopped was never
802
+ sent, and gets no `lb-request-done`. An element the answer removed from the
803
+ document, such as the row a delete took away, dispatches the event where it
804
+ now is, and no ancestor it had on the page hears it.
805
+
748
806
  #### Request state
749
807
 
750
808
  The hub stamps `lb-request-pending` on the element that issued a request
@@ -782,6 +840,14 @@ A page load that fails lands nothing and is reported to the console. A load
782
840
  started by a request for `lb-url` stamps the element that issued it, as any
783
841
  request stamps its element.
784
842
 
843
+ A response answers for the URL its request was sent from, path and query
844
+ string both. One that returns after the URL has moved lands nothing, since
845
+ it describes what the user is no longer looking at: a request's answer,
846
+ including any `url()` it carries, and a page load overtaken by a newer one.
847
+ A write whose answer is dropped this way has still happened. Its
848
+ `lb-request-done` carries no items and no error, and an insert still resets
849
+ its form.
850
+
785
851
  ### The URL
786
852
 
787
853
  ---- UNEDITED ----
@@ -812,6 +878,14 @@ server runs its `onPageEnter`, then its queries. When a query parm takes a
812
878
  new value, the hub reloads the page's queries, and keeps the page's DOM, so
813
879
  live rows that come back keep their place.
814
880
 
881
+ Once an entered page's queries have landed, the hub focuses the first
882
+ element in `<main>` the user can operate: not disabled, not in a closed
883
+ `<dialog>`, not `inert`, not `hidden`, not in an absent `lb-show` branch,
884
+ and one that takes the focus. Entering is a cold load, a new `lb-path`,
885
+ and Back or Forward to another page. A change of query parm enters no
886
+ page, and focus stays where it is, as it does when it is already in
887
+ `<main>`.
888
+
815
889
  The page's title is the text of the page file's `<title>`, which the builder
816
890
  stamps on the page as `lb-page-title`. The hub sets the document title to
817
891
  `lb-page-label` when it is not null.
@@ -981,8 +1055,8 @@ rowInsert: {
981
1055
  carries the `lb-url` item alone, and the hub loads the page itself. A
982
1056
  failure there is a page load's, and is not stamped on the element that
983
1057
  issued the request.
984
- - A user who has left the page by the time the response arrives keeps the
985
- URL they are on.
1058
+ - A user who has moved by the time the response arrives, to another page or
1059
+ to other query parms, keeps the URL they are on.
986
1060
 
987
1061
  The page is loaded with a second context, built by `contextFor` from the new
988
1062
  parms — see [The Express server](#the-express-server).
@@ -1005,6 +1079,7 @@ to build on any of these:
1005
1079
  `<lb-hub>`.
1006
1080
  - `lb-show` on a `<template>`, on a row template's root, or with no
1007
1081
  `lb-query` around it.
1082
+ - `lb-show="!"` or `lb-show="!!column"`, which reverse no column.
1008
1083
 
1009
1084
  `lb-column` with no ancestor row is allowed: the hub gathers from it.
1010
1085
 
@@ -1254,6 +1329,9 @@ request carrying no name answers 400, and a `run` that throws answers 500.
1254
1329
 
1255
1330
  Every handler under `handlers` and `crud` has the same two members. `run`
1256
1331
  performs the work, and `refresh` names the queries to re-run once it has.
1332
+ A query belongs there when its answer changed, never to fill an element the
1333
+ request's answer creates: that element is filled from the last answer its
1334
+ query landed.
1257
1335
 
1258
1336
  `run` receives the same `ctx` and the request less its name: `query`, `key`
1259
1337
  and `values`, as present.
@@ -1262,6 +1340,15 @@ and `values`, as present.
1262
1340
  the refreshed ones. That is how a delta reaches the browser: wrap it in
1263
1341
  `patch()`, naming the rows that arrived or changed and the keys that went.
1264
1342
 
1343
+ A refreshed `rows` query sends every row, and the hub places every row
1344
+ again, which moves each element and takes focus from the control the user
1345
+ is in. An update or a delete names its row by key, and removing a row
1346
+ never reorders the rest, so its handler always knows what changed:
1347
+ `createHub` refuses at startup a `crud` `rowUpdate` or `rowDelete` on a
1348
+ `rows` query whose `refresh` names that same query. A `row` query sends one
1349
+ row and may refresh itself. An insert may refresh its own query; see
1350
+ [Who creates and orders rows](#who-creates-and-orders-rows).
1351
+
1265
1352
  `run` may instead return `url()`, a new row for `lb-url`, naming query parms
1266
1353
  only the write can know, such as the key of a row it inserted. The page
1267
1354
  then loads at them in the same round trip, in place of the refresh set — see
@@ -1375,6 +1462,25 @@ export default ["@scope/library-name"];
1375
1462
  Import every attribute name from `@loadbare/app/constants` — see
1376
1463
  [Constants](#constants). Never write one as a string literal.
1377
1464
 
1465
+ ### What the hub and an element say to each other
1466
+
1467
+ Each direction has one mechanism for each kind of message:
1468
+
1469
+ | Direction | What | How | Today |
1470
+ | -------------- | ------------------------------------------- | ---------------------------- | ------------------------------------------------ |
1471
+ | Hub to element | State that lasts | An attribute the hub stamps | `lb-column-value`, `lb-key-value`, `lb-query-row-count`, `lb-request-pending`, `lb-request-error` |
1472
+ | Hub to element | State the platform already names | A property | A control's `value` |
1473
+ | Hub to element | Work the hub needs done now, while landing | An optional `lb` method | `lbPlaceRow`, `lbRowsLanded` |
1474
+ | Hub to element | A moment an element started | A bubbling event | `lb-request-done` |
1475
+ | Element to hub | A moment | A bubbling event | `lb-request`, and `change`, `click` and `submit` |
1476
+
1477
+ State is an attribute, because a stylesheet can select on it and an element
1478
+ that upgrades late still finds it. A method is for work the hub cannot go
1479
+ on without, since it acts on one element at one point in landing and nothing
1480
+ else can do it. A moment is an event, because the element that cares is
1481
+ often an ancestor of the one it concerns, and an event reaches it with no
1482
+ knowledge of either.
1483
+
1378
1484
  ### Controls
1379
1485
 
1380
1486
  A form-associated custom element with a `value` property that fires
@@ -1390,9 +1496,33 @@ class NoteField extends HTMLElement {
1390
1496
  }
1391
1497
  ```
1392
1498
 
1499
+ A control that is not yet upgraded when its column lands receives
1500
+ `lb-column-value` alone, since the hub sets `value` only on a control. That
1501
+ happens to an element the builder shipped absent, and to one whose
1502
+ definition loads after the hub lands. A control takes the stamp up the
1503
+ first time it connects, and never again.
1504
+
1393
1505
  A custom element that is not a control receives `lb-column-value` and
1394
1506
  renders it; its content is never replaced.
1395
1507
 
1508
+ ### Lifecycle
1509
+
1510
+ | The hub | A custom element sees |
1511
+ | ---------------------------------------- | -------------------------------------------- |
1512
+ | Shows a page | `constructor`, `connectedCallback` |
1513
+ | Creates a live row, before placing it | `constructor` |
1514
+ | Places a live row the first time | `connectedCallback` |
1515
+ | Places it again, on every set of all rows | `disconnectedCallback`, `connectedCallback` |
1516
+ | Removes a live row | `disconnectedCallback` |
1517
+ | Turns `lb-show` off | `disconnectedCallback`, `adoptedCallback` |
1518
+ | Turns `lb-show` on | `adoptedCallback`, `connectedCallback` |
1519
+ | Turns on an element shipped absent | `constructor`, `connectedCallback` |
1520
+
1521
+ A listener on the element itself goes in the constructor, which reads no
1522
+ attribute and no child. A child is looked up when it is needed.
1523
+ `connectedCallback` runs on every move and is written to run again; work
1524
+ done once per instance goes behind a flag.
1525
+
1396
1526
  ### Row hooks
1397
1527
 
1398
1528
  | Method | Implemented By | The hub calls it |
@@ -1404,6 +1534,11 @@ The hub calls both on a custom element with `lb-query` and a row template.
1404
1534
  Both are optional. The `RowsHost` interface in `@loadbare/app/types`
1405
1535
  declares them.
1406
1536
 
1537
+ A custom element the builder shipped absent under `lb-show` has not
1538
+ upgraded while its column is off, and rows that land on it then are placed
1539
+ without it. When the column first turns on and it upgrades, the hub lands
1540
+ the query's last answer on it again, as all rows, so both hooks run.
1541
+
1407
1542
  `applyRow(root, row)`, exported by `@loadbare/app`, fills `root` from one
1408
1543
  row, the same operation that fills a live row.
1409
1544
 
@@ -1505,6 +1640,7 @@ it may define tomorrow.
1505
1640
  | `lb-exp-template` | Developer | An element file, and a `<template>` | [Slots and templates](#slots-and-templates) |
1506
1641
  | `lb-column-value` | Hub | Every element set from a column | [How a column lands](#how-a-column-lands) |
1507
1642
  | `lb-key-value` | Hub | A live row, and an element a `row` lands on | [Rows](#rows) |
1643
+ | `lb-row-live` | Hub | A live row | [Rows](#rows) |
1508
1644
  | `lb-query-row-count` | Hub | An element with a row template | [Rows](#rows) |
1509
1645
  | `lb-request-pending` | Hub | The element that issued a request | [Request state](#request-state) |
1510
1646
  | `lb-request-error` | Hub | The element that issued a request | [Request state](#request-state) |
@@ -1546,6 +1682,7 @@ Loadbare/app owns every DOM event in this table, both the name and what its
1546
1682
  | Event | Dispatched from | Bubbles | Cancelable | Defined in |
1547
1683
  | ------------ | -------------------------------- | ------- | ---------- | --------------------------------------- |
1548
1684
  | `lb-request` | The element that committed | Yes | No | [The request event](#the-request-event) |
1685
+ | `lb-request-done` | The element that committed | Yes | No | [The request event](#the-request-event) |
1549
1686
 
1550
1687
  ### Reserved methods
1551
1688
 
@@ -1566,6 +1703,8 @@ imports them rather than writing a string.
1566
1703
  | `ATTR_SHOW` | `lb-show` |
1567
1704
  | `ATTR_COLUMN_VALUE` | `lb-column-value` |
1568
1705
  | `ATTR_KEY_VALUE` | `lb-key-value` |
1706
+ | `ATTR_ROW_LIVE` | `lb-row-live` |
1707
+ | `LIVE_ROW` | `[lb-row-live]`, the selector for a live row |
1569
1708
  | `ATTR_QUERY_ROW_COUNT` | `lb-query-row-count` |
1570
1709
  | `ATTR_REQUEST` | `lb-request` |
1571
1710
  | `ATTR_REQUEST_PENDING` | `lb-request-pending` |
@@ -1579,6 +1718,8 @@ imports them rather than writing a string.
1579
1718
  | `ATTR_PAGE_TITLE` | `lb-page-title` |
1580
1719
  | `DEVELOPER_ATTRIBUTES` | The seven attributes a developer writes |
1581
1720
  | `LB_EVENT_NAME` | `lb-request` |
1721
+ | `LB_DONE_EVENT_NAME` | `lb-request-done` |
1722
+ | `SHOW_NOT` | `!`, written before a column in `lb-show` |
1582
1723
  | `LB_RESERVED_PREFIX` | `lb-` |
1583
1724
  | `REQUEST_ROW_INSERT` | `lb-row-insert` |
1584
1725
  | `REQUEST_ROW_UPDATE` | `lb-row-update` |
@@ -108,6 +108,21 @@ tab and the browser history show the page.
108
108
  `lb-url` is the one query a page may use that the server does not declare. A
109
109
  page names it the same way, anywhere inside the hub.
110
110
 
111
+ ### Where focus starts
112
+
113
+ On entering a page, once its queries have landed, the hub focuses the first
114
+ element in `<main>` the user can operate, so a keyboard user starts in the
115
+ page rather than on the document. The chrome comes first in the document,
116
+ and is passed over. So is anything disabled, in a closed `<dialog>`,
117
+ `inert`, `hidden`, in an absent `lb-show` branch, or that does not take
118
+ the focus when asked. A widget's native control counts, so write no
119
+ `autofocus` to restate this.
120
+
121
+ A page is entered on a cold load, a new `lb-path` from a link or from a
122
+ handler's `url()`, and Back or Forward to another page. A change of query
123
+ parm enters no page: the user who chose a record in a picker stays in the
124
+ picker. Focus the user has already put in `<main>` is left there.
125
+
111
126
  ### Links
112
127
 
113
128
  Write `lb-url-link` on an `<a>` to move between pages: