@loadbare/widgets 0.3.2 → 0.5.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.
@@ -1,16 +1,16 @@
1
1
  /// <reference lib="dom" />
2
- import { ATTR_CELL, ATTR_KEY_VALUE } from "@loadbare/app/constants";
2
+ import { ATTR_COLUMN, ATTR_KEY_VALUE } from "@loadbare/app/constants";
3
3
  /**
4
- * A table that owns its own scaffolding, and the case that shows what a list
5
- * widget is for once the hub reconciles lists on its own.
4
+ * A table that owns its own scaffolding, and the case that shows what a
5
+ * widget holding rows is for once the hub reconciles rows on its own.
6
6
  *
7
- * A plain `<table lb-list>` with a row template inside it needs no widget at
7
+ * A plain `<table lb-query>` with a row template inside it needs no widget at
8
8
  * all. This one supplies the table, and the developer writes what only the
9
9
  * page knows: the heading row and the row template. That division is forced
10
10
  * rather than chosen — a `<thead>` written inside a custom element is not
11
11
  * inside a table, so the parser drops the tag and keeps the text. A
12
12
  * `<template>` survives anywhere, so the heading row arrives wrapped and
13
- * expansion unwraps it into the `lb-template` destination beside the slot.
13
+ * expansion unwraps it into the `lb-exp-template` destination beside the slot.
14
14
  *
15
15
  * What it adds at run time is placement, and only placement. `data-sort`
16
16
  * names the column rows are ordered by; `data-group` names the column they
@@ -21,9 +21,9 @@ import { ATTR_CELL, ATTR_KEY_VALUE } from "@loadbare/app/constants";
21
21
  *
22
22
  * The `foot` destination is the head's argument a second time and needs no
23
23
  * code here. A `<tfoot>` is a place on the page, not a row the hub delivers,
24
- * so what lands in it is an ordinary scope carrying its own `lb-row` — a
25
- * grand total is a second query over the same data, and the hub resolves it
26
- * by name like any other. See demo/pages/ledger.html.
24
+ * so what lands in it is an element carrying its own `lb-query` — a grand
25
+ * total is a second query over the same data, of kind `row`, and the hub
26
+ * lands it by name like any other.
27
27
  */
28
28
  /** Named on the row template: the column whose distinct values become sections. */
29
29
  const GROUP_COLUMN = "data-group";
@@ -108,7 +108,7 @@ class LbTable extends HTMLElement {
108
108
  }
109
109
  /** What a live row is showing in one column. */
