@loadbare/app 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/dist/build/assemble.js +1 -0
  2. package/dist/build/assemble.js.map +1 -0
  3. package/dist/build/cli.js +1 -0
  4. package/dist/build/cli.js.map +1 -0
  5. package/dist/build/elements.js +1 -0
  6. package/dist/build/elements.js.map +1 -0
  7. package/dist/build/expand.js +1 -0
  8. package/dist/build/expand.js.map +1 -0
  9. package/dist/build/format.js +1 -0
  10. package/dist/build/format.js.map +1 -0
  11. package/dist/build/locations.d.ts +2 -2
  12. package/dist/build/locations.d.ts.map +1 -1
  13. package/dist/build/locations.js +3 -4
  14. package/dist/build/locations.js.map +1 -0
  15. package/dist/build/origins.js +1 -0
  16. package/dist/build/origins.js.map +1 -0
  17. package/dist/build/package-root.js +1 -0
  18. package/dist/build/package-root.js.map +1 -0
  19. package/dist/build/pages.d.ts +4 -0
  20. package/dist/build/pages.d.ts.map +1 -1
  21. package/dist/build/pages.js +5 -0
  22. package/dist/build/pages.js.map +1 -0
  23. package/dist/build/styles.js +1 -0
  24. package/dist/build/styles.js.map +1 -0
  25. package/dist/core/lb-constants.js +1 -0
  26. package/dist/core/lb-constants.js.map +1 -0
  27. package/dist/core/lb-types.d.ts +1 -1
  28. package/dist/core/lb-types.d.ts.map +1 -1
  29. package/dist/core/lb-types.js +2 -1
  30. package/dist/core/lb-types.js.map +1 -0
  31. package/dist/hub/lb-apply.d.ts +11 -1
  32. package/dist/hub/lb-apply.d.ts.map +1 -1
  33. package/dist/hub/lb-apply.js +50 -13
  34. package/dist/hub/lb-apply.js.map +1 -0
  35. package/dist/hub/lb-hub.browser.d.ts +1 -1
  36. package/dist/hub/lb-hub.browser.d.ts.map +1 -1
  37. package/dist/hub/lb-hub.browser.js +143 -112
  38. package/dist/hub/lb-hub.browser.js.map +1 -0
  39. package/dist/server/lb-express.d.ts +1 -1
  40. package/dist/server/lb-express.d.ts.map +1 -1
  41. package/dist/server/lb-express.js +1 -0
  42. package/dist/server/lb-express.js.map +1 -0
  43. package/dist/server/lb-server.d.ts +1 -1
  44. package/dist/server/lb-server.d.ts.map +1 -1
  45. package/dist/server/lb-server.js +2 -1
  46. package/dist/server/lb-server.js.map +1 -0
  47. package/docs/TECHREF-1.0.md +158 -43
  48. package/docs/analysis-accidental-complexity.md +149 -0
  49. package/docs/reference/custom-elements.md +27 -17
  50. package/docs/reference/data-binding.md +88 -29
  51. package/docs/reference/server.md +18 -0
  52. package/docs/reference/widgets.md +3 -3
  53. package/docs/theory.md +114 -1
  54. package/docs/tutorials/072-inserting-into-a-list.md +14 -11
  55. package/docs/tutorials/080-widget-requests.md +9 -26
  56. package/package.json +2 -2
  57. package/dist/tests/assemble.test.d.ts +0 -8
  58. package/dist/tests/assemble.test.d.ts.map +0 -1
  59. package/dist/tests/assemble.test.js +0 -210
  60. package/dist/tests/elements.test.d.ts +0 -8
  61. package/dist/tests/elements.test.d.ts.map +0 -1
  62. package/dist/tests/elements.test.js +0 -118
  63. package/dist/tests/expand.test.d.ts +0 -10
  64. package/dist/tests/expand.test.d.ts.map +0 -1
  65. package/dist/tests/expand.test.js +0 -253
  66. package/dist/tests/fixtures/elements/collision/imports.d.ts +0 -3
  67. package/dist/tests/fixtures/elements/collision/imports.d.ts.map +0 -1
  68. package/dist/tests/fixtures/elements/collision/imports.js +0 -1
  69. package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.d.ts +0 -2
  70. package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.d.ts.map +0 -1
  71. package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.js +0 -1
  72. package/dist/tests/fixtures/elements/local/widgets/app-box.browser.d.ts +0 -2
  73. package/dist/tests/fixtures/elements/local/widgets/app-box.browser.d.ts.map +0 -1
  74. package/dist/tests/fixtures/elements/local/widgets/app-box.browser.js +0 -1
  75. package/dist/tests/fixtures/elements/manifest/imports.d.ts +0 -3
  76. package/dist/tests/fixtures/elements/manifest/imports.d.ts.map +0 -1
  77. package/dist/tests/fixtures/elements/manifest/imports.js +0 -1
  78. package/dist/tests/fixtures/elements/manifest-bad-entry/imports.d.ts +0 -3
  79. package/dist/tests/fixtures/elements/manifest-bad-entry/imports.d.ts.map +0 -1
  80. package/dist/tests/fixtures/elements/manifest-bad-entry/imports.js +0 -1
  81. package/dist/tests/fixtures/elements/manifest-not-array/imports.d.ts +0 -5
  82. package/dist/tests/fixtures/elements/manifest-not-array/imports.d.ts.map +0 -1
  83. package/dist/tests/fixtures/elements/manifest-not-array/imports.js +0 -1
  84. package/dist/tests/fixtures/elements/pkg/acme-widget.browser.d.ts +0 -2
  85. package/dist/tests/fixtures/elements/pkg/acme-widget.browser.d.ts.map +0 -1
  86. package/dist/tests/fixtures/elements/pkg/acme-widget.browser.js +0 -1
  87. package/dist/tests/fixtures/elements/unmarked/widgets/app-box.d.ts +0 -6
  88. package/dist/tests/fixtures/elements/unmarked/widgets/app-box.d.ts.map +0 -1
  89. package/dist/tests/fixtures/elements/unmarked/widgets/app-box.js +0 -1
  90. package/dist/tests/helpers/console.d.ts +0 -20
  91. package/dist/tests/helpers/console.d.ts.map +0 -1
  92. package/dist/tests/helpers/console.js +0 -28
  93. package/dist/tests/helpers/dom.d.ts +0 -18
  94. package/dist/tests/helpers/dom.d.ts.map +0 -1
  95. package/dist/tests/helpers/dom.js +0 -22
  96. package/dist/tests/helpers/hub.d.ts +0 -73
  97. package/dist/tests/helpers/hub.d.ts.map +0 -1
  98. package/dist/tests/helpers/hub.js +0 -151
  99. package/dist/tests/lb-apply.test.d.ts +0 -8
  100. package/dist/tests/lb-apply.test.d.ts.map +0 -1
  101. package/dist/tests/lb-apply.test.js +0 -177
  102. package/dist/tests/lb-express.test.d.ts +0 -14
  103. package/dist/tests/lb-express.test.d.ts.map +0 -1
  104. package/dist/tests/lb-express.test.js +0 -243
  105. package/dist/tests/lb-hub.test.d.ts +0 -14
  106. package/dist/tests/lb-hub.test.d.ts.map +0 -1
  107. package/dist/tests/lb-hub.test.js +0 -319
  108. package/dist/tests/lb-list.test.d.ts +0 -12
  109. package/dist/tests/lb-list.test.d.ts.map +0 -1
  110. package/dist/tests/lb-list.test.js +0 -339
  111. package/dist/tests/lb-server.test.d.ts +0 -9
  112. package/dist/tests/lb-server.test.d.ts.map +0 -1
  113. package/dist/tests/lb-server.test.js +0 -546
  114. package/dist/tests/origins.test.d.ts +0 -10
  115. package/dist/tests/origins.test.d.ts.map +0 -1
  116. package/dist/tests/origins.test.js +0 -387
  117. package/dist/tests/pages.test.d.ts +0 -6
  118. package/dist/tests/pages.test.d.ts.map +0 -1
  119. package/dist/tests/pages.test.js +0 -148
  120. package/dist/tests/styles.test.d.ts +0 -7
  121. package/dist/tests/styles.test.d.ts.map +0 -1
  122. package/dist/tests/styles.test.js +0 -76
