@loadbare/app 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +61 -1
- package/dist/build/assemble.js.map +1 -1
- package/dist/build/cli.js +2 -1
- package/dist/build/cli.js.map +1 -1
- package/dist/build/elements.d.ts +9 -1
- package/dist/build/elements.d.ts.map +1 -1
- package/dist/build/elements.js +50 -0
- package/dist/build/elements.js.map +1 -1
- package/dist/build/origins.d.ts +2 -0
- package/dist/build/origins.d.ts.map +1 -1
- package/dist/build/origins.js +1 -1
- package/dist/build/origins.js.map +1 -1
- package/dist/core/lb-constants.d.ts +26 -1
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +79 -0
- package/dist/core/lb-constants.js.map +1 -1
- package/dist/core/lb-types.d.ts +58 -18
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js +74 -11
- package/dist/core/lb-types.js.map +1 -1
- package/dist/hub/lb-apply.d.ts +44 -16
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +694 -49
- package/dist/hub/lb-apply.js.map +1 -1
- package/dist/hub/lb-hub.browser.d.ts.map +1 -1
- package/dist/hub/lb-hub.browser.js +168 -36
- package/dist/hub/lb-hub.browser.js.map +1 -1
- package/dist/server/lb-express.d.ts +6 -4
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +32 -14
- package/dist/server/lb-express.js.map +1 -1
- package/dist/server/lb-server.d.ts +49 -10
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +87 -28
- package/dist/server/lb-server.js.map +1 -1
- package/docs/TECHREF-1.0.md +364 -72
- package/docs/comparison.md +16 -10
- package/docs/possible-ideas.md +188 -0
- package/docs/reference/chrome.md +22 -1
- package/docs/reference/custom-elements.md +90 -29
- package/docs/reference/data-binding.md +132 -7
- package/docs/reference/page-files.md +48 -11
- package/docs/reference/server.md +4 -0
- package/docs/reference/widgets.md +94 -24
- package/docs/roadmap.md +10 -6
- package/docs/testing.md +21 -10
- package/package.json +2 -3
- package/skills/loadbare-app/SKILL.md +85 -12
- package/skills/loadbare-app/references/TECHREF-1.0.md +364 -72
- package/skills/loadbare-app/references/chrome.md +22 -1
- package/skills/loadbare-app/references/custom-elements.md +90 -29
- package/skills/loadbare-app/references/data-binding.md +132 -7
- package/skills/loadbare-app/references/page-files.md +48 -11
- package/skills/loadbare-app/references/server.md +4 -0
- package/skills/loadbare-app/references/widgets.md +94 -24
|
@@ -72,6 +72,10 @@ Put everything the user interacts with inside `<lb-hub>`. The hub normally
|
|
|
72
72
|
sits directly inside `<body>`, with banner, nav, footer and `<main>` inside
|
|
73
73
|
it, so Loadbare can act on all of them.
|
|
74
74
|
|
|
75
|
+
Write exactly one `<lb-hub>` and exactly one `<main>`, with the `<main>`
|
|
76
|
+
inside the hub and empty but for whitespace. The builder rejects a chrome
|
|
77
|
+
that does otherwise.
|
|
78
|
+
|
|
75
79
|
The chrome's `<title>` is the document title until the first page shows.
|
|
76
80
|
From then on the hub sets the document title to the page's label; see
|
|
77
81
|
[The URL](#the-url).
|
|
@@ -108,6 +112,21 @@ tab and the browser history show the page.
|
|
|
108
112
|
`lb-url` is the one query a page may use that the server does not declare. A
|
|
109
113
|
page names it the same way, anywhere inside the hub.
|
|
110
114
|
|
|
115
|
+
### Where focus starts
|
|
116
|
+
|
|
117
|
+
On entering a page, once its queries have landed, the hub focuses the first
|
|
118
|
+
element in `<main>` the user can operate, so a keyboard user starts in the
|
|
119
|
+
page rather than on the document. The chrome comes first in the document,
|
|
120
|
+
and is passed over. So is anything disabled, in a closed `<dialog>`,
|
|
121
|
+
`inert`, `hidden`, in an absent `lb-show` branch, or that does not take
|
|
122
|
+
the focus when asked. A widget's native control counts, so write no
|
|
123
|
+
`autofocus` to restate this.
|
|
124
|
+
|
|
125
|
+
A page is entered on a cold load, a new `lb-path` from a link or from a
|
|
126
|
+
handler's `url()`, and Back or Forward to another page. A change of query
|
|
127
|
+
parm enters no page: the user who chose a record in a picker stays in the
|
|
128
|
+
picker. Focus the user has already put in `<main>` is left there.
|
|
129
|
+
|
|
111
130
|
### Links
|
|
112
131
|
|
|
113
132
|
Write `lb-url-link` on an `<a>` to move between pages:
|
|
@@ -150,7 +169,9 @@ pushes one instead when the element carrying `lb-request` also carries
|
|
|
150
169
|
|
|
151
170
|
The hub answers the request itself, with no round trip, then reloads the
|
|
152
171
|
page's queries at the new URL. The page stays in place, and rows that come
|
|
153
|
-
back keep their place.
|
|
172
|
+
back keep their place. A request that changes only `lb-order-<query>`, the
|
|
173
|
+
user's [order](./data-binding.md#order) for a query the hub sorts itself,
|
|
174
|
+
re-sorts the rows already on the page and loads nothing.
|
|
154
175
|
|
|
155
176
|
A control whose column the URL does not carry lands empty, so every control
|
|
156
177
|
inside `lb-query="lb-url"` shows what the address bar says, after a reload
|
|
@@ -268,6 +268,8 @@ one as a string literal:
|
|
|
268
268
|
| `REQUEST_ROW_INSERT`, `REQUEST_ROW_UPDATE`, `REQUEST_ROW_DELETE` | The requests Loadbare provides |
|
|
269
269
|
| `LB_ROW_REQUESTS` | All three, in one array |
|
|
270
270
|
| `LB_EVENT_NAME` | `lb-request`, the event every request travels as |
|
|
271
|
+
| `LB_DONE_EVENT_NAME` | `lb-request-done`, the event that says how it turned out |
|
|
272
|
+
| `SHOW_NOT` | `!`, written before a column in `lb-show` |
|
|
271
273
|
| `URL_QUERY` | `lb-url` |
|
|
272
274
|
| `URL_COLUMN_PATH`, `URL_COLUMN_PAGE_LABEL`, `URL_COLUMN_PAGE_UNKNOWN` | `lb-path`, `lb-page-label`, `lb-page-unknown` |
|
|
273
275
|
| `LB_RESERVED_PREFIX` | `lb-` |
|
|
@@ -276,7 +278,28 @@ A custom element reads `lb-` attributes and never assigns one. The developer
|
|
|
276
278
|
writes them in markup, and the hub and the builder write their stamps.
|
|
277
279
|
|
|
278
280
|
Loadbare reserves method names beginning with `lb` on a custom element, for
|
|
279
|
-
the methods it calls; see [Holding rows](#holding-rows).
|
|
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.
|
|
285
|
+
|
|
286
|
+
### What the hub and an element say to each other
|
|
287
|
+
|
|
288
|
+
Each direction has one mechanism for each kind of message:
|
|
289
|
+
|
|
290
|
+
| Direction | What | How | Today |
|
|
291
|
+
|----------------|--------------------------------------------|-----------------------------|-------|
|
|
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
|
+
| 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` |
|
|
295
|
+
| Hub to element | A moment an element started | A bubbling event | `lb-request-done` |
|
|
296
|
+
| Element to hub | A moment | A bubbling event | `lb-request`, and `change`, `click` and `submit` |
|
|
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.
|
|
280
303
|
|
|
281
304
|
### When its code runs
|
|
282
305
|
|
|
@@ -309,7 +332,7 @@ So each kind of work has one place:
|
|
|
309
332
|
reads no attribute and no child: an element made with
|
|
310
333
|
`document.createElement` has neither when it runs.
|
|
311
334
|
2. **A child is looked up when it is needed**, in a handler, a getter,
|
|
312
|
-
|
|
335
|
+
or `lbRowsLanded`, and never held from setup.
|
|
313
336
|
3. **`connectedCallback` runs on every move**, so it is written to run
|
|
314
337
|
again. It rearranges the element's own children into a state it checks
|
|
315
338
|
for first, or adds a listener to `document` or `window`, which
|
|
@@ -450,36 +473,67 @@ missing a field its name needs, and stamps the dispatching element with
|
|
|
450
473
|
|
|
451
474
|
Let the event bubble, so an ancestor can stop it before the hub sends it.
|
|
452
475
|
|
|
476
|
+
### Hearing how it turned out
|
|
477
|
+
|
|
478
|
+
Once what a request brought back has landed, or its round trip has failed,
|
|
479
|
+
the hub dispatches `lb-request-done` from the element that committed. It
|
|
480
|
+
bubbles, so the element that cares need not be the one that committed: a
|
|
481
|
+
dialog hears it from the form inside it.
|
|
482
|
+
|
|
483
|
+
```ts
|
|
484
|
+
import { LB_DONE_EVENT_NAME } from "@loadbare/app/constants";
|
|
485
|
+
import type { HubRequestDone } from "@loadbare/app/types";
|
|
486
|
+
|
|
487
|
+
this.addEventListener(LB_DONE_EVENT_NAME, (e) => {
|
|
488
|
+
const { request, items, error } = (e as CustomEvent<HubRequestDone>).detail;
|
|
489
|
+
if (error) return;
|
|
490
|
+
const item = items.find((i) => i.query === request.query);
|
|
491
|
+
const created = item?.patch?.rows?.[0];
|
|
492
|
+
if (created) this.choose(String(created[item!.key]));
|
|
493
|
+
});
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
`items` is every response item that landed because of the request, in
|
|
497
|
+
order. An answer that moved the URL contributes its `lb-url` item, which
|
|
498
|
+
carries a key only the server knew, and the page load. `error` is set when
|
|
499
|
+
the round trip failed, and then nothing landed. A new row's key is in the
|
|
500
|
+
answer only when the handler returned it in a patch: a refresh sends all
|
|
501
|
+
rows, and nothing marks which one is new.
|
|
502
|
+
|
|
503
|
+
By the time the event is dispatched, `lb-request-pending` is gone and the
|
|
504
|
+
page is as the answer left it. A request that was never sent, because the
|
|
505
|
+
hub or an ancestor stopped it, gets no `lb-request-done`. An element the
|
|
506
|
+
answer removed, such as the row a delete took away, dispatches the event
|
|
507
|
+
outside the document, and no ancestor it had there hears it.
|
|
508
|
+
|
|
453
509
|
### Holding rows
|
|
454
510
|
|
|
455
|
-
The hub lands every row template itself
|
|
456
|
-
|
|
457
|
-
|
|
511
|
+
The hub lands every row template itself, places every row by the query's
|
|
512
|
+
order, and builds every group the markup's group templates describe; see
|
|
513
|
+
[Order](./data-binding.md#order) and [Groups](./data-binding.md#groups). A
|
|
514
|
+
plain element carrying `lb-query` with a row template inside it needs no
|
|
515
|
+
code, so a custom element holds rows only to do something with them once
|
|
516
|
+
they land.
|
|
458
517
|
|
|
459
|
-
A custom element carrying `lb-query` and a row template may implement
|
|
460
|
-
optional
|
|
518
|
+
A custom element carrying `lb-query` and a row template may implement one
|
|
519
|
+
optional method, which the hub calls:
|
|
461
520
|
|
|
462
521
|
```ts
|
|
463
|
-
import type { RowsHost
|
|
464
|
-
|
|
465
|
-
class SortedList extends HTMLElement implements RowsHost {
|
|
466
|
-
lbPlaceRow(el: Element, row: Row, template: HTMLTemplateElement) {
|
|
467
|
-
// Where this live row goes. Called with the live row detached, on its
|
|
468
|
-
// first appearance and again whenever all rows decide the order.
|
|
469
|
-
}
|
|
522
|
+
import type { RowsHost } from "@loadbare/app/types";
|
|
523
|
+
import { ATTR_ROW_REQUESTED, REQUESTED_CREATED } from "@loadbare/app/constants";
|
|
470
524
|
|
|
525
|
+
class ScrollingList extends HTMLElement implements RowsHost {
|
|
471
526
|
lbRowsLanded() {
|
|
472
|
-
// Once, after the rows have landed
|
|
473
|
-
|
|
474
|
-
|
|
527
|
+
// Once, after the rows have landed, every row placed and stamped.
|
|
528
|
+
this.querySelector(`[${ATTR_ROW_REQUESTED}="${REQUESTED_CREATED}"]`)
|
|
529
|
+
?.scrollIntoView({ block: "nearest" });
|
|
475
530
|
}
|
|
476
531
|
}
|
|
477
532
|
```
|
|
478
533
|
|
|
479
|
-
| Method | The hub calls it
|
|
480
|
-
|
|
481
|
-
| `
|
|
482
|
-
| `lbRowsLanded` | After the rows land |
|
|
534
|
+
| Method | The hub calls it |
|
|
535
|
+
|----------------|-------------------------------------------------------|
|
|
536
|
+
| `lbRowsLanded` | After the rows land, with every row placed and stamped |
|
|
483
537
|
|
|
484
538
|
Everything else is the hub's:
|
|
485
539
|
|
|
@@ -487,16 +541,23 @@ Everything else is the hub's:
|
|
|
487
541
|
|-------------|----------------------------------------------------------|
|
|
488
542
|
| Cloning | Clones the row template once per new key |
|
|
489
543
|
| Matching | Fills the live row already showing that key |
|
|
490
|
-
|
|
|
491
|
-
|
|
|
544
|
+
| Placing | Places every live row by the query's order |
|
|
545
|
+
| Grouping | Builds a group where the order breaks, and removes it with its last row |
|
|
546
|
+
| Removing | Marks the live rows the response says are gone as leaving, and removes them |
|
|
547
|
+
| Stamping | Stamps each live row with `lb-row-live`, `lb-key-value`, and what happened to it |
|
|
492
548
|
| Counting | Stamps `lb-query-row-count` with the number of live rows |
|
|
493
549
|
|
|
494
|
-
`
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
550
|
+
`lb-options.browser.ts` and `lb-table.browser.ts` in
|
|
551
|
+
[`@loadbare/widgets`](./widgets.md) are two uses of `lbRowsLanded`: one
|
|
552
|
+
gives each option and `<optgroup>` what landing does not, a value and a
|
|
553
|
+
label, and the other scrolls to the row the page's request created or
|
|
554
|
+
moved.
|
|
555
|
+
|
|
556
|
+
A custom element the builder shipped absent under `lb-show` has not
|
|
557
|
+
upgraded while its column is off, and the hub places the rows that land on
|
|
558
|
+
it then as it places any. When the column first turns on and the element
|
|
559
|
+
upgrades, the hub lands the query's last answer on it again, as all rows,
|
|
560
|
+
so `lbRowsLanded` runs.
|
|
500
561
|
|
|
501
562
|
A custom element that walks its own rows finds them with `LIVE_ROW` from
|
|
502
563
|
`@loadbare/app/constants`, never by `lb-key-value`: an element a `row` lands
|
|
@@ -153,9 +153,9 @@ places it immediately before the template:
|
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
The hub matches each row to a live row by its key. All rows decide
|
|
156
|
-
membership
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
membership: a live row whose key did not arrive leaves. A patch changes only
|
|
157
|
+
the rows it names, and every other live row keeps its content. The hub
|
|
158
|
+
places every live row by the query's order; see [Order](#order).
|
|
159
159
|
|
|
160
160
|
A key is unique within a query. Two rows with one key in the same answer,
|
|
161
161
|
or two live rows showing one key, are reported on the console.
|
|
@@ -163,10 +163,121 @@ or two live rows showing one key, are reported on the console.
|
|
|
163
163
|
A live row's root counts as a column when it carries `lb-column`, which is
|
|
164
164
|
how an `<option>`, whose content is text, shows the column it is.
|
|
165
165
|
|
|
166
|
-
A custom element that carries `lb-query` and a row template
|
|
167
|
-
|
|
168
|
-
[
|
|
169
|
-
|
|
166
|
+
A custom element that carries `lb-query` and a row template reacts to the
|
|
167
|
+
rows once they land; see [Holding rows](./custom-elements.md#holding-rows)
|
|
168
|
+
and [The Basic Widget Library](./widgets.md).
|
|
169
|
+
|
|
170
|
+
### Order
|
|
171
|
+
|
|
172
|
+
A `rows` query declares its order with the query, as columns separated by
|
|
173
|
+
commas, each descending when written after `-`:
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
roster: rows("id", (ctx) => ctx.db.members(), { order: "team,name" }),
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The query parm `lb-order-<query>` replaces it for that query, so a control
|
|
180
|
+
inside `lb-query="lb-url"` writing that parm re-sorts the rows the hub
|
|
181
|
+
already has, with no round trip. With no order, rows show in the order they
|
|
182
|
+
arrived, and a patch's new rows go last.
|
|
183
|
+
|
|
184
|
+
Values compare by their JSON type: numbers numerically, strings as the
|
|
185
|
+
user's language orders them, `false` before `true`, and `null` last. A
|
|
186
|
+
number sent as a string sorts as a string. Rows that compare equal keep the
|
|
187
|
+
order they arrived in. A row moves only when the order puts it somewhere
|
|
188
|
+
else.
|
|
189
|
+
|
|
190
|
+
A query that pages or limits its rows reads the parm itself and declares
|
|
191
|
+
`serverSortedByUrl: true`, so a change of order loads the page again.
|
|
192
|
+
|
|
193
|
+
### Groups
|
|
194
|
+
|
|
195
|
+
A group is a run of rows sharing the order's leading term. Write a
|
|
196
|
+
`<template lb-group>` where the row template goes, holding the group's
|
|
197
|
+
heading and one nested `<template>`: the next group template, or the row
|
|
198
|
+
template. The nth group template breaks on the order's nth term.
|
|
199
|
+
|
|
200
|
+
```html
|
|
201
|
+
<ul lb-query="roster">
|
|
202
|
+
<template lb-group>
|
|
203
|
+
<li>
|
|
204
|
+
<h3 lb-column="team"></h3>
|
|
205
|
+
<ul>
|
|
206
|
+
<template><li lb-column="name"></li></template>
|
|
207
|
+
</ul>
|
|
208
|
+
</li>
|
|
209
|
+
</template>
|
|
210
|
+
</ul>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The group's contents land immediately before its nested template. When that
|
|
214
|
+
template is inside the heading's element, the contents land inside it; when
|
|
215
|
+
it is beside the heading, they land after it. `<tbody>` and `<optgroup>` do
|
|
216
|
+
not nest, so a table's deeper levels are heading rows. Content after the
|
|
217
|
+
nested template is the group's footer.
|
|
218
|
+
|
|
219
|
+
A heading is filled from its group's first row, so it may show a column
|
|
220
|
+
other than the one the group breaks on. The hub stamps each top-level
|
|
221
|
+
element of a group with `lb-group-live`, `lb-group-column` and
|
|
222
|
+
`lb-group-value`. A row creates its group, and a group leaves with its last
|
|
223
|
+
row.
|
|
224
|
+
|
|
225
|
+
### Aggregates
|
|
226
|
+
|
|
227
|
+
`lb-count`, `lb-sum="col"`, `lb-avg="col"`, `lb-min="col"` and
|
|
228
|
+
`lb-max="col"` set an element from the rows of the nearest group around it,
|
|
229
|
+
or of the whole list outside any group:
|
|
230
|
+
|
|
231
|
+
```html
|
|
232
|
+
<tr><td><span lb-count></span> people</td><td lb-sum="salary"></td></tr>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
A sum and an average are exact, read from JSON numbers and from strings
|
|
236
|
+
holding a plain decimal. `null` and empty values are left out. The result
|
|
237
|
+
lands unformatted, and covers the rows the browser holds.
|
|
238
|
+
|
|
239
|
+
### What happened to a row
|
|
240
|
+
|
|
241
|
+
Every change to a list is one a stylesheet can see:
|
|
242
|
+
|
|
243
|
+
| Attribute | Means |
|
|
244
|
+
| ------------------ | ------------------------------------------------------ |
|
|
245
|
+
| `lb-row-created` | Created when the hub last touched the row |
|
|
246
|
+
| `lb-row-changed` | Its values changed when the hub last touched it |
|
|
247
|
+
| `lb-row-moved` | The order put it somewhere else when last touched |
|
|
248
|
+
| `lb-row-requested` | `created`, `changed` or `moved` by this page's request |
|
|
249
|
+
| `lb-row-leaving` | A row on its way out |
|
|
250
|
+
| `lb-group-leaving` | A group on its way out |
|
|
251
|
+
|
|
252
|
+
A row that leaves loses `lb-row-live`, takes `inert`, and is removed once
|
|
253
|
+
its animations finish, at once when there are none. A moved row moves, and
|
|
254
|
+
a copy stays behind where it was, leaving. `lb-row-requested` is cleared at
|
|
255
|
+
every landing; the others when the hub next touches the row.
|
|
256
|
+
|
|
257
|
+
```css
|
|
258
|
+
[lb-row-created] { animation: arrive 200ms; }
|
|
259
|
+
[lb-row-leaving] { animation: depart 200ms forwards; }
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### What arrives later
|
|
263
|
+
|
|
264
|
+
The hub keeps each query's last answer until the page changes, with any
|
|
265
|
+
patch since applied to it. An element that names a query and arrives after
|
|
266
|
+
that query's answer is filled from what was kept, at the end of the landing
|
|
267
|
+
that follows its arrival. Such an element is a picker in a new live row, or
|
|
268
|
+
scaffolding around rows, such as a group's heading or a ghost row. So
|
|
269
|
+
a query nested in another query's rows need not land again, or land after
|
|
270
|
+
the outer query, for a new outer row to show its choices.
|
|
271
|
+
|
|
272
|
+
A custom element the builder shipped absent has not upgraded while its
|
|
273
|
+
`lb-show` column is off. When the column first turns on and the element
|
|
274
|
+
upgrades, the hub lands the kept answer on it again, so its `lbRowsLanded`
|
|
275
|
+
runs. See [Holding rows](./custom-elements.md#holding-rows).
|
|
276
|
+
|
|
277
|
+
Nothing is answered from what the hub kept: a request always goes to the
|
|
278
|
+
server, and a query re-runs only when a response names it. A patch that
|
|
279
|
+
arrives before all of a query's rows is not kept, since it is not the whole
|
|
280
|
+
of anything.
|
|
170
281
|
|
|
171
282
|
A `rows` query on an element with no row template lands nothing. That is the
|
|
172
283
|
insert form above: it sits inside `lb-query="roster"` so its request is for
|
|
@@ -225,6 +336,14 @@ have the query answer with a boolean or a null. A row that does not carry
|
|
|
225
336
|
the column leaves the element as it is, so a query answers with the column
|
|
226
337
|
in every row.
|
|
227
338
|
|
|
339
|
+
Write `!` before the column to reverse it. One column then decides between
|
|
340
|
+
two elements, rather than a column and its opposite, which could disagree:
|
|
341
|
+
|
|
342
|
+
```html
|
|
343
|
+
<h2 lb-show="chosen" lb-column="title"></h2>
|
|
344
|
+
<p lb-show="!chosen">Choose a batch.</p>
|
|
345
|
+
```
|
|
346
|
+
|
|
228
347
|
`lb-show` reads from the nearest ancestor row, as `lb-column` does. On an
|
|
229
348
|
element that also carries `lb-query`, the column belongs to the row around
|
|
230
349
|
it, so this picker takes its choices from `groups` and whether it is present
|
|
@@ -463,6 +582,11 @@ An ancestor may stop the event, and the request is not sent. A custom
|
|
|
463
582
|
element may dispatch the event itself; see
|
|
464
583
|
[Sending a request](./custom-elements.md#sending-a-request).
|
|
465
584
|
|
|
585
|
+
Once the answer has landed, or the round trip has failed, the hub dispatches
|
|
586
|
+
a bubbling `lb-request-done` event from the same element, carrying the
|
|
587
|
+
request and what landed because of it, or the error; see
|
|
588
|
+
[Hearing how it turned out](./custom-elements.md#hearing-how-it-turned-out).
|
|
589
|
+
|
|
466
590
|
## The URL
|
|
467
591
|
|
|
468
592
|
The hub serves one query of its own, `lb-url`: one row holding the path, the
|
|
@@ -495,3 +619,4 @@ expansion, and refuses to build:
|
|
|
495
619
|
- `lb-url-unknown` on anything but a `<dialog>`, or outside `<lb-hub>`
|
|
496
620
|
- `lb-show` on a `<template>`, or on a row template's root
|
|
497
621
|
- `lb-show` with no `lb-query` around it
|
|
622
|
+
- `lb-show="!"` or `lb-show="!!column"`, which reverse no column
|
|
@@ -48,7 +48,8 @@ page touches that page's files.
|
|
|
48
48
|
## HTML
|
|
49
49
|
|
|
50
50
|
Write the page as a fragment. The fragment lands in `<main>`, which
|
|
51
|
-
[chrome.html](./chrome.md) supplies.
|
|
51
|
+
[chrome.html](./chrome.md) supplies. A page carries no `<main>` and no
|
|
52
|
+
`<lb-hub>` of its own; the builder rejects one that does.
|
|
52
53
|
|
|
53
54
|
```html
|
|
54
55
|
<!-- src/pages/about.page.html -->
|
|
@@ -89,14 +90,18 @@ export const queries: Queries = {
|
|
|
89
90
|
page: "directory",
|
|
90
91
|
count: String(await ctx.db.visitCount()),
|
|
91
92
|
})),
|
|
92
|
-
directory: rows("id", (ctx) => ctx.db.directory()),
|
|
93
|
+
directory: rows("id", (ctx) => ctx.db.directory(), { order: "name" }),
|
|
93
94
|
};
|
|
94
95
|
```
|
|
95
96
|
|
|
96
|
-
| Declared with
|
|
97
|
-
|
|
98
|
-
| `row(key, run)`
|
|
99
|
-
| `rows(key, run)` | All its rows, an array of objects
|
|
97
|
+
| Declared with | Answers with |
|
|
98
|
+
|----------------------------|-------------------------------------------|
|
|
99
|
+
| `row(key, run)` | One row, an object |
|
|
100
|
+
| `rows(key, run, options)` | All its rows, an array of objects |
|
|
101
|
+
|
|
102
|
+
`options` are a `rows` query's order: `order`, the columns the hub places
|
|
103
|
+
its rows by, and `serverSortedByUrl`, set when the query reads the user's
|
|
104
|
+
order parm itself. See [Order](./data-binding.md#order).
|
|
100
105
|
|
|
101
106
|
`key` names the column that identifies a row. Give every row that column,
|
|
102
107
|
including a `row` query's: an aggregate row answers with a constant key. The
|
|
@@ -109,8 +114,8 @@ page that needs the same data as one row and as a set declares two queries.
|
|
|
109
114
|
The hub hands each value to the browser untouched, so what a number, a date
|
|
110
115
|
or a null looks like is decided here, in the query.
|
|
111
116
|
|
|
112
|
-
Return the full answer every time
|
|
113
|
-
|
|
117
|
+
Return the full answer every time. With no `order`, the rows show in the
|
|
118
|
+
order they are returned. Sending only what changed is a request's job — see
|
|
114
119
|
[refresh and patch](#refresh-and-patch).
|
|
115
120
|
|
|
116
121
|
## Requests
|
|
@@ -120,7 +125,7 @@ optional:
|
|
|
120
125
|
|
|
121
126
|
| Key | Runs |
|
|
122
127
|
|---------------|--------------------------------------------------------|
|
|
123
|
-
| `onPageEnter` | Before the page's queries, when the page loads
|
|
128
|
+
| `onPageEnter` | Before the page's queries, when the page loads; may move the URL |
|
|
124
129
|
| `handlers` | The page's declared requests, by request name |
|
|
125
130
|
| `crud` | The requests Loadbare provides, by query name |
|
|
126
131
|
|
|
@@ -150,6 +155,12 @@ export const requests: Requests = {
|
|
|
150
155
|
|
|
151
156
|
Declare no refresh set here. The page's queries run afterward.
|
|
152
157
|
|
|
158
|
+
It may return `url()` instead, to move the page to other query parms before
|
|
159
|
+
anything shows, as restoring the filters and order the user last had does.
|
|
160
|
+
The page loads there in the same round trip, and that load does not follow
|
|
161
|
+
`onPageEnter` again. A `url()` leading to the parms already in hand is
|
|
162
|
+
ignored. See [Moving the URL](#moving-the-url).
|
|
163
|
+
|
|
153
164
|
### handlers
|
|
154
165
|
|
|
155
166
|
Declare a request under the name the HTML gives `lb-request`. Pair what it
|
|
@@ -223,6 +234,9 @@ Every value in `values` is a string, as a control's value is.
|
|
|
223
234
|
### refresh and patch
|
|
224
235
|
|
|
225
236
|
List in `refresh` every query whose whole answer the request changed.
|
|
237
|
+
Never list one only to fill what the request's answer creates, such as the
|
|
238
|
+
picker in a new row: the hub fills that from the query's last answer. See
|
|
239
|
+
[What arrives later](./data-binding.md#what-arrives-later).
|
|
226
240
|
|
|
227
241
|
Return answers from `run` to state a narrower change than re-running a query
|
|
228
242
|
would. What `run` returns is keyed by query name and laid over the refreshed
|
|
@@ -236,8 +250,31 @@ queries:
|
|
|
236
250
|
| `patch({ drop: [...] })` | These keys are gone; the rest stand |
|
|
237
251
|
|
|
238
252
|
Return a patch for a change the request knows the extent of — one row added,
|
|
239
|
-
one row dropped, one row edited — and leave `refresh` empty.
|
|
240
|
-
|
|
253
|
+
one row dropped, one row edited — and leave `refresh` empty. A refreshed
|
|
254
|
+
`rows` query sends every row to say what one row could.
|
|
255
|
+
|
|
256
|
+
A change that seems to need a refresh usually has a patch:
|
|
257
|
+
|
|
258
|
+
- A row whose position changes: the hub places it by the query's
|
|
259
|
+
[order](./data-binding.md#order).
|
|
260
|
+
- A group that appears or goes: the hub makes a group with its first row
|
|
261
|
+
and removes it with its last, so no row stands in for an empty one.
|
|
262
|
+
- A row whose other columns change with the edit: re-read the row and patch
|
|
263
|
+
it.
|
|
264
|
+
- Rows the database removes with a deleted one: select their keys before the
|
|
265
|
+
delete and drop them too.
|
|
266
|
+
|
|
267
|
+
An update or a delete always has a patch, since it names its row by key and
|
|
268
|
+
removing a row never reorders the rest. `createHub` refuses at startup a
|
|
269
|
+
`crud` `rowUpdate` or `rowDelete` on a `rows` query whose `refresh` names
|
|
270
|
+
that same query. An insert may refresh its own query: where a new row goes
|
|
271
|
+
depends on whether its host places it, which the server cannot see.
|
|
272
|
+
|
|
273
|
+
Each request's `refresh` is its own, and every query in it needs its own
|
|
274
|
+
reason. A list shared by several requests is a warning sign.
|
|
275
|
+
|
|
276
|
+
Re-run the query when membership or order changed in a way the request
|
|
277
|
+
cannot name:
|
|
241
278
|
|
|
242
279
|
```ts
|
|
243
280
|
resetRoster: {
|
|
@@ -91,6 +91,10 @@ TypeScript types itself. `dist/pages.ts` imports each `.requests.ts` and
|
|
|
91
91
|
`.queries.ts` file by its full name, extension included, because Node looks
|
|
92
92
|
for exactly the path an import names.
|
|
93
93
|
|
|
94
|
+
The `hub` it exports comes from `createHub`, the only implementation of
|
|
95
|
+
the `Hub` interface. That interface may gain members in any release, so an
|
|
96
|
+
application never implements it.
|
|
97
|
+
|
|
94
98
|
Enable `allowImportingTsExtensions` in the application's `tsconfig.json`
|
|
95
99
|
when `tsc` type-checks the server. Without it, `tsc` rejects the `.ts`
|
|
96
100
|
extensions in `dist/pages.ts`:
|