110
110
  function cellOf(row, column) {
111
- const selector = `[${ATTR_CELL}="${column}"]`;
111
+ const selector = `[${ATTR_COLUMN}="${column}"]`;
112
112
  const el = row.matches(selector) ? row : row.querySelector(selector);
113
113
  return el?.textContent ?? "";
114
114
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lb-table.browser.js","sourceRoot":"","sources":["../widgets/lb-table.browser.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference lib=\"dom\" />\n\nimport { ATTR_CELL, ATTR_KEY_VALUE } from \"@loadbare/app/constants\";\nimport type { ListHost, Row } from \"@loadbare/app/types\";\n\n/**\n * A table that owns its own scaffolding, and the case that shows what a list\n * widget is for once the hub reconciles lists on its own.\n *\n * A plain `<table lb-list>` with a row template inside it needs no widget at\n * all. This one supplies the table, and the developer writes what only the\n * page knows: the heading row and the row template. That division is forced\n * rather than chosen — a `<thead>` written inside a custom element is not\n * inside a table, so the parser drops the tag and keeps the text. A\n * `<template>` survives anywhere, so the heading row arrives wrapped and\n * expansion unwraps it into the `lb-template` destination beside the slot.\n *\n * What it adds at run time is placement, and only placement. `data-sort`\n * names the column rows are ordered by; `data-group` names the column they\n * are sectioned by. Both are this widget's own attributes, in HTML's `data-`\n * namespace rather than the hub's: the hub never reads them, and `lb-` is\n * reserved for what the hub does read. `lb-options` happens to spell its\n * grouping attribute the same way, which is a convenience and not a contract.\n *\n * The `foot` destination is the head's argument a second time and needs no\n * code here. A `<tfoot>` is a place on the page, not a row the hub delivers,\n * so what lands in it is an ordinary scope carrying its own `lb-row` — a\n * grand total is a second query over the same data, and the hub resolves it\n * by name like any other. See demo/pages/ledger.html.\n */\n\n/** Named on the row template: the column whose distinct values become sections. */\nconst GROUP_COLUMN = \"data-group\";\n\n/** Named on the row template: the column rows are ordered by within a section. */\nconst SORT_COLUMN = \"data-sort\";\n\n/**\n * The heading row of a section, carrying the value it stands for.\n *\n * The widget's own scaffolding, derived from rows the hub knows and invisible\n * to the hub itself, exactly as the `<optgroup>` in `lb-options` is. Nothing\n * binds to it, so it carries no `lb-key-value` and no name from the vocabulary.\n */\nconst SECTION_ROW = \"data-section\";\n\nclass LbTable extends HTMLElement implements ListHost {\n /**\n * Group first, then sort within the group.\n *\n * Inserting a row before the first one that sorts after it keeps the\n * section ordered, whatever else is in it — which is what lets a whole set\n * and a single patched row take the same path.\n */\n lbPlaceRow(el: Element, row: Row, template: HTMLTemplateElement): void {\n const groupColumn = template.getAttribute(GROUP_COLUMN);\n const sortColumn = template.getAttribute(SORT_COLUMN);\n const heading = groupColumn\n ? this.headingFor(template, String(row[groupColumn] ?? \"\"))\n : null;\n const { rows, end } = this.section(heading);\n\n const value = sortColumn ? String(row[sortColumn] ?? \"\") : \"\";\n const after = sortColumn\n ? rows.find((other) => other !== el && cellOf(other, sortColumn) > value)\n : undefined;\n template.parentElement!.insertBefore(el, after ?? end);\n }\n\n /** A section is derived, so it goes when its last row does. */\n lbRowsLanded(): void {\n for (const heading of this.querySelectorAll(`tr[${SECTION_ROW}]`)) {\n if (this.section(heading).rows.length === 0) heading.remove();\n }\n }\n\n /**\n * The rows of one section, and the node it ends at.\n *\n * A section runs from its heading to the next heading, or to the template\n * if there is none after it. Without grouping there is one section and it\n * is the whole body — the heading row the developer wrote is skipped along\n * the way, because it carries no key and is therefore not a row.\n */\n private section(heading: Element | null): { rows: Element[]; end: Element } {\n const template = this.querySelector(\"template\")!;\n const rows: Element[] = [];\n let node = heading\n ? heading.nextElementSibling\n : template.parentElement!.firstElementChild;\n while (node && node !== template && !node.hasAttribute(SECTION_ROW)) {\n if (node.hasAttribute(ATTR_KEY_VALUE)) rows.push(node);\n node = node.nextElementSibling;\n }\n return { rows, end: node ?? template };\n }\n\n /** The section with this label, or a new one in front of the template. */\n private headingFor(template: HTMLTemplateElement, label: string): Element {\n const body = template.parentElement!;\n for (const heading of body.querySelectorAll(`tr[${SECTION_ROW}]`)) {\n if (heading.getAttribute(SECTION_ROW) === label) return heading;\n }\n const heading = document.createElement(\"tr\");\n heading.setAttribute(SECTION_ROW, label);\n const cell = document.createElement(\"th\");\n cell.setAttribute(\"scope\", \"rowgroup\");\n // How wide the table is, from the row the developer wrote. Nothing\n // else knows, and nothing had to be told twice.\n cell.colSpan = template.content.firstElementChild?.childElementCount ?? 1;\n cell.textContent = label;\n heading.append(cell);\n body.insertBefore(heading, template);\n return heading;\n }\n}\n\n/** What a live row is showing in one column. */\nfunction cellOf(row: Element, column: string): string {\n const selector = `[${ATTR_CELL}=\"${column}\"]`;\n const el = row.matches(selector) ? row : row.querySelector(selector);\n return el?.textContent ?? \"\";\n}\n\ncustomElements.define(\"lb-table\", LbTable);\n"]}
1
+ {"version":3,"file":"lb-table.browser.js","sourceRoot":"","sources":["../widgets/lb-table.browser.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference lib=\"dom\" />\n\nimport { ATTR_COLUMN, ATTR_KEY_VALUE } from \"@loadbare/app/constants\";\nimport type { Row, RowsHost } from \"@loadbare/app/types\";\n\n/**\n * A table that owns its own scaffolding, and the case that shows what a\n * widget holding rows is for once the hub reconciles rows on its own.\n *\n * A plain `<table lb-query>` with a row template inside it needs no widget at\n * all. This one supplies the table, and the developer writes what only the\n * page knows: the heading row and the row template. That division is forced\n * rather than chosen — a `<thead>` written inside a custom element is not\n * inside a table, so the parser drops the tag and keeps the text. A\n * `<template>` survives anywhere, so the heading row arrives wrapped and\n * expansion unwraps it into the `lb-exp-template` destination beside the slot.\n *\n * What it adds at run time is placement, and only placement. `data-sort`\n * names the column rows are ordered by; `data-group` names the column they\n * are sectioned by. Both are this widget's own attributes, in HTML's `data-`\n * namespace rather than the hub's: the hub never reads them, and `lb-` is\n * reserved for what the hub does read. `lb-options` happens to spell its\n * grouping attribute the same way, which is a convenience and not a contract.\n *\n * The `foot` destination is the head's argument a second time and needs no\n * code here. A `<tfoot>` is a place on the page, not a row the hub delivers,\n * so what lands in it is an element carrying its own `lb-query` — a grand\n * total is a second query over the same data, of kind `row`, and the hub\n * lands it by name like any other.\n */\n\n/** Named on the row template: the column whose distinct values become sections. */\nconst GROUP_COLUMN = \"data-group\";\n\n/** Named on the row template: the column rows are ordered by within a section. */\nconst SORT_COLUMN = \"data-sort\";\n\n/**\n * The heading row of a section, carrying the value it stands for.\n *\n * The widget's own scaffolding, derived from rows the hub knows and invisible\n * to the hub itself, exactly as the `<optgroup>` in `lb-options` is. Nothing\n * binds to it, so it carries no `lb-key-value` and no name from the vocabulary.\n */\nconst SECTION_ROW = \"data-section\";\n\nclass LbTable extends HTMLElement implements RowsHost {\n /**\n * Group first, then sort within the group.\n *\n * Inserting a row before the first one that sorts after it keeps the\n * section ordered, whatever else is in it — which is what lets a whole set\n * and a single patched row take the same path.\n */\n lbPlaceRow(el: Element, row: Row, template: HTMLTemplateElement): void {\n const groupColumn = template.getAttribute(GROUP_COLUMN);\n const sortColumn = template.getAttribute(SORT_COLUMN);\n const heading = groupColumn\n ? this.headingFor(template, String(row[groupColumn] ?? \"\"))\n : null;\n const { rows, end } = this.section(heading);\n\n const value = sortColumn ? String(row[sortColumn] ?? \"\") : \"\";\n const after = sortColumn\n ? rows.find((other) => other !== el && cellOf(other, sortColumn) > value)\n : undefined;\n template.parentElement!.insertBefore(el, after ?? end);\n }\n\n /** A section is derived, so it goes when its last row does. */\n lbRowsLanded(): void {\n for (const heading of this.querySelectorAll(`tr[${SECTION_ROW}]`)) {\n if (this.section(heading).rows.length === 0) heading.remove();\n }\n }\n\n /**\n * The rows of one section, and the node it ends at.\n *\n * A section runs from its heading to the next heading, or to the template\n * if there is none after it. Without grouping there is one section and it\n * is the whole body — the heading row the developer wrote is skipped along\n * the way, because it carries no key and is therefore not a row.\n */\n private section(heading: Element | null): { rows: Element[]; end: Element } {\n const template = this.querySelector(\"template\")!;\n const rows: Element[] = [];\n let node = heading\n ? heading.nextElementSibling\n : template.parentElement!.firstElementChild;\n while (node && node !== template && !node.hasAttribute(SECTION_ROW)) {\n if (node.hasAttribute(ATTR_KEY_VALUE)) rows.push(node);\n node = node.nextElementSibling;\n }\n return { rows, end: node ?? template };\n }\n\n /** The section with this label, or a new one in front of the template. */\n private headingFor(template: HTMLTemplateElement, label: string): Element {\n const body = template.parentElement!;\n for (const heading of body.querySelectorAll(`tr[${SECTION_ROW}]`)) {\n if (heading.getAttribute(SECTION_ROW) === label) return heading;\n }\n const heading = document.createElement(\"tr\");\n heading.setAttribute(SECTION_ROW, label);\n const cell = document.createElement(\"th\");\n cell.setAttribute(\"scope\", \"rowgroup\");\n // How wide the table is, from the row the developer wrote. Nothing\n // else knows, and nothing had to be told twice.\n cell.colSpan = template.content.firstElementChild?.childElementCount ?? 1;\n cell.textContent = label;\n heading.append(cell);\n body.insertBefore(heading, template);\n return heading;\n }\n}\n\n/** What a live row is showing in one column. */\nfunction cellOf(row: Element, column: string): string {\n const selector = `[${ATTR_COLUMN}=\"${column}\"]`;\n const el = row.matches(selector) ? row : row.querySelector(selector);\n return el?.textContent ?? \"\";\n}\n\ncustomElements.define(\"lb-table\", LbTable);\n"]}
@@ -2,7 +2,7 @@
2
2
  <caption>
