@loadbare/app 0.5.6 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +3 -4
  2. package/dist/build/assemble.d.ts +1 -1
  3. package/dist/build/assemble.d.ts.map +1 -1
  4. package/dist/build/assemble.js +80 -7
  5. package/dist/build/cli.d.ts +2 -2
  6. package/dist/build/cli.js +2 -2
  7. package/dist/build/expand.d.ts.map +1 -1
  8. package/dist/build/expand.js +19 -32
  9. package/dist/build/locations.d.ts +3 -3
  10. package/dist/build/locations.d.ts.map +1 -1
  11. package/dist/build/locations.js +15 -3
  12. package/dist/build/origins.d.ts +0 -13
  13. package/dist/build/origins.d.ts.map +1 -1
  14. package/dist/build/origins.js +32 -8
  15. package/dist/build/pages.d.ts +3 -3
  16. package/dist/build/pages.d.ts.map +1 -1
  17. package/dist/build/pages.js +9 -7
  18. package/dist/core/lb-constants.d.ts +15 -13
  19. package/dist/core/lb-constants.d.ts.map +1 -1
  20. package/dist/core/lb-constants.js +103 -53
  21. package/dist/core/lb-types.d.ts +103 -62
  22. package/dist/core/lb-types.d.ts.map +1 -1
  23. package/dist/core/lb-types.js +11 -3
  24. package/dist/hub/lb-apply.d.ts +18 -4
  25. package/dist/hub/lb-apply.d.ts.map +1 -1
  26. package/dist/hub/lb-apply.js +184 -34
  27. package/dist/hub/lb-hub.browser.d.ts +1 -1
  28. package/dist/hub/lb-hub.browser.d.ts.map +1 -1
  29. package/dist/hub/lb-hub.browser.js +131 -84
  30. package/dist/server/lb-express.d.ts +7 -4
  31. package/dist/server/lb-express.d.ts.map +1 -1
  32. package/dist/server/lb-express.js +45 -33
  33. package/dist/server/lb-server.d.ts +67 -45
  34. package/dist/server/lb-server.d.ts.map +1 -1
  35. package/dist/server/lb-server.js +55 -18
  36. package/dist/tests/assemble.test.js +154 -2
  37. package/dist/tests/expand.test.js +6 -3
  38. package/dist/tests/helpers/hub.d.ts +73 -0
  39. package/dist/tests/helpers/hub.d.ts.map +1 -0
  40. package/dist/tests/helpers/hub.js +151 -0
  41. package/dist/tests/lb-apply.test.js +86 -62
  42. package/dist/tests/lb-express.test.d.ts +1 -1
  43. package/dist/tests/lb-express.test.js +43 -38
  44. package/dist/tests/lb-hub.test.d.ts +14 -0
  45. package/dist/tests/lb-hub.test.d.ts.map +1 -0
  46. package/dist/tests/lb-hub.test.js +319 -0
  47. package/dist/tests/{lb-rows.test.d.ts → lb-list.test.d.ts} +1 -1
  48. package/dist/tests/lb-list.test.d.ts.map +1 -0
  49. package/dist/tests/{lb-rows.test.js → lb-list.test.js} +109 -106
  50. package/dist/tests/lb-server.test.js +151 -100
  51. package/dist/tests/origins.test.js +19 -1
  52. package/dist/tests/pages.test.d.ts +1 -1
  53. package/dist/tests/pages.test.js +64 -14
  54. package/docs/TECHREF-1.0.md +1000 -0
  55. package/docs/reference/builder.md +4 -4
  56. package/docs/reference/chrome.md +10 -9
  57. package/docs/reference/custom-elements.md +51 -36
  58. package/docs/reference/data-binding.md +141 -88
  59. package/docs/reference/overview.md +1 -1
  60. package/docs/reference/page-files.md +64 -49
  61. package/docs/reference/server.md +7 -6
  62. package/docs/reference/widgets.md +22 -30
  63. package/docs/roadmap.md +68 -22
  64. package/docs/testing.md +47 -17
  65. package/docs/theory.md +2 -2
  66. package/docs/tutorials/010-pages-and-navigation.md +8 -8
  67. package/docs/tutorials/040-displaying-data.md +9 -9
  68. package/docs/tutorials/050-actions.md +5 -5
  69. package/docs/tutorials/060-custom-element-code.md +1 -1
  70. package/docs/tutorials/065-conditional-rendering.md +4 -4
  71. package/docs/tutorials/070-displaying-a-list.md +24 -47
  72. package/docs/tutorials/072-inserting-into-a-list.md +17 -17
  73. package/docs/tutorials/074-deleting-from-a-list.md +15 -17
  74. package/docs/tutorials/076-updating-a-list-item.md +20 -22
  75. package/docs/tutorials/080-widget-requests.md +27 -23
  76. package/docs/tutorials/090-using-widget-libraries.md +1 -1
  77. package/package.json +1 -2
  78. package/dist/hub/lb-rows.d.ts +0 -18
  79. package/dist/hub/lb-rows.d.ts.map +0 -1
  80. package/dist/hub/lb-rows.js +0 -106
  81. package/dist/tests/lb-rows.test.d.ts.map +0 -1
