@loadbare/widgets 0.4.0 → 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 (54) hide show
  1. package/README.md +9 -5
  2. package/dist/control.d.ts +54 -0
  3. package/dist/control.d.ts.map +1 -0
  4. package/dist/control.js +100 -0
  5. package/dist/control.js.map +1 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +3 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/lb-confirm.browser.d.ts +2 -0
  11. package/dist/lb-confirm.browser.d.ts.map +1 -0
  12. package/dist/lb-confirm.browser.js +36 -0
  13. package/dist/lb-confirm.browser.js.map +1 -0
  14. package/dist/lb-confirm.html +27 -0
  15. package/dist/lb-form.browser.d.ts +2 -0
  16. package/dist/lb-form.browser.d.ts.map +1 -0
  17. package/dist/lb-form.browser.js +28 -0
  18. package/dist/lb-form.browser.js.map +1 -0
  19. package/dist/lb-form.html +7 -0
  20. package/dist/lb-input.browser.js +9 -37
  21. package/dist/lb-input.browser.js.map +1 -1
  22. package/dist/lb-input.html +1 -1
  23. package/dist/lb-insert-dialog.browser.d.ts +2 -0
  24. package/dist/lb-insert-dialog.browser.d.ts.map +1 -0
  25. package/dist/lb-insert-dialog.browser.js +59 -0
  26. package/dist/lb-insert-dialog.browser.js.map +1 -0
  27. package/dist/lb-insert-dialog.html +25 -0
  28. package/dist/lb-master.html +51 -0
  29. package/dist/lb-options.browser.d.ts +38 -11
  30. package/dist/lb-options.browser.d.ts.map +1 -1
  31. package/dist/lb-options.browser.js +50 -80
  32. package/dist/lb-options.browser.js.map +1 -1
  33. package/dist/lb-options.html +1 -1
  34. package/dist/lb-picker.browser.js +9 -11
  35. package/dist/lb-picker.browser.js.map +1 -1
  36. package/dist/lb-picker.html +2 -2
  37. package/dist/lb-select.browser.js +7 -39
  38. package/dist/lb-select.browser.js.map +1 -1
  39. package/dist/lb-select.html +1 -1
  40. package/dist/lb-table.browser.js +63 -90
  41. package/dist/lb-table.browser.js.map +1 -1
  42. package/dist/lb-table.html +4 -3
  43. package/dist/lb-unknown-page.html +2 -4
  44. package/docs/lb-confirm.md +45 -0
  45. package/docs/lb-form.md +33 -0
  46. package/docs/lb-input.md +42 -40
  47. package/docs/lb-insert-dialog.md +50 -0
  48. package/docs/lb-master.md +53 -0
  49. package/docs/lb-options.md +54 -34
  50. package/docs/lb-picker.md +24 -24
  51. package/docs/lb-select.md +37 -19
  52. package/docs/lb-table.md +63 -29
  53. package/docs/lb-unknown-page.md +11 -13
  54. package/package.json +4 -4