3
3
  {{caption}}
4
4
  </caption>
5
- <thead lb-template="head"></thead>
6
- <tbody lb-slot></tbody>
7
- <tfoot lb-template="foot"></tfoot>
5
+ <thead lb-exp-template="head"></thead>
6
+ <tbody lb-exp-slot></tbody>
7
+ <tfoot lb-exp-template="foot"></tfoot>
8
8
  </table>
@@ -1,10 +1,8 @@
1
- <dialog lb-unknown-page lb-row="lb-navigation">
1
+ <dialog lb-url-unknown lb-query="lb-url">
2
2
  <p>This page is not in the app.</p>
3
3
  <dl>
4
- <dt>Page</dt>
5
- <dd lb-cell="page-label"></dd>
6
4
  <dt>URL</dt>
7
- <dd lb-cell="page-uri"></dd>
5
+ <dd lb-column="lb-path"></dd>
8
6
  </dl>
9
7
  <form method="dialog"><button>{{button-text | OK}}</button></form>
10
8
  </dialog>
package/docs/lb-input.md CHANGED
@@ -2,16 +2,17 @@
2
2
 
3
3
  > Status: a barebones starter. Iterative use will drive improvements.
4
4
 
5
- A wrapper for HTML Input.
5
+ A control wrapping an HTML `<input>`. The hub sets it from a column and
6
+ gathers it back under the same column, as it does a native `<input>`.
6
7
 
