@loadbare/app 0.23.0 → 0.24.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 +28 -1
  3. package/dist/build/assemble.js.map +1 -1
  4. package/dist/core/lb-constants.d.ts +8 -5
  5. package/dist/core/lb-constants.d.ts.map +1 -1
  6. package/dist/core/lb-constants.js +20 -11
  7. package/dist/core/lb-constants.js.map +1 -1
  8. package/dist/core/lb-types.d.ts +19 -3
  9. package/dist/core/lb-types.d.ts.map +1 -1
  10. package/dist/core/lb-types.js +35 -5
  11. package/dist/core/lb-types.js.map +1 -1
  12. package/dist/hub/lb-apply.d.ts +18 -22
  13. package/dist/hub/lb-apply.d.ts.map +1 -1
  14. package/dist/hub/lb-apply.js +115 -51
  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 +15 -22
  18. package/dist/hub/lb-hub.browser.js.map +1 -1
  19. package/dist/server/lb-server.d.ts +2 -2
  20. package/dist/server/lb-server.d.ts.map +1 -1
  21. package/dist/server/lb-server.js +29 -9
  22. package/dist/server/lb-server.js.map +1 -1
  23. package/docs/comparison.md +11 -11
  24. package/docs/miswarts.md +4 -3
  25. package/docs/reference/chrome.md +42 -0
  26. package/docs/reference/custom-elements.md +9 -4
  27. package/docs/reference/data-binding.md +79 -61
  28. package/docs/reference/names.md +10 -7
  29. package/docs/reference/page-files.md +8 -4
  30. package/docs/testing.md +3 -3
  31. package/package.json +1 -1
  32. package/skills/loadbare-app/SKILL.md +23 -12
  33. package/skills/loadbare-app/references/chrome.md +42 -0
  34. package/skills/loadbare-app/references/custom-elements.md +9 -4
  35. package/skills/loadbare-app/references/data-binding.md +79 -61
  36. package/skills/loadbare-app/references/miswarts.md +4 -3
  37. package/skills/loadbare-app/references/names.md +10 -7
  38. package/skills/loadbare-app/references/page-files.md +8 -4
@@ -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
- seventeen `lb-` attributes. The server declares every query a page may show and
5
+ eighteen `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`,
@@ -67,10 +67,10 @@ builder writes a stamp, and a stylesheet or a custom element reads it.
67
67
  | `lb-key-value` | The key of a live row, or of a `row` landed on an element |
68
68
  | `lb-row-live` | The element is a live row |
69
69
  | `lb-query-row-count` | The number of live rows an element shows |