@@ -19,19 +19,21 @@ Here is a page that binds a scalar, a list, and three requests:
19
19
 
20
20
  <p lb-row="dues">Dues collected this year: <span lb-cell="total"></span></p>
21
21
 
22
- <form lb-action="lb-row-insert" lb-list="roster">
23
- <input lb-cell="name" placeholder="Name" />
24
- <button type="submit">Add member</button>
25
- </form>
26
-
27
- <ul lb-list="roster">
28
- <template lb-key="id">
29
- <li>
30
- <lb-input lb-cell="name"></lb-input>
31
- <button lb-action="lb-row-delete">Remove</button>
32
- </li>
33
- </template>
34
- </ul>
22
+ <section lb-list="roster">
23
+ <form lb-action="lb-row-insert">
24
+ <input lb-cell="name" placeholder="Name" />
25
+ <button type="submit">Add member</button>
26
+ </form>
27
+
28
+ <ul>
29
+ <template lb-key="id">
30
+ <li>
31
+ <lb-input lb-cell="name"></lb-input>
32
+ <button lb-action="lb-row-delete">Remove</button>
33
+ </li>
34
+ </template>
35
+ </ul>
36
+ </section>
35
37
  ```
36
38
 
37
39
  The queries named here — `dues` and `roster` — and the operations the page
@@ -46,7 +48,7 @@ asks for are declared on the server; see [page files](./page-files.md).
46
48
  | `lb-key` | a developer | The column that identifies a row |
47
49
  | `lb-cell` | a developer | The column an element displays |
48
50
  | `lb-key-value` | the hub | A live row's own key |
49
- | `lb-value` | the hub | Where a widget receives its value |
51
+ | `lb-value` | the hub | The value that landed on a cell |
50
52
 
51
53
  A binding is scoped by ancestry. Either scope attribute scopes its DOM
52
54
  children, and a nested one of either kind begins a new scope, so an element
@@ -85,20 +87,23 @@ value of it. A developer writes the first and never the second.
85
87
 
86
88
  ### Where a bound value lands
87
89
 
88
- A value lands on a bound element one of two ways.
90
+ A value lands on a bound element one of three ways.
89
91
 
90
- | Element | Receives the value as |
91
- |------------------|--------------------------|
92
- | A native element | Its `textContent` |
93
- | A custom element | Its `lb-value` attribute |
92
+ | Element | Receives the value as |
93
+ |----------------------------------------|-----------------------------------|
94
+ | A custom element | Its `lb-value` attribute |
95
+ | `<select>`, `<textarea>`, or `<input>` | Its `value`, and `lb-value` |
96
+ | Any other native element | Its `textContent`, and `lb-value` |
94
97
 
95
- A native element has no behavior of its own, so its value is its text. A
96
- widget owns whatever control it wraps, so it is handed the value and renders
97
- it itself — see [Custom Elements](./custom-elements.md#code) for
98
- observing `lb-value`.
98
+ A widget owns whatever control it wraps, so it is handed the value and
99
+ renders it itself — see [Custom Elements](./custom-elements.md#code) for
100
+ observing `lb-value`. A form control shows its state as its `value`, so a
101
+ `<select>` keeps its options. Any other native element has no behavior of its
102
+ own, so its value is its text. Checkboxes, radio buttons and file inputs
103
+ receive nothing, not even `lb-value`, and the hub reports it to the console.
99
104
 
100
105
  Nothing an application writes ever sets `lb-value`. It is written by
101
- Loadbare and read by the widget it is written on.
106
+ Loadbare and read by a widget or a stylesheet.
102
107
 
103
108
  ### Lists
104
109
 
@@ -154,7 +159,9 @@ the wire discriminant: a value beginning with `lb-` is an operation, and
154
159
  anything else is a name the page declared.
155
160
 
156
161
  The hub sends the first four from a native element on the element's own
157
- event: a form on submit, anything else on click. A cell change needs a
162
+ event: a form on submit, anything else on click. An insert or update clicked
163
+ from a button reads the cells around the button (see [Forms](#forms)). A
164
+ cell change needs a
158
165
  widget to say what a change is, so only a widget sends it: the shipped
159
166
  `<lb-input>` does when it carries `lb-action="lb-cell-change"`, and stays
160
167
  quiet otherwise — an input inside an `lb-row-insert` or `lb-row-update` form is read
@@ -222,7 +229,35 @@ from the same ancestor a delete button reads:
222
229
 
223
230
  Give every `lb-cell` in a form a control to read. A cell that is neither an
224
231
  `<input>`, `<select>`, or `<textarea>` nor wraps one is left out of the
225
- values map.
232
+ values map, and a form with no cell to read sends nothing.
233
+
234
+ Where the cells cannot sit in a form, as in a table row, put the action on a
235
+ button beside them. The hub gathers from the nearest element around the
236
+ button that holds a cell to read, so a button in a row for a new entry
237
+ inserts that row's cells:
238
+
239
+ ```html
240
+ <table lb-list="roster">
241
+ <tbody>
242
+ <tr>
243
+ <td><input lb-cell="name" /></td>
244
+ <td><input lb-cell="role" /></td>
245
+ <td><button lb-action="lb-row-insert">Add</button></td>
246
+ </tr>
247
+ </tbody>
248
+ </table>
249
+ ```
250
+
251
+ The search stops at the live row the button is in, so an `lb-row-update`
252
+ button in a row saves only that row, and it never takes in the list scope
253
+ itself. Keep the button's own cell apart from the others: a button sharing
254
+ a `<td>` with one input gathers only that input.
255
+
256
+ Put these two actions on a form or a button, not on an element that holds
257
+ the cells. A click into one of its inputs would send the row, so the hub
258
+ refuses it. A widget may dispatch either from any element, and the hub
259
+ gathers the same way; see
260
+ [Sending a request](./custom-elements.md#sending-a-request).
226
261
 
227
262
  ## Conditional rendering
228
263
 
@@ -230,9 +265,33 @@ Loadbare ships static HTML and hydrates elements that are already in the
230
265
  document. There is no `if`, and none is needed: write every possibility into
231
266
  the page, and control which of them is showing.
232
267
 
233
- Use the standard `hidden` attribute, or CSS, and set it from a value the
234
- server sent. A widget that receives a value in `lb-value` is the natural
235
- place to do it — it is handed the current state and decides what to show:
268
+ Every cell carries the value that landed on it as `lb-value`, so a
269
+ stylesheet can show or hide part of a page from a value the server sent. Bind
270
+ a column the page does not display to a hidden element:
271
+
272
+ ```html
273
+ <template lb-key="id">
274
+ <tr>
275
+ <td lb-cell="name"></td>
276
+ <td lb-cell="locked" hidden></td>
277
+ <td><button lb-action="lb-row-delete">Remove</button></td>
278
+ </tr>
279
+ </template>
280
+ ```
281
+
282
+ ```css
283
+ tr:has([lb-cell="locked"][lb-value="true"]) button {
284
+ display: none;
285
+ }
286
+ ```
287
+
288
+ `lb-value` holds the string the query sent, so the query decides the spelling
289
+ the selector matches. A stylesheet can hide a control but cannot disable one,
290
+ and hiding is presentation: the server still refuses what a request may not
291
+ do.
292
+
293
+ Where showing a case takes more than a selector, a widget receives the value
294
+ in `lb-value` and decides what to show:
236
295
 
237
296
  ```ts