@@ -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":"AAAA,2BAA2B;AAE3B,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,GAChB,MAAM,yBAAyB,CAAC;AAGjC;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,KAAK,GAAG,YAAY,CAAC;AAE3B,iFAAiF;AACjF,IAAI,UAAU,GAAG,CAAC,CAAC;AAEnB,MAAM,OAAQ,SAAQ,WAAW;IAC/B;;;;;;OAMG;IACH;QACE,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,CAAC,CAAC,EAAE,EAAE;YAC9C,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAI,CAAiC,CAAC,MAAM,CAAC;YACrE,IAAI,KAAK,IAAI,OAAO,CAAC,IAAI,KAAK,kBAAkB;gBAAE,OAAO;YACzD,MAAM,GAAG,GAAI,CAAC,CAAC,MAAkB,CAAC,OAAO,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;YACxD,IAAI,GAAG;gBAAE,YAAY,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;QACtC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,iBAAiB;QACf,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,KAAK,GAAG,CAAC,CAAC;QACpD,IAAI,CAAC,KAAK,EAAE,iBAAiB,IAAI,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO;QAE1D,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,EAAE,GAAG,aAAa,EAAE,UAAU,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAClB,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,gBAAgB,CAAC,IAAI,WAAW,WAAW,CAAC,EAAE,CAAC;YACpE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,uEAAuE;IACvE,YAAY;QACV,IAAI,CAAC,aAAa,CAChB,IAAI,kBAAkB,KAAK,iBAAiB,MAAM;YAChD,IAAI,kBAAkB,KAAK,eAAe,IAAI,CACjD,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,yDAAyD;IACjD,SAAS;QACf,OAAO,IAAI,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;IAC7C,CAAC;CACF;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,GAAY;IAChC,OAAO;QACL,GAAG,GAAG,CAAC,gBAAgB,CACrB,8CAA8C,CAC/C;KACF,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,eAAe,EAAE,EAAE,KAAK,KAAK,CAAC,CAAC;AAC/E,CAAC;AAED,cAAc,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC","sourcesContent":["/// <reference lib=\"dom\" />\n\nimport {\n ATTR_COLUMN,\n ATTR_ROW_REQUESTED,\n LB_DONE_EVENT_NAME,\n REQUEST_ROW_INSERT,\n REQUESTED_CREATED,\n REQUESTED_MOVED,\n} from \"@loadbare/app/constants\";\nimport type { HubRequestDone, RowsHost } from \"@loadbare/app/types\";\n\n/**\n * A table that owns its own scaffolding.\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 * Placement and groups are the hub's, by the query's order and the page's\n * group templates. What this widget adds at run time is the ghost row, and\n * scrolling to the row the page's own request created or moved.\n *\n * The `foot` destination needs no code here. A `<tfoot>` is a place on the\n * page, not a row the hub delivers, so what lands in it is an aggregate or a\n * `row` query of its own, named on its `<tr>`.\n */\n\n/**\n * The ghost row's `<tbody>`, a blank row for entering a new one, between the\n * rows and the footer. The page writes it as the `ghost` template, so it\n * ships in the document like the heading and the footer, and the hub lands\n * on whatever in it names a query. It is not a live row: the hub never\n * reconciles it and never counts it.\n *\n * A `<form>` cannot wrap a `<tr>`, so the widget makes one outside the table\n * and joins the row's controls and buttons to it with the HTML `form`\n * attribute. Sending is the page's: a button in the row carrying\n * `lb-row-insert` submits that form, and the hub gathers what the form owns.\n * The hub resets the form once the insert succeeds.\n */\nconst GHOST = \"data-ghost\";\n\n/** Ghost row forms need ids for the `form` attribute; any unique one will do. */\nlet ghostForms = 0;\n\nclass LbTable extends HTMLElement implements RowsHost {\n /**\n * A ghost row is for entering the next row, so once one is saved the\n * cursor goes back to the start of it. `lb-request-done` comes after the\n * response items have landed, so nothing the hub places afterwards takes\n * the focus away again. A refused insert leaves the cursor where the user\n * left it.\n */\n constructor() {\n super();\n this.addEventListener(LB_DONE_EVENT_NAME, (e) => {\n const { request, error } = (e as CustomEvent<HubRequestDone>).detail;\n if (error || request.name !== REQUEST_ROW_INSERT) return;\n const row = (e.target as Element).closest(`[${GHOST}]`);\n if (row) firstControl(row)?.focus();\n });\n }\n\n connectedCallback(): void {\n const ghost = this.querySelector(`tbody[${GHOST}]`);\n if (!ghost?.firstElementChild || this.ghostForm()) return;\n\n const form = document.createElement(\"form\");\n form.id = `ghost-row-${++ghostForms}`;\n form.hidden = true;\n this.append(form);\n for (const el of ghost.querySelectorAll(`[${ATTR_COLUMN}], button`)) {\n el.setAttribute(\"form\", form.id);\n }\n }\n\n /** The row this page's request created or moved, brought into view. */\n lbRowsLanded(): void {\n this.querySelector(\n `[${ATTR_ROW_REQUESTED}=\"${REQUESTED_CREATED}\"], ` +\n `[${ATTR_ROW_REQUESTED}=\"${REQUESTED_MOVED}\"]`,\n )?.scrollIntoView({ block: \"nearest\" });\n }\n\n /** The ghost row's form, once this table has made it. */\n private ghostForm(): HTMLFormElement | null {\n return this.querySelector(\":scope > form\");\n }\n}\n\n/**\n * The first control in a row the user can type into or choose from. A\n * control a widget wraps is found inside it, since the wrapper takes no focus.\n */\nfunction firstControl(row: Element): HTMLElement | undefined {\n return [\n ...row.querySelectorAll<HTMLElement>(\n 'input:not([type=\"hidden\"]), select, textarea',\n ),\n ].find((el) => !el.matches(\":disabled\") && el.checkVisibility?.() !== false);\n}\n\ncustomElements.define(\"lb-table\", LbTable);\n"]}
@@ -2,7 +2,8 @@
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
+ <tbody data-ghost lb-exp-template="ghost"></tbody>
8
+ <tfoot lb-exp-template="foot"></tfoot>
8
9
  </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>
