@loadbare/app 0.8.2 → 0.10.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/README.md +3 -3
- package/dist/build/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +74 -66
- package/dist/build/assemble.js.map +1 -1
- package/dist/build/expand.d.ts.map +1 -1
- package/dist/build/expand.js +20 -19
- package/dist/build/expand.js.map +1 -1
- package/dist/build/locations.d.ts +2 -3
- package/dist/build/locations.d.ts.map +1 -1
- package/dist/build/locations.js +2 -3
- package/dist/build/locations.js.map +1 -1
- package/dist/build/pages.d.ts +3 -4
- package/dist/build/pages.d.ts.map +1 -1
- package/dist/build/pages.js +3 -4
- package/dist/build/pages.js.map +1 -1
- package/dist/core/lb-constants.d.ts +25 -23
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +95 -158
- package/dist/core/lb-constants.js.map +1 -1
- package/dist/core/lb-types.d.ts +73 -75
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js +58 -5
- package/dist/core/lb-types.js.map +1 -1
- package/dist/hub/lb-apply.d.ts +47 -37
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +174 -193
- package/dist/hub/lb-apply.js.map +1 -1
- package/dist/hub/lb-hub.browser.d.ts +1 -1
- package/dist/hub/lb-hub.browser.d.ts.map +1 -1
- package/dist/hub/lb-hub.browser.js +419 -415
- package/dist/hub/lb-hub.browser.js.map +1 -1
- package/dist/server/lb-express.d.ts +20 -13
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +50 -52
- package/dist/server/lb-express.js.map +1 -1
- package/dist/server/lb-server.d.ts +81 -116
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +151 -48
- package/dist/server/lb-server.js.map +1 -1
- package/docs/TECHREF-1.0.md +893 -558
- package/docs/comparison.md +222 -185
- package/docs/prior-art.md +15 -14
- package/docs/reference/builder.md +9 -3
- package/docs/reference/chrome.md +107 -56
- package/docs/reference/custom-elements.md +199 -173
- package/docs/reference/data-binding.md +375 -370
- package/docs/reference/overview.md +12 -10
- package/docs/reference/page-files.md +161 -86
- package/docs/reference/server.md +13 -7
- package/docs/reference/widgets.md +104 -110
- package/docs/roadmap.md +36 -31
- package/docs/terms-of-art.md +57 -0
- package/docs/testing.md +97 -68
- package/docs/theory.md +92 -58
- package/docs/tutorials/010-pages-and-navigation.md +20 -12
- package/docs/tutorials/020-css.md +6 -3
- package/docs/tutorials/030-html-decomposition.md +9 -7
- package/docs/tutorials/040-displaying-data.md +30 -13
- package/docs/tutorials/{050-actions.md → 050-requests.md} +25 -15
- package/docs/tutorials/060-custom-element-code.md +17 -16
- package/docs/tutorials/065-conditional-rendering.md +34 -23
- package/docs/tutorials/070-displaying-a-list.md +29 -21
- package/docs/tutorials/072-inserting-into-a-list.md +24 -16
- package/docs/tutorials/074-deleting-from-a-list.md +9 -7
- package/docs/tutorials/076-updating-a-list-item.md +11 -10
- package/docs/tutorials/080-widget-requests.md +71 -43
- package/docs/tutorials/090-using-widget-libraries.md +22 -22
- package/package.json +1 -1
- package/skills/loadbare-app/SKILL.md +178 -111
- package/skills/loadbare-app/references/TECHREF-1.0.md +893 -558
- package/skills/loadbare-app/references/builder.md +9 -3
- package/skills/loadbare-app/references/chrome.md +107 -56
- package/skills/loadbare-app/references/custom-elements.md +199 -173
- package/skills/loadbare-app/references/data-binding.md +375 -370
- package/skills/loadbare-app/references/overview.md +12 -10
- package/skills/loadbare-app/references/page-files.md +161 -86
- package/skills/loadbare-app/references/server.md +13 -7
- package/skills/loadbare-app/references/widgets.md +104 -110
- package/docs/analysis-accidental-complexity.md +0 -149
- package/docs/analysis-closed-set.md +0 -210
package/docs/testing.md
CHANGED
|
@@ -49,8 +49,8 @@ application override a built-in.
|
|
|
49
49
|
## The console is an interface
|
|
50
50
|
|
|
51
51
|
The browser half of Loadbare App reports every failure it survives through
|
|
52
|
-
`console.error` and `console.warn` — a query
|
|
53
|
-
|
|
52
|
+
`console.error` and `console.warn` — a query nothing names, a row with no
|
|
53
|
+
key column, a request missing what it needs. These are not
|
|
54
54
|
diagnostics. They are the framework's entire error channel on the client, and
|
|
55
55
|
the behavior under test is frequently *that Loadbare reported and carried on*
|
|
56
56
|
rather than that it produced a value.
|
|
@@ -71,11 +71,23 @@ the most of what Loadbare promises, so it is first.
|
|
|
71
71
|
- `exp-foo` supplied to a definition with no `{{foo}}`
|
|
72
72
|
- `{{camelCase}}` in a definition, which no tag could ever supply
|
|
73
73
|
- a cycle in the definition graph
|
|
74
|
-
- two `lb-template` destinations with one name
|
|
74
|
+
- two `lb-exp-template` destinations with one name
|
|
75
75
|
- two authored templates for one destination
|
|
76
76
|
- a template naming a destination the definition does not have
|
|
77
|
-
- content written inside a definition with no `lb-slot`
|
|
78
|
-
- more than one `lb-slot`
|
|
77
|
+
- content written inside a definition with no `lb-exp-slot`
|
|
78
|
+
- more than one `lb-exp-slot`
|
|
79
|
+
|
|
80
|
+
**Markup checks.** The assembled chrome and every page are checked before
|
|
81
|
+
anything ships:
|
|
82
|
+
|
|
83
|
+
- an `lb-` attribute that is not one the developer writes
|
|
84
|
+
- `lb-request="lb-…"` naming none of the requests Loadbare provides
|
|
85
|
+
- `lb-url-link` off an `<a>`, `lb-url-push` without `lb-request`, and
|
|
86
|
+
`lb-url-unknown` off a `<dialog>` or outside `<lb-hub>`
|
|
87
|
+
- `lb-show` on a `<template>`, on a row template's root, or with no row
|
|
88
|
+
around it
|
|
89
|
+
|
|
90
|
+
A page file's `<title>` is lifted out and stamped as `lb-page-title`.
|
|
79
91
|
|
|
80
92
|
**Substitution.** An unset placeholder drops the attribute rather than
|
|
81
93
|
emitting it empty, which is what HTML's boolean attributes require and what
|
|
@@ -113,48 +125,59 @@ needs a browser either.
|
|
|
113
125
|
`server/lb-server.ts` opens a socket, so the fixtures are counting stubs.
|
|
114
126
|
|
|
115
127
|
- `onPageEnter` runs before any query, and the whole query set runs after it
|
|
116
|
-
- an unknown page answers `
|
|
128
|
+
- an unknown page answers `[]` and says so
|
|
129
|
+
- every answer goes out as a response item carrying the kind and key its
|
|
130
|
+
query declared; an answer of the wrong kind is left out and reported
|
|
117
131
|
- an unknown query name in a refresh set is skipped, and its siblings run
|
|
118
|
-
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
-
|
|
132
|
+
- a declared request the page did not declare under `handlers`, and a row
|
|
133
|
+
request with no `crud` entry for its query, are refused — the rule that
|
|
134
|
+
keeps the wire from reaching anything the page has not published
|
|
135
|
+
- a reserved name declared as a query or a handler is refused at startup
|
|
136
|
+
- the refresh set runs after the handler, against the same context
|
|
137
|
+
- what the handler stated wins over what the refresh produced, because
|
|
122
138
|
that is how a `patch` reaches the browser at all, and it is one spread
|
|
123
139
|
operator away from silently reversing
|
|
140
|
+
- a handler returning `url()` answers with the `lb-url` item alone, and runs
|
|
141
|
+
no refresh set
|
|
124
142
|
|
|
125
143
|
`tests/lb-express.test.ts` runs against a listening app: the page comes from
|
|
126
|
-
the
|
|
144
|
+
the path, the query parms reach `contextFor`, a `url()` answer is followed by
|
|
145
|
+
the page loaded at the new URL, an undeclared request is refused, and a
|
|
127
146
|
malformed body does not throw.
|
|
128
147
|
|
|
129
148
|
## Tier 3 — Landing
|
|
130
149
|
|
|
131
|
-
`
|
|
132
|
-
framework and the most likely to break in ways nobody notices. They are
|
|
133
|
-
pure DOM: no fetch, no
|
|
134
|
-
here.
|
|
135
|
-
|
|
136
|
-
- a
|
|
137
|
-
|
|
150
|
+
`applyResponse`, `applyRow` and `applyRows` are the most intricate code in
|
|
151
|
+
the framework and the most likely to break in ways nobody notices. They are
|
|
152
|
+
also pure DOM: no fetch, no custom element upgrade, no history. jsdom is real
|
|
153
|
+
evidence here.
|
|
154
|
+
|
|
155
|
+
- a control receives its value as `value`, any other native element as text,
|
|
156
|
+
a custom element that is not a control as `lb-column-value` alone, and
|
|
157
|
+
every one carries `lb-column-value`
|
|
158
|
+
- the four combinations of kind and row template: a `row` with no row
|
|
159
|
+
template lands on the element itself and stamps its `lb-key-value`
|
|
160
|
+
- a live row's root counts as a column if it carries one, which is what makes
|
|
138
161
|
an `<option>` row possible
|
|
139
|
-
- an element carrying both
|
|
140
|
-
it, and the
|
|
141
|
-
- a query
|
|
142
|
-
are all filled
|
|
143
|
-
|
|
144
|
-
-
|
|
145
|
-
- `
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
examples.
|
|
162
|
+
- an element carrying both `lb-query` and `lb-column` takes its column from
|
|
163
|
+
the row around it, and the columns inside it are its own query's
|
|
164
|
+
- a query nothing names is reported and skipped; several elements naming one
|
|
165
|
+
query are all filled
|
|
166
|
+
- all rows decide membership and order, so a key that did not arrive is gone
|
|
167
|
+
- a patch disturbs only what it names, in contents and in position
|
|
168
|
+
- `lb-query-row-count` is counted from the DOM after reconciliation, so all
|
|
169
|
+
rows and a patch ending in the same state report the same number
|
|
170
|
+
- `lbPlaceRow` is called for a fresh row always, and for an existing row
|
|
171
|
+
only under all rows. That is today's behavior, not a decision — a patch
|
|
172
|
+
therefore never re-places a row whose sort key changed. The test states
|
|
173
|
+
what is true now, and is the one that flips if that changes.
|
|
174
|
+
- `lb-show` moves an element into a template and back, and landing reaches
|
|
175
|
+
inside it
|
|
176
|
+
|
|
177
|
+
Two properties, written as loops rather than with a library. Applying the
|
|
178
|
+
same rows twice is applying them once. And rows reached through any sequence
|
|
179
|
+
of patches are those rows reached from empty. Convergence is the actual
|
|
180
|
+
contract of a reconciler, and those two say it better than twenty examples.
|
|
158
181
|
|
|
159
182
|
## Tier 4 — The hub
|
|
160
183
|
|
|
@@ -179,41 +202,47 @@ help.
|
|
|
179
202
|
|
|
180
203
|
What the hub is tested for here:
|
|
181
204
|
|
|
182
|
-
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
form
|
|
186
|
-
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
-
|
|
198
|
-
|
|
199
|
-
- `lb-pending` lands on the element that
|
|
200
|
-
on failure, and the next request clears
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
- a
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
- `lb-
|
|
207
|
-
|
|
205
|
+
- an element commits on its own event: a form on `submit`, a control on
|
|
206
|
+
`change`, anything else on `click`; a submit button with a form owner
|
|
207
|
+
never commits on click, and the submitter's `lb-request` wins over the
|
|
208
|
+
form's
|
|
209
|
+
- gathering follows the order: an element with `lb-column` alone, a
|
|
210
|
+
`<form>`'s controls, a live row's controls, the form owner's controls,
|
|
211
|
+
including controls joined by the `form` attribute and form-associated
|
|
212
|
+
custom elements
|
|
213
|
+
- gathering skips the controls of a nested `lb-query`, so a picker in a row
|
|
214
|
+
sends its own column and nothing about its options
|
|
215
|
+
- the request goes to the nearest ancestor `lb-query` of what was gathered,
|
|
216
|
+
and takes `key` from the nearest ancestor row of that query
|
|
217
|
+
- `lb-row-insert`, `lb-row-update` and `lb-row-delete` missing what they
|
|
218
|
+
need are not sent, and a reserved name that is not one of the three is
|
|
219
|
+
refused before it reaches the wire
|
|
220
|
+
- an ancestor may stop the `lb-request` event, and a request is complete
|
|
221
|
+
before an ancestor sees it
|
|
222
|
+
- `lb-request-pending` lands on the element that committed,
|
|
223
|
+
`lb-request-error` replaces it on failure, and the next request clears
|
|
224
|
+
it; `aria-busy` comes and goes with `lb-request-pending`, and a commit on
|
|
225
|
+
a pending element is ignored
|
|
226
|
+
- a successful insert gathered from a form resets the form
|
|
227
|
+
- a path with no page reports, lands `lb-page-unknown`, and opens the
|
|
228
|
+
`lb-url-unknown` dialog; two pages for one stub report and take the first
|
|
229
|
+
- `lb-url` lands with the path, the page's `lb-page-title` or stub as
|
|
230
|
+
`lb-page-label`, and a column per query parm, and sets the document title
|
|
231
|
+
- `lb-url-link` pushes state and swaps the page on a plain primary click,
|
|
232
|
+
`popstate` reverses it, and an ordinary anchor is left alone
|
|
233
|
+
- a control inside `lb-query="lb-url"` writes its parm, replacing the history
|
|
234
|
+
entry or pushing one with `lb-url-push`, and the page's queries reload
|
|
235
|
+
- an `lb-url` item in a server answer writes the URL and lands the page
|
|
236
|
+
loaded there
|
|
208
237
|
- `hidden` comes off the body once a page has landed, including the page
|
|
209
238
|
that failed to load
|
|
210
239
|
|
|
211
240
|
Widgets are small and their logic is local, so jsdom carries them: a value
|
|
212
|
-
reaches the control the widget owns,
|
|
213
|
-
|
|
214
|
-
`lb-
|
|
215
|
-
|
|
216
|
-
|
|
241
|
+
reaches the control the widget owns, the widget is a form-associated control
|
|
242
|
+
the hub commits and gathers, `lb-options` turns a key into a value and
|
|
243
|
+
removes an emptied `<optgroup>`, `lb-table` groups and sorts, removes an
|
|
244
|
+
emptied section heading, and takes its `colSpan` from the row template the
|
|
245
|
+
page wrote.
|
|
217
246
|
|
|
218
247
|
A widget extends `HTMLElement` and registers itself as its module loads, so
|
|
219
248
|
unlike tier 3 it needs a window before the module exists. `installWindow()`
|
package/docs/theory.md
CHANGED
|
@@ -425,33 +425,45 @@ database, but it does force server code to return data in the shapes of
|
|
|
425
425
|
rows and sets of rows.
|
|
426
426
|
|
|
427
427
|
That scoping decision allowed us to reduce the data binding vocabulary
|
|
428
|
-
to
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
|
435
|
-
|
|
|
428
|
+
to one idea: everything is a query. A query is a name for rows, of kind
|
|
429
|
+
`row` or `rows`, with a key. The server declares all three facts about a
|
|
430
|
+
query, so the markup names the query and the columns it shows, and
|
|
431
|
+
repeats nothing the server already said. Three attributes carry the whole
|
|
432
|
+
of it:
|
|
433
|
+
|
|
434
|
+
| Attribute | Relational idea | HTML precedent |
|
|
435
|
+
| ----------- | -------------------------- | ---------------------------------------------- |
|
|
436
|
+
| `lb-query` | A query, one row or many | `<select>` or `<form>`, a container of records |
|
|
437
|
+
| `lb-column` | A column | `name` on a form control, which field this is |
|
|
438
|
+
| `lb-show` | A column deciding presence | `hidden`, whether an element is shown |
|
|
439
|
+
|
|
440
|
+
The key never appears in the markup. The server declares it with the
|
|
441
|
+
query, `rows("id", run)`, and the hub stamps each live row with its
|
|
442
|
+
value as `lb-key-value`, the way an `<option>` carries a `value` apart
|
|
443
|
+
from its text.
|
|
436
444
|
|
|
437
445
|
A highly simplified page showing
|
|
438
446
|
an invoice and its lines looks like this:
|
|
439
447
|
|
|
440
448
|
```html
|
|
441
|
-
<section lb-
|
|
442
|
-
<h2 lb-
|
|
443
|
-
<span lb-
|
|
449
|
+
<section lb-query="invoice">
|
|
450
|
+
<h2 lb-column="number"></h2>
|
|
451
|
+
<span lb-column="customer"></span>
|
|
444
452
|
</section>
|
|
445
453
|
|
|
446
454
|
<table>
|
|
447
|
-
<tbody lb-
|
|
448
|
-
<template
|
|
449
|
-
<tr><td lb-
|
|
455
|
+
<tbody lb-query="invoiceLines">
|
|
456
|
+
<template>
|
|
457
|
+
<tr><td lb-column="item"></td><td lb-column="amount"></td></tr>
|
|
450
458
|
</template>
|
|
451
459
|
</tbody>
|
|
452
460
|
</table>
|
|
453
461
|
```
|
|
454
462
|
|
|
463
|
+
`invoice` is of kind `row`, and with no row template inside it the row
|
|
464
|
+
lands on the `<section>` itself. `invoiceLines` is of kind `rows`, and
|
|
465
|
+
the hub clones the row template once per row.
|
|
466
|
+
|
|
455
467
|
This decision proved decisive in keeping the browser code robust,
|
|
456
468
|
expressive, and performant.
|
|
457
469
|
|
|
@@ -465,45 +477,58 @@ The idea is that user interaction triggers framework browser code
|
|
|
465
477
|
that "knows what to do", assembling a request driven purely from
|
|
466
478
|
attributes.
|
|
467
479
|
|
|
468
|
-
This requirement is satisfied with a single new attribute, `lb-
|
|
469
|
-
Three
|
|
470
|
-
|
|
480
|
+
This requirement is satisfied with a single new attribute, `lb-request`.
|
|
481
|
+
Three request names are Loadbare's: `lb-row-insert`, `lb-row-update` and
|
|
482
|
+
`lb-row-delete`. Any other value names a request the page declares on
|
|
483
|
+
the server.
|
|
471
484
|
|
|
472
|
-
A request is
|
|
473
|
-
|
|
474
|
-
ancestor rule that decides where a value lands.
|
|
475
|
-
|
|
476
|
-
|
|
485
|
+
A request is a name and a position. The author writes the name, and
|
|
486
|
+
the hub supplies the position from the document, using the same
|
|
487
|
+
ancestor rule that decides where a value lands. An element commits the
|
|
488
|
+
way HTML already says it does: a form on `submit`, a control on
|
|
489
|
+
`change`, anything else on `click`. To add a delete button to the
|
|
490
|
+
invoice lines from the previous section, the row template gains one
|
|
491
|
+
element:
|
|
477
492
|
|
|
478
493
|
```html
|
|
479
|
-
<template
|
|
494
|
+
<template>
|
|
480
495
|
<tr>
|
|
481
|
-
<td lb-
|
|
482
|
-
<td lb-
|
|
483
|
-
<td><button lb-
|
|
496
|
+
<td lb-column="item"></td>
|
|
497
|
+
<td lb-column="amount"></td>
|
|
498
|
+
<td><button lb-request="lb-row-delete">Remove</button></td>
|
|
484
499
|
</tr>
|
|
485
500
|
</template>
|
|
486
501
|
```
|
|
487
502
|
|
|
488
|
-
The button names neither the
|
|
489
|
-
the hub reads `invoiceLines` from the nearest
|
|
490
|
-
from the live row around the button, and sends:
|
|
503
|
+
The button names neither the query nor the key. When it is clicked,
|
|
504
|
+
the hub reads `invoiceLines` from the nearest ancestor `lb-query` and
|
|
505
|
+
the key from the live row around the button, and sends:
|
|
491
506
|
|
|
492
507
|
```
|
|
493
|
-
{
|
|
508
|
+
{ name: "lb-row-delete", query: "invoiceLines", key: "42" }
|
|
494
509
|
```
|
|
495
510
|
|
|
511
|
+
Every request has that one shape: a name, and `query`, `key` and
|
|
512
|
+
`values` as present. An insert or update gathers `values` from the
|
|
513
|
+
controls around the element that committed: a form's controls, a live
|
|
514
|
+
row's, or the controls whose form owner is the element's.
|
|
515
|
+
|
|
496
516
|
But we need to be more modern than that. Nowadays we expect the
|
|
497
517
|
button to be disabled after it is clicked, and to display a wait
|
|
498
518
|
state in case the network is congested. In other words, the
|
|
499
519
|
framework browser code needs to notify the event target of its
|
|
500
520
|
request state.
|
|
501
521
|
|
|
502
|
-
To do this, framework browser code
|
|
503
|
-
`lb-
|
|
504
|
-
a
|
|
505
|
-
|
|
506
|
-
JavaScript
|
|
522
|
+
To do this, framework browser code stamps two attributes about a
|
|
523
|
+
request, `lb-request-pending` and `lb-request-error`, and a third about
|
|
524
|
+
a query, `lb-query-row-count`. The framework browser code ignores a
|
|
525
|
+
commit on an element stamped with `lb-request-pending`, meaning the
|
|
526
|
+
disabled state can be represented entirely by CSS, no JavaScript
|
|
527
|
+
needed.
|
|
528
|
+
|
|
529
|
+
The developer writes `lb-*` attributes in markup, and script never
|
|
530
|
+
assigns them. The hub and the builder write only their stamps. That
|
|
531
|
+
rule is what lets the builder check the markup once, at build time.
|
|
507
532
|
|
|
508
533
|
Over many iterations I gradually reduced the core attributes to
|
|
509
534
|
these, from a larger set that had more overlapping behaviors and edge
|
|
@@ -524,24 +549,30 @@ chrome and ships it as `app.html`. With all pages already present
|
|
|
524
549
|
in the browser, providing an SPA is fairly simple.
|
|
525
550
|
|
|
526
551
|
The hub catches anchor clicks, and checks if the anchor contains the
|
|
527
|
-
attribute `lb-
|
|
528
|
-
navigation,
|
|
529
|
-
to the server for the page data. The
|
|
530
|
-
server always knows what URL the
|
|
552
|
+
attribute `lb-url-link`. If so, the hub interprets a plain primary
|
|
553
|
+
click as in-app navigation, shows the page the `href` names in
|
|
554
|
+
`<main>`, and sends a request to the server for the page data. The
|
|
555
|
+
query string rides along, so the server always knows what URL the
|
|
556
|
+
browser is showing.
|
|
531
557
|
|
|
532
558
|
Anchors without the attribute behave as normal links.
|
|
533
559
|
|
|
534
560
|
#### Synthetic Context
|
|
535
561
|
|
|
536
562
|
Synthetic context emerged as a useful feature near the very end of
|
|
537
|
-
development. Realizing that the hub knows about the current
|
|
538
|
-
|
|
539
|
-
`lb-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
the hub.
|
|
563
|
+
development. Realizing that the hub knows about the current URL, we
|
|
564
|
+
had it serve a query of its own, `lb-url`, of kind `row` and keyed by
|
|
565
|
+
`lb-path`. Its columns are the path, the page's title as
|
|
566
|
+
`lb-page-label`, whether the path names no page as `lb-page-unknown`,
|
|
567
|
+
and one column per query parm.
|
|
568
|
+
|
|
569
|
+
These can be bound to any HTML, same as any other query the
|
|
570
|
+
application declares, but the hub supplies the row. The hub also
|
|
571
|
+
answers requests for it: a control writing a query parm is
|
|
572
|
+
`<input lb-column="q" lb-request="lb-row-update">` inside
|
|
573
|
+
`lb-query="lb-url"`, an ordinary row update that the hub answers by
|
|
574
|
+
writing the URL and reloading the page's queries. A handler on the
|
|
575
|
+
server moves the URL the same way, by returning `url({ q: "5" })`.
|
|
545
576
|
|
|
546
577
|
There may be more synthetic context in the future.
|
|
547
578
|
|
|
@@ -562,8 +593,10 @@ files that share a name: `invoice.page.html`, `invoice.queries.ts`
|
|
|
562
593
|
and `invoice.requests.ts`. The HTML is the page as the user sees it,
|
|
563
594
|
the queries provide the data, and the requests are what it allows.
|
|
564
595
|
|
|
565
|
-
Each query is declared
|
|
566
|
-
|
|
596
|
+
Each query is declared with `row(key, run)` or `rows(key, run)`,
|
|
597
|
+
naming its kind and its key. Every response carries response items,
|
|
598
|
+
each naming its query, kind and key beside the row, the rows, or a
|
|
599
|
+
patch. Each request declares the work it does and the
|
|
567
600
|
queries to run again afterward. Here is the delete button from
|
|
568
601
|
Requests and Request State, as the server sees it:
|
|
569
602
|
|
|
@@ -575,9 +608,9 @@ export const requests: Requests = {
|
|
|
575
608
|
crud: {
|
|
576
609
|
invoiceLines: {
|
|
577
610
|
rowDelete: {
|
|
578
|
-
run: async (ctx,
|
|
579
|
-
await ctx.db.deleteLine(
|
|
580
|
-
return { invoiceLines: patch({ drop: [
|
|
611
|
+
run: async (ctx, { key }) => {
|
|
612
|
+
await ctx.db.deleteLine(key);
|
|
613
|
+
return { invoiceLines: patch({ drop: [key] }) };
|
|
581
614
|
},
|
|
582
615
|
refresh: ["invoice"],
|
|
583
616
|
},
|
|
@@ -587,14 +620,15 @@ export const requests: Requests = {
|
|
|
587
620
|
```
|
|
588
621
|
|
|
589
622
|
Deleting a line changes the invoice total, so the entry refreshes
|
|
590
|
-
`invoice`.
|
|
623
|
+
`invoice`. `invoiceLines` itself does not need to be queried again, since
|
|
591
624
|
the only change is one row that went away, and `run` says so with a
|
|
592
|
-
patch. The response carries
|
|
593
|
-
patch that removes one line.
|
|
625
|
+
patch. The response carries two response items: the refreshed invoice
|
|
626
|
+
row, and the patch that removes one line.
|
|
594
627
|
|
|
595
628
|
Loadbare, as it turns out, does not have "endpoints" as such. If the
|
|
596
|
-
HTML
|
|
597
|
-
server carries a CRUD entry `rowDelete` under object invoiceLines.
|
|
629
|
+
HTML places an `lb-row-delete` inside `lb-query="invoiceLines"`, then the
|
|
630
|
+
server carries a CRUD entry `rowDelete` under object invoiceLines. A
|
|
631
|
+
declared request is the same, keyed by its name under `handlers`.
|
|
598
632
|
Since the browser and server exist to talk to each other in the same
|
|
599
633
|
language, no additional abstraction is needed.
|
|
600
634
|
|
|
@@ -727,7 +761,7 @@ has been made to make them "set and forget".
|
|
|
727
761
|
|
|
728
762
|
Second, Loadbare is missing entire categories of developer effort,
|
|
729
763
|
while providing expected benefits. HTML includes without imports,
|
|
730
|
-
declarative browser data binding and
|
|
764
|
+
declarative browser data binding and requests, free tree shaking,
|
|
731
765
|
extendable with custom elements, a small set of mechanisms, and
|
|
732
766
|
nothing to learn about the "the Loadbare way for CSS", among many
|
|
733
767
|
others.
|
|
@@ -21,21 +21,28 @@ a page specified, Loadbare displays `index.page.html`.
|
|
|
21
21
|
|
|
22
22
|
```html
|
|
23
23
|
<!-- src/pages/index.page.html -->
|
|
24
|
+
<title>Home</title>
|
|
24
25
|
<h1>Home</h1>
|
|
25
26
|
<p>This is the home page.</p>
|
|
26
27
|
```
|
|
27
28
|
|
|
28
29
|
```html
|
|
29
30
|
<!-- src/pages/about.page.html -->
|
|
31
|
+
<title>About</title>
|
|
30
32
|
<h1>About</h1>
|
|
31
33
|
<p>This is the about page.</p>
|
|
32
34
|
```
|
|
33
35
|
|
|
36
|
+
A page file's `<title>` names the page. The builder removes it from the
|
|
37
|
+
page and keeps its text, and the hub sets the document title to it whenever
|
|
38
|
+
the page is showing. A page without a `<title>` is named by its stub, the
|
|
39
|
+
filename less `.page.html`.
|
|
40
|
+
|
|
34
41
|
## Linking between them
|
|
35
42
|
|
|
36
43
|
Add a `<nav>` to the chrome, with one anchor per page.
|
|
37
44
|
|
|
38
|
-
Also add
|
|
45
|
+
Also add a `<dialog lb-url-unknown>` element, so the hub `<lb-hub>` can
|
|
39
46
|
display something to the user if they type a URL that has no matching page.
|
|
40
47
|
|
|
41
48
|
```html
|
|
@@ -52,32 +59,33 @@ display something to the user if they type a URL that has no matching page.
|
|
|
52
59
|
<lb-hub>
|
|
53
60
|
<nav>
|
|
54
61
|
<!-- a bare link to "/" goes to page "index" -->
|
|
55
|
-
<a href="/" lb-
|
|
56
|
-
<a href="/about" lb-
|
|
62
|
+
<a href="/" lb-url-link>Home</a>
|
|
63
|
+
<a href="/about" lb-url-link>About</a>
|
|
57
64
|
</nav>
|
|
58
65
|
<main></main>
|
|
59
|
-
<dialog lb-unknown
|
|
60
|
-
The URL <span lb-
|
|
66
|
+
<dialog lb-url-unknown lb-query="lb-url">
|
|
67
|
+
The URL <span lb-column="lb-path"></span> is not in this app.
|
|
61
68
|
</dialog>
|
|
62
69
|
</lb-hub>
|
|
63
70
|
</body>
|
|
64
71
|
</html>
|
|
65
72
|
```
|
|
66
73
|
|
|
67
|
-
Here we introduce the attribute `lb-
|
|
74
|
+
Here we introduce the attribute `lb-url-link`, which indicates the link
|
|
68
75
|
should navigate within the app. When the user clicks, the hub `<lb-hub>`
|
|
69
76
|
intercepts the event and swaps the page's HTML into the `<main>` element.
|
|
70
77
|
The hub ensures the back button works by calling `history.pushState` on
|
|
71
78
|
navigation, and catching the browser's `popstate` event.
|
|
72
79
|
|
|
73
|
-
An anchor without `lb-
|
|
74
|
-
link
|
|
80
|
+
An anchor without `lb-url-link` is left alone and behaves like an ordinary
|
|
81
|
+
link, and so is a click with a modifier key or a middle click, which opens
|
|
82
|
+
a new tab as usual.
|
|
75
83
|
|
|
76
|
-
If a `<dialog lb-unknown
|
|
84
|
+
If a `<dialog lb-url-unknown>` element is present in the HTML, it will be
|
|
77
85
|
displayed to the user when a URL is entered that has no matching page in the app.
|
|
78
|
-
The `lb-
|
|
79
|
-
binding; for now, know that `lb-
|
|
80
|
-
|
|
86
|
+
The `lb-query` and `lb-column` attributes will be explained when we get to data
|
|
87
|
+
binding; for now, know that `lb-url` is a query the hub itself serves, one
|
|
88
|
+
row describing the URL, and its column `lb-path` is the path that was
|
|
81
89
|
asked for. The
|
|
82
90
|
widget library ships this dialog ready-made as `<lb-unknown-page>`, which
|
|
83
91
|
[Using Widget Libraries](./090-using-widget-libraries.md) covers.
|
|
@@ -42,6 +42,7 @@ Use the class in the page:
|
|
|
42
42
|
|
|
43
43
|
```html
|
|
44
44
|
<!-- src/pages/about.page.html -->
|
|
45
|
+
<title>About</title>
|
|
45
46
|
<h1>About</h1>
|
|
46
47
|
<p class="about-note">This is the about page.</p>
|
|
47
48
|
```
|
|
@@ -65,11 +66,13 @@ Add the stylesheet link to the chrome:
|
|
|
65
66
|
<body hidden>
|
|
66
67
|
<lb-hub>
|
|
67
68
|
<nav>
|
|
68
|
-
<a href="/" lb-
|
|
69
|
-
<a href="/about" lb-
|
|
69
|
+
<a href="/" lb-url-link>Home</a>
|
|
70
|
+
<a href="/about" lb-url-link>About</a>
|
|
70
71
|
</nav>
|
|
71
72
|
<main></main>
|
|
72
|
-
<dialog lb-unknown
|
|
73
|
+
<dialog lb-url-unknown lb-query="lb-url">
|
|
74
|
+
The URL <span lb-column="lb-path"></span> is not in this app.
|
|
75
|
+
</dialog>
|
|
73
76
|
</lb-hub>
|
|
74
77
|
</body>
|
|
75
78
|
</html>
|
|
@@ -33,14 +33,16 @@ element, not yet defined:
|
|
|
33
33
|
<lb-hub>
|
|
34
34
|
<!-- replace this from the previous tutorial...
|
|
35
35
|
<nav>
|
|
36
|
-
<a href="/" lb-
|
|
37
|
-
<a href="/about" lb-
|
|
36
|
+
<a href="/" lb-url-link>Home</a>
|
|
37
|
+
<a href="/about" lb-url-link>About</a>
|
|
38
38
|
</nav>
|
|
39
39
|
-->
|
|
40
40
|
<!-- ...with this empty custom element: -->
|
|
41
41
|
<app-nav></app-nav>
|
|
42
42
|
<main></main>
|
|
43
|
-
<dialog lb-unknown
|
|
43
|
+
<dialog lb-url-unknown lb-query="lb-url">
|
|
44
|
+
The URL <span lb-column="lb-path"></span> is not in this app.
|
|
45
|
+
</dialog>
|
|
44
46
|
</lb-hub>
|
|
45
47
|
</body>
|
|
46
48
|
</html>
|
|
@@ -51,8 +53,8 @@ of `<app-nav>` by writing `src/app-nav.html`.
|
|
|
51
53
|
|
|
52
54
|
```html
|
|
53
55
|
<nav>
|
|
54
|
-
<a href="/" lb-
|
|
55
|
-
<a href="/about" lb-
|
|
56
|
+
<a href="/" lb-url-link>Home</a>
|
|
57
|
+
<a href="/about" lb-url-link>About</a>
|
|
56
58
|
</nav>
|
|
57
59
|
```
|
|
58
60
|
|
|
@@ -62,8 +64,8 @@ into the `<app-nav>` custom element, so the actual result will look like this:
|
|
|
62
64
|
```html
|
|
63
65
|
<app-nav>
|
|
64
66
|
<nav>
|
|
65
|
-
<a href="/" lb-
|
|
66
|
-
<a href="/about" lb-
|
|
67
|
+
<a href="/" lb-url-link>Home</a>
|
|
68
|
+
<a href="/about" lb-url-link>About</a>
|
|
67
69
|
</nav>
|
|
68
70
|
</app-nav>
|
|
69
71
|
```
|