238
297
  for (const step of steps) step.hidden = step.dataset.step !== value;
@@ -86,6 +86,24 @@ Run the server under a TypeScript-capable runner. The builder writes
86
86
  }
87
87
  ```
88
88
 
89
+ Node 22.18 and later also runs it with no runner at all, since Node strips
90
+ TypeScript types itself. `dist/pages.ts` imports each `.requests.ts` and
91
+ `.queries.ts` file by its full name, extension included, because Node looks
92
+ for exactly the path an import names.
93
+
94
+ Enable `allowImportingTsExtensions` in the application's `tsconfig.json`
95
+ when `tsc` type-checks the server. Without it, `tsc` rejects the `.ts`
96
+ extensions in `dist/pages.ts`:
97
+
98
+ ```json
99
+ {
100
+ "compilerOptions": {
101
+ "noEmit": true,
102
+ "allowImportingTsExtensions": true
103
+ }
104
+ }
105
+ ```
106
+
89
107
  Restart the server after adding or changing a `.requests.ts` or `.queries.ts`
90
108
  file.
91
109
 
@@ -27,9 +27,9 @@ for where a listed package sits in the cascade.
27
27
 
28
28
  Wraps an `<input>`. `lb-value` sets the input's `.value`. The widget sends
29
29
  nothing on its own: `lb-action` names what the input's `change` sends. The
30
- reserved `lb-cell-change` sends `lb-cell-change`, addressed by the widget's own
31
- `lb-list`/`lb-key-value`/`lb-cell` coordinates; any other name sends that
32
- action with the input's value.
30
+ reserved `lb-cell-change` sends `lb-cell-change`; any other name sends that
31
+ action. Either carries the input's value, and the hub adds the scope the
32
+ input sits in.
33
33
 
34
34
  An input inside an `lb-row-insert` or `lb-row-update` form leaves the attribute off.
35
35
  The form reads every `lb-cell` in it on submit and sends one request for all
package/docs/theory.md CHANGED
@@ -344,4 +344,117 @@ I wrote Loadbare originally in 2003, as "Andromeda", before Node existed
344
344
  and even 4 years before we had JQuery. Andromeda was not nearly as
345
345
  optimized as Loadbare is now, but the principles were the same then as
346
346
  they are now. It has always worked for me, and I hope that it will work
347
- for you.
347
+ for you.
348
+
349
+ ## A Consistency Model
350
+
351
+ > **LLM-authored, not yet revised by a person.** Drafted by Claude on
352
+ > 2026-09-13, from a design session against `@loadbare/app` 0.6.0. Treat
353
+ > it as a proposal for this essay, not as the author's statement.
354
+
355
+ ### From a subjective claim to a checkable one
356
+
357
+ Whether Loadbare carries less accidental complexity than another tool will
358
+ always be disputable. A developer fluent in React has muscle memory for its
359
+ rules and will see Loadbare's rules as foreign, and foreign work feels
360
+ accidental. That objection is about familiarity, and familiarity is a cost
361
+ paid once, in the same way this essay treats the Express boilerplate.
362
+
363
+ A stronger claim can be checked, and a single counterexample in markup
364
+ would refute it. The model for applying data to HTML is:
365
+
366
+ 1. **Internally consistent.** A few rules, each applied the same way
367
+ everywhere, with no exceptions.
368
+ 2. **Consistent with HTML.** Each rule has a counterpart in how HTML
369
+ already behaves, and none contradicts it.
370
+ 3. **Consistent with relational data.** What the server sends is the shape
371
+ a SQL query already returns, so nothing is translated on the way.
372
+
373
+ ### Where the vocabulary comes from
374
+
375
+ This essay says the vocabulary is small because the operations a database
376
+ affords are small and coherent. A sharper statement is that the vocabulary
377
+ sits where two systems that are already coherent meet: the relational model
378
+ and HTML's containment model. Each attribute is a relational idea placed on
379
+ an element.
380
+
381
+ | Loadbare | Relational | HTML precedent |
382
+ | ---------------------------------------- | -------------------------------------------- | ------------------------------------------------------------ |
383
+ | `lb-list` | a relation, a set of rows | `<select>`, `<ul>`: a container whose contents are its items |
384
+ | `lb-row` | a single row | `<form>`: one record of fields |
385
+ | `lb-cell` | a column | `name` on a control: which field this is |
386
+ | `lb-key`, `lb-key-value` | the primary key | `value` on `<option>`: identity apart from the label |
387
+ | `lb-action` | a closed set of writes, and named procedures | `action` on `<form>`: where a submission goes |
388
+ | scope from ancestors | | form ownership, `lang`, `<fieldset disabled>` |
389
+ | a nested scope begins a new one | | a nested element owns its own contents |
390
+ | `lb-row-count`, `lb-pending`, `lb-error` | | state attributes such as `open` on `<details>` |
391
+
392
+ ### The rules
393
+
394
+ 1. **An element's own attributes describe what it displays. Its ancestors
395
+ describe where it belongs.** A picker carrying `lb-list` displays that
396
+ list, and its choice is addressed to the row it sits in, the way a
397
+ `<select>` displays its options and submits to the form around it.
398
+ 2. **A name answers with one shape, a row or a set of rows, and a cell holds
399
+ one value.** Master-detail is a row and a list under two names. Many
400
+ masters with their details is one list of joined rows, grouped for
401
+ display.
402
+ 3. **A value lands where the element shows its state, and a form gathers
403
+ from the same places.** A widget receives `lb-value`, a form control its
404
+ `value`, and any other element its text.
405
+ 4. **A request is an action and a position.** The hub supplies the position
406
+ from the document. Only an interaction supplies a value.
407
+ 5. **The hub owns position and reconciliation. A widget owns interaction and
408
+ placement.**
409
+
410
+ ### Evidence
411
+
412
+ The following decisions each removed an exception or a conflict, and none
413
+ added an attribute. Over the same span the model gained pickers inside rows,
414
+ native selects that show their own state, and nested lists that do not
415
+ destroy each other.
416
+
417
+ | Decision | Removed |
418
+ | ------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
419
+ | The hub scopes every request | Native requests were scoped and widget requests were not |
420
+ | Scope comes from ancestors, never the element's own `lb-list` | `lb-list` meant "display" in one place and "address" in another |
421
+ | A form control receives its value as `value` | A `<select>` was treated as text, against HTML, and landing did not mirror gathering |
422
+ | Row and template lookups stop at a nested scope | Cells respected nesting while rows and templates did not |
423
+ | Master-detail is a row and a list | An open question born of document-shaped data |
424
+ | `lb-value` on every cell | Only a widget's landed value was visible to a stylesheet |
425
+
426
+ Two signs suggest these are rules rather than patches. Fixing where a
427
+ request's scope comes from also fixed an unrelated console error about insert
428
+ forms, which was never worked on directly. Making rows respect nested scopes
429
+ needed no change to the reference, which already described that behavior.
430
+
431
+ ### Where the claim is not yet proven
432
+
433
+ - **Parameters and view state.** A query takes no argument from the
434
+ browser, so there is nowhere to put which record is selected, a filter, a
435
+ sort, or a collapsed section. The model is incomplete here rather than
436
+ inconsistent. If the answer is HTML's own, the URL, in the way
437
+ `<form method="get">` puts its fields in the query string, the claim grows
438
+ stronger. If it needs a state mechanism with no HTML precedent, that will
439
+ be the model's first real exception.
440
+ - **Checkboxes and radio buttons.** HTML's boolean convention is presence or
441
+ absence, as with `checked`, `hidden` and `disabled`. A rule following it
442
+ would avoid inventing truthiness, though it still touches data types.
443
+ - **Data types.** HTML attributes are strings, so comparing values as strings
444
+ in a stylesheet is consistent with HTML. What is not yet reconciled is
445
+ that a database's typed values arrive untyped.
446
+ - **A nested list shows the same rows in every outer row.** That follows
447
+ from the second rule: one name is one relation. Only a new outer row
448
+ starting with an empty nested list is a mechanical gap.
449
+
450
+ ### A test for every change
451
+
452
+ Before a change is made, ask:
453
+
454
+ - Does it remove an exception, or add one?
455
+ - Does it have an HTML precedent?
456
+ - Does it have a relational counterpart?
457
+
458
+ Stamping every column of a row onto the row element as `data-*` fails the
459
+ first two: attributes nobody wrote, with no precedent in HTML. Writing
460
+ `lb-value` on every cell passes all three.
@@ -8,20 +8,23 @@ that allows a user to add a note.
8
8
  ```html