70
- | `lb-query-unsorted-count` | The number of them out of order; see [Rows the user is working in](#rows-the-user-is-working-in) |
70
+ | `lb-query-unplaced-count` | The number of them out of place; see [Placing by hand](#placing-by-hand) |
71
71
  | `lb-query-order` | The order placing them; see [Order](#order) |
72
72
  | `lb-group-live`, `lb-group-column`, `lb-group-value` | A group, the column it breaks on, and its value; see [Groups](#groups) |
73
- | `lb-row-created`, `lb-row-changed`, `lb-row-moved`, `lb-row-requested`, `lb-row-unsorted` | What happened to a row; see [What happened to a row](#what-happened-to-a-row) |
73
+ | `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
74
  | `lb-row-leaving`, `lb-group-leaving` | A row or group on its way out |
75
75
  | `lb-row-editing` | The row a form is pointed at; see [Editing a row in one form](#editing-a-row-in-one-form) |
76
76
  | `lb-row-selected` | A row whose `lb-select` is ticked |
@@ -146,6 +146,7 @@ no row fills.
146
146
  | `<input>`, `<select>`, `<textarea>` | Its `value`, and `lb-column-value` |
147
147
  | `<input type="checkbox">` | Its `checked`, and `lb-column-value` |
148
148
  | `<input type="radio">` | Its `checked`, and `lb-column-value` |
149
+ | `<select multiple>` | Its selected options, and `lb-column-value` |
149
150
  | A form-associated custom element with `value` | Its `value`, and `lb-column-value` |
150
151
  | Any other custom element | `lb-column-value` only |
151
152
  | Any other element | Its text content, and `lb-column-value` |
@@ -186,8 +187,9 @@ A checkbox lands what it sends, so it shows the same state after a round
186
187
  trip through a query whose values are strings, such as `lb-url` or
187
188
  `lb-choices`. A query parm the URL does not carry lands as `""`, so the
188
189
  checkbox is unchecked. Unchecked sends `"false"` rather than nothing, so a
189
- flag can be set back. The checkbox's `value` attribute means nothing to the
190
- hub.
190
+ flag can be set back. A checkbox carrying a `value` attribute is not a flag
191
+ but one of a set, holding a query parm the URL repeats; see
192
+ [Several values](./chrome.md#several-values).
191
193
 
192
194
  ### Radio buttons
193
195
 
@@ -228,9 +230,9 @@ places it immediately before the template:
228
230
  The hub matches each row to a live row by its key. All rows decide
229
231
  membership: a live row whose key did not arrive leaves. A patch changes only
230
232
  the rows it names, and every other live row keeps its content. The hub
231
- places live rows by the query's order, except that an edit from inside the
232
- list leaves them where they stand; see [Order](#order) and
233
- [Rows the user is working in](#rows-the-user-is-working-in). A row that
233
+ places live rows by the query's order, except where the list's `lb-manual`
234
+ leaves them where they stand; see [Order](#order) and
235
+ [Placing by hand](#placing-by-hand). A row that
234
236
  does not pass the user's filter is `hidden`; see [Filters](#filters).
235
237
 
236
238
  A live row whose values arrive as it already shows them is not filled
@@ -310,41 +312,50 @@ Rows that compare equal keep the order they arrived in, a patch's new rows
310
312
  after the rows already showing. A row moves only when the order
311
313
  puts it somewhere else, so all rows landing again unchanged move nothing.
312
314
 
313
- ### Rows the user is working in
314
-
315
- A landing that answers a request sent from inside a list moves none of that
316
- list's rows. The request is from inside the list when the list is the
317
- nearest element carrying `lb-query` around the element that sent it: a
318
- control in a live row, or a ghost row. A form that
319
- [`--lb-edit`](#editing-a-row-in-one-form) pointed at a live row finishes an
320
- edit begun in that row, so its request is from inside the row's list,
321
- wherever the form sits. Any other form elsewhere on the page is not inside
322
- the list. A user who changes a value sees the value change, and the row
323
- stays where they were working:
324
-
325
- | The answer | The row |
326
- |------------------------------------------|---------------------------------------------------|
327
- | Changes a column the order reads | Stays where it stands |
328
- | Creates a row | Goes after the last row of its group, or last of all |
329
- | Drops a row | Leaves |
330
- | Changes a row so the filter hides it | Stays showing |
331
- | Changes the column its group breaks on | Stays under its group's heading |
332
-
333
- A patch and all rows answering the same edit land the same way. A row left
334
- where the order or the filter would not leave it is stamped
335
- `lb-row-unsorted`, and the element carrying `lb-query` carries
336
- `lb-query-unsorted-count`, the number of such rows. Another element naming
337
- the same query places its rows by the order: the user was not working
338
- there.
339
-
340
- The rows are placed by the order again, and every `lb-row-unsorted` is
341
- cleared, when the user asks: when the page loads, when the user's order or
342
- filter changes, or on `--lb-sort`. A button names the list with HTML's
315
+ ### Placing by hand
316
+
317
+ By default the hub places a list's rows by the order and the filter on
318
+ every landing. Write `lb-manual` on the element carrying `lb-query` to leave
319
+ one side or both to the user, so rows do not jump about or vanish while the
320
+ user works through them. It names `order`, `filter` or both, separated by a
321
+ space:
322
+
323
+ ```html
324
+ <lb-table lb-query="entries" lb-order="entries_order"
325
+ lb-filter="description, is_unmatched" lb-manual="order filter">
326
+ ```
327
+
328
+ A landing that answers a request leaves alone each side the list names,
329
+ wherever the request was sent from: a control in a live row, a ghost row, a
330
+ form `--lb-edit` pointed at a row, or a form elsewhere on the page.
331
+
332
+ | The answer | Under `lb-manual`, the row | Side |
333
+ |----------------------------------------|---------------------------------------------------|----------|
334
+ | 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` |
337
+ | Changes a row so the filter hides it | Stays showing | `filter` |
338
+ | Drops a row | Leaves, under either side | |
339
+
340
+ A side not named is placed on every landing. A list that names only
341
+ `filter` moves an edited row into the order and keeps it showing; one that
342
+ names only `order` leaves it where it stands and hides it once the filter
343
+ says so.
344
+
345
+ A patch and all rows answering the same edit land the same way. A showing
346
+ row left where the order or the filter would not leave it is stamped
347
+ `lb-row-unplaced`, and the element carrying `lb-query` carries
348
+ `lb-query-unplaced-count`, the number of such rows. One stamp covers both
349
+ sides. Each element naming the query places by its own `lb-manual`.
350
+
351
+ The rows are placed by the order and the filter again, and every
352
+ `lb-row-unplaced` is cleared, when the page loads, when the user's order or
353
+ filter changes, or on `--lb-place`. A button names the list with HTML's
343
354
  `commandfor`, and a stylesheet shows it only while it has something to do:
344
355
 
345
356
  ```html
346
- <div id="roster" lb-query="roster">
347
- <button type="button" class="resort" commandfor="roster" command="--lb-sort">Re-sort</button>
357
+ <div id="roster" lb-query="roster" lb-manual="order">
358
+ <button type="button" class="place" commandfor="roster" command="--lb-place">Re-sort</button>
348
359
  <ul>
349
360
  <template><li><input lb-column="name" lb-request="lb-row-update" /></li></template>
350
361
  </ul>
@@ -352,19 +363,19 @@ filter changes, or on `--lb-sort`. A button names the list with HTML's
352
363
  ```
353
364
 
354
365
  ```css
355
- .resort {
366
+ .place {
356
367
  display: none;
357
368
  }
358
- [lb-query-unsorted-count]:not([lb-query-unsorted-count="0"]) .resort {
369
+ [lb-query-unplaced-count]:not([lb-query-unplaced-count="0"]) .place {
359
370
  display: revert;
360
371
  }
361
372
  ```
362
373
 
363
- `--lb-sort` places the rows the hub holds, with no round trip. Aimed at an
374
+ `--lb-place` places the rows the hub holds, with no round trip. Aimed at an
364
375
  element that carries no `lb-query`, or shows no rows the hub holds, it is
365
376
  reported to the console. A custom element that reorders rows by a request of
366
377
  its own dispatches the same command on the list once the answer lands, with
367
- `new CommandEvent("command", { command: "--lb-sort" })`: the user asked for
378
+ `new CommandEvent("command", { command: "--lb-place" })`: the user asked for
368
379
  the move.
369
380
 
370
381
  ### Filters
@@ -384,9 +395,9 @@ its rows the user may filter, separated by commas. The column of
384
395
  ```
385
396
 
386
397
  A row that does not pass every filter is `hidden`, and a ticked `lb-select`
387
- in it is cleared. A row an edit from inside the list changed stays showing
388
- until the rows are placed again; see
389
- [Rows the user is working in](#rows-the-user-is-working-in). A group whose rows are all hidden is hidden too
398
+ in it is cleared. Under `lb-manual="filter"`, a row an answer changed stays
399
+ 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
390
401
  ([miswart](../miswarts.md#rows-hidden-by-a-filter)). An empty
391
402
  choice filters nothing.
392
403
 
@@ -469,7 +480,7 @@ where a group's footer goes.
469
480
 
470
481
  A heading is filled from its group's first row as an element in a row is,
471
482
  `href`s from `lb-url-parm` included, so it may show a column other than the
472
- one the group breaks on. A row an edit from inside the list
483
+ one the group breaks on. Under `lb-manual="order"`, a row an answer
473
484
  moved to another group stays under its old heading, and the heading shows
474
485
  the row as it stood, until the rows are placed again. The hub stamps each top-level
475
486
  element of a group with `lb-group-live`, `lb-group-column` and
@@ -520,7 +531,7 @@ Every change to a list is one a stylesheet can see:
520
531
  | `lb-row-changed` | Its values changed when the hub last touched it |
521
532
  | `lb-row-moved` | Its values changed, and it now stands elsewhere |
522
533
  | `lb-row-requested` | `created`, `changed` or `moved` by this page's request |
523
- | `lb-row-unsorted` | Stands where the order would not put it |
534
+ | `lb-row-unplaced` | Stands where the order or the filter would not leave it |
524
535
  | `lb-row-leaving` | A row on its way out |
525
536
  | `lb-group-leaving` | A group on its way out |
526
537
 
@@ -529,9 +540,9 @@ The hub touches a row when an answer names it. `lb-row-created`,
529
540
  applied again is removed and added as two changes, so an animation keyed to
530
541
  it plays again. `lb-row-requested` is cleared at every landing of the row's
531
542
  query, and set only by a landing that answers a request from the page.
532
- `lb-row-unsorted` is a state, not an event: it stays until the rows are
533
- placed by the order; see
534
- [Rows the user is working in](#rows-the-user-is-working-in).
543
+ `lb-row-unplaced` is a state, not an event: it stays until the rows are
544
+ placed again; see
545
+ [Placing by hand](#placing-by-hand).
535
546
 
536
547
  A row or a group that leaves loses `lb-row-live` or `lb-group-live`, takes
537
548
  `lb-row-leaving` or `lb-group-leaving` and `inert`
@@ -580,9 +591,9 @@ insert form above: it sits inside `lb-query="roster"` so its request is for
580
591
 
581
592
  The hub stamps every element that holds a row template with
582
593
  `lb-query-row-count`, the number of live rows it shows. A row a filter
583
- hides is not counted. Beside it, `lb-query-unsorted-count` is the number of
584
- rows out of order; see
585
- [Rows the user is working in](#rows-the-user-is-working-in). An empty query is a stylesheet rule:
594
+ hides is not counted. Beside it, `lb-query-unplaced-count` is the number of
595
+ rows out of place; see
596
+ [Placing by hand](#placing-by-hand). An empty query is a stylesheet rule:
586
597
 
587
598
  ```html
588
599
  <div lb-query="roster">
@@ -793,6 +804,10 @@ carrying `lb-request`:
793
804
  3. The element is in a live row: every control in that live row.
794
805
  4. Otherwise: every control whose form owner is the element's form owner.
795
806
 
807
+ A control that holds several values, a `<select multiple>` or a set of
808
+ checkboxes carrying `value`, gathers an array of strings; see
809
+ [Several values](./chrome.md#several-values).
810
+
796
811
  The hub gathers from controls only, and skips a control that belongs to a
797
812
  query nested inside the group, except that a form gathers the rows of a
798
813
  query nested in its own; see [A form over a set of rows](#a-form-over-a-set-of-rows).
@@ -1044,11 +1059,9 @@ An `lb-show` in the form reads from the row it was pointed at. With no row,
1044
1059
  every column is null, so `lb-show="!id"` shows only while entering a new
1045
1060
  row.
1046
1061
 
1047
- A request from a form Edit pointed is sent from inside the row's list, so
1048
- the row stays where the user was working, even when the filter would now
1049
- hide it or the order would move it; see
1050
- [Rows the user is working in](#rows-the-user-is-working-in). A form New
1051
- pointed is not inside any list, and its rows are placed by the order.
1062
+ The rows a form's answer lands are placed by the list's `lb-manual`, as any
1063
+ answer's are, whether the form was pointed by Edit or by New; see
1064
+ [Placing by hand](#placing-by-hand).
1052
1065
 
1053
1066
  A successful request from the form lets it go, as Cancel does: the form
1054
1067
  empties, its controls return to their defaults, and the stamps go. A failed
@@ -1324,7 +1337,7 @@ The developer writes `lb-` attributes in markup, and script never assigns
1324
1337
  them. The builder checks the markup of the chrome and of every page, after
1325
1338
  expansion, and refuses to build:
1326
1339
 
1327
- - an `lb-` attribute that is not one of the seventeen the developer writes;
1340
+ - an `lb-` attribute that is not one of the eighteen the developer writes;
1328
1341
  `lb-exp-slot` and `lb-exp-template` are consumed by expansion and never
1329
1342
  reach the check
1330
1343
  - `lb-request` naming an `lb-` request other than the three Loadbare provides
@@ -1341,12 +1354,17 @@ expansion, and refuses to build:
1341
1354
  column, naming columns not separated by commas or one beginning `lb-`, or
1342
1355
  with no row around it
1343
1356
  - `lb-url-push` on an element without `lb-request`
1357
+ - `lb-column` on an element carrying `multiple`, or on a checkbox carrying
1358
+ `value`, with any query but `lb-url` around it: only a query parm holds
1359
+ several values
1344
1360
  - `lb-url-unknown` on anything but a `<dialog>`, or outside `<lb-hub>`
1345
1361
  - `lb-show` on a `<template>`, or on a row template's root
1346
1362
  - `lb-show` with no `lb-query` around it
1347
1363
  - `lb-show="!"` or `lb-show="!!column"`, which reverse no column
1348
1364
  - `lb-order` or `lb-filter` on an element not carrying `lb-query`, or naming
1349
1365
  no column
1366
+ - `lb-manual` on an element not carrying `lb-query`, or naming anything but
1367
+ `order`, `filter` or both
1350
1368
  - `lb-select` on anything but `<input type="checkbox">`, outside a row
1351
1369
  template, or beside `lb-column` or `lb-request`
1352
1370
  - a `command` beginning `--lb-` that is not one of the four commands
@@ -66,7 +66,7 @@ A widget package declares these. An application never does.
66
66
 
67
67
  ## Attributes
68
68
 
69
- The developer writes seventeen attributes in pages and the chrome, and the
69
+ The developer writes eighteen attributes in pages and the chrome, and the
70
70
  builder refuses any other `lb-` attribute there. `lb-exp-slot` and
71
71
  `lb-exp-template` are written for expansion, which removes them before the
72
72
  check. The hub and the builder write the rest, as stamps, and script never
@@ -79,6 +79,7 @@ assigns one.
79
79
  | `lb-show` | Developer | [Conditional rendering](./data-binding.md#conditional-rendering) |
80
80
  | `lb-order` | Developer | [Order](./data-binding.md#order) |
81
81
  | `lb-filter` | Developer | [Filters](./data-binding.md#filters) |
82
+ | `lb-manual` | Developer | [Placing by hand](./data-binding.md#placing-by-hand) |
82
83
  | `lb-group` | Developer | [Groups](./data-binding.md#groups) |
83
84
  | `lb-count`, `lb-sum`, `lb-avg`, `lb-min`, `lb-max` | Developer | [Aggregates](./data-binding.md#aggregates) |
84
85
  | `lb-select` | Developer | [Selecting rows](./data-binding.md#selecting-rows) |
@@ -93,7 +94,7 @@ assigns one.
93
94
  | `lb-key-value` | Hub | [Landing](./data-binding.md#landing), [Editing a row in one form](./data-binding.md#editing-a-row-in-one-form) |
94
95
  | `lb-row-live` | Hub | [Landing](./data-binding.md#landing) |
95
96
  | `lb-query-row-count` | Hub | [Counting rows](./data-binding.md#counting-rows) |
96
- | `lb-query-unsorted-count`, `lb-row-unsorted` | Hub | [Rows the user is working in](./data-binding.md#rows-the-user-is-working-in) |
97
+ | `lb-query-unplaced-count`, `lb-row-unplaced` | Hub | [Placing by hand](./data-binding.md#placing-by-hand) |
97
98
  | `lb-query-order` | Hub | [Order](./data-binding.md#order) |
98
99
  | `lb-group-live`, `lb-group-column`, `lb-group-value` | Hub | [Groups](./data-binding.md#groups) |
99
100
  | `lb-row-created`, `lb-row-changed`, `lb-row-moved`, `lb-row-requested` | Hub | [What happened to a row](./data-binding.md#what-happened-to-a-row) |
@@ -140,7 +141,7 @@ Loadbare/app owns every value of HTML's `command` attribute beginning with
140
141
  | `--lb-edit` | [Editing a row in one form](./data-binding.md#editing-a-row-in-one-form) |
141
142
  | `--lb-new` | [Editing a row in one form](./data-binding.md#editing-a-row-in-one-form) |
142
143
  | `--lb-release` | [Editing a row in one form](./data-binding.md#editing-a-row-in-one-form) |
143
- | `--lb-sort` | [Rows the user is working in](./data-binding.md#rows-the-user-is-working-in) |
144
+ | `--lb-place` | [Placing by hand](./data-binding.md#placing-by-hand) |
144
145
 
145
146
  ## Reserved tags
146
147
 
@@ -199,7 +200,7 @@ imports them rather than writing a string.
199
200
  | `ATTR_ROW_LIVE` | `lb-row-live` |
200
201
  | `LIVE_ROW` | `[lb-row-live]`, the selector for a live row |
201
202
  | `ATTR_QUERY_ROW_COUNT` | `lb-query-row-count` |
202
- | `ATTR_QUERY_UNSORTED_COUNT` | `lb-query-unsorted-count` |
203
+ | `ATTR_QUERY_UNPLACED_COUNT` | `lb-query-unplaced-count` |
203
204
  | `ATTR_QUERY_ORDER` | `lb-query-order` |
204
205
  | `ATTR_GROUP` | `lb-group` |
205
206
  | `ATTR_GROUP_LIVE` | `lb-group-live` |
@@ -213,13 +214,15 @@ imports them rather than writing a string.
213
214
  | `CHOICES_QUERY` | `lb-choices` |
214
215
  | `ATTR_ORDER` | `lb-order` |
215
216
  | `ATTR_FILTER` | `lb-filter` |
217
+ | `ATTR_MANUAL` | `lb-manual` |
218
+ | `MANUAL_ORDER`, `MANUAL_FILTER` | `order`, `filter`: the sides `lb-manual` names |
216
219
  | `FILTER_OPERATORS` | Every filter operator, the longer first |
217
220
  | `ATTR_ROW_CREATED` | `lb-row-created` |
218
221
  | `ATTR_ROW_CHANGED` | `lb-row-changed` |
219
222
  | `ATTR_ROW_MOVED` | `lb-row-moved` |
220
223
  | `ATTR_ROW_REQUESTED` | `lb-row-requested` |
221
224
  | `REQUESTED_CREATED`, `REQUESTED_CHANGED`, `REQUESTED_MOVED` | `created`, `changed`, `moved` |
222
- | `ATTR_ROW_UNSORTED` | `lb-row-unsorted` |
225
+ | `ATTR_ROW_UNPLACED` | `lb-row-unplaced` |
223
226
  | `ATTR_ROW_LEAVING` | `lb-row-leaving` |
224
227
  | `ATTR_GROUP_LEAVING` | `lb-group-leaving` |
225
228
  | `ATTR_REQUEST` | `lb-request` |
@@ -229,7 +232,7 @@ imports them rather than writing a string.
229
232
  | `ATTR_REQUEST_ERROR` | `lb-request-error` |
230
233
  | `ATTR_REQUEST_REFUSED` | `lb-request-refused` |
231
234
  | `LB_REFUSAL_STATUS` | `422` |
232
- | `COMMAND_EDIT`, `COMMAND_NEW`, `COMMAND_RELEASE`, `COMMAND_SORT` | `--lb-edit`, `--lb-new`, `--lb-release`, `--lb-sort` |
235
+ | `COMMAND_EDIT`, `COMMAND_NEW`, `COMMAND_RELEASE`, `COMMAND_PLACE` | `--lb-edit`, `--lb-new`, `--lb-release`, `--lb-place` |
233
236
  | `LB_COMMANDS` | All four commands |
234
237
  | `LB_COMMAND_PREFIX` | `--lb-` |
235
238
  | `ATTR_ROW_EDITING` | `lb-row-editing` |
@@ -265,7 +268,7 @@ imports them rather than writing a string.
265
268
  | `ATTR_EXP_TEMPLATE` | `lb-exp-template` |
266
269
  | `ATTR_PAGE` | `lb-page` |
267
270
  | `ATTR_PAGE_TITLE` | `lb-page-title` |
268
- | `DEVELOPER_ATTRIBUTES` | The seventeen attributes a developer writes |
271
+ | `DEVELOPER_ATTRIBUTES` | The eighteen attributes a developer writes |
269
272
  | `HUB_TAG_NAME` | `lb-hub` |
270
273
  | `LB_ENDPOINT` | `/lb` |
271
274
  | `LB_REQUEST_TIMEOUT_MS` | `10000` |
@@ -382,8 +382,9 @@ one row dropped, one row edited — and leave `refresh` empty. A refreshed
382
382
  A change that seems to need a refresh usually has a patch:
383
383
 
384
384
  - A row whose position changes: the hub places it by the query's
385
- [order](./data-binding.md#order), or leaves it where the user is working
386
- when the request came from inside its list; a refresh would do the same.
385
+ [order](./data-binding.md#order), or leaves it where it stands under the
386
+ list's [`lb-manual`](./data-binding.md#placing-by-hand); a refresh would do
387
+ the same.
387
388
  - A group that appears or goes: the hub makes a group with its first row
388
389
  and removes it with its last, so no row stands in for an empty one.
389
390
  - A row whose other columns change with the edit: re-read the row and patch
@@ -445,7 +446,9 @@ export const requests: Requests = {
445
446
  ```
446
447
 
447
448
  A column other than `lb-path` is a query parm: set, or taken out when its
448
- value is an empty string. Name at least one column, and give each a string.
449
+ value is an empty string. Name at least one column, and give each a string,
450
+ or an array of strings to repeat the parm; see
451
+ [Several values](./chrome.md#several-values).
449
452
  With `lb-path` naming another page, the page is entered with only the parms
450
453
  named:
451
454
 
@@ -462,7 +465,8 @@ nothing else `run` returned is sent, since both answered for the URL the page
462
465
  left. The loaded page reads the new parms through `contextFor`; see
463
466
  [the Express server](./server.md#database-layer).
464
467
 
465
- - A `url()` naming no column, or holding a value that is not a string, is
468
+ - A `url()` naming no column, or holding a value that is neither a string
469
+ nor an array of strings, is
466
470
  warned about on the server console and ignored, and the refresh set runs
467
471
  as usual.
468
472
  - If loading the page fails, the write has still happened. The response
package/docs/testing.md CHANGED
@@ -208,9 +208,9 @@ evidence here.
208
208
  - `lb-query-row-count` counts the rows showing, so all rows and a patch
209
209
  ending in the same state report the same number, and a row a filter hides
210
210
  is not counted
211
- - the order places all rows and a patch alike, except where an edit came
212
- from inside the list, which leaves its rows standing and stamped until
213
- the user asks for the order; groups come and go with
211
+ - the order and the filter place all rows and a patch alike, except where
212
+ the list's `lb-manual` leaves a side to the user, which keeps its rows
213
+ standing and stamped until they are placed again; groups come and go with
214
214
  their rows to any depth, aggregates follow every landing, and every change
215
215
  leaves a stamp a stylesheet can see (`lb-order.test.ts`)
216
216
  - a filter's text reads and passes values as documented, and a choice of
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.23.0",
4
+ "version": "0.24.0",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",
@@ -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; re-sorting a list with `--lb-sort`; 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 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.
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  package: "@loadbare/app"
@@ -194,11 +194,17 @@ row is filled from the nested query's last answer, whatever order they land
194
194
  in.
195
195
 
196
196
  **A `rows` query declares its order, and the hub places every row by it.**
197
- An edit sent from inside a list, or from a form `--lb-edit` pointed at one
198
- of its rows, leaves that list's rows where they stand, stamped `lb-row-unsorted` where out of order, until the page loads, the
199
- user's order or filter changes, or a button with `commandfor` naming the
200
- list sends `command="--lb-sort"`, which places them with no round trip;
201
- show it by `[lb-query-unsorted-count]`. Never refresh to make rows hold
197
+ It places them by the order and the user's filter on every landing. A list
198
+ whose rows must hold still while the user works through them carries
199
+ `lb-manual="order"`, `"filter"` or `"order filter"`: an answer to any
200
+ request, from wherever it was sent, then leaves that side alone, so an
201
+ edited row stays where it stands, a new row goes last in its group, and a
202
+ row the filter would hide stays showing. A row left out of place is
203
+ stamped `lb-row-unplaced` until the page loads, the user's order or filter
204
+ changes, or a button with `commandfor` naming the list sends
205
+ `command="--lb-place"`, which places them with no round trip; show it by
206
+ `[lb-query-unplaced-count]`. An editable list with a ghost row usually
207
+ wants `lb-manual="order"`. Never refresh to make rows hold
202
208
  still, and never to put them back in order.
203
209
  `rows("id", run, { order: "category_order,name" })`; a column after `-` sorts
204
210
  descending, and values compare by JSON type, so a numeric sort column is a
@@ -266,7 +272,8 @@ row. Anything else gathers its form owner's controls. A `<form>` cannot
266
272
  wrap a `<tr>`, so an insert row in a table puts its controls in a form
267
273
  elsewhere with the HTML `form` attribute. A checkbox is a control: checked
268
274
  unless its column is `false`, null, `"false"` or `""`, and gathered as
269
- `"true"` or `"false"`.
275
+ `"true"` or `"false"`; one carrying `value` is instead one of a set
276
+ holding a repeated query parm.
270
277
  A radio button is a control: checked when its `value` is the column's
271
278
  value, and the group gathers the one checked, or `""`. In a row template
272
279
  the hub stamps its `name` with the row's key, so each row is its own group.
@@ -284,7 +291,7 @@ in every row. Write one form naming the set's query, outside every row,
284
291
  often in a `<dialog>`, and give it an `id`. A `type="button"` with
285
292
  `commandfor="<id>"` and `command="--lb-edit"` in a live row fills it from
286
293
  that row with no round trip; `--lb-new` starts it empty for an insert;
287
- `--lb-release` lets it go. (`--lb-sort` is the fourth command, for a list,
294
+ `--lb-release` lets it go. (`--lb-place` is the fourth command, for a list,
288
295
  not a form.) The form's `lb-key-value` is the row's key, empty
289
296
  for a new row, absent when not in use, and a stylesheet shows Save or Save
290
297
  new by it. A successful request from the form, or its dialog closing, lets
@@ -342,7 +349,7 @@ fills a new row from the last one.
342
349
 
343
350
  **The usual reasons for a refresh each have a patch.**
344
351
  - A row whose position changes: the hub places it by the query's order, or
345
- holds it where the user is working; a refresh changes neither.
352
+ holds it where it stands under `lb-manual`; a refresh changes neither.
346
353
  - A group that appears or goes: the hub makes it with its first row and
347
354
  removes it with its last.
348
355
  - A row whose other columns change with the edit: re-read the row and patch
@@ -379,7 +386,11 @@ keyed by `lb-path`, with `lb-page-label`, `lb-page-unknown`, and one column
379
386
  per query parm. A control inside `lb-query="lb-url"` carrying
380
387
  `lb-request="lb-row-update"` writes its parm; the hub replaces the history
381
388
  entry, or pushes one when the control carries `lb-url-push`, and reloads the
382
- page's queries with no request to the server. An `<a lb-url-link>` moves
389
+ page's queries with no request to the server. A parm the URL repeats,
390
+ `?band=3&band=5`, is one column holding an array of strings: a
391
+ `<select multiple>`, or checkboxes carrying `value` and naming one column,
392
+ write it, `url()` takes an array, and `contextFor` reads it with
393
+ `parms.getAll`. Only a query parm holds several values. An `<a lb-url-link>` moves
383
394
  to its `href`; inside a row, `lb-url-parm="topic_id"` adds that column of
384
395
  the row to the `href` as the parm of the same name, so a link to another
385
396
  page narrowed by the row is a link, never a request or script. Queries still take no arguments: `contextFor(req, parms)`
@@ -416,8 +427,8 @@ whole data channel.
416
427
 
417
428
  **The developer writes `lb-*` attributes in markup, and script never
418
429
  assigns them.** The builder refuses any `lb-` attribute other than the
419
- seventeen in [Attributes](references/names.md#attributes): `lb-query`,
420
- `lb-column`, `lb-show`, `lb-order`, `lb-filter`, `lb-group`, `lb-count`,
430
+ eighteen in [Attributes](references/names.md#attributes): `lb-query`,
431
+ `lb-column`, `lb-show`, `lb-order`, `lb-filter`, `lb-manual`, `lb-group`, `lb-count`,
421
432
  `lb-sum`, `lb-avg`, `lb-min`, `lb-max`, `lb-select`, `lb-request`,
422
433
  `lb-url-link`, `lb-url-parm`, `lb-url-push` and `lb-url-unknown`. `lb-exp-slot` and
423
434
  `lb-exp-template` are expansion's, and never reach the page. Everything
@@ -238,6 +238,48 @@ and after Back alike
238
238
  Loadbare assigns no parm a meaning, and a column of `lb-url` whose name
239
239
  begins with `lb-` is never a query parm.
240
240
 
241
+ ### Several values
242
+
243
+ A URL may repeat a parm, `/statements?band=3&band=5`, as an HTML form does
244
+ for a `<select multiple>` or for checkboxes sharing a name. That column of
245
+ `lb-url` holds every value, in order, as an array of strings. A parm the URL
246
+ carries once is a string, as it always is. Two controls hold several values:
247
+
248
+ ```html
249
+ <div lb-query="lb-url">
250
+ <select multiple lb-column="band" lb-request="lb-row-update">
251
+ <option value="3">Cash</option>
252
+ <option value="5">Investments</option>
253
+ </select>
254
+ </div>
255
+
256
+ <form lb-query="lb-url" lb-request="lb-row-update">
257
+ <label><input type="checkbox" lb-column="band" value="3" /> Cash</label>
258
+ <label><input type="checkbox" lb-column="band" value="5" /> Investments</label>
259
+ <button>Show</button>
260
+ </form>
261
+ ```
262
+
263
+ - A `<select multiple>` selects each option whose `value` is among the
264
+ column's, and writes the values of the options selected.
265
+ - A checkbox carrying `value` is one of a set naming the column. It is
266
+ checked when its `value` is among the column's, and the set writes the
267
+ values of the ones checked. A checkbox without `value` is a flag; see
268
+ [Checkboxes](./data-binding.md#checkboxes).
269
+ - Writing no value takes the parm out of the URL.
270
+ - Either control carries the values as a JSON array in `lb-column-value`.
271
+ - A control that holds one value, and any other element, shows the first,
272
+ as `URLSearchParams.get` reads it. A form-associated custom element
273
+ receives the value as it is, a string or an array, and may give an array
274
+ back; see [Being a control](./custom-elements.md#being-a-control).
275
+ - A handler's `url()` writes several values as an array of strings, and
276
+ `contextFor` reads them with `parms.getAll`.
277
+
278
+ Only a column of `lb-url` holds several values. Every other column holds
279
+ one, so the builder refuses a control holding several anywhere but inside
280
+ `lb-query="lb-url"`, and the server ignores a request that carries several
281
+ for a column of its own.
282
+
241
283
  A query parm is user input. A query still takes no argument from the
242
284
  browser, and a parm arrives the way the session cookie does, as part of the
243
285
  request the context is built from, but a user who types `?acct=99999`
@@ -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-unsorted-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-group-*`, `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` |
@@ -404,6 +404,11 @@ customElements.define("star-rating", StarRating);
404
404
  Dispatch `change` with `bubbles: true`, as a native control's does, when the
405
405
  user commits an edit. Setting `value` from the hub fires nothing.
406
406
 
407
+ Inside `lb-query="lb-url"`, a control may hold several values. The hub sets
408
+ its `value` to a string, or to an array of strings when the URL repeats the
409
+ parm, and gathers an array back when `value` gives one; see
410
+ [Several values](./chrome.md#several-values).
411
+
407
412
  A form owns a form-associated custom element the way it owns an `<input>`,
408
413
  including through the HTML `form` attribute, so a form gathers it on submit.
409
414
  Write `formResetCallback` to restore the default value: the hub resets a form
@@ -627,7 +632,7 @@ had there hears the event, and no custom element the hub has removed hears
627
632
  ### Holding rows
628
633
 
629
634
  The hub lands every row template itself, places every row by the query's
630
- order (see [Rows the user is working in](./data-binding.md#rows-the-user-is-working-in)
635
+ order (see [Placing by hand](./data-binding.md#placing-by-hand)
631
636
  for when it does not), and builds every group the markup's group templates describe; see
632
637
  [Order](./data-binding.md#order) and [Groups](./data-binding.md#groups). A
633
638
  plain element carrying `lb-query` with a row template inside it needs no
@@ -656,12 +661,12 @@ Everything else is the hub's:
656
661
  |-------------|----------------------------------------------------------|
657
662
  | Cloning | Clones the row template once per new key |
658
663
  | Matching | Fills the live row already showing that key, when its values changed |
659
- | Placing | Places every live row by the user's order or the query's, and leaves them where they stand for an edit from inside the list |
664
+ | 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` |
660
665
  | Filtering | Hides a row the user's filter does not pass, and a group whose rows are all hidden |
661
666
  | Grouping | Builds a group where the order breaks, keeps it while its run goes on, and removes it with its last row |
662
667
  | Removing | Marks the live rows the response says are gone as leaving, and removes them |
663
668
  | Stamping | Stamps each live row with `lb-row-live`, `lb-key-value`, and what happened to it |
664
- | Counting | Stamps `lb-query-row-count` with the number of live rows showing, `lb-query-unsorted-count` with the number out of order, and `lb-query-order` with the order placing them |
669
+ | 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 |
665
670
 
666
671
  `lb-options.browser.ts` and `lb-table.browser.ts` in
667
672
  [`@loadbare/widgets`](./widgets.md) are two uses of `lbRowsLanded`: one