@loadbare/app 0.12.0 → 0.14.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 +87 -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 +34 -1
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +105 -0
- package/dist/core/lb-constants.js.map +1 -1
- package/dist/core/lb-types.d.ts +74 -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 +57 -18
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +661 -63
- 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 +400 -33
- package/dist/hub/lb-hub.browser.js.map +1 -1
- package/dist/server/lb-express.d.ts +19 -7
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +62 -26
- package/dist/server/lb-express.js.map +1 -1
- package/dist/server/lb-server.d.ts +80 -11
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +185 -33
- package/dist/server/lb-server.js.map +1 -1
- package/docs/TECHREF-1.0.md +584 -111
- package/docs/comparison.md +17 -13
- package/docs/possible-ideas.md +308 -0
- package/docs/reference/chrome.md +7 -1
- package/docs/reference/custom-elements.md +134 -39
- package/docs/reference/data-binding.md +405 -17
- package/docs/reference/page-files.md +101 -19
- package/docs/reference/server.md +24 -0
- package/docs/reference/widgets.md +118 -25
- package/docs/roadmap.md +17 -6
- package/docs/testing.md +21 -10
- package/package.json +2 -3
- package/skills/loadbare-app/SKILL.md +92 -30
- package/skills/loadbare-app/references/TECHREF-1.0.md +584 -111
- package/skills/loadbare-app/references/chrome.md +7 -1
- package/skills/loadbare-app/references/custom-elements.md +134 -39
- package/skills/loadbare-app/references/data-binding.md +405 -17
- package/skills/loadbare-app/references/page-files.md +101 -19
- package/skills/loadbare-app/references/server.md +24 -0
- package/skills/loadbare-app/references/widgets.md +118 -25
package/docs/comparison.md
CHANGED
|
@@ -320,9 +320,9 @@ without `.browser` is not bundled, and the build error names it.
|
|
|
320
320
|
`CustomEvent`. The hub completes it with `query`, `key` and `values` from
|
|
321
321
|
where the element sits. An ancestor may stop the event.
|
|
322
322
|
- A custom element carrying `lb-query` and a row template may implement
|
|
323
|
-
`
|
|
324
|
-
|
|
325
|
-
|
|
323
|
+
`lbRowsLanded()` to act on the rows once they land, such as scrolling to
|
|
324
|
+
the one a request created. The hub does the cloning, matching, ordering,
|
|
325
|
+
grouping, removal and counting.
|
|
326
326
|
- Method names beginning with `lb` on a custom element are reserved.
|
|
327
327
|
|
|
328
328
|
Widget libraries are npm packages listed in `imports.ts`. The builder scans
|
|
@@ -396,7 +396,7 @@ query, the kind and key the server declared, and one of these:
|
|
|
396
396
|
A column holds one value. A set of values is a query of its own. A row
|
|
397
397
|
never contains rows. Master-detail is a row and rows answered under two
|
|
398
398
|
names. Many masters with their details is one `rows` query of joined rows,
|
|
399
|
-
|
|
399
|
+
shown in groups by a group template.
|
|
400
400
|
|
|
401
401
|
The application designs no endpoints, routes or serialization format. The
|
|
402
402
|
names in the markup are the keys in the page's `.queries.ts` and
|
|
@@ -467,11 +467,13 @@ The hub stamps the element that committed:
|
|
|
467
467
|
|----------------------|------------------------------------------------------------|
|
|
468
468
|
| `lb-request-pending` | The round trip is in flight; `aria-busy="true"` is set with it |
|
|
469
469
|
| `lb-request-error` | The last round trip from this element failed |
|
|
470
|
+
| `lb-request-refused` | The handler refused it, carrying its message |
|
|
470
471
|
|
|
471
472
|
A commit on an element carrying `lb-request-pending` is ignored. A round
|
|
472
473
|
trip has a ten-second deadline, after which it is aborted and treated as a
|
|
473
474
|
failure. `lb-request-error` records that a round trip failed, and carries
|
|
474
|
-
no message.
|
|
475
|
+
no message. A handler's `refusal()` carries words for the user, shown on the
|
|
476
|
+
element and on each control it names.
|
|
475
477
|
|
|
476
478
|
After a successful `lb-row-insert` gathered from a form, the hub resets the
|
|
477
479
|
form. A failed insert resets nothing.
|
|
@@ -785,11 +787,12 @@ TECHREF-1.0 lists every name Loadbare/app owns in one cross-reference.
|
|
|
785
787
|
|
|
786
788
|
| Owned | Count | Names |
|
|
787
789
|
|-------------------------------------|-------|---------------------------------------------------------------|
|
|
788
|
-
| `lb-*` attributes a developer writes |
|
|
789
|
-
| `lb-*` stamps |
|
|
790
|
+
| `lb-*` attributes a developer writes | 13 | `lb-query`, `lb-column`, `lb-show`, `lb-group`, `lb-count`, `lb-sum`, `lb-avg`, `lb-min`, `lb-max`, `lb-request`, `lb-url-link`, `lb-url-push`, `lb-url-unknown` |
|
|
791
|
+
| `lb-*` stamps | 17 | `lb-column-value`, `lb-key-value`, `lb-row-live`, `lb-query-row-count`, `lb-group-live`, `lb-group-column`, `lb-group-value`, `lb-row-created`, `lb-row-changed`, `lb-row-moved`, `lb-row-requested`, `lb-row-leaving`, `lb-group-leaving`, `lb-request-pending`, `lb-request-error`, `lb-page`, `lb-page-title` |
|
|
790
792
|
| `lb-*` build-time attributes | 2 | `lb-exp-slot`, `lb-exp-template` |
|
|
791
793
|
| Requests Loadbare provides | 3 | `lb-row-insert`, `lb-row-update`, `lb-row-delete` |
|
|
792
|
-
| `lb*` methods on custom elements |
|
|
794
|
+
| `lb*` methods on custom elements | 1 | `lbRowsLanded` |
|
|
795
|
+
| Query parms Loadbare reads | 1 | `lb-order-<query>` |
|
|
793
796
|
| Attribute namespace for expansion | 1 | `exp-*` |
|
|
794
797
|
| Reserved tags | 1 | `<lb-hub>` |
|
|
795
798
|
| Reserved events | 1 | `lb-request` |
|
|
@@ -821,7 +824,7 @@ cross-reference, so no count is given for them here.
|
|
|
821
824
|
- A widget's markup is in the document and in view-source.
|
|
822
825
|
- On navigation the hub sets `document.title` from the page's `<title>`. It
|
|
823
826
|
does not move focus or announce the new page.
|
|
824
|
-
-
|
|
827
|
+
- Radio buttons are not implemented for landing or gathering.
|
|
825
828
|
|
|
826
829
|
### Elsewhere
|
|
827
830
|
|
|
@@ -916,9 +919,9 @@ Roadmap.
|
|
|
916
919
|
| Nested data | A row never holds rows. Trees are sent as joined rows. |
|
|
917
920
|
| Record URLs and view parameters | A path names a page. A record or a filter is a query parm, `/accounts?acct=23`. |
|
|
918
921
|
| Query arguments from the browser | None. Query parms reach queries through `contextFor` and `ctx`. |
|
|
919
|
-
|
|
|
922
|
+
| Radio buttons, file inputs | Not landed and not gathered. Radio buttons are an open blocker. Checkboxes land and gather as `true` and `false`. |
|
|
920
923
|
| Data types and formatting | No conversion. Queries format values. Open blocker. |
|
|
921
|
-
| Error messages | `
|
|
924
|
+
| Error messages | A handler's `refusal()` reaches the user; any other failure carries no message. |
|
|
922
925
|
| Field validation | No mechanism. Per-keystroke validation is a roadmap question. |
|
|
923
926
|
| Optimistic updates | None. |
|
|
924
927
|
| Concurrent writers | No version or conflict check. Roadmap question. |
|
|
@@ -926,7 +929,7 @@ Roadmap.
|
|
|
926
929
|
| Offline or local-first use | None. |
|
|
927
930
|
| Server-side rendering for crawlers | None. Every path answers the same document with status 200. |
|
|
928
931
|
| Subpath hosting | Not possible. The hub uses absolute paths. |
|
|
929
|
-
| Chrome-level queries | A widget in the chrome bound to a query requires every page to declare it.
|
|
932
|
+
| Chrome-level queries | A widget in the chrome bound to a query requires every page to declare it. On the roadmap. |
|
|
930
933
|
| A widget receiving a whole row | Not available. Open blocker. |
|
|
931
934
|
| A nested `rows` query per outer row | A nested query receives the same rows in every outer row. A new outer row's nested query stays empty until its name lands again. Open blocker. |
|
|
932
935
|
| Configurable request deadline | Fixed at ten seconds. |
|
|
@@ -1181,7 +1184,8 @@ Each equivalent is approximate.
|
|
|
1181
1184
|
| Expansion | Server-side include; partial; build-time component render (Astro) |
|
|
1182
1185
|
| `exp-` parameter | A prop fixed at build time |
|
|
1183
1186
|
| `lb-exp-slot`, `lb-exp-template` | Default slot and named slots; `ng-content` with `select` |
|
|
1184
|
-
| `
|
|
1187
|
+
| `lbRowsLanded` | An effect after a list renders |
|
|
1188
|
+
| `order`, `lb-group` | `ORDER BY` with report-writer control breaks (`BREAK ON`, group bands) |
|
|
1185
1189
|
| `ctx` / `HubContext` | `event.locals` (SvelteKit); loader `context` (React Router 7); request-scoped dependency injection |
|
|
1186
1190
|
| `imports.ts` | Installing and registering a component library |
|
|
1187
1191
|
| `pages.ts` | Generated route manifest |
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# Possible Ideas
|
|
2
|
+
|
|
3
|
+
> **LLM-authored, not yet revised by a person.** Drafted by Claude on
|
|
4
|
+
> 2026-10-02 against `@loadbare/app` 0.12.0, from a session in ef. Nothing
|
|
5
|
+
> here is decided or planned.
|
|
6
|
+
|
|
7
|
+
## Why these are being considered
|
|
8
|
+
|
|
9
|
+
A custom element is the platform's one way to attach code to markup, and
|
|
10
|
+
widget work keeps running into what that costs.
|
|
11
|
+
|
|
12
|
+
- No decomposition. Code that concerns one row has nowhere to live but a
|
|
13
|
+
wrapper around the whole table, so `lb-table` owns everything inside it.
|
|
14
|
+
That is how page-specific behavior ends up in a shared widget: the COA
|
|
15
|
+
page's ghost row per section grew because row-level code could attach
|
|
16
|
+
only to the table. It is the overfit the README's test guards against.
|
|
17
|
+
- The browser's lifecycle is not the hub's. A moved row is disconnected and
|
|
18
|
+
connected, the copy a move leaves behind is constructed, and an `lb-show`
|
|
19
|
+
toggle adopts twice, so `connectedCallback` must be written to run again.
|
|
20
|
+
- Clutter. The page's source is the widget's dialect of named templates
|
|
21
|
+
rather than HTML, and the shipped page keeps a wrapper tag around what it
|
|
22
|
+
expanded.
|
|
23
|
+
- Plumbing through the wrapper. The tag carrying the code is not the
|
|
24
|
+
control: `WrappedControl` forwards `value`, fires `change` again, and needs
|
|
25
|
+
`ElementInternals` to join a form. Focus is searched for inside the
|
|
26
|
+
wrapper, and `autofocus` on a wrapped `<input>` is lost.
|
|
27
|
+
- Content models reach forms too. A `<form>` cannot wrap a `<tr>`, so the
|
|
28
|
+
ghost row joins its controls to a hidden form outside the table by the
|
|
29
|
+
`form` attribute. A form cannot hold a form, so `lb-insert-dialog` is
|
|
30
|
+
written once outside every row instead of beside the control it serves.
|
|
31
|
+
- Ids repeat in a row template. `lb-confirm` finds its dialog by position,
|
|
32
|
+
because `commandfor`, HTML's own way to open one with no script, works
|
|
33
|
+
only by id.
|
|
34
|
+
- Upgrade timing is the hub's problem. An element shipped inside an
|
|
35
|
+
`lb-show` template, or whose definition loads late, has not upgraded, so
|
|
36
|
+
the hub keeps `waiting()`, lands kept answers again once it upgrades, and
|
|
37
|
+
stamps `lb-column-value` for a control to take up on first connect.
|
|
38
|
+
- A few lines cost a wrapper. `lb-options` has a script mostly to copy
|
|
39
|
+
`lb-group-value` into an `<optgroup>`'s `label` and each key into an
|
|
40
|
+
option's `value`, and for that the whole `<select>` sits inside a custom
|
|
41
|
+
element.
|
|
42
|
+
- Parameters cannot carry structure. Expansion parameters are words, so
|
|
43
|
+
when groups became a template, `lb-picker` lost `exp-group` and a grouped
|
|
44
|
+
picker went back to a hand-written `lb-options`.
|
|
45
|
+
- CSS and accessibility see an element HTML does not define. A wrapper tag
|
|
46
|
+
has no role, shifts child and `:nth-child` selectors, and has no place in
|
|
47
|
+
a table. The usual remedy, `display: contents`, has a history of
|
|
48
|
+
accessibility bugs.
|
|
49
|
+
- One element, one class. A ghost row that should also save as it is typed
|
|
50
|
+
in needs a widget combining the two.
|
|
51
|
+
|
|
52
|
+
## Code on elements the parser constrains
|
|
53
|
+
|
|
54
|
+
Loadbare's data vocabulary is attributes, and an attribute goes on any
|
|
55
|
+
element: `<tr lb-query>`, `<option lb-column>`, `<tbody lb-show>`. Behavior
|
|
56
|
+
is different. The platform attaches code to markup through a custom element
|
|
57
|
+
tag, and a tag must survive the parser. In a table, a `<select>`, or a `<p>`
|
|
58
|
+
it does not: an unexpected tag is dropped, moved out of the table, or closes
|
|
59
|
+
the paragraph.
|
|
60
|
+
|
|
61
|
+
So code that concerns one row lives on a wrapper around the whole table.
|
|
62
|
+
`lb-table` owns the `<table>`, and the page writes its contents as named
|
|
63
|
+
templates (`head`, `foot`, `ghost`) that the builder joins back into a
|
|
64
|
+
table. Each new need is another named destination, and the page's source
|
|
65
|
+
reads as `lb-table`'s shape rather than a table's. The shipped HTML is
|
|
66
|
+
honest; the source is a dialect.
|
|
67
|
+
|
|
68
|
+
Two ways to put code on the element itself follow, and only the second
|
|
69
|
+
survives.
|
|
70
|
+
|
|
71
|
+
### Customized built-ins
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<tr is="lb-ghost-row">…</tr>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The platform's own answer: a class extending `HTMLTableRowElement`, which
|
|
78
|
+
the parser treats as a `<tr>`. WebKit has declined to ship it, and every
|
|
79
|
+
browser on iOS and iPadOS is WebKit, so a polyfill, usually a
|
|
80
|
+
`MutationObserver`, would be the code path on every iPhone and iPad for
|
|
81
|
+
good. That rules it out.
|
|
82
|
+
|
|
83
|
+
### Behavior by attribute
|
|
84
|
+
|
|
85
|
+
```html
|
|
86
|
+
<tr lb-behavior="ghost-row">…</tr>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Code bound to any element by an attribute, as Stimulus does with
|
|
90
|
+
`data-controller`. It fits the parser everywhere and needs no polyfill.
|
|
91
|
+
|
|
92
|
+
- What usually drives it is a `MutationObserver`, which is incidental: the
|
|
93
|
+
code runs because something noticed. The hub, though, already knows every
|
|
94
|
+
time it puts an element in the page: it shows a page, clones a row, builds
|
|
95
|
+
a group, and moves an element in and out of an `lb-show` template. A
|
|
96
|
+
behavior attached and detached at those moments would be explicit.
|
|
97
|
+
- Elements the hub does not add (a widget's own scaffolding, anything a
|
|
98
|
+
script creates) would get no behavior, which may be the right rule rather
|
|
99
|
+
than a limitation.
|
|
100
|
+
- No form association of its own, and mostly none needed: a behavior on a
|
|
101
|
+
native control leaves it a native control, which joins its form, takes
|
|
102
|
+
focus and honors `autofocus` as it always did. Only a control whose UI is
|
|
103
|
+
built from scratch needs `ElementInternals`, and stays an autonomous
|
|
104
|
+
custom element.
|
|
105
|
+
- The platform has discussed a registry for custom attributes, which would
|
|
106
|
+
be this idea built in. It has not shipped. Without it the browser has no
|
|
107
|
+
part in a behavior: everything below is the hub's.
|
|
108
|
+
|
|
109
|
+
#### A lifecycle in the hub's terms
|
|
110
|
+
|
|
111
|
+
A custom element's lifecycle is the browser's, and it speaks of insertion:
|
|
112
|
+
connected, disconnected, adopted. The hub's work rarely lines up with it. A
|
|
113
|
+
moved row is disconnected and connected, the copy a move leaves behind is
|
|
114
|
+
constructed, an `lb-show` toggle adopts twice, which is why a custom element
|
|
115
|
+
must write `connectedCallback` to run again.
|
|
116
|
+
|
|
117
|
+
A behavior's lifecycle would be calls the hub makes from its own code, at
|
|
118
|
+
moments it already knows, named for what the hub did:
|
|
119
|
+
|
|
120
|
+
| The hub | A behavior hears |
|
|
121
|
+
| -------------------------------------------------------------- | ----------------------------------------------------------- |
|
|
122
|
+
| Shows a page, places a cloned row, builds a group, first shows an element shipped absent | `attached()`, once, after the element is placed and filled |
|
|
123
|
+
| Lands new values on the element's row | `landed(row)` |
|
|
124
|
+
| Moves a row the order put elsewhere | `moved()`; the instance is kept |
|
|
125
|
+
| Leaves a copy of a moved row behind | Nothing; the copy gets no behavior |
|
|
126
|
+
| Turns `lb-show` off, then on | `hidden()`, then `shown()`; the instance is kept |
|
|
127
|
+
| Removes a row, once its animations end | `detached()`, once |
|
|
128
|
+
| Finishes a request issued from inside the element | `requestDone(detail)` |
|
|
129
|
+
|
|
130
|
+
- `attached` and `detached` pair exactly, so setup and teardown need no
|
|
131
|
+
guard against running twice.
|
|
132
|
+
- The calls are the code-side twin of the stamps a stylesheet already sees
|
|
133
|
+
(`lb-row-created`, `lb-row-changed`, `lb-row-moved`, `lb-row-leaving`):
|
|
134
|
+
one vocabulary for CSS and code, in the hub's relational terms.
|
|
135
|
+
- The hub keeps one instance per element and behavior name. An element may
|
|
136
|
+
carry several, `lb-behavior="ghost-row autosave"`, which a tag cannot.
|
|
137
|
+
- A behavior is a class in a file the builder finds by name, as it finds
|
|
138
|
+
`<tag>.browser.ts`, and a name with no file is a build error.
|
|
139
|
+
|
|
140
|
+
What the hub would have to define and keep: whether `attached` comes before
|
|
141
|
+
or after the rows inside a host land, and the order of calls among nested
|
|
142
|
+
behaviors. A custom element gets its ordering from the platform; here it is
|
|
143
|
+
loadbare's to state.
|
|
144
|
+
|
|
145
|
+
A rule for choosing: a custom element makes a new kind of element (its own
|
|
146
|
+
markup, expansion, or a control built from scratch); a behavior adds code to
|
|
147
|
+
an element HTML already has.
|
|
148
|
+
|
|
149
|
+
### Where it would land
|
|
150
|
+
|
|
151
|
+
Behavior by attribute lets a page write a real `<table lb-query>` with code
|
|
152
|
+
on the rows that need it, and leaves `lb-table` to supply only what is truly
|
|
153
|
+
the table's. It keeps the parser and the browser matrix out of it, at the
|
|
154
|
+
cost of a lifecycle the hub would define.
|
|
155
|
+
|
|
156
|
+
## Expansion that replaces the tag
|
|
157
|
+
|
|
158
|
+
Today a custom element's tag does two jobs: at build time it is expanded,
|
|
159
|
+
and at run time it carries code. Because the code needs the tag, the tag
|
|
160
|
+
ships as a wrapper around what it expanded, and the wrapper is the source of
|
|
161
|
+
most widget friction: focus searched for inside it, values forwarded through
|
|
162
|
+
it, `autofocus` lost, and no place in a table.
|
|
163
|
+
|
|
164
|
+
With behaviors carrying the code, expansion is free to stop shipping the
|
|
165
|
+
tag.
|
|
166
|
+
|
|
167
|
+
```html
|
|
168
|
+
<!-- page -->
|
|
169
|
+
<lb-field exp-label="Name" lb-column="name"></lb-field>
|
|
170
|
+
|
|
171
|
+
<!-- what ships -->
|
|
172
|
+
<!-- lb-field exp-label="Name" lb-column="name" -->
|
|
173
|
+
<label lb-behavior="lb-field">Name <input lb-column="name" /></label>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- **The tag becomes a comment.** The shipped DOM keeps a trail back to the
|
|
177
|
+
source at no run-time cost. A comment is legal anywhere, tables included,
|
|
178
|
+
and is invisible to CSS, `:nth-child` and the hub's counting.
|
|
179
|
+
- **The builder puts the behavior on the root.** When
|
|
180
|
+
`<name>.behavior.ts` exists, the expanded root gets `lb-behavior="<name>"`;
|
|
181
|
+
the author never writes it. `[lb-behavior~="lb-field"]` then serves CSS
|
|
182
|
+
and `querySelector` as the tag selector `lb-field` does now.
|
|
183
|
+
- **Every widget inherits one base class.** A `Behavior` holds `this.el`,
|
|
184
|
+
has empty methods for every call in the hub's lifecycle, and types
|
|
185
|
+
`landed(row)` and `requestDone(detail)`. A widget overrides only what it
|
|
186
|
+
uses, as it extends `HTMLElement` or `WrappedControl` today.
|
|
187
|
+
|
|
188
|
+
The file convention decides which path an element takes:
|
|
189
|
+
|
|
190
|
+
| The element has | The builder | At run time |
|
|
191
|
+
| ------------------------------------------ | -------------------------------------------------- | ---------------------- |
|
|
192
|
+
| An element file, no script | Replaces the tag, leaves a comment | Plain HTML |
|
|
193
|
+
| An element file and `<name>.behavior.ts` | Replaces the tag, puts `lb-behavior` on the root | The hub's lifecycle |
|
|
194
|
+
| `<name>.browser.ts` | Keeps the tag, as today | The browser's lifecycle |
|
|
195
|
+
|
|
196
|
+
The third row is for controls built from scratch, the only elements that
|
|
197
|
+
still need `ElementInternals`.
|
|
198
|
+
|
|
199
|
+
### A flag to keep the tag
|
|
200
|
+
|
|
201
|
+
Considered and set aside. It gives every widget two DOM shapes, so its CSS
|
|
202
|
+
and code must work in both. It cannot apply where replacing pays off most,
|
|
203
|
+
since a kept tag still has no place in a table. And what people would keep
|
|
204
|
+
the tag for, a name to find and style the widget by and a trail to its
|
|
205
|
+
source, the `lb-behavior` attribute and the comment already give.
|
|
206
|
+
|
|
207
|
+
### To settle
|
|
208
|
+
|
|
209
|
+
1. **One root.** A behavior attaches to one element, so an element file with
|
|
210
|
+
a behavior has exactly one top-level element, and anything more is a
|
|
211
|
+
build error.
|
|
212
|
+
2. **Where the tag's attributes go.** The wrapper used to keep them; once
|
|
213
|
+
the tag is gone each needs a destination. The natural default is the
|
|
214
|
+
root, and `lb-field` breaks it: its root is the `<label>`, but
|
|
215
|
+
`lb-column` belongs on the `<input>` inside while `lb-show` belongs on the
|
|
216
|
+
label. Either the element file marks a target for data attributes, or
|
|
217
|
+
the page passes them as parameters (`exp-column`, written into the file as
|
|
218
|
+
`lb-column="{{column}}"`), which works today but reads less naturally
|
|
219
|
+
than `lb-column`. This wants worked examples before a rule.
|
|
220
|
+
|
|
221
|
+
## The case for behaviors
|
|
222
|
+
|
|
223
|
+
Today's model is simple to state: a widget is a name, and a name is one or
|
|
224
|
+
both of a `.html` and a `.browser.ts`. One name is one element the
|
|
225
|
+
developer writes, so what a widget does is easy to predict. Most of what
|
|
226
|
+
that costs is paid inside Loadbare, in service of the simple model. The one
|
|
227
|
+
place it leaks to the developer is the lifecycle, where a widget author must
|
|
228
|
+
follow strict rules to get the behavior they expect.
|
|
229
|
+
|
|
230
|
+
Behaviors would simplify the hub, but they seem to spread code that a
|
|
231
|
+
custom element keeps in one file. The containment comes from the element
|
|
232
|
+
file, though, not from the tag.
|
|
233
|
+
|
|
234
|
+
### What one file bundles today
|
|
235
|
+
|
|
236
|
+
`lb-table`'s script does three jobs, joined by where they sit rather than by
|
|
237
|
+
any data they share:
|
|
238
|
+
|
|
239
|
+
| Job | Reads and writes |
|
|
240
|
+
| -------------------------- | -------------------------------------------------- |
|
|
241
|
+
| Table scaffolding | Caption, head and foot destinations; expansion only |
|
|
242
|
+
| The ghost row | The ghost row's controls, its form, and focus |
|
|
243
|
+
| Scrolling to the saved row | The list's rows and their stamps |
|
|
244
|
+
|
|
245
|
+
It is one file, and three concerns in one class, none reusable without the
|
|
246
|
+
others. A `<ul>` cannot have a ghost row without becoming an `lb-table`.
|
|
247
|
+
|
|
248
|
+
### Two units
|
|
249
|
+
|
|
250
|
+
- A widget is markup. Its element file decides what elements exist, at
|
|
251
|
+
build time, as now.
|
|
252
|
+
- A behavior is what one element does. Its code attaches to the element
|
|
253
|
+
that carries it, and runs on the hub's lifecycle.
|
|
254
|
+
|
|
255
|
+
An element file may write `lb-behavior` on its own inner elements:
|
|
256
|
+
|
|
257
|
+
```html
|
|
258
|
+
<!-- lb-table.html -->
|
|
259
|
+
<table lb-behavior="scroll-to-saved">
|
|
260
|
+
<thead lb-exp-template="head"></thead>
|
|
261
|
+
<tbody lb-exp-slot></tbody>
|
|
262
|
+
<tbody lb-exp-template="ghost" lb-behavior="ghost-row"></tbody>
|
|
263
|
+
</table>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
`lb-table` stays the one place to read, and its element file says which code
|
|
267
|
+
runs on which element. The code is split into files by job, and the widget
|
|
268
|
+
assembles them. A page that wants a ghost row in a `<ul>` writes
|
|
269
|
+
`lb-behavior="ghost-row"` on it.
|
|
270
|
+
|
|
271
|
+
### Where code goes
|
|
272
|
+
|
|
273
|
+
Code goes on the element it reads and writes.
|
|
274
|
+
|
|
275
|
+
- The ghost row's code touches the ghost row's controls and form, so it is a
|
|
276
|
+
behavior on the ghost row.
|
|
277
|
+
- Scrolling touches the list's rows, so it is a behavior on the list.
|
|
278
|
+
- Code that seems to need two elements is the sign to stop. Behaviors never
|
|
279
|
+
call each other. They speak only through what widgets use today:
|
|
280
|
+
attributes the hub stamps, and bubbling events such as `lb-request-done`.
|
|
281
|
+
The scrolling behavior knows nothing of the ghost row; it reads
|
|
282
|
+
`lb-row-requested`.
|
|
283
|
+
|
|
284
|
+
The rule also refuses the overfit that started this: a ghost row per
|
|
285
|
+
section would be a behavior on the table reaching into every section.
|
|
286
|
+
|
|
287
|
+
### Where the leak moves
|
|
288
|
+
|
|
289
|
+
Today the leak is the browser's lifecycle: `connectedCallback` written to
|
|
290
|
+
run again, and a value stamp adopted on first connect. With behaviors it
|
|
291
|
+
becomes three rules Loadbare states:
|
|
292
|
+
|
|
293
|
+
1. Only elements the hub places get behaviors.
|
|
294
|
+
2. A behavior is attached and detached exactly once.
|
|
295
|
+
3. Behaviors speak only through stamps and events.
|
|
296
|
+
|
|
297
|
+
They are fewer, and in the hub's terms rather than the browser's. The cost
|
|
298
|
+
moves to Loadbare's design: the ordering the platform gives custom elements
|
|
299
|
+
must be stated, such as whether a host's behavior attaches before or after
|
|
300
|
+
its rows land, and the order among nested behaviors.
|
|
301
|
+
|
|
302
|
+
### Both, not one
|
|
303
|
+
|
|
304
|
+
Custom elements stay. A control built from scratch still needs
|
|
305
|
+
`ElementInternals`, and a tag with no code is expansion alone. Behaviors
|
|
306
|
+
cover what a tag does badly: code on an element HTML already has, inside
|
|
307
|
+
content models that forbid a tag. The developer's question becomes whether
|
|
308
|
+
they are making a new kind of element, or giving an existing element a job.
|
package/docs/reference/chrome.md
CHANGED
|
@@ -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).
|
|
@@ -165,7 +169,9 @@ pushes one instead when the element carrying `lb-request` also carries
|
|
|
165
169
|
|
|
166
170
|
The hub answers the request itself, with no round trip, then reloads the
|
|
167
171
|
page's queries at the new URL. The page stays in place, and rows that come
|
|
168
|
-
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.
|
|
169
175
|
|
|
170
176
|
A control whose column the URL does not carry lands empty, so every control
|
|
171
177
|
inside `lb-query="lb-url"` shows what the address bar says, after a reload
|