9
9
  <!-- src/pages/about.page.html -->
10
10
  <h2>Notes</h2>
11
- <form lb-action="lb-row-insert" lb-list="notes">
12
- <input lb-cell="text" placeholder="Write a note" />
13
- <button type="submit">Add</button>
14
- </form>
15
-
16
- <ul lb-list="notes">
17
- <template lb-key="id">
18
- <li lb-cell="text"></li>
19
- </template>
20
- </ul>
11
+ <section lb-list="notes">
12
+ <form lb-action="lb-row-insert">
13
+ <input lb-cell="text" placeholder="Write a note" />
14
+ <button type="submit">Add</button>
15
+ </form>
16
+
17
+ <ul>
18
+ <template lb-key="id">
19
+ <li lb-cell="text"></li>
20
+ </template>
21
+ </ul>
22
+ </section>
21
23
  ```
22
24
 
23
25
  `lb-action="lb-row-insert"` on a `<form>` gathers its `lb-cell`s into a values
24
- map on submit and sends them against `lb-list`. The name is one of
26
+ map on submit and sends them against the `lb-list` around it, the way an
27
+ `<input>` belongs to the `<form>` around it. The name is one of
25
28
  Loadbare's reserved ones — every value beginning with `lb-` is — so it
26
29
  needs no declaring, and nothing of yours may be called that. There's no `lb-key-value` — there's no row
27
30
  yet.
@@ -9,11 +9,9 @@ changes.
9
9
 
10
10
  ## Writing the widget
11
11
 
12
- Our new custom input will no longer rely on Loadbare's form handling,
13
- and so it must reimplement the data scoping logic that is in
14
- Loadbare. This reimplementation approach is neither encouraged nor
15
- discouraged. This tutorial shows only that it is possible, and exposes
16
- how Loadbare internals work.
12
+ Our new custom input will no longer rely on Loadbare's form handling. It
13
+ says what happened and what its value is. Where it sits, the list, the row
14
+ and the cell, is added by the hub.
17
15
 
18
16
  ```html