7
- Requires attributes `lb-cell` and `exp-label`.
8
+ Requires attributes `lb-column` and `exp-label`.
8
9
 
9
10
  ```html
10
- <div lb-list="people">
11
- <template lb-key="id">
11
+ <div lb-query="people">
12
+ <template>
12
13
  <div>
13
- <lb-input lb-cell="name" exp-label="Name" lb-action="lb-row-update"></lb-input>
14
- <lb-input lb-cell="email" exp-label="Email" exp-readonly></lb-input>
14
+ <lb-input lb-column="name" exp-label="Name" lb-request="lb-row-update"></lb-input>
15
+ <lb-input lb-column="email" exp-label="Email" exp-readonly></lb-input>
15
16
  </div>
16
17
  </template>
17
18
  </div>
@@ -21,57 +22,57 @@ Requires attributes `lb-cell` and `exp-label`.
21
22
 
22
23
  These are the parameters that control build-time expansion of the final HTML.
23
24
 
24
- | Parameter | Fills |
25
- |-----------------------|--------------------------------------------------|
26
- | `exp-label` | the visible `<label>` text |
27
- | `exp-readonly` | the input's `readonly` attribute |
25
+ | Parameter | Fills |
26
+ | -------------- | -------------------------------- |
27
+ | `exp-label` | the visible `<label>` text |
28
+ | `exp-readonly` | the input's `readonly` attribute |
28
29
 
29
- Any value on `exp-readonly` makes the input readonly. Leaving the parameter
30
+ Any value on `exp-readonly` makes the input readonly. Leaving the parameter
30
31
  off drops the attribute.
31
32
 
33
+ ## Attributes
32
34
 
33
- ## Behavioral Attributes
35
+ | Attribute | Names |
36
+ | ------------ | ----------------------------------------- |
37
+ | `lb-column` | the column the input shows and gathers to |
38
+ | `lb-request` | the request a change issues |
34
39
 
35
- | Attribute | Names |
36
- |-------------|----------------------------------------------------|
37
- | `lb-action` | what a change sends; nothing is sent without it |
40
+ ## The control
38
41
 
39
- `lb-action="lb-row-update"` saves the input's own cell: the hub sends an
40
- `lb-row-update` whose `values` holds that one cell, and the list's `rowUpdate`
41
- answers it the same way it answers a form. Any other name sends that action,
42
- carrying the input's value. The hub adds the binding below to either.
42
+ `lb-input` is a form-associated custom element. Its `value` property gets and
43
+ sets the value of the `<input>` inside it, and it fires `change` on itself when
44
+ that `<input>` fires `change`. A form reset returns the `<input>` to its
45
+ default.
43
46
 
44
- ## Binding
45
-
46
- | Attribute | Read from | Names |
47
- |------------|----------------------|------------------------------|
48
- | `lb-cell` | the element itself | the column the input shows |
49
- | `lb-key-value` | the nearest ancestor | the row |
50
- | `lb-list` | the nearest ancestor | the query |
47
+ A value that lands on a live row before the row's elements upgrade reaches the
48
+ widget as `lb-column-value`, and the widget takes it up when it first
49
+ connects.
51
50
 
52
51
  ## Inside a form
53
52
 
54
53
  An `lb-input` inside an `lb-row-insert` or `lb-row-update` form carries no
55
- `lb-action`. The form reads every `lb-cell` in it on submit and sends one
56
- request for all of them, so an input that also sent its own would write the
57
- same edit twice.
54
+ `lb-request`. The hub gathers every `lb-column` whose form owner is the form
55
+ on submit and sends one request for all of them.
58
56
 
59
57
  ```html