@@ -0,0 +1,45 @@
1
+ # `lb-confirm`
2
+
3
+ > Status: a barebones starter. Iterative use will drive improvements.
4
+
5
+ A button that asks before it sends. The button opens a dialog holding the
6
+ message the page writes inside the tag, a Cancel button, and a confirm button
7
+ carrying the request.
8
+
9
+ Requires `exp-label` and `exp-request`.
10
+
11
+ ```html
12
+ <template>
13
+ <tr>
14
+ <td lb-column="name"></td>
15
+ <td>
16
+ <lb-confirm exp-label="Delete" exp-request="lb-row-delete">
17
+ Delete <b lb-column="name"></b>?
18
+ </lb-confirm>
19
+ </td>
20
+ </tr>
21
+ </template>
22
+ ```
23
+
24
+ ## Expansion Attributes
25
+
26
+ | Parameter | Fills |
27
+ | ------------- | --------------------------------------------- |
28
+ | `exp-label` | the text of the button that opens the dialog |
29
+ | `exp-request` | the confirm button's `lb-request` |
30
+ | `exp-confirm` | the confirm button's text, `Confirm` when absent |
31
+
32
+ ## The request
33
+
34
+ The request is on the dialog's confirm button, never on the tag, so nothing
35
+ reaches the hub until the user says yes. The dialog sits in the row the tag
36
+ sits in, so the request carries that row's query and key, and the message
37
+ can show the row's columns with `lb-column`.
38
+
39
+ Cancel comes first and takes focus, so Enter on a dialog opened by accident
40
+ sends nothing. The widget closes the dialog when `lb-request-done` says the
41
+ request succeeded. A refused request leaves it open, with the hub's
42
+ `lb-request-error` on the confirm button.
43
+
44
+ The dialog is found by its place in the tag, not by id, so the tag works in a
45
+ row template, where the hub clones it once per row.
@@ -0,0 +1,33 @@
1
+ # `lb-form`
2
+
3
+ > Status: a barebones starter. Iterative use will drive improvements.
4
+
5
+ A `<form>` over one row's controls, in one of two modes the row decides.
6
+
7
+ Requires `exp-id`.
8
+
9
+ ```html
10
+ <section lb-query="account">
11
+ <lb-form exp-id="account-form">
12
+ <lb-input lb-column="name" exp-label="Name" lb-request="lb-row-update"></lb-input>
13
+ </lb-form>
14
+ <button type="submit" form="account-form" lb-request="lb-row-insert">Save new</button>
15
+ </section>
16
+ ```
17
+
18
+ ## Expansion Attributes
19
+
20
+ | Parameter | Fills |
21
+ | --------- | ------------------ |
22
+ | `exp-id` | the form's `id` |
23
+
24
+ ## Two modes
25
+
26
+ A row with a key is on record, and each control saves its own column on
27
+ `change` with `lb-request="lb-row-update"`. A row whose key is empty is a new
28
+ one not yet entered: there is nothing to update, so the widget stops an
29
+ `lb-row-update` that carries an empty key, and the controls only hold what a
30
+ submit will gather.
31
+
32
+ A button that acts on the row as a whole, such as one adding it to the set,
33
+ belongs to whatever holds the set, and reaches the form by `form="<id>"`.
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,58 @@ 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-type` | the input's `type`, `text` when absent |
29
+ | `exp-readonly` | the input's `readonly` attribute |
28
30
 
29
- Any value on `exp-readonly` makes the input readonly. Leaving the parameter
31
+ Any value on `exp-readonly` makes the input readonly. Leaving the parameter
30
32
  off drops the attribute.
31
33
 
34
+ ## Attributes
32
35
 
33
- ## Behavioral Attributes
36
+ | Attribute | Names |
37
+ | ------------ | ----------------------------------------- |
38
+ | `lb-column` | the column the input shows and gathers to |
39
+ | `lb-request` | the request a change issues |
34
40
 
35
- | Attribute | Names |
36
- |-------------|----------------------------------------------------|
37
- | `lb-action` | what a change sends; nothing is sent without it |
41
+ ## The control
38
42
 
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.
43
+ `lb-input` is a form-associated custom element. Its `value` property gets and
44
+ sets the value of the `<input>` inside it, and it fires `change` on itself when
45
+ that `<input>` fires `change`. A form reset returns the `<input>` to its
46
+ default.
43
47
 
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 |
48
+ A value that lands on a live row before the row's elements upgrade reaches the
49
+ widget as `lb-column-value`, and the widget takes it up when it first
50
+ connects.
51
51
 
52
52
  ## Inside a form
53
53
 
54
54
  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.
55
+ `lb-request`. The hub gathers every `lb-column` whose form owner is the form
56
+ on submit and sends one request for all of them.
58
57
 
59
58
  ```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>