19
17
  <!-- src/note-input.html -->
@@ -22,13 +20,7 @@ how Loadbare internals work.
22
20
 
23
21
  ```ts
24
22
  // src/note-input.browser.ts
25
- import {
26
- ATTR_CELL,
27
- ATTR_KEY_VALUE,
28
- ATTR_LIST,
29
- ATTR_VALUE,
30
- LB_EVENT_NAME,
31
- } from "@loadbare/app/constants";
23
+ import { ATTR_VALUE, LB_EVENT_NAME } from "@loadbare/app/constants";
32
24
 
33
25
  class NoteInput extends HTMLElement {
34
26
  static observedAttributes = [ATTR_VALUE];
@@ -42,20 +34,10 @@ class NoteInput extends HTMLElement {
42
34
  this.addEventListener("change", () => {
43
35
  const input = this.querySelector("input");
44
36
  if (!input) return;
45
- const list = this.closest(`[${ATTR_LIST}]`)?.getAttribute(ATTR_LIST);
46
- const key = this.closest(`[${ATTR_KEY_VALUE}]`)?.getAttribute(ATTR_KEY_VALUE);
47
- const cell = this.getAttribute(ATTR_CELL);
48
- if (!list || !key || !cell) return;
49
37
  this.dispatchEvent(
50
38
  new CustomEvent(LB_EVENT_NAME, {
51
39
  bubbles: true,
52
- detail: {
53
- action: "lb-cell-change",
54
- list,
55
- key,
56
- cell,
57
- value: input.value,
58
- },
40
+ detail: { action: "lb-cell-change", value: input.value },
59
41
  }),
60
42
  );
61
43
  });
@@ -66,9 +48,10 @@ customElements.define("note-input", NoteInput);
66
48
  ```