60
- <form lb-action="lb-row-update">
61
- <lb-input lb-cell="name" exp-label="Name"></lb-input>
62
- <lb-input lb-cell="email" exp-label="Email"></lb-input>
63
- <button type="submit">Save</button>
58
+ <form lb-query="people" lb-request="lb-row-insert">
59
+ <lb-input lb-column="name" exp-label="Name"></lb-input>
60
+ <lb-input lb-column="email" exp-label="Email"></lb-input>
61
+ <button type="submit">Add</button>
64
62
  </form>
65
63
  ```
66
64
 
67
- ## Editing
65
+ ## Editing one column
68
66
 
69
- By default an `lb-input` does not notify the hub when its value changes, as shown
70
- in the form example above.
67
+ With `lb-request`, the `lb-input` commits on `change`. The hub gathers its
68
+ value alone, so `lb-request="lb-row-update"` sends an `lb-row-update` for the
69
+ query and key of the row around it, with `values` holding that one column.
70
+ Any other request name sends that request with the same `query`, `key` and
71
+ `values`.
71
72
 
72
- To send on change, add `lb-action`. The hub catches the event and sends the
73
- request to the server.
73
+ Inside `lb-query="lb-url"`, `lb-request="lb-row-update"` writes the column as a
74
+ query parm, and the hub answers with no round trip.
74
75
 
75
76
  While the request is in flight the hub marks the `lb-input` with
76
- `lb-pending`, and on failure with `lb-error`. Both are there for a
77
- stylesheet to read.
77
+ `lb-request-pending`, and on failure with `lb-request-error`. Both are there
78
+ for a stylesheet to read.
@@ -1,18 +1,25 @@
1
1
  # `lb-options`
2
2
 
3
- A wrapper for HTML Select, with the `<option>`s coming from a query.
3
+ A control wrapping an HTML `<select>`, with the `<option>`s coming from a
4
+ query.
4
5
 
5
- Apply the `lb-list` attribute to name the query, and `lb-action` to name the
6
- action a change sends. Write a `<template lb-key="...">` inside the tag, and
7
- apply `lb-cell` to the `<option>` itself — an `<option>` holds text, not
8
- elements. Apply `data-group` to the template to section the options.
6
+ Apply the `lb-query` attribute to name the query, `lb-column` to name the
7
+ column the choice shows and gathers to, and `lb-request` to name the request a
8
+ change issues. Write a `<template>` inside the tag, and apply `lb-column` to
9
+ the `<option>` itself — an `<option>` holds text, not elements. Apply
10
+ `data-group` to the template to section the options.
9
11
 
10
12
  Provide `exp-label`.
11
13
 
12
14
  ```html
13
- <lb-options lb-list="statuses" exp-label="Status" lb-action="setStatus">
14
- <template lb-key="id" data-group="category">
15
- <option lb-cell="label"></option>
15
+ <lb-options
16
+ lb-query="statuses"
17
+ lb-column="status_id"
18
+ exp-label="Status"
19
+ lb-request="setStatus"
20
+ >
21
+ <template data-group="category">
22
+ <option lb-column="label"></option>
16
23
  </template>
17
24
  </lb-options>