59
+ <form lb-query="people" lb-request="lb-row-insert">
60
+ <lb-input lb-column="name" exp-label="Name"></lb-input>
61
+ <lb-input lb-column="email" exp-label="Email"></lb-input>
62
+ <button type="submit">Add</button>
64
63
  </form>
65
64
  ```
66
65
 
67
- ## Editing
66
+ ## Editing one column
68
67
 
69
- By default an `lb-input` does not notify the hub when its value changes, as shown
70
- in the form example above.
68
+ With `lb-request`, the `lb-input` commits on `change`. The hub gathers its
69
+ value alone, so `lb-request="lb-row-update"` sends an `lb-row-update` for the
70
+ query and key of the row around it, with `values` holding that one column.
71
+ Any other request name sends that request with the same `query`, `key` and
72
+ `values`.
71
73
 
72
- To send on change, add `lb-action`. The hub catches the event and sends the
73
- request to the server.
74
+ Inside `lb-query="lb-url"`, `lb-request="lb-row-update"` writes the column as a
75
+ query parm, and the hub answers with no round trip.
74
76
 
75
77
  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.
78
+ `lb-request-pending`, and on failure with `lb-request-error`. Both are there
79
+ for a stylesheet to read.
@@ -0,0 +1,50 @@
1
+ # `lb-insert-dialog`
2
+
3
+ > Status: a barebones starter. Iterative use will drive improvements.
4
+
5
+ A row entered in a modal dialog, for a set being chosen from elsewhere on the
6
+ page, such as creating an account while a picker chooses one. The page
7
+ writes the controls inside the tag; the dialog supplies the form, Cancel and
8
+ Save.
9
+
10
+ Requires `exp-id`, `exp-title` and `exp-query`.
11
+
12
+ ```html
13
+ <lb-options lb-query="accounts" lb-column="account_id" exp-label="Account">
14
+ <template><option lb-column="name"></option></template>
15
+ </lb-options>
16
+ <button type="button" commandfor="new-account" command="show-modal">New</button>
17
+
18
+ <lb-insert-dialog exp-id="new-account" exp-title="New account" exp-query="accounts">
19
+ <lb-input lb-column="name" exp-label="Name"></lb-input>
20
+ </lb-insert-dialog>
21
+ ```
22
+
23
+ ## Expansion Attributes
24
+
25
+ | Parameter | Fills |
26
+ | ----------- | -------------------------------------------- |
27
+ | `exp-id` | the dialog's `id`, which buttons open |
28
+ | `exp-title` | the dialog's heading |
29
+ | `exp-query` | the `lb-query` of the form, which it inserts into |
30
+ | `exp-save` | the Save button's text, `Save` when absent |
31
+
32
+ ## Where it goes
33
+
34
+ Write it once, outside every row and every form, and open it with HTML's own
35
+ `commandfor` and `command="show-modal"` from as many buttons as need it. A
36
+ form inside another form is dropped by the parser, and `lb-table` joins every
37
+ control in a ghost row to that row's form, so the dialog cannot sit beside
38
+ the control it serves.
39
+
40
+ ## What it does
41
+
42
+ Save is the form's `lb-row-insert`. When `lb-request-done` says it
43
+ succeeded, the widget chooses the new row in the nearest `lb-options` around
44
+ the button that opened the dialog, fires `change` on it, and closes the
45
+ dialog. The new key comes from the patch the insert answers with, so the
46
+ handler answers with the row it created. A picker that saves its own column
47
+ saves it; one in a ghost row or a new record holds it until Save.
48
+
49
+ A refused insert leaves the dialog open to correct. Cancel and Escape close
50
+ it, and closing resets the form.
@@ -0,0 +1,53 @@
1
+ # `lb-master`
2
+
3
+ > Status: a barebones starter. Iterative use will drive improvements.
4
+
5
+ A set on one side, one of its rows on the other, and the buttons that act on
6
+ the set. A definition only, with no script of its own: it is built from
7
+ [`lb-form`](./lb-form.md) and [`lb-confirm`](./lb-confirm.md).
8
+
9
+ Requires `exp-row`.
10
+
11
+ ```html
12
+ <lb-master exp-row="account">
13
+ <template lb-exp-template="picker">
14
+ <div lb-query="lb-url">
15
+ <lb-picker lb-query="accounts" lb-column="acct" lb-request="lb-row-update"
16
+ exp-label="Account" exp-column="name"></lb-picker>
17
+ </div>
18
+ </template>
19
+ <lb-input lb-column="name" exp-label="Name" lb-request="lb-row-update"></lb-input>
20
+ <template lb-exp-template="delete-message">
21
+ Delete <b lb-column="name"></b>?
22
+ </template>
23
+ </lb-master>
24
+ ```
25
+
26
+ ## Expansion Attributes
27
+
28
+ | Parameter | Fills |
29
+ | -------------- | ---------------------------------------------- |
30
+ | `exp-row` | the `lb-query` of the row the form shows |
31
+ | `exp-form-id` | the form's `id`, `master-form` when absent |
32
+
33
+ | Template | Fills |
34
+ | ------------------------------------------- | ----------------------------- |
35
+ | `<template lb-exp-template="picker">` | the side that chooses the row |
36
+ | `<template lb-exp-template="delete-message">` | the question Delete asks |
37
+
38
+ ## The buttons
39
+
40
+ | Button | Shows | Does |
41
+ | -------- | ----------------- | --------------------------------------------- |
42
+ | Copy | A row on record | Sends `copy`, a request the page declares |
43
+ | Delete | A row on record | Asks, then sends `lb-row-delete` |
44
+ | Save new | A new row | Submits the form as `lb-row-insert`, and pushes a history entry |
45
+ | Cancel | A new row | Resets the form |
46
+
47
+ Each carries `data-mode`, `on-record` or `new`, and the stylesheet shows each
48
+ only in its mode. A new row is one whose key is empty. New is not a button:
49
+ a button cannot write a query parm, so choosing the picker's blank starts a
50
+ new row.
51
+
52
+ Copy goes to the server rather than filling the form, because a copy is a new
53
+ row in the set, and only the server can bring its children along.
@@ -1,18 +1,27 @@
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. For options
10
+ in groups, write a group template holding an `<optgroup>`.
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 lb-group>
22
+ <optgroup>
23
+ <template><option lb-column="label"></option></template>
24
+ </optgroup>
16
25
  </template>
17
26
  </lb-options>
18
27
  ```