@@ -56,7 +56,7 @@ display something to the user if they type a URL that has no matching page.
56
56
  <a href="/about" lb-nav-link>About</a>
57
57
  </nav>
58
58
  <main></main>
59
- <dialog lb-unknown-page lb-query="lb-navigation">
59
+ <dialog lb-unknown-page lb-row="lb-navigation">
60
60
  The URL <span lb-cell="page-uri"></span> is not in this app.
61
61
  </dialog>
62
62
  </lb-hub>
@@ -75,7 +75,7 @@ link.
75
75
 
76
76
  If a `<dialog lb-unknown-page>` element is present in the HTML, it will be
77
77
  displayed to the user when a URL is entered that has no matching page in the app.
78
- The `lb-query` and `lb-cell` attributes will be explained when we get to data
78
+ The `lb-row` and `lb-cell` attributes will be explained when we get to data
79
79
  binding; for now, know that `lb-navigation` is a query the hub itself answers
80
80
  on every navigation, and `page-uri` is the path that was asked for. The
81
81
  widget library ships this dialog ready-made as `<lb-unknown-page>`, which
@@ -112,12 +112,12 @@ catch-all answers that request too.
112
112
 
113
113
  ## If you look at the console
114
114
 
115
- On every navigation, the hub also asks the server for that page's data, at
116
- `/lb/data?page=<name>`. Neither of these pages is serving any data, and our
117
- Express server has no route handler for /lb/*, so the catch-all route is currently
118
- returning the same `app.html` for data requests as it does for the inital
119
- app load. This is expected in this early stage of the tutorial, we will fix
120
- it when we get to data binding and handling.
115
+ On every navigation, the hub also asks the server for that page's data. It
116
+ posts that request, so it never collides with the routes above. Neither of
117
+ these pages is serving any data, and our Express server has no route handler
118
+ for the hub yet, so those posts currently go unanswered. This is expected in
119
+ this early stage of the tutorial, we will fix it when we get to data binding
120
+ and handling.
121
121
 
122
122
  ## Run it
123
123
 
@@ -20,7 +20,7 @@ the cell `count`.
20
20
  <h1>About</h1>
21
21
  <p class="about-note">This is the about page.</p>
22
22
 
23
- <div lb-query="visits">
23
+ <div lb-row="visits">
24
24
  <p>This page has been visited <span lb-cell="count"></span> times.</p>
25
25
  </div>
26
26
  ```
@@ -45,14 +45,14 @@ When the user navigates to a page, the loadbare hub `<lb-hub>` swaps
45
45
  the contents of `<main>` and sends a request to the server for the
46
46
  query results defined for that page.
47
47
 
48
- For a visit counter, we need to write a `beforeGet` hook to fire first
48
+ For a visit counter, we need to write a `onPageEnter` hook to fire first
49
49
  and increment the counter:
50
50
 
51
51
 
52
52
  ```ts
53
- // src/pages/about.hooks.ts
54
- export const hooks = {
55
- beforeGet: (ctx) => ctx.db.recordVisit(),
53
+ // src/pages/about.requests.ts
54
+ export const requests = {
55
+ onPageEnter: (ctx) => ctx.db.recordVisit(),
56
56
  };
57
57
  ```
58
58
 
@@ -103,7 +103,7 @@ the authenticated caller instead of the same file for everyone.
103
103
 
104
104
  ## Wiring the server
105
105
 
106
- The Loadbare builder compiles a list of all hooks and queries.
106
+ The Loadbare builder compiles a list of all requests and queries.
107
107
  Add the four
108
108
  lines below, and our server will correctly route all data channel
109
109
  requests to the correct page code.
@@ -121,8 +121,8 @@ const app = express();
121
121
  app.use("/client.js", express.static("dist/client.js"));
122
122
  app.use("/app.css", express.static("dist/app.css"));
123
123
 
124
- // New: answers GET /lb/data?page=<name> and POST /lb?page=<name>.
125
- // Must come before the catch-all, or the catch-all answers these too.
124
+ // New: answers the hub's own route, which is the one the browser posts to.
125
+ // Must come before the catch-all, or the catch-all answers it too.
126
126
  function contextFor(_req) {
127
127
  return { db: openDb() };
128
128
  }
@@ -160,7 +160,7 @@ to About.
160
160
  View source on either page: the host HTML is exactly what's on disk. Only
161
161
  the `<span>`'s text changes, never the markup around it.
162
162
 
163
- This tutorial only used `lb-query` and `lb-cell` to display one value.
163
+ This tutorial only used `lb-row` and `lb-cell` to display one value.
164
164
  The full attribute vocabulary — actions, CRUD operations, lists, and
165
165
  navigation — is in [Data Binding](../reference/data-binding.md).
166
166
 
@@ -15,7 +15,7 @@ to reset the visit count.
15
15
  <h1>About</h1>
16
16
  <p class="about-note">This is the about page.</p>
17
17
 
18
- <div lb-query="visits">
18
+ <div lb-row="visits">
19
19
  <p>This page has been visited <span lb-cell="count"></span> times.</p>
20
20
  <button lb-action="resetVisits">Reset count</button>
21
21
  </div>
@@ -23,12 +23,12 @@ to reset the visit count.
23
23
 
24
24
  ## Implement the action on the server
25
25
 
26
- Actions go into the page's hooks file:
26
+ Actions go into the page's requests file:
27
27
 
28
28
  ```ts
29
- // src/pages/about.hooks.ts
30
- export const hooks = {
31
- beforeGet: (ctx) => ctx.db.recordVisit(),
29
+ // src/pages/about.requests.ts
30
+ export const requests = {
31
+ onPageEnter: (ctx) => ctx.db.recordVisit(),
32
32
  actions: {
33
33
  resetVisits: {
34
34
  run: (ctx) => ctx.db.resetVisits(),
@@ -16,7 +16,7 @@ then implement.
16
16
 
17
17
  ```html
18
18
  <!-- src/pages/about.page.html -->
19
- <div lb-query="visits">
19
+ <div lb-row="visits">
20
20
  <p>This page has been <visit-count lb-cell="count"></visit-count>.</p>
21
21
  <button lb-action="resetVisits">Reset count</button>
22
22
  </div>
@@ -19,7 +19,7 @@ two of them carry the standard `hidden` attribute.
19
19
  ```html
20
20
  <!-- src/pages/about.page.html -->
21
21
  <h2>Sign up</h2>
22
- <lb-wizard lb-query="signup" lb-cell="step">
22
+ <lb-wizard lb-row="signup" lb-cell="step">
23
23
  <section data-step="name">
24
24
  <h3>1. Name</h3>
25
25
  <p>This step is not hidden, because it's the one the page ships showing.</p>
@@ -96,9 +96,9 @@ export const queries = {
96
96
  ```
97
97
 
98
98
  ```ts
99
- // src/pages/about.hooks.ts
100
- export const hooks = {
101
- // ...beforeGet, resetVisits, and crud unchanged...
99
+ // src/pages/about.requests.ts
100
+ export const requests = {
101
+ // ...onPageEnter, resetVisits, and crud unchanged...
102
102
  actions: {
103
103
  resetVisits: { run: (ctx) => ctx.db.resetVisits(), refresh: ["visits"] },
104
104
  wizardBack: { run: (ctx) => ctx.db.moveSignup(-1), refresh: ["signup"] },
@@ -1,22 +1,24 @@
1
1
  # Displaying a List
2
2
 
3
- Every query so far has answered with one tuple. Now we add a query that
4
- answers with many rows, and a widget to show them: `<lb-list>`.
3
+ Every query so far has answered with one row. Now we add a query that
4
+ answers with many rows, and markup to show them. No widget is involved: the
5
+ hub reconciles a list itself.
5
6
 
6
7
  ## Writing the query
7
8
 
8
9
  ```ts
9
10
  // src/pages/about.queries.ts
10
- import { rows } from "@loadbare/app/server";
11
+ import { list, row } from "@loadbare/app/server";
11
12
 
12
13
  export const queries = {
13
- visits: async (ctx) => ({ count: String(await ctx.db.visitCount()) }),
14
- notes: async (ctx) => rows(await ctx.db.notes()),
14
+ visits: row(async (ctx) => ({ count: String(await ctx.db.visitCount()) })),
15
+ notes: list((ctx) => ctx.db.notes()),
15
16
  };
16
17
  ```
17
18
 
18
- Loadbare assumes a query's results are always one tuple. We tell it that
19
- we are returning a set of rows by wrapping the query in `rows()`.
19
+ Every query says which shape it answers with. `row()` answers with one row
20
+ and `list()` answers with a set of them, and a name never answers with both.
21
+ A page that needs the same data both ways declares two queries.
20
22
 
21
23
  ## Extending the database
22
24
 
@@ -64,35 +66,11 @@ export function openDb() {
64
66
  `write` is now a merge, not a whole-file replace — otherwise saving
65
67
  `notes` would erase `visitCount`.
66
68
 
67
- ## Installing the widget library
68
-
69
- `<lb-list>` is the first widget we take from a library rather than write
70
- ourselves. It ships in `@loadbare/widgets`:
71
-
72
- ```
73
- npm install @loadbare/widgets
74
- ```
75
-
76
- The builder scans `src/` on its own, but it will not go looking through
77
- `node_modules` uninvited. List the package in `src/imports.ts`:
78
-
79
- ```ts
80
- // src/imports.ts
81
- export default ["@loadbare/widgets"];
82
- ```
83
-
84
- That is the whole of it. List the package, not its tags — the builder finds
85
- `<lb-list>` by filename, and bundles only the tags a page actually uses.
86
- [Using Widget Libraries](./090-using-widget-libraries.md) covers the same
87
- step for anyone else's package.
88
-
89
69
  ## A list display with a template
90
70
 
91
- The `<lb-list>` widget will display a list, but when we use that widget
92
- we need to tell it what a list item looks like. We do that by
93
- putting a `<template>` inside of it. The widget itself contains a loop
94
- to display one template per row, and invokes code in the hub `<lb-hub>` to
95
- display bound data values.
71
+ A list scope needs to be told what one row looks like, and we say so with a
72
+ `<template>` inside it. The hub clones that template once per row and fills
73
+ each clone the same way it fills any other scope.
96
74
 
97
75
  > Loadbare does not use Shadow DOM because Shadow DOM is generally obtuse,
98
76
  > interferes with CSS scoping, and makes templates difficult to supply at point
@@ -103,24 +81,23 @@ display bound data values.
103
81
  > Rather than mess with the Shadow DOM, we use Light DOM.
104
82
  > We take advantage of the fact that `<template>`
105
83
  > elements are not rendered, so we drop the `<template>` directly
106
- > into the light DOM, where it's easy to see how `<lb-list>` is
107
- > going to render our list.
84
+ > into the light DOM, where it's easy to see how a row
85
+ > is going to be rendered.
108
86
 
109
- We know already that a DOM subtree can be scoped to a query with
110
- `lb-query="X"`. Now we see the use of `lb-key="id"`, which further
111
- scopes a subtree to a specific row within the query, naming the
112
- primary key field that uniquely identifies the row.
87
+ We know already that a DOM subtree can be scoped to one row with
88
+ `lb-row="X"`. A set of rows uses `lb-list="X"` instead, and adds
89
+ `lb-key="id"`, which names the column that uniquely identifies a row. Each
90
+ row the hub clones is stamped with `lb-key-value`, holding that row's key,
91
+ which is what scopes the row's subtree to one specific row.
113
92
 
114
93
  ```html
115
94
  <!-- src/pages/about.page.html -->
116
95
  <h2>Notes</h2>
117
- <lb-list lb-query="notes">
118
- <ul>
119
- <template lb-key="id">
120
- <li lb-cell="text"></li>
121
- </template>
122
- </ul>
123
- </lb-list>
96
+ <ul lb-list="notes">
97
+ <template lb-key="id">
98
+ <li lb-cell="text"></li>
99
+ </template>
100
+ </ul>
124
101
  ```
125
102
 
126
103
  ## Run it
@@ -8,37 +8,37 @@ 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-insert lb-query="notes">
11
+ <form lb-action="lb-row-insert" lb-list="notes">
12
12
  <input lb-cell="text" placeholder="Write a note" />
13
13
  <button type="submit">Add</button>
14
14
  </form>
15
15
 
16
- <lb-list lb-query="notes">
17
- <ul>
18
- <template lb-key="id">
19
- <li lb-cell="text"></li>
20
- </template>
21
- </ul>
22
- </lb-list>
16
+ <ul lb-list="notes">
17
+ <template lb-key="id">
18
+ <li lb-cell="text"></li>
19
+ </template>
20
+ </ul>
23
21
  ```
24
22
 
25
- `lb-insert` on a `<form>` gathers its `lb-cell`s into a values map and
26
- submits them against `lb-query`. There's no `lb-key` — there's no row
23
+ `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
25
+ Loadbare's reserved ones — every value beginning with `lb-` is — so it
26
+ needs no declaring, and nothing of yours may be called that. There's no `lb-key-value` — there's no row
27
27
  yet.
28
28
 
29
29
  ## Implementing CRUD server-side
30
30
 
31
- Our CRUD operations go into the page's hooks file:
31
+ Our CRUD operations go into the page's requests file:
32
32
 
33
33
  ```ts
34
- // src/pages/about.hooks.ts
34
+ // src/pages/about.requests.ts
35
35
  import { patch } from "@loadbare/app/server";
36
36
 
37
- export const hooks = {
38
- // ...beforeGet and actions unchanged...
37
+ export const requests = {
38
+ // ...onPageEnter and actions unchanged...
39
39
  crud: {
40
40
  notes: {
41
- tupleInsert: {
41
+ rowInsert: {
42
42
  run: async (ctx, { values }) => {
43
43
  const note = await ctx.db.addNote(values.text);
44
44
  return { notes: patch({ rows: [note] }) };
@@ -50,12 +50,12 @@ export const hooks = {
50
50
  };
51
51
  ```
52
52
 
53
- `tupleInsert` is a CRUD operation, declared under `crud` and keyed by
53
+ `rowInsert` is a CRUD operation, declared under `crud` and keyed by
54
54
  query name.
55
55
 
56
56
  Notice that we do not use the `refresh` mechanism, as that would return
57
57
  the entire query which would be wasteful. We instead return a `patch`
58
- with a single new row and the `<lb-list>` element just adds the row.
58
+ with a single new row and the hub just adds the row.
59
59
 
60
60
  ## Extending the database
61
61
 
@@ -6,34 +6,32 @@ Now that we can add notes, we need to be able to delete a note.
6
6
 
7
7
  ```html
8
8
  <!-- src/pages/about.page.html -->
9
- <lb-list lb-query="notes">
10
- <ul>
11
- <template lb-key="id">
12
- <li>
13
- <span lb-cell="text"></span>
14
- <button lb-delete>Delete</button>
15
- </li>
16
- </template>
17
- </ul>
18
- </lb-list>
9
+ <ul lb-list="notes">
10
+ <template lb-key="id">
11
+ <li>
12
+ <span lb-cell="text"></span>
13
+ <button lb-action="lb-row-delete">Delete</button>
14
+ </li>
15
+ </template>
16
+ </ul>
19
17
  ```
20
18
 
21
19
  The `<li>` now has two children, so the text moves onto its own `<span>`.
22
- `lb-delete` needs no form — just the `lb-query` and `lb-key` already in
23
- scope from its ancestors.
20
+ `lb-row-delete` is a reserved name, so it needs no form and no declaring — just
21
+ the `lb-list` and `lb-key-value` already in scope from its ancestors.
24
22
 
25
23
  ## Implementing delete server-side
26
24
 
27
25
  ```ts
28
- // src/pages/about.hooks.ts
26
+ // src/pages/about.requests.ts
29
27
  import { patch } from "@loadbare/app/server";
30
28
 
31
- export const hooks = {
32
- // ...beforeGet and actions unchanged...
29
+ export const requests = {
30
+ // ...onPageEnter and actions unchanged...
33
31
  crud: {
34
32
  notes: {
35
- // ...tupleInsert unchanged...
36
- tupleDelete: {
33
+ // ...rowInsert unchanged...
34
+ rowDelete: {
37
35
  run: async (ctx, { key }) => {
38
36
  await ctx.db.deleteNote(key);
39
37
  return { notes: patch({ drop: [key] }) };
@@ -6,37 +6,35 @@ Now we edit a row in place, instead of removing and re-adding it.
6
6
 
7
7
  ```html
8
8
  <!-- src/pages/about.page.html -->
9
- <lb-list lb-query="notes">
10
- <ul>
11
- <template lb-key="id">
12
- <li>
13
- <form lb-update>
14
- <input lb-cell="text" />
15
- <button type="submit">Save</button>
16
- </form>
17
- <button lb-delete>Delete</button>
18
- </li>
19
- </template>
20
- </ul>
21
- </lb-list>
9
+ <ul lb-list="notes">
10
+ <template lb-key="id">
11
+ <li>
12
+ <form lb-action="lb-row-update">
13
+ <input lb-cell="text" />
14
+ <button type="submit">Save</button>
15
+ </form>
16
+ <button lb-action="lb-row-delete">Delete</button>
17
+ </li>
18
+ </template>
19
+ </ul>
22
20
  ```
23
21
 
24
- `lb-update` gathers its `lb-cell`s the same way `lb-insert` does, but
25
- also reads `lb-key` from the row it's inside — the same ancestor
26
- `lb-delete` already reads.
22
+ `lb-row-update` gathers its `lb-cell`s the same way `lb-row-insert` does, but
23
+ also reads `lb-key-value` from the row it's inside — the same ancestor
24
+ `lb-row-delete` already reads.
27
25
 
28
26
  ## Implementing update server-side
29
27
 
30
28
  ```ts
31
- // src/pages/about.hooks.ts
29
+ // src/pages/about.requests.ts
32
30
  import { patch } from "@loadbare/app/server";
33
31
 
34
- export const hooks = {
35
- // ...beforeGet and actions unchanged...
32
+ export const requests = {
33
+ // ...onPageEnter and actions unchanged...
36
34
  crud: {
37
35
  notes: {
38
- // ...tupleInsert, tupleDelete unchanged...
39
- tupleUpdate: {
36
+ // ...rowInsert, rowDelete unchanged...
37
+ rowUpdate: {
40
38
  run: async (ctx, { key, values }) => {
41
39
  const note = await ctx.db.updateNote(key, values.text);
42
40
  return { notes: patch({ rows: [note] }) };
@@ -50,7 +48,7 @@ export const hooks = {
50
48
 
51
49
  With more than one note, `key` is what says which row this request
52
50
  means. `patch({ rows: [note] })` looks like insert's response, but since
53
- this id already exists, `<lb-list>` updates that row instead of adding
51
+ this id already exists, the hub updates that row instead of adding
54
52
  one.
55
53
 
56
54
  ## Extending the database
@@ -24,8 +24,8 @@ how Loadbare internals work.
24
24
  // src/note-input.browser.ts
25
25
  import {
26
26
  ATTR_CELL,
27
- ATTR_KEY,
28
- ATTR_QUERY,
27
+ ATTR_KEY_VALUE,
28
+ ATTR_LIST,
29
29
  ATTR_VALUE,
30
30
  LB_EVENT_NAME,
31
31
  } from "@loadbare/app/constants";
@@ -42,14 +42,20 @@ class NoteInput extends HTMLElement {
42
42
  this.addEventListener("change", () => {
43
43
  const input = this.querySelector("input");
44
44
  if (!input) return;
45
- const query = this.closest(`[${ATTR_QUERY}]`)?.getAttribute(ATTR_QUERY);
46
- const key = this.closest(`[${ATTR_KEY}]`)?.getAttribute(ATTR_KEY);
45
+ const list = this.closest(`[${ATTR_LIST}]`)?.getAttribute(ATTR_LIST);
46
+ const key = this.closest(`[${ATTR_KEY_VALUE}]`)?.getAttribute(ATTR_KEY_VALUE);
47
47
  const cell = this.getAttribute(ATTR_CELL);
48
- if (!query || !key || !cell) return;
48
+ if (!list || !key || !cell) return;
49
49
  this.dispatchEvent(
50
50
  new CustomEvent(LB_EVENT_NAME, {
51
51
  bubbles: true,
52
- detail: { op: "cell-change", query, key, cell, value: input.value },
52
+ detail: {
53
+ action: "lb-cell-change",
54
+ list,
55
+ key,
56
+ cell,
57
+ value: input.value,
58
+ },
53
59
  }),
54
60
  );
55
61
  });
@@ -61,39 +67,37 @@ customElements.define("note-input", NoteInput);
61
67
 
62
68
  A widget with both a `.html` and a `.browser.ts` file shares one tag name; the
63
69
  build finds each half independently. On change, it reads its own
64
- `lb-query`/`lb-key`/`lb-cell` coordinates and dispatches a `cell-change`
65
- request itself, the way `lb-update` does for a whole form.
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.
66
72
 
67
73
  ## Using it
68
74
 
69
75
  ```html
70
76
  <!-- src/pages/about.page.html -->
71
- <lb-list lb-query="notes">
72
- <ul>
73
- <template lb-key="id">
74
- <li>
75
- <note-input lb-cell="text"></note-input>
76
- <button lb-delete>Delete</button>
77
- </li>
78
- </template>
79
- </ul>
80
- </lb-list>
77
+ <ul lb-list="notes">
78
+ <template lb-key="id">
79
+ <li>
80
+ <note-input lb-cell="text"></note-input>
81
+ <button lb-action="lb-row-delete">Delete</button>
82
+ </li>
83
+ </template>
84
+ </ul>
81
85
  ```
82
86
 
83
- The `<form lb-update>` and Save button are gone — `note-input` commits on
87
+ The `lb-row-update` form and Save button are gone — `note-input` commits on
84
88
  every change, so there's nothing left to batch.
85
89
 
86
90
  ## Answering it server-side
87
91
 
88
92
  ```ts
89
- // src/pages/about.hooks.ts
93
+ // src/pages/about.requests.ts
90
94
  import { patch } from "@loadbare/app/server";
91
95
 
92
- export const hooks = {
93
- // ...beforeGet and actions unchanged...
96
+ export const requests = {
97
+ // ...onPageEnter and actions unchanged...
94
98
  crud: {
95
99
  notes: {
96
- // ...tupleInsert, tupleDelete, tupleUpdate unchanged...
100
+ // ...rowInsert, rowDelete, rowUpdate unchanged...
97
101
  cellChange: {
98
102
  run: async (ctx, { key, value }) => {
99
103
  const note = await ctx.db.updateNote(key, value);
@@ -46,7 +46,7 @@ regardless of where the script came from:
46
46
 
47
47
  ```html
48
48
  <!-- src/pages/index.page.html -->
49
- <div lb-query="status">
49
+ <div lb-row="status">
50
50
  <lb-select lb-cell="state">
51
51
  <option value="open">Open</option>
52
52
  <option value="closed">Closed</option>
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.5.6",
4
+ "version": "0.6.0",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",
@@ -18,7 +18,6 @@
18
18
  ".": "./dist/hub/lb-hub.browser.js",
19
19
  "./constants": "./dist/core/lb-constants.js",
20
20
  "./types": "./dist/core/lb-types.js",
21
- "./rows": "./dist/hub/lb-rows.js",
22
21
  "./server": "./dist/server/lb-server.js",
23
22
  "./express": "./dist/server/lb-express.js",
24
23
  "./build": "./dist/build/elements.js"
@@ -1,18 +0,0 @@
1
- import type { Projection, QueryResult } from "../core/lb-types";
2
- /**
3
- * Where a row belongs, called with the row detached on its first appearance.
4
- *
5
- * The default puts it immediately before the template, so rows accumulate in
6
- * the order they arrive and the template stays put as the insertion marker.
7
- */
8
- export type Place = (row: Element, tuple: QueryResult, template: HTMLTemplateElement) => void;
9
- /**
10
- * Land a projection in a widget's subtree.
11
- *
12
- * `rows` is the whole set, so it decides membership and order: every row is
13
- * placed in the order given, and a row whose key did not arrive is gone.
14
- * `patch` disturbs only what it names — a row it did not mention keeps its
15
- * contents and its position.
16
- */
17
- export declare function applyRows(scope: Element, result: Projection, place?: Place): void;
18
- //# sourceMappingURL=lb-rows.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lb-rows.d.ts","sourceRoot":"","sources":["../../hub/lb-rows.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGhE;;;;;GAKG;AACH,MAAM,MAAM,KAAK,GAAG,CAClB,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,mBAAmB,KAC1B,IAAI,CAAC;AAsCV;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,UAAU,EAClB,KAAK,GAAE,KAAc,GACpB,IAAI,CAkDN"}