18
25
  ```
@@ -25,49 +32,58 @@ improvements through iterative use.
25
32
  ## Parameters
26
33
 
27
34
  | Parameter | Fills |
28
- |-------------|----------------------------|
35
+ | ----------- | -------------------------- |
29
36
  | `exp-label` | the visible `<label>` text |
30
37
 
31
- ## Binding
38
+ ## Attributes
32
39
 
33
- | Attribute | Read from | Names |
34
- |-------------|--------------------|--------------------------------------|
35
- | `lb-list` | the element itself | the query whose rows become options |
36
- | `lb-action` | the element itself | the action a change sends |
37
- | `lb-query-parm` | the element itself | the query parm a change writes |
38
- | `lb-key` | the row template | the column that identifies a row |
39
- | `data-group` | the row template | the column that sections the options |
40
- | `lb-cell` | the `<option>` | the column the option shows |
40
+ | Attribute | On | Names |
41
+ | ------------ | ------------------ | ------------------------------------------ |
42
+ | `lb-query` | the element itself | the query whose rows become options |
43
+ | `lb-column` | the element itself | the column the choice shows and gathers to |
44
+ | `lb-request` | the element itself | the request a change issues |
45
+ | `data-group` | the row template | the column that sections the options |
46
+ | `lb-column` | the `<option>` | the column the option shows |
41
47
 
42
- The key column gives the option its `value`, so the page names it once.
48
+ The server declares the query's key. The widget copies each option's
49
+ `lb-key-value` to its `value`.
43
50
 
44
51
  ## Grouping
45
52
 
46
53
  `data-group` names a column whose distinct values become `<optgroup>`s, each
47
54
  labelled with the value.
48
55
 
56
+ ## The control
57
+
58
+ `lb-options` is a form-associated custom element. Its `value` property gets
59
+ and sets the value of the `<select>` inside it, and it fires `change` on itself
60
+ when that `<select>` fires `change`. A form reset selects the first option.
61
+
62
+ The hub reads the element's own `lb-column` from the row around it, and lands
63
+ the rows of its `lb-query` inside it. A value set before the option it names
64
+ has landed selects that option when the rows land.
65
+
49
66
  ## Choosing
50
67
 
51
- Choosing an option notifies the hub, which fires a request to the server: the
52
- action named by `lb-action`, carrying the key of the chosen row. Declare that
53
- action on the server.
68
+ With `lb-request`, the `lb-options` commits on `change`. The hub gathers its
69
+ value alone, the key of the chosen row, and sends the request with the `query`
70
+ and `key` of the row around it. Declare that request on the server, or name
71
+ `lb-row-update`.
54
72
 
55
- `lb-action` is required, unless the element carries `lb-query-parm`, in
56
- which case the hub writes the chosen key into the URL and lands the URL's
57
- back on the widget, and the widget sends nothing. Without either, a change
58
- does nothing.
73
+ Without `lb-request`, a change commits nothing, and the form or live row around
74
+ the `lb-options` gathers its value.
59
75
 
60
- A value landed on the widget, as `lb-value`, selects the option with that
61
- key, including one that arrives after the value did.
76
+ Inside `lb-query="lb-url"`, `lb-request="lb-row-update"` writes the chosen key
77
+ as a query parm, and the hub answers with no round trip.
62
78
 
63
79
  While the request is in flight the hub marks the `lb-options` with
64
- `lb-pending`, and on failure with `lb-error`. Both are there for a
65
- stylesheet to read.
80
+ `lb-request-pending`, and on failure with `lb-request-error`. Both are there
81
+ for a stylesheet to read.
66
82
 
67
83
  ## Styling an empty list
68
84
 
69
- `lb-options` carries `lb-row-count` with the number of options showing, for a
70
- stylesheet to read.
85
+ `lb-options` carries `lb-query-row-count` with the number of options showing,
86
+ for a stylesheet to read.
71
87
 
72
88
  For the case where every row is one option and only the column names vary,
73
89
  see [`lb-picker`](./lb-picker.md).
package/docs/lb-picker.md CHANGED
@@ -1,21 +1,22 @@
1
1
  # `lb-picker`
2
2
 
3
3
  [`lb-options`](./lb-options.md) with the row template built in, for when every
4
- row is one option and only the column names vary. It takes no children.
4
+ row is one option and only the column names vary. It takes no children.
5
5
 
6
- Apply the `lb-list` attribute to name the query, and `lb-action` to name the
7
- action a change sends.
6
+ Apply the `lb-query` attribute to name the query, `lb-column` to name the
7
+ column the choice shows and gathers to, and `lb-request` to name the request a
8
+ change issues.
8
9
 
9
- Provide `exp-label`, `exp-key`, `exp-cell`, and optionally `exp-group`.
10
+ Provide `exp-label`, `exp-column`, and optionally `exp-group`.
10
11
 
11
12
  ```html
12
13
  <lb-picker
13
- lb-list="statuses"
14
+ lb-query="statuses"
15
+ lb-column="status_id"
14
16
  exp-label="Status"
15
- exp-key="id"
16
- exp-cell="label"
17
+ exp-column="label"
17
18
  exp-group="category"
18
- lb-action="setStatus"
19
+ lb-request="setStatus"
19
20
  ></lb-picker>