@@ -25,49 +34,60 @@ improvements through iterative use.
25
34
  ## Parameters
26
35
 
27
36
  | Parameter | Fills |
28
- |-------------|----------------------------|
37
+ | ----------- | -------------------------- |
29
38
  | `exp-label` | the visible `<label>` text |
30
39
 
31
- ## Binding
40
+ ## Attributes
32
41
 
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 |
42
+ | Attribute | On | Names |
43
+ | ------------ | ------------------ | ------------------------------------------ |
44
+ | `lb-query` | the element itself | the query whose rows become options |
45
+ | `lb-column` | the element itself | the column the choice shows and gathers to |
46
+ | `lb-request` | the element itself | the request a change issues |
47
+ | `lb-group` | a template holding an `<optgroup>` | a group of options |
48
+ | `lb-column` | the `<option>` | the column the option shows |
41
49
 
42
- The key column gives the option its `value`, so the page names it once.
50
+ The server declares the query's key. The widget copies each option's
51
+ `lb-key-value` to its `value`.
43
52
 
44
53
  ## Grouping
45
54
 
46
- `data-group` names a column whose distinct values become `<optgroup>`s, each
47
- labelled with the value.
55
+ The hub makes one `<optgroup>` per run of the query's order's leading term.
56
+ An `<optgroup>` takes its label from an attribute, which landing does not
57
+ set, so the widget labels each from the `lb-group-value` the hub stamps on
58
+ it: the value the group breaks on.
59
+
60
+ ## The control
61
+
62
+ `lb-options` is a form-associated custom element. Its `value` property gets
63
+ and sets the value of the `<select>` inside it, and it fires `change` on itself
64
+ when that `<select>` fires `change`. A form reset selects the first option.
65
+
66
+ The hub reads the element's own `lb-column` from the row around it, and lands
67
+ the rows of its `lb-query` inside it. A value set before the option it names
68
+ has landed selects that option when the rows land.
48
69
 