67
49
 
68
50
  A widget with both a `.html` and a `.browser.ts` file shares one tag name; the
69
- build finds each half independently. On change, it reads its own
70
- `lb-list`/`lb-key-value`/`lb-cell` coordinates and dispatches a `lb-cell-change`
71
- request itself, the way `lb-row-update` does for a whole form.
51
+ build finds each half independently. On change, it dispatches a
52
+ `lb-cell-change` carrying the input's value. The hub adds the list, the key
53
+ of the row it is in and its own `lb-cell`, the same way it does for a
54
+ `lb-row-update` form.
72
55
 
73
56
  ## Using it
74
57
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@loadbare/app",
3
3
  "description": "High performance web app framework for server-bound applications",
4
- "version": "0.6.0",
4
+ "version": "0.7.1",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",
@@ -32,7 +32,7 @@
32
32
  "prebuild": "node --eval \"fs.rmSync('dist',{recursive:true,force:true})\" --input-type=module",
33
33
  "build:server": "tsc --project tsconfig.build.json",
34
34
  "build": "npm run build:server",
35
- "postbuild": "node --eval \"fs.chmodSync('dist/build/cli.js', 0o755)\" --input-type=module",
35
+ "postbuild": "node --eval \"fs.chmodSync('dist/build/cli.js', 0o755)\" --input-type=module && node ../../scripts/check-dist-imports.mjs",
36
36
  "test": "tsx --test \"tests/**/*.test.ts\"",
37
37
  "typecheck": "tsc --noEmit",
38
38
  "format": "prettier --write .",
@@ -1,8 +0,0 @@
1
- /**
2
- * Assembling a chrome file and its pages — see build/assemble.ts.
3
- *
4
- * The chrome and pages together are one write to a temp tree per test,
5
- * since assemble() reads from disk rather than taking sources in memory.
6
- */
7
- export {};
8
- //# sourceMappingURL=assemble.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"assemble.test.d.ts","sourceRoot":"","sources":["../../tests/assemble.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}