20
21
  ```
21
22
 
@@ -26,28 +27,28 @@ improvements through iterative use.
26
27
 
27
28
  ## Parameters
28
29
 
29
- | Parameter | Fills |
30
- |-------------|--------------------------------------------------|
31
- | `exp-label` | the visible `<label>` text |
32
- | `exp-key` | the column that identifies a row |
33
- | `exp-cell` | the column the option shows |
34
- | `exp-group` | the column that sections the options, if grouped |
30
+ | Parameter | Fills |
31
+ | ------------ | ------------------------------------------------ |
32
+ | `exp-label` | the visible `<label>` text |
33
+ | `exp-column` | the column the option shows |
34
+ | `exp-group` | the column that sections the options, if grouped |
35
35
 
36
- `exp-key` and `exp-cell` are required. Leaving `exp-group` off leaves the
37
- options ungrouped.
36
+ `exp-column` is required. Leaving `exp-group` off leaves the options
37
+ ungrouped. The server declares the query's key, which becomes each option's
38
+ `value`.
38
39
 
39
- ## Binding
40
+ ## Attributes
40
41
 
41
- | Attribute | Read from | Names |
42
- |-------------|--------------------|-------------------------------------|
43
- | `lb-list` | the element itself | the query whose rows become options |
44
- | `lb-action` | the element itself | the action a change sends |
45
- | `lb-query-parm` | the element itself | the query parm a change writes |
42
+ | Attribute | Names |
43
+ | ------------ | ------------------------------------------ |
44
+ | `lb-query` | the query whose rows become options |
45
+ | `lb-column` | the column the choice shows and gathers to |
46
+ | `lb-request` | the request a change issues |
46
47
 
47
48
  ## Choosing
48
49
 
49
50
  The same as [`lb-options`](./lb-options.md): grouping, the key as the option's
50
- value, `lb-row-count`, and the action or query parm on change.
51
+ value, the control's `value`, `lb-query-row-count`, and the request on change.
51
52
 
52
53
  Write `lb-options` and the template instead for a row that needs a second
53
54
  element, or an option built from two columns.
package/docs/lb-select.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # `lb-select`
2
2
 
3
- A wrapper for HTML Select, with the `<option>`s written by hand.
3
+ A control wrapping an HTML `<select>`, with the `<option>`s written by hand.
4
4
 
5
- Apply the `lb-cell` attribute to name the column, and `lb-action` to name the
6
- action a change sends.
5
+ Apply the `lb-column` attribute to name the column, and `lb-request` to name the
6
+ request a change issues.
7
7
 
8
8
  Provide `exp-label`.
9
9
 
10
10
  ```html
11
- <lb-select lb-cell="status" exp-label="Status" lb-action="setStatus">
11
+ <lb-select lb-column="status" exp-label="Status" lb-request="setStatus">
12
12
  <option value="open">Open</option>
13
13
  <option value="closed">Closed</option>
14
14
  </lb-select>
@@ -22,30 +22,48 @@ improvements through iterative use.
22
22
  ## Parameters
23
23
 
24
24
  | Parameter | Fills |
25
- |-------------|----------------------------|
25
+ | ----------- | -------------------------- |
26
26
  | `exp-label` | the visible `<label>` text |
27
27
 
28
- ## Binding
28
+ ## Attributes
29
29
 
30
- | Attribute | Read from | Names |
31
- |-------------|--------------------|-----------------------------|
32
- | `lb-cell` | the element itself | the column the select shows |
33
- | `lb-action` | the element itself | the action a change sends |
34
- | `lb-query-parm` | the element itself | the query parm a change writes |
30
+ | Attribute | Names |
31
+ | ------------ | ------------------------------------------ |
32
+ | `lb-column` | the column the select shows and gathers to |
33
+ | `lb-request` | the request a change issues |
34
+
35
+ ## The control
36
+
37
+ `lb-select` is a form-associated custom element. Its `value` property gets
38
+ and sets the value of the `<select>` inside it, and it fires `change` on itself
39
+ when that `<select>` fires `change`. A form reset selects the option marked
40
+ `selected`, or the first option.
35
41
 
36
42
  ## Choosing
37
43
 
38
- Choosing an option notifies the hub, which fires a request to the server: the
39
- action named by `lb-action`, carrying the chosen option's value. Declare that
40
- action on the server.
44
+ With `lb-request`, the `lb-select` commits on `change`. The hub gathers its
45
+ value alone and sends the request with the `query` and `key` of the row around
46
+ it, and `values` holding the chosen option's value under its column. Declare
47
+ that request on the server, or name `lb-row-update`.
48
+
49
+ Without `lb-request`, a change commits nothing, and the form or live row around
50
+ the `lb-select` gathers its value.
41
51
 
42
- `lb-action` is required, unless the select carries `lb-query-parm`, in which
43
- case the hub writes the choice into the URL and the select sends nothing.
44
- Without either, a change does nothing.
52
+ Inside `lb-query="lb-url"`, `lb-request="lb-row-update"` writes the choice as a
53
+ query parm, and the hub answers with no round trip.
54
+
55
+ ```html
56
+ <div lb-query="lb-url">
57
+ <lb-select lb-column="status" exp-label="Status" lb-request="lb-row-update">
58
+ <option value="">All</option>
59
+ <option value="open">Open</option>
60
+ </lb-select>
61
+ </div>
62
+ ```
45
63
 
46
64
  While the request is in flight the hub marks the `lb-select` with