49
70
  ## Choosing
50
71
 
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.
72
+ With `lb-request`, the `lb-options` commits on `change`. The hub gathers its
73
+ value alone, the key of the chosen row, and sends the request with the `query`
74
+ and `key` of the row around it. Declare that request on the server, or name
75
+ `lb-row-update`.
54
76
 
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.
77
+ Without `lb-request`, a change commits nothing, and the form or live row around
78
+ the `lb-options` gathers its value.
59
79
 
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.
80
+ Inside `lb-query="lb-url"`, `lb-request="lb-row-update"` writes the chosen key
81
+ as a query parm, and the hub answers with no round trip.
62
82
 
63
83
  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.
84
+ `lb-request-pending`, and on failure with `lb-request-error`. Both are there
85
+ for a stylesheet to read.
66
86
 
67
87
  ## Styling an empty list
68
88
 
69
- `lb-options` carries `lb-row-count` with the number of options showing, for a
70
- stylesheet to read.
89
+ `lb-options` carries `lb-query-row-count` with the number of options showing,
90
+ for a stylesheet to read.
71
91
 
72
92
  For the case where every row is one option and only the column names vary,
73
93
  see [`lb-picker`](./lb-picker.md).
package/docs/lb-picker.md CHANGED
@@ -1,21 +1,23 @@
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 it shows varies. It takes no
5
+ children. Options in groups take a group template, which is `lb-options`
6
+ with the page's own templates.
5
7
 
6
- Apply the `lb-list` attribute to name the query, and `lb-action` to name the
7
- action a change sends.
8
+ Apply the `lb-query` attribute to name the query, `lb-column` to name the
9
+ column the choice shows and gathers to, and `lb-request` to name the request a
10
+ change issues.
8
11
 
9
- Provide `exp-label`, `exp-key`, `exp-cell`, and optionally `exp-group`.
12
+ Provide `exp-label` and `exp-column`.
10
13
 
11
14
  ```html
12
15
  <lb-picker
13
- lb-list="statuses"
16
+ lb-query="statuses"
17
+ lb-column="status_id"
14
18
  exp-label="Status"
15
- exp-key="id"
16
- exp-cell="label"
17
- exp-group="category"
18
- lb-action="setStatus"
19
+ exp-column="label"
20
+ lb-request="setStatus"
19
21
  ></lb-picker>
20
22
  ```
21
23
 
@@ -26,28 +28,26 @@ improvements through iterative use.
26
28
 
27
29
  ## Parameters
28
30
 
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 |
31
+ | Parameter | Fills |
32
+ | ------------ | --------------------------- |
33
+ | `exp-label` | the visible `<label>` text |
34
+ | `exp-column` | the column the option shows |
35
35
 
36
- `exp-key` and `exp-cell` are required. Leaving `exp-group` off leaves the
37
- options ungrouped.
36
+ `exp-column` is required. The server declares the query's key, which
37
+ becomes each option's `value`.
38
38
 
39
- ## Binding
39
+ ## Attributes
40
40
 
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 |
41
+ | Attribute | Names |
42
+ | ------------ | ------------------------------------------ |
43
+ | `lb-query` | the query whose rows become options |
44
+ | `lb-column` | the column the choice shows and gathers to |
45
+ | `lb-request` | the request a change issues |
46
46
 
47
47
  ## Choosing
48
48
 
49
49
  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.
50
+ value, the control's `value`, `lb-query-row-count`, and the request on change.
51
51
 
52
52
  Write `lb-options` and the template instead for a row that needs a second
53
53
  element, or an option built from two columns.