47
- `lb-pending`, and on failure with `lb-error`. Both are there for a
48
- stylesheet to read.
65
+ `lb-request-pending`, and on failure with `lb-request-error`. Both are there
66
+ for a stylesheet to read.
49
67
 
50
68
  For a `<select>` whose options come from a query, see
51
69
  [`lb-options`](./lb-options.md) and [`lb-picker`](./lb-picker.md).
package/docs/lb-table.md CHANGED
@@ -2,24 +2,24 @@
2
2
 
3
3
  A wrapper for HTML Table, supplying the `<table>` itself.
4
4
 
5
- Apply the `lb-list` attribute to name the query. Write three `<template>`s
6
- inside the tag: one marked `lb-template="head"` for the heading row, one
7
- carrying `lb-key` for the row, and optionally one marked `lb-template="foot"`
8
- for the footer. Apply `data-sort` and `data-group` to the row template to order
5
+ Apply the `lb-query` attribute to name the query. Write three `<template>`s
6
+ inside the tag: one marked `lb-exp-template="head"` for the heading row, one
7
+ unmarked for the row, and optionally one marked `lb-exp-template="foot"` for
8
+ the footer. Apply `data-sort` and `data-group` to the row template to order
9
9
  and section the rows.
10
10
 
11
11
  Provide `exp-caption`.
12
12
 
13
13
  ```html
14
- <lb-table lb-list="ledger" exp-caption="Ledger">
15
- <template lb-template="head">
14
+ <lb-table lb-query="ledger" exp-caption="Ledger">
15
+ <template lb-exp-template="head">
16
16
  <tr><th>Date</th><th>Amount</th></tr>
17
17
  </template>
18
- <template lb-key="id" data-sort="date" data-group="month">
19
- <tr><td lb-cell="date"></td><td lb-cell="amount"></td></tr>
18
+ <template data-sort="date" data-group="month">
19
+ <tr><td lb-column="date"></td><td lb-column="amount"></td></tr>
20
20
  </template>
21
- <template lb-template="foot">
22
- <tr lb-row="ledger-total"><td>Total</td><td lb-cell="total"></td></tr>
21
+ <template lb-exp-template="foot">
22
+ <tr lb-query="ledger-total"><td>Total</td><td lb-column="total"></td></tr>
23
23
  </template>
24
24
  </lb-table>
25
25
  ```
@@ -35,35 +35,37 @@ improvements through iterative use.
35
35
  ## Parameters
36
36
 
37
37
  | Parameter | Fills |
38
- |---------------|----------------------|
38
+ | ------------- | -------------------- |
39
39
  | `exp-caption` | the `<caption>` text |
40
40
 
41
- ## Binding
41
+ ## Attributes
42
42
 
43
- | Attribute | Read from | Names |
44
- |------------|----------------------------------|------------------------------------|
45
- | `lb-list` | the element itself | the query whose rows fill the body |
46
- | `lb-key` | the row template | the column that identifies a row |
43
+ | Attribute | On | Names |
44
+ | ------------ | -------------------------------- | ---------------------------------- |
45
+ | `lb-query` | the element itself | the query whose rows fill the body |
47
46
  | `data-sort` | the row template | the column rows are ordered by |
48
47
  | `data-group` | the row template | the column rows are sectioned by |
49
- | `lb-cell` | elements inside the row template | the column each element shows |
48
+ | `lb-column` | elements inside the row template | the column each element shows |
49
+
50
+ The server declares the query's key.
50
51
 
51
52
  ## Sorting and grouping
52
53
 
53
54
  `data-sort` names the column rows are ordered by, comparing the text each row
54
- shows. `data-group` names the column they are sectioned by: each distinct value
55
- gets a heading row of its own, spanning the width of the row template, and
56
- rows sort within their section.
55
+ shows. `data-group` names the column they are sectioned by: each distinct
56
+ value gets a heading row of its own, spanning the width of the row template,
57
+ and rows sort within their section.
57
58
 
58
59
  ## The footer
59
60
 
60
61
  The footer is an ordinary part of the page, not a row from the table's query.
61
- Give it its own `lb-row` — a grand total is a second query, declared on the
62
- server like any other.
62
+ Give it its own `lb-query` — a grand total is a second query of kind `row`,
63
+ declared on the server like any other.
63
64
 
64
65
  ## Styling an empty table
65
66
 
66
- `lb-table` carries `lb-row-count` with the number of rows showing, for a
67
+ `lb-table` carries `lb-query-row-count` with the number of rows showing, for a
67
68
  stylesheet to read.
68
69
 
69
- For a repeater with no table of its own, see [`lb-list`](./lb-list.md).
70
+ For rows with no table of their own, write the row template inside any element
71
+ that carries `lb-query`.