@loadbare/widgets 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/lb-input.browser.js +11 -24
- package/dist/lb-input.browser.js.map +1 -1
- package/dist/lb-options.browser.d.ts +9 -21
- package/dist/lb-options.browser.d.ts.map +1 -1
- package/dist/lb-options.browser.js +30 -21
- package/dist/lb-options.browser.js.map +1 -1
- package/dist/lb-picker.html +1 -1
- package/dist/lb-select.browser.js +5 -8
- package/dist/lb-select.browser.js.map +1 -1
- package/dist/lb-table.browser.js +53 -51
- package/dist/lb-table.browser.js.map +1 -1
- package/dist/lb-unknown-page.html +1 -1
- package/docs/lb-input.md +20 -16
- package/docs/lb-options.md +11 -12
- package/docs/lb-picker.md +4 -4
- package/docs/lb-select.md +1 -1
- package/docs/lb-table.md +13 -13
- package/package.json +5 -4
- package/dist/lb-list.browser.d.ts +0 -2
- package/dist/lb-list.browser.d.ts.map +0 -1
- package/dist/lb-list.browser.js +0 -18
- package/dist/lb-list.browser.js.map +0 -1
- package/dist/lb-list.html +0 -1
- package/docs/lb-list.md +0 -61
package/README.md
CHANGED
|
@@ -8,7 +8,6 @@ Inventory:
|
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| [`lb-input`](docs/lb-input.md) | an editable cell wrapping an `<input>` |
|
|
10
10
|
| [`lb-select`](docs/lb-select.md) | a `<select>` whose options the page writes |
|
|
11
|
-
| [`lb-list`](docs/lb-list.md) | the plain repeater |
|
|
12
11
|
| [`lb-options`](docs/lb-options.md) | a `<select>` whose options come from a query |
|
|
13
12
|
| [`lb-picker`](docs/lb-picker.md) | `lb-options` with the row template built in |
|
|
14
13
|
| [`lb-table`](docs/lb-table.md) | a `<table>` supplying its own scaffolding |
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../widgets/index.ts"],"names":[],"mappings":"AAAA,OAAO,uBAAuB,CAAC;AAC/B,OAAO,wBAAwB,CAAC;AAChC,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../widgets/index.ts"],"names":[],"mappings":"AAAA,OAAO,uBAAuB,CAAC;AAC/B,OAAO,wBAAwB,CAAC;AAChC,OAAO,yBAAyB,CAAC;AACjC,OAAO,uBAAuB,CAAC;AAC/B,OAAO,wBAAwB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../widgets/index.ts"],"names":[],"mappings":"AAAA,OAAO,uBAAuB,CAAC;AAC/B,OAAO,wBAAwB,CAAC;AAChC,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../widgets/index.ts"],"names":[],"mappings":"AAAA,OAAO,uBAAuB,CAAC;AAC/B,OAAO,wBAAwB,CAAC;AAChC,OAAO,yBAAyB,CAAC;AACjC,OAAO,uBAAuB,CAAC;AAC/B,OAAO,wBAAwB,CAAC","sourcesContent":["import \"./lb-input.browser.js\";\nimport \"./lb-select.browser.js\";\nimport \"./lb-options.browser.js\";\nimport \"./lb-table.browser.js\";\nimport \"./lb-picker.browser.js\";\n"]}
|
package/dist/lb-input.browser.js
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
|
-
import {
|
|
3
|
-
/** The page asks for a request. A widget that was not asked stays quiet. */
|
|
4
|
-
const FIRE_ON_CHANGE = "data-fire-on-change";
|
|
2
|
+
import { ATTR_ACTION, ATTR_VALUE, LB_EVENT_NAME, } from "@loadbare/app/constants";
|
|
5
3
|
/**
|
|
6
4
|
* A leaf cell wrapping an <input>. The hub writes one attribute name for
|
|
7
5
|
* every widget type, so the value lands in `lb-value` and the widget
|
|
8
6
|
* forwards it to the control it owns.
|
|
9
7
|
*
|
|
10
|
-
* The other direction of the same cell: `
|
|
11
|
-
*
|
|
12
|
-
* lb-
|
|
8
|
+
* The other direction of the same cell: `lb-action` names what a change
|
|
9
|
+
* sends, carrying the input's value. The hub scopes it like any request, so
|
|
10
|
+
* the reserved `lb-cell-change` reaches the server with the list, key and
|
|
11
|
+
* cell this input sits in. Without `lb-action` a change sends nothing.
|
|
13
12
|
*
|
|
14
|
-
* The
|
|
15
|
-
* and not the only one. An input inside an `lb-insert` or `lb-update`
|
|
16
|
-
* is read again by the form on submit, so a widget that sent on its own
|
|
13
|
+
* The developer has to ask, because committing per field is one editing model
|
|
14
|
+
* and not the only one. An input inside an `lb-row-insert` or `lb-row-update`
|
|
15
|
+
* form is read again by the form on submit, so a widget that sent on its own
|
|
17
16
|
* would write the same edit twice and defeat the batch the form is for.
|
|
18
17
|
*/
|
|
19
18
|
class LbInput extends HTMLElement {
|
|
@@ -28,27 +27,15 @@ class LbInput extends HTMLElement {
|
|
|
28
27
|
}
|
|
29
28
|
connectedCallback() {
|
|
30
29
|
this.addEventListener("change", () => {
|
|
31
|
-
|
|
30
|
+
const action = this.getAttribute(ATTR_ACTION);
|
|
31
|
+
if (!action)
|
|
32
32
|
return;
|
|
33
33
|
const input = this.querySelector("input");
|
|
34
34
|
if (!input) {
|
|
35
35
|
console.error(`lb-input: change with no <input>, ignoring`);
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
|
-
const
|
|
39
|
-
const key = this.closest(`[${ATTR_KEY}]`)?.getAttribute(ATTR_KEY);
|
|
40
|
-
const cell = this.getAttribute(ATTR_CELL);
|
|
41
|
-
if (!query || !key || !cell) {
|
|
42
|
-
console.error(`lb-input: change with no query/key/cell binding, ignoring`);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
const detail = {
|
|
46
|
-
op: "cell-change",
|
|
47
|
-
query,
|
|
48
|
-
key,
|
|
49
|
-
cell,
|
|
50
|
-
value: input.value,
|
|
51
|
-
};
|
|
38
|
+
const detail = { action, value: input.value };
|
|
52
39
|
this.dispatchEvent(new CustomEvent(LB_EVENT_NAME, { bubbles: true, detail }));
|
|
53
40
|
});
|
|
54
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-input.browser.js","sourceRoot":"","sources":["../widgets/lb-input.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"lb-input.browser.js","sourceRoot":"","sources":["../widgets/lb-input.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,OAAO,EACL,WAAW,EACX,UAAU,EACV,aAAa,GACd,MAAM,yBAAyB,CAAC;AAGjC;;;;;;;;;;;;;;GAcG;AAEH,MAAM,OAAQ,SAAQ,WAAW;IAC/B,MAAM,CAAC,kBAAkB,GAAG,CAAC,UAAU,CAAC,CAAC;IAEzC,wBAAwB,CAAC,KAAa,EAAE,IAAY,EAAE,KAAa;QACjE,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;YAC3D,OAAO;QACT,CAAC;QACD,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;IACtB,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE;YACnC,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM;gBAAE,OAAO;YACpB,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;gBAC5D,OAAO;YACT,CAAC;YACD,MAAM,MAAM,GAAe,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;YAC1D,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAC1D,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;;AAGH,cAAc,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC","sourcesContent":["/// <reference lib=\"dom\" />\n\nimport {\n ATTR_ACTION,\n ATTR_VALUE,\n LB_EVENT_NAME,\n} from \"@loadbare/app/constants\";\nimport type { HubRequest } from \"@loadbare/app/types\";\n\n/**\n * A leaf cell wrapping an <input>. The hub writes one attribute name for\n * every widget type, so the value lands in `lb-value` and the widget\n * forwards it to the control it owns.\n *\n * The other direction of the same cell: `lb-action` names what a change\n * sends, carrying the input's value. The hub scopes it like any request, so\n * the reserved `lb-cell-change` reaches the server with the list, key and\n * cell this input sits in. Without `lb-action` a change sends nothing.\n *\n * The developer has to ask, because committing per field is one editing model\n * and not the only one. An input inside an `lb-row-insert` or `lb-row-update`\n * form is read again by the form on submit, so a widget that sent on its own\n * would write the same edit twice and defeat the batch the form is for.\n */\n\nclass LbInput extends HTMLElement {\n static observedAttributes = [ATTR_VALUE];\n\n attributeChangedCallback(_name: string, _old: string, value: string) {\n const input = this.querySelector(\"input\");\n if (!input) {\n console.error(`lb-input: no <input> to receive the value`);\n return;\n }\n input.value = value;\n }\n\n connectedCallback() {\n this.addEventListener(\"change\", () => {\n const action = this.getAttribute(ATTR_ACTION);\n if (!action) return;\n const input = this.querySelector(\"input\");\n if (!input) {\n console.error(`lb-input: change with no <input>, ignoring`);\n return;\n }\n const detail: HubRequest = { action, value: input.value };\n this.dispatchEvent(\n new CustomEvent(LB_EVENT_NAME, { bubbles: true, detail }),\n );\n });\n }\n}\n\ncustomElements.define(\"lb-input\", LbInput);\n"]}
|
|
@@ -1,24 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* headings would read the same `lb-group` and build something else.
|
|
11
|
-
*
|
|
12
|
-
* The identity of a row is the value of the option, so `lb-key` supplies
|
|
13
|
-
* both and the page declares it once.
|
|
14
|
-
*
|
|
15
|
-
* Exported because `lb-picker` is this widget with its row template
|
|
16
|
-
* supplied by the definition instead of by the page. Nothing about the
|
|
17
|
-
* behavior changes, so nothing about the behavior is written twice.
|
|
18
|
-
*/
|
|
19
|
-
export declare class LbOptions extends HTMLElement implements HubRowHost {
|
|
20
|
-
acceptRows(result: Projection): void;
|
|
21
|
-
private place;
|
|
1
|
+
import type { ListHost, Row } from "@loadbare/app/types";
|
|
2
|
+
export declare class LbOptions extends HTMLElement implements ListHost {
|
|
3
|
+
/**
|
|
4
|
+
* The hub reconciles the list and calls this for placement only, so this
|
|
5
|
+
* widget carries no copy of what a row is or when one arrives.
|
|
6
|
+
*/
|
|
7
|
+
lbPlaceRow(el: Element, row: Row, template: HTMLTemplateElement): void;
|
|
8
|
+
/** Everything derived from the rows, once the whole result has landed. */
|
|
9
|
+
lbRowsLanded(): void;
|
|
22
10
|
/** The group with this label, or a new one in front of the template. */
|
|
23
11
|
private groupFor;
|
|
24
12
|
connectedCallback(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-options.browser.d.ts","sourceRoot":"","sources":["../widgets/lb-options.browser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lb-options.browser.d.ts","sourceRoot":"","sources":["../widgets/lb-options.browser.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAc,QAAQ,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAyBrE,qBAAa,SAAU,SAAQ,WAAY,YAAW,QAAQ;IAC5D;;;OAGG;IACH,UAAU,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAUtE,0EAA0E;IAC1E,YAAY,IAAI,IAAI;IAYpB,wEAAwE;IACxE,OAAO,CAAC,QAAQ;IAUhB,iBAAiB;CAmBlB"}
|
|
@@ -1,30 +1,47 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
|
-
import { ATTR_ACTION,
|
|
3
|
-
import { applyRows } from "@loadbare/app/rows";
|
|
2
|
+
import { ATTR_ACTION, ATTR_KEY_VALUE, LB_EVENT_NAME, } from "@loadbare/app/constants";
|
|
4
3
|
/**
|
|
5
|
-
* A `<select>` whose choices come from a
|
|
4
|
+
* A `<select>` whose choices come from a list, and the case that shows why
|
|
6
5
|
* placement belongs to the widget rather than to the framework.
|
|
7
6
|
*
|
|
8
7
|
* The rows arrive flat and keyed. Nothing on the wire knows about
|
|
9
8
|
* `<optgroup>`, and nothing needs to: a group is a column like any other, and
|
|
10
9
|
* turning a repeated column value into an element is exactly the local
|
|
11
|
-
* knowledge a widget has and the hub does not.
|
|
12
|
-
*
|
|
10
|
+
* knowledge a widget has and the hub does not. So the attribute that names
|
|
11
|
+
* the column is this widget's own, in HTML's `data-` namespace rather than
|
|
12
|
+
* the hub's: the hub never reads it, and `lb-` is reserved for what the hub
|
|
13
|
+
* does read.
|
|
13
14
|
*
|
|
14
|
-
* The identity of a row is the value of the option, so `lb-key`
|
|
15
|
-
*
|
|
15
|
+
* The identity of a row is the value of the option, so the `lb-key-value`
|
|
16
|
+
* the hub stamps on each option is copied to its `value`, and the developer
|
|
17
|
+
* names the key column once, with `lb-key` on the template.
|
|
16
18
|
*
|
|
17
19
|
* Exported because `lb-picker` is this widget with its row template
|
|
18
20
|
* supplied by the definition instead of by the page. Nothing about the
|
|
19
21
|
* behavior changes, so nothing about the behavior is written twice.
|
|
20
22
|
*/
|
|
23
|
+
/** Named on the row template: the column whose distinct values become groups. */
|
|
24
|
+
const GROUP_COLUMN = "data-group";
|
|
21
25
|
export class LbOptions extends HTMLElement {
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The hub reconciles the list and calls this for placement only, so this
|
|
28
|
+
* widget carries no copy of what a row is or when one arrives.
|
|
29
|
+
*/
|
|
30
|
+
lbPlaceRow(el, row, template) {
|
|
31
|
+
const select = this.querySelector("select");
|
|
32
|
+
const groupColumn = template.getAttribute(GROUP_COLUMN);
|
|
33
|
+
if (!groupColumn) {
|
|
34
|
+
select.insertBefore(el, template);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
this.groupFor(select, template, String(row[groupColumn] ?? "")).append(el);
|
|
38
|
+
}
|
|
39
|
+
/** Everything derived from the rows, once the whole result has landed. */
|
|
40
|
+
lbRowsLanded() {
|
|
24
41
|
// The identity of the row is the value of the option, so nothing on the
|
|
25
42
|
// page has to say so twice.
|
|
26
|
-
for (const option of this.querySelectorAll(`option[${
|
|
27
|
-
option.setAttribute("value", option.getAttribute(
|
|
43
|
+
for (const option of this.querySelectorAll(`option[${ATTR_KEY_VALUE}]`)) {
|
|
44
|
+
option.setAttribute("value", option.getAttribute(ATTR_KEY_VALUE));
|
|
28
45
|
}
|
|
29
46
|
// A group is derived, so it goes when its last row does.
|
|
30
47
|
for (const group of this.querySelectorAll("optgroup")) {
|
|
@@ -32,15 +49,6 @@ export class LbOptions extends HTMLElement {
|
|
|
32
49
|
group.remove();
|
|
33
50
|
}
|
|
34
51
|
}
|
|
35
|
-
place = (row, tuple, template) => {
|
|
36
|
-
const select = this.querySelector("select");
|
|
37
|
-
const groupCell = template.getAttribute(ATTR_GROUP);
|
|
38
|
-
if (!groupCell) {
|
|
39
|
-
select.insertBefore(row, template);
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
this.groupFor(select, template, tuple[groupCell] ?? "").append(row);
|
|
43
|
-
};
|
|
44
52
|
/** The group with this label, or a new one in front of the template. */
|
|
45
53
|
groupFor(select, template, label) {
|
|
46
54
|
for (const group of select.querySelectorAll("optgroup")) {
|
|
@@ -64,7 +72,8 @@ export class LbOptions extends HTMLElement {
|
|
|
64
72
|
console.error(`lb-options: no ${ATTR_ACTION}, nothing to send`);
|
|
65
73
|
return;
|
|
66
74
|
}
|
|
67
|
-
|
|
75
|
+
// The hub scopes it, so a choice inside a row says which row.
|
|
76
|
+
const detail = { action: name, value: select.value };
|
|
68
77
|
this.dispatchEvent(new CustomEvent(LB_EVENT_NAME, { bubbles: true, detail }));
|
|
69
78
|
});
|
|
70
79
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-options.browser.js","sourceRoot":"","sources":["../widgets/lb-options.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,OAAO,EACL,WAAW,EACX,
|
|
1
|
+
{"version":3,"file":"lb-options.browser.js","sourceRoot":"","sources":["../widgets/lb-options.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,OAAO,EACL,WAAW,EACX,cAAc,EACd,aAAa,GACd,MAAM,yBAAyB,CAAC;AAGjC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,iFAAiF;AACjF,MAAM,YAAY,GAAG,YAAY,CAAC;AAElC,MAAM,OAAO,SAAU,SAAQ,WAAW;IACxC;;;OAGG;IACH,UAAU,CAAC,EAAW,EAAE,GAAQ,EAAE,QAA6B;QAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAE,CAAC;QAC7C,MAAM,WAAW,GAAG,QAAQ,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;QACxD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,CAAC,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;YAClC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,0EAA0E;IAC1E,YAAY;QACV,wEAAwE;QACxE,4BAA4B;QAC5B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,gBAAgB,CAAC,UAAU,cAAc,GAAG,CAAC,EAAE,CAAC;YACxE,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,cAAc,CAAE,CAAC,CAAC;QACrE,CAAC;QACD,yDAAyD;QACzD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE,CAAC;YACtD,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,QAAQ,CAAC;gBAAE,KAAK,CAAC,MAAM,EAAE,CAAC;QACrD,CAAC;IACH,CAAC;IAED,wEAAwE;IAChE,QAAQ,CAAC,MAAe,EAAE,QAAiB,EAAE,KAAa;QAChE,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE,CAAC;YACxD,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK;gBAAE,OAAO,KAAK,CAAC;QAC1C,CAAC;QACD,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACjD,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QACpB,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACrC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE;YACnC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;gBAC/D,OAAO;YACT,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,CAAC,KAAK,CAAC,kBAAkB,WAAW,mBAAmB,CAAC,CAAC;gBAChE,OAAO;YACT,CAAC;YACD,8DAA8D;YAC9D,MAAM,MAAM,GAAe,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;YACjE,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAC1D,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAED,cAAc,CAAC,MAAM,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC","sourcesContent":["/// <reference lib=\"dom\" />\n\nimport {\n ATTR_ACTION,\n ATTR_KEY_VALUE,\n LB_EVENT_NAME,\n} from \"@loadbare/app/constants\";\nimport type { HubRequest, ListHost, Row } from \"@loadbare/app/types\";\n\n/**\n * A `<select>` whose choices come from a list, and the case that shows why\n * placement belongs to the widget rather than to the framework.\n *\n * The rows arrive flat and keyed. Nothing on the wire knows about\n * `<optgroup>`, and nothing needs to: a group is a column like any other, and\n * turning a repeated column value into an element is exactly the local\n * knowledge a widget has and the hub does not. So the attribute that names\n * the column is this widget's own, in HTML's `data-` namespace rather than\n * the hub's: the hub never reads it, and `lb-` is reserved for what the hub\n * does read.\n *\n * The identity of a row is the value of the option, so the `lb-key-value`\n * the hub stamps on each option is copied to its `value`, and the developer\n * names the key column once, with `lb-key` on the template.\n *\n * Exported because `lb-picker` is this widget with its row template\n * supplied by the definition instead of by the page. Nothing about the\n * behavior changes, so nothing about the behavior is written twice.\n */\n/** Named on the row template: the column whose distinct values become groups. */\nconst GROUP_COLUMN = \"data-group\";\n\nexport class LbOptions extends HTMLElement implements ListHost {\n /**\n * The hub reconciles the list and calls this for placement only, so this\n * widget carries no copy of what a row is or when one arrives.\n */\n lbPlaceRow(el: Element, row: Row, template: HTMLTemplateElement): void {\n const select = this.querySelector(\"select\")!;\n const groupColumn = template.getAttribute(GROUP_COLUMN);\n if (!groupColumn) {\n select.insertBefore(el, template);\n return;\n }\n this.groupFor(select, template, String(row[groupColumn] ?? \"\")).append(el);\n }\n\n /** Everything derived from the rows, once the whole result has landed. */\n lbRowsLanded(): void {\n // The identity of the row is the value of the option, so nothing on the\n // page has to say so twice.\n for (const option of this.querySelectorAll(`option[${ATTR_KEY_VALUE}]`)) {\n option.setAttribute(\"value\", option.getAttribute(ATTR_KEY_VALUE)!);\n }\n // A group is derived, so it goes when its last row does.\n for (const group of this.querySelectorAll(\"optgroup\")) {\n if (!group.querySelector(\"option\")) group.remove();\n }\n }\n\n /** The group with this label, or a new one in front of the template. */\n private groupFor(select: Element, template: Element, label: string): Element {\n for (const group of select.querySelectorAll(\"optgroup\")) {\n if (group.label === label) return group;\n }\n const group = document.createElement(\"optgroup\");\n group.label = label;\n select.insertBefore(group, template);\n return group;\n }\n\n connectedCallback() {\n this.addEventListener(\"change\", () => {\n const select = this.querySelector(\"select\");\n if (!select) {\n console.error(`lb-options: change with no <select>, ignoring`);\n return;\n }\n const name = this.getAttribute(ATTR_ACTION);\n if (!name) {\n console.error(`lb-options: no ${ATTR_ACTION}, nothing to send`);\n return;\n }\n // The hub scopes it, so a choice inside a row says which row.\n const detail: HubRequest = { action: name, value: select.value };\n this.dispatchEvent(\n new CustomEvent(LB_EVENT_NAME, { bubbles: true, detail }),\n );\n });\n }\n}\n\ncustomElements.define(\"lb-options\", LbOptions);\n"]}
|
package/dist/lb-picker.html
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import { ATTR_ACTION, ATTR_VALUE, LB_EVENT_NAME, } from "@loadbare/app/constants";
|
|
3
3
|
/**
|
|
4
4
|
* A leaf cell wrapping a <select>. It receives its value like any other cell,
|
|
5
|
-
* and on change it sends the action the
|
|
6
|
-
* vocabulary and the binding vocabulary running in both directions on
|
|
7
|
-
* widget.
|
|
5
|
+
* and on change it sends the action the developer declared for it — the
|
|
6
|
+
* request vocabulary and the binding vocabulary running in both directions on
|
|
7
|
+
* one widget.
|
|
8
8
|
*
|
|
9
9
|
* The choice is the interaction, so this is the case where an action carries
|
|
10
10
|
* a value. The server still decides what that choice means.
|
|
@@ -31,11 +31,8 @@ class LbSelect extends HTMLElement {
|
|
|
31
31
|
console.error(`lb-select: no ${ATTR_ACTION}, nothing to send`);
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
name,
|
|
37
|
-
value: select.value,
|
|
38
|
-
};
|
|
34
|
+
// The hub scopes it, so a choice inside a row says which row.
|
|
35
|
+
const detail = { action: name, value: select.value };
|
|
39
36
|
this.dispatchEvent(new CustomEvent(LB_EVENT_NAME, { bubbles: true, detail }));
|
|
40
37
|
});
|
|
41
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-select.browser.js","sourceRoot":"","sources":["../widgets/lb-select.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,OAAO,EACL,WAAW,EACX,UAAU,EACV,aAAa,GACd,MAAM,yBAAyB,CAAC;AAGjC;;;;;;;;GAQG;AACH,MAAM,QAAS,SAAQ,WAAW;IAChC,MAAM,CAAC,kBAAkB,GAAG,CAAC,UAAU,CAAC,CAAC;IAEzC,wBAAwB,CAAC,KAAa,EAAE,IAAY,EAAE,KAAa;QACjE,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;YAC7D,OAAO;QACT,CAAC;QACD,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE;YACnC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;gBAC9D,OAAO;YACT,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,CAAC,KAAK,CAAC,iBAAiB,WAAW,mBAAmB,CAAC,CAAC;gBAC/D,OAAO;YACT,CAAC;YACD,MAAM,MAAM,GAAe
|
|
1
|
+
{"version":3,"file":"lb-select.browser.js","sourceRoot":"","sources":["../widgets/lb-select.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,OAAO,EACL,WAAW,EACX,UAAU,EACV,aAAa,GACd,MAAM,yBAAyB,CAAC;AAGjC;;;;;;;;GAQG;AACH,MAAM,QAAS,SAAQ,WAAW;IAChC,MAAM,CAAC,kBAAkB,GAAG,CAAC,UAAU,CAAC,CAAC;IAEzC,wBAAwB,CAAC,KAAa,EAAE,IAAY,EAAE,KAAa;QACjE,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;YAC7D,OAAO;QACT,CAAC;QACD,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE;YACnC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;gBAC9D,OAAO;YACT,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,CAAC,KAAK,CAAC,iBAAiB,WAAW,mBAAmB,CAAC,CAAC;gBAC/D,OAAO;YACT,CAAC;YACD,8DAA8D;YAC9D,MAAM,MAAM,GAAe,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;YACjE,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAC1D,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;;AAGH,cAAc,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC","sourcesContent":["/// <reference lib=\"dom\" />\n\nimport {\n ATTR_ACTION,\n ATTR_VALUE,\n LB_EVENT_NAME,\n} from \"@loadbare/app/constants\";\nimport type { HubRequest } from \"@loadbare/app/types\";\n\n/**\n * A leaf cell wrapping a <select>. It receives its value like any other cell,\n * and on change it sends the action the developer declared for it — the\n * request vocabulary and the binding vocabulary running in both directions on\n * one widget.\n *\n * The choice is the interaction, so this is the case where an action carries\n * a value. The server still decides what that choice means.\n */\nclass LbSelect extends HTMLElement {\n static observedAttributes = [ATTR_VALUE];\n\n attributeChangedCallback(_name: string, _old: string, value: string) {\n const select = this.querySelector(\"select\");\n if (!select) {\n console.error(`lb-select: no <select> to receive the value`);\n return;\n }\n select.value = value;\n }\n\n connectedCallback() {\n this.addEventListener(\"change\", () => {\n const select = this.querySelector(\"select\");\n if (!select) {\n console.error(`lb-select: change with no <select>, ignoring`);\n return;\n }\n const name = this.getAttribute(ATTR_ACTION);\n if (!name) {\n console.error(`lb-select: no ${ATTR_ACTION}, nothing to send`);\n return;\n }\n // The hub scopes it, so a choice inside a row says which row.\n const detail: HubRequest = { action: name, value: select.value };\n this.dispatchEvent(\n new CustomEvent(LB_EVENT_NAME, { bubbles: true, detail }),\n );\n });\n }\n}\n\ncustomElements.define(\"lb-select\", LbSelect);\n"]}
|
package/dist/lb-table.browser.js
CHANGED
|
@@ -1,48 +1,43 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
|
-
import { ATTR_CELL,
|
|
3
|
-
import { applyRows } from "@loadbare/app/rows";
|
|
2
|
+
import { ATTR_CELL, ATTR_KEY_VALUE } from "@loadbare/app/constants";
|
|
4
3
|
/**
|
|
5
4
|
* A table that owns its own scaffolding, and the case that shows what a list
|
|
6
|
-
* widget is for once the
|
|
5
|
+
* widget is for once the hub reconciles lists on its own.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
* the table, and the
|
|
10
|
-
* and the row template. That division is forced
|
|
11
|
-
* `<thead>` written inside a custom element is not
|
|
12
|
-
* parser drops the tag and keeps the text. A
|
|
13
|
-
* so the heading row arrives wrapped and
|
|
14
|
-
* `lb-template` destination beside the slot.
|
|
7
|
+
* A plain `<table lb-list>` with a row template inside it needs no widget at
|
|
8
|
+
* all. This one supplies the table, and the developer writes what only the
|
|
9
|
+
* page knows: the heading row and the row template. That division is forced
|
|
10
|
+
* rather than chosen — a `<thead>` written inside a custom element is not
|
|
11
|
+
* inside a table, so the parser drops the tag and keeps the text. A
|
|
12
|
+
* `<template>` survives anywhere, so the heading row arrives wrapped and
|
|
13
|
+
* expansion unwraps it into the `lb-template` destination beside the slot.
|
|
15
14
|
*
|
|
16
|
-
* What it adds at run time is placement, and only placement. `
|
|
17
|
-
* the column rows are ordered by; `
|
|
18
|
-
* sectioned by
|
|
19
|
-
*
|
|
20
|
-
*
|
|
15
|
+
* What it adds at run time is placement, and only placement. `data-sort`
|
|
16
|
+
* names the column rows are ordered by; `data-group` names the column they
|
|
17
|
+
* are sectioned by. Both are this widget's own attributes, in HTML's `data-`
|
|
18
|
+
* namespace rather than the hub's: the hub never reads them, and `lb-` is
|
|
19
|
+
* reserved for what the hub does read. `lb-options` happens to spell its
|
|
20
|
+
* grouping attribute the same way, which is a convenience and not a contract.
|
|
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-
|
|
25
|
-
* grand total is a second
|
|
26
|
-
*
|
|
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.
|
|
27
27
|
*/
|
|
28
|
+
/** Named on the row template: the column whose distinct values become sections. */
|
|
29
|
+
const GROUP_COLUMN = "data-group";
|
|
30
|
+
/** Named on the row template: the column rows are ordered by within a section. */
|
|
31
|
+
const SORT_COLUMN = "data-sort";
|
|
28
32
|
/**
|
|
29
33
|
* The heading row of a section, carrying the value it stands for.
|
|
30
34
|
*
|
|
31
|
-
* The widget's own scaffolding,
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* binds to it, so it carries no `lb-key` and no name from the vocabulary.
|
|
35
|
+
* The widget's own scaffolding, derived from rows the hub knows and invisible
|
|
36
|
+
* to the hub itself, exactly as the `<optgroup>` in `lb-options` is. Nothing
|
|
37
|
+
* binds to it, so it carries no `lb-key-value` and no name from the vocabulary.
|
|
35
38
|
*/
|
|
36
|
-
const
|
|
39
|
+
const SECTION_ROW = "data-section";
|
|
37
40
|
class LbTable extends HTMLElement {
|
|
38
|
-
acceptRows(result) {
|
|
39
|
-
applyRows(this, result, this.place);
|
|
40
|
-
// A section is derived, so it goes when its last row does.
|
|
41
|
-
for (const heading of this.querySelectorAll(`tr[${GROUP_ROW}]`)) {
|
|
42
|
-
if (this.section(heading).rows.length === 0)
|
|
43
|
-
heading.remove();
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
41
|
/**
|
|
47
42
|
* Group first, then sort within the group.
|
|
48
43
|
*
|
|
@@ -50,26 +45,33 @@ class LbTable extends HTMLElement {
|
|
|
50
45
|
* section ordered, whatever else is in it — which is what lets a whole set
|
|
51
46
|
* and a single patched row take the same path.
|
|
52
47
|
*/
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
const heading =
|
|
57
|
-
? this.headingFor(template,
|
|
48
|
+
lbPlaceRow(el, row, template) {
|
|
49
|
+
const groupColumn = template.getAttribute(GROUP_COLUMN);
|
|
50
|
+
const sortColumn = template.getAttribute(SORT_COLUMN);
|
|
51
|
+
const heading = groupColumn
|
|
52
|
+
? this.headingFor(template, String(row[groupColumn] ?? ""))
|
|
58
53
|
: null;
|
|
59
54
|
const { rows, end } = this.section(heading);
|
|
60
|
-
const value =
|
|
61
|
-
const after =
|
|
62
|
-
? rows.find((other) => other !==
|
|
55
|
+
const value = sortColumn ? String(row[sortColumn] ?? "") : "";
|
|
56
|
+
const after = sortColumn
|
|
57
|
+
? rows.find((other) => other !== el && cellOf(other, sortColumn) > value)
|
|
63
58
|
: undefined;
|
|
64
|
-
template.parentElement.insertBefore(
|
|
65
|
-
}
|
|
59
|
+
template.parentElement.insertBefore(el, after ?? end);
|
|
60
|
+
}
|
|
61
|
+
/** A section is derived, so it goes when its last row does. */
|
|
62
|
+
lbRowsLanded() {
|
|
63
|
+
for (const heading of this.querySelectorAll(`tr[${SECTION_ROW}]`)) {
|
|
64
|
+
if (this.section(heading).rows.length === 0)
|
|
65
|
+
heading.remove();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
66
68
|
/**
|
|
67
69
|
* The rows of one section, and the node it ends at.
|
|
68
70
|
*
|
|
69
71
|
* A section runs from its heading to the next heading, or to the template
|
|
70
72
|
* if there is none after it. Without grouping there is one section and it
|
|
71
|
-
* is the whole body — the heading row the
|
|
72
|
-
* way, because it carries no key and is therefore not a row.
|
|
73
|
+
* is the whole body — the heading row the developer wrote is skipped along
|
|
74
|
+
* the way, because it carries no key and is therefore not a row.
|
|
73
75
|
*/
|
|
74
76
|
section(heading) {
|
|
75
77
|
const template = this.querySelector("template");
|
|
@@ -77,8 +79,8 @@ class LbTable extends HTMLElement {
|
|
|
77
79
|
let node = heading
|
|
78
80
|
? heading.nextElementSibling
|
|
79
81
|
: template.parentElement.firstElementChild;
|
|
80
|
-
while (node && node !== template && !node.hasAttribute(
|
|
81
|
-
if (node.hasAttribute(
|
|
82
|
+
while (node && node !== template && !node.hasAttribute(SECTION_ROW)) {
|
|
83
|
+
if (node.hasAttribute(ATTR_KEY_VALUE))
|
|
82
84
|
rows.push(node);
|
|
83
85
|
node = node.nextElementSibling;
|
|
84
86
|
}
|
|
@@ -87,15 +89,15 @@ class LbTable extends HTMLElement {
|
|
|
87
89
|
/** The section with this label, or a new one in front of the template. */
|
|
88
90
|
headingFor(template, label) {
|
|
89
91
|
const body = template.parentElement;
|
|
90
|
-
for (const heading of body.querySelectorAll(`tr[${
|
|
91
|
-
if (heading.getAttribute(
|
|
92
|
+
for (const heading of body.querySelectorAll(`tr[${SECTION_ROW}]`)) {
|
|
93
|
+
if (heading.getAttribute(SECTION_ROW) === label)
|
|
92
94
|
return heading;
|
|
93
95
|
}
|
|
94
96
|
const heading = document.createElement("tr");
|
|
95
|
-
heading.setAttribute(
|
|
97
|
+
heading.setAttribute(SECTION_ROW, label);
|
|
96
98
|
const cell = document.createElement("th");
|
|
97
99
|
cell.setAttribute("scope", "rowgroup");
|
|
98
|
-
// How wide the table is, from the row the
|
|
100
|
+
// How wide the table is, from the row the developer wrote. Nothing
|
|
99
101
|
// else knows, and nothing had to be told twice.
|
|
100
102
|
cell.colSpan = template.content.firstElementChild?.childElementCount ?? 1;
|
|
101
103
|
cell.textContent = label;
|
|
@@ -105,8 +107,8 @@ class LbTable extends HTMLElement {
|
|
|
105
107
|
}
|
|
106
108
|
}
|
|
107
109
|
/** What a live row is showing in one column. */
|
|
108
|
-
function cellOf(row,
|
|
109
|
-
const selector = `[${ATTR_CELL}="${
|
|
110
|
+
function cellOf(row, column) {
|
|
111
|
+
const selector = `[${ATTR_CELL}="${column}"]`;
|
|
110
112
|
const el = row.matches(selector) ? row : row.querySelector(selector);
|
|
111
113
|
return el?.textContent ?? "";
|
|
112
114
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-table.browser.js","sourceRoot":"","sources":["../widgets/lb-table.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,OAAO,
|
|
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"]}
|
package/docs/lb-input.md
CHANGED
|
@@ -7,14 +7,14 @@ A wrapper for HTML Input.
|
|
|
7
7
|
Requires attributes `lb-cell` and `exp-label`.
|
|
8
8
|
|
|
9
9
|
```html
|
|
10
|
-
<lb-list
|
|
10
|
+
<div lb-list="people">
|
|
11
11
|
<template lb-key="id">
|
|
12
12
|
<div>
|
|
13
|
-
<lb-input lb-cell="name" exp-label="Name"
|
|
13
|
+
<lb-input lb-cell="name" exp-label="Name" lb-action="lb-cell-change"></lb-input>
|
|
14
14
|
<lb-input lb-cell="email" exp-label="Email" exp-readonly></lb-input>
|
|
15
15
|
</div>
|
|
16
16
|
</template>
|
|
17
|
-
</
|
|
17
|
+
</div>
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
## Expansion Attributes
|
|
@@ -32,27 +32,31 @@ off drops the attribute.
|
|
|
32
32
|
|
|
33
33
|
## Behavioral Attributes
|
|
34
34
|
|
|
35
|
-
|
|
|
36
|
-
|
|
37
|
-
| `
|
|
35
|
+
| Attribute | Names |
|
|
36
|
+
|-------------|----------------------------------------------------|
|
|
37
|
+
| `lb-action` | what a change sends; nothing is sent without it |
|
|
38
|
+
|
|
39
|
+
`lb-action="lb-cell-change"` sends the lb-cell-change operation for the input's
|
|
40
|
+
own cell. Any other name sends that action. Either carries the input's
|
|
41
|
+
value, and the hub adds the binding below.
|
|
38
42
|
|
|
39
43
|
## Binding
|
|
40
44
|
|
|
41
45
|
| Attribute | Read from | Names |
|
|
42
46
|
|------------|----------------------|------------------------------|
|
|
43
47
|
| `lb-cell` | the element itself | the column the input shows |
|
|
44
|
-
| `lb-key`
|
|
45
|
-
| `lb-
|
|
48
|
+
| `lb-key-value` | the nearest ancestor | the row |
|
|
49
|
+
| `lb-list` | the nearest ancestor | the query |
|
|
46
50
|
|
|
47
51
|
## Inside a form
|
|
48
52
|
|
|
49
|
-
An `lb-input` inside an `lb-insert` or `lb-update` form
|
|
50
|
-
`
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
An `lb-input` inside an `lb-row-insert` or `lb-row-update` form carries no
|
|
54
|
+
`lb-action`. The form reads every `lb-cell` in it on submit and sends one
|
|
55
|
+
request for all of them, so an input that also sent its own would write the
|
|
56
|
+
same edit twice.
|
|
53
57
|
|
|
54
58
|
```html
|
|
55
|
-
<form lb-update>
|
|
59
|
+
<form lb-action="lb-row-update">
|
|
56
60
|
<lb-input lb-cell="name" exp-label="Name"></lb-input>
|
|
57
61
|
<lb-input lb-cell="email" exp-label="Email"></lb-input>
|
|
58
62
|
<button type="submit">Save</button>
|
|
@@ -64,9 +68,9 @@ write the same edit twice.
|
|
|
64
68
|
By default an `lb-input` does not notify the hub when its value changes, as shown
|
|
65
69
|
in the form example above.
|
|
66
70
|
|
|
67
|
-
To
|
|
68
|
-
|
|
71
|
+
To send on change, add `lb-action`. The hub catches the event and sends the
|
|
72
|
+
request to the server.
|
|
69
73
|
|
|
70
74
|
While the request is in flight the hub marks the `lb-input` with
|
|
71
|
-
`
|
|
75
|
+
`lb-pending`, and on failure with `lb-error`. Both are there for a
|
|
72
76
|
stylesheet to read.
|
package/docs/lb-options.md
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
A wrapper for HTML Select, with the `<option>`s coming from a query.
|
|
4
4
|
|
|
5
|
-
Apply the `lb-
|
|
5
|
+
Apply the `lb-list` attribute to name the query, and `lb-action` to name the
|
|
6
6
|
action a change sends. Write a `<template lb-key="...">` inside the tag, and
|
|
7
7
|
apply `lb-cell` to the `<option>` itself — an `<option>` holds text, not
|
|
8
|
-
elements. Apply `
|
|
8
|
+
elements. Apply `data-group` to the template to section the options.
|
|
9
9
|
|
|
10
10
|
Provide `exp-label`.
|
|
11
11
|
|
|
12
12
|
```html
|
|
13
|
-
<lb-options lb-
|
|
14
|
-
<template lb-key="id"
|
|
13
|
+
<lb-options lb-list="statuses" exp-label="Status" lb-action="setStatus">
|
|
14
|
+
<template lb-key="id" data-group="category">
|
|
15
15
|
<option lb-cell="label"></option>
|
|
16
16
|
</template>
|
|
17
17
|
</lb-options>
|
|
@@ -32,18 +32,17 @@ improvements through iterative use.
|
|
|
32
32
|
|
|
33
33
|
| Attribute | Read from | Names |
|
|
34
34
|
|-------------|--------------------|--------------------------------------|
|
|
35
|
-
| `lb-
|
|
35
|
+
| `lb-list` | the element itself | the query whose rows become options |
|
|
36
36
|
| `lb-action` | the element itself | the action a change sends |
|
|
37
|
-
| `lb-key`
|
|
38
|
-
| `
|
|
37
|
+
| `lb-key` | the row template | the column that identifies a row |
|
|
38
|
+
| `data-group` | the row template | the column that sections the options |
|
|
39
39
|
| `lb-cell` | the `<option>` | the column the option shows |
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
`lb-sort` is not read here.
|
|
41
|
+
The key column gives the option its `value`, so the page names it once.
|
|
43
42
|
|
|
44
43
|
## Grouping
|
|
45
44
|
|
|
46
|
-
`
|
|
45
|
+
`data-group` names a column whose distinct values become `<optgroup>`s, each
|
|
47
46
|
labelled with the value.
|
|
48
47
|
|
|
49
48
|
## Choosing
|
|
@@ -55,12 +54,12 @@ action on the server.
|
|
|
55
54
|
`lb-action` is required. Without it, a change does nothing.
|
|
56
55
|
|
|
57
56
|
While the request is in flight the hub marks the `lb-options` with
|
|
58
|
-
`
|
|
57
|
+
`lb-pending`, and on failure with `lb-error`. Both are there for a
|
|
59
58
|
stylesheet to read.
|
|
60
59
|
|
|
61
60
|
## Styling an empty list
|
|
62
61
|
|
|
63
|
-
`lb-options` carries `
|
|
62
|
+
`lb-options` carries `lb-row-count` with the number of options showing, for a
|
|
64
63
|
stylesheet to read.
|
|
65
64
|
|
|
66
65
|
For the case where every row is one option and only the column names vary,
|
package/docs/lb-picker.md
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
[`lb-options`](./lb-options.md) with the row template built in, for when every
|
|
4
4
|
row is one option and only the column names vary. It takes no children.
|
|
5
5
|
|
|
6
|
-
Apply the `lb-
|
|
6
|
+
Apply the `lb-list` attribute to name the query, and `lb-action` to name the
|
|
7
7
|
action a change sends.
|
|
8
8
|
|
|
9
9
|
Provide `exp-label`, `exp-key`, `exp-cell`, and optionally `exp-group`.
|
|
10
10
|
|
|
11
11
|
```html
|
|
12
12
|
<lb-picker
|
|
13
|
-
lb-
|
|
13
|
+
lb-list="statuses"
|
|
14
14
|
exp-label="Status"
|
|
15
15
|
exp-key="id"
|
|
16
16
|
exp-cell="label"
|
|
@@ -40,13 +40,13 @@ options ungrouped.
|
|
|
40
40
|
|
|
41
41
|
| Attribute | Read from | Names |
|
|
42
42
|
|-------------|--------------------|-------------------------------------|
|
|
43
|
-
| `lb-
|
|
43
|
+
| `lb-list` | the element itself | the query whose rows become options |
|
|
44
44
|
| `lb-action` | the element itself | the action a change sends |
|
|
45
45
|
|
|
46
46
|
## Choosing
|
|
47
47
|
|
|
48
48
|
The same as [`lb-options`](./lb-options.md): grouping, the key as the option's
|
|
49
|
-
value, `
|
|
49
|
+
value, `lb-row-count`, and the action on change.
|
|
50
50
|
|
|
51
51
|
Write `lb-options` and the template instead for a row that needs a second
|
|
52
52
|
element, or an option built from two columns.
|
package/docs/lb-select.md
CHANGED
|
@@ -41,7 +41,7 @@ action on the server.
|
|
|
41
41
|
`lb-action` is required. Without it, a change does nothing.
|
|
42
42
|
|
|
43
43
|
While the request is in flight the hub marks the `lb-select` with
|
|
44
|
-
`
|
|
44
|
+
`lb-pending`, and on failure with `lb-error`. Both are there for a
|
|
45
45
|
stylesheet to read.
|
|
46
46
|
|
|
47
47
|
For a `<select>` whose options come from a query, see
|
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-
|
|
5
|
+
Apply the `lb-list` attribute to name the query. Write three `<template>`s
|
|
6
6
|
inside the tag: one marked `lb-template="head"` for the heading row, one
|
|
7
7
|
carrying `lb-key` for the row, and optionally one marked `lb-template="foot"`
|
|
8
|
-
for the footer. Apply `
|
|
8
|
+
for 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-
|
|
14
|
+
<lb-table lb-list="ledger" exp-caption="Ledger">
|
|
15
15
|
<template lb-template="head">
|
|
16
16
|
<tr><th>Date</th><th>Amount</th></tr>
|
|
17
17
|
</template>
|
|
18
|
-
<template lb-key="id"
|
|
18
|
+
<template lb-key="id" data-sort="date" data-group="month">
|
|
19
19
|
<tr><td lb-cell="date"></td><td lb-cell="amount"></td></tr>
|
|
20
20
|
</template>
|
|
21
21
|
<template lb-template="foot">
|
|
22
|
-
<tr lb-
|
|
22
|
+
<tr lb-row="ledger-total"><td>Total</td><td lb-cell="total"></td></tr>
|
|
23
23
|
</template>
|
|
24
24
|
</lb-table>
|
|
25
25
|
```
|
|
@@ -42,28 +42,28 @@ improvements through iterative use.
|
|
|
42
42
|
|
|
43
43
|
| Attribute | Read from | Names |
|
|
44
44
|
|------------|----------------------------------|------------------------------------|
|
|
45
|
-
| `lb-
|
|
46
|
-
| `lb-key`
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
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 |
|
|
47
|
+
| `data-sort` | the row template | the column rows are ordered by |
|
|
48
|
+
| `data-group` | the row template | the column rows are sectioned by |
|
|
49
49
|
| `lb-cell` | elements inside the row template | the column each element shows |
|
|
50
50
|
|
|
51
51
|
## Sorting and grouping
|
|
52
52
|
|
|
53
|
-
`
|
|
54
|
-
shows. `
|
|
53
|
+
`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
55
|
gets a heading row of its own, spanning the width of the row template, and
|
|
56
56
|
rows sort within their section.
|
|
57
57
|
|
|
58
58
|
## The footer
|
|
59
59
|
|
|
60
60
|
The footer is an ordinary part of the page, not a row from the table's query.
|
|
61
|
-
Give it its own `lb-
|
|
61
|
+
Give it its own `lb-row` — a grand total is a second query, declared on the
|
|
62
62
|
server like any other.
|
|
63
63
|
|
|
64
64
|
## Styling an empty table
|
|
65
65
|
|
|
66
|
-
`lb-table` carries `
|
|
66
|
+
`lb-table` carries `lb-row-count` with the number of rows showing, for a
|
|
67
67
|
stylesheet to read.
|
|
68
68
|
|
|
69
69
|
For a repeater with no table of its own, see [`lb-list`](./lb-list.md).
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loadbare/widgets",
|
|
3
|
-
"description": "The basic widget set for @loadbare/app: input, select,
|
|
4
|
-
"version": "0.
|
|
3
|
+
"description": "The basic widget set for @loadbare/app: input, select, options, picker, table, unknown-page",
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "node scripts/build.mjs",
|
|
25
|
+
"postbuild": "node ../../scripts/check-dist-imports.mjs",
|
|
25
26
|
"test": "tsx --test \"tests/**/*.test.ts\"",
|
|
26
27
|
"typecheck": "tsc --noEmit",
|
|
27
28
|
"format": "prettier --write .",
|
|
@@ -48,10 +49,10 @@
|
|
|
48
49
|
"node": ">=22"
|
|
49
50
|
},
|
|
50
51
|
"peerDependencies": {
|
|
51
|
-
"@loadbare/app": "^0.
|
|
52
|
+
"@loadbare/app": "^0.7.0"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
|
-
"@loadbare/app": "^0.
|
|
55
|
+
"@loadbare/app": "^0.7.0",
|
|
55
56
|
"@types/jsdom": "^21.1.7",
|
|
56
57
|
"@types/node": "^22.0.0",
|
|
57
58
|
"jsdom": "^25.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lb-list.browser.d.ts","sourceRoot":"","sources":["../widgets/lb-list.browser.ts"],"names":[],"mappings":""}
|
package/dist/lb-list.browser.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/// <reference lib="dom" />
|
|
2
|
-
import { applyRows } from "@loadbare/app/rows";
|
|
3
|
-
/**
|
|
4
|
-
* The plain repeater: whatever the page author wrote inside is repeated once
|
|
5
|
-
* per tuple, in the order the server sent.
|
|
6
|
-
*
|
|
7
|
-
* It exists because a projection has to land on a widget, and because a
|
|
8
|
-
* `<table>` cannot be one — a custom element written inside `<tbody>` is
|
|
9
|
-
* discarded by the parser, so the widget goes around the table and the
|
|
10
|
-
* template goes inside it, where the content model already allows it.
|
|
11
|
-
*/
|
|
12
|
-
class LbList extends HTMLElement {
|
|
13
|
-
acceptRows(result) {
|
|
14
|
-
applyRows(this, result);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
customElements.define("lb-list", LbList);
|
|
18
|
-
//# sourceMappingURL=lb-list.browser.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lb-list.browser.js","sourceRoot":"","sources":["../widgets/lb-list.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAG3B,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C;;;;;;;;GAQG;AACH,MAAM,MAAO,SAAQ,WAAW;IAC9B,UAAU,CAAC,MAAkB;QAC3B,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC;CACF;AAED,cAAc,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC","sourcesContent":["/// <reference lib=\"dom\" />\n\nimport type { Projection, HubRowHost } from \"@loadbare/app/types\";\nimport { applyRows } from \"@loadbare/app/rows\";\n\n/**\n * The plain repeater: whatever the page author wrote inside is repeated once\n * per tuple, in the order the server sent.\n *\n * It exists because a projection has to land on a widget, and because a\n * `<table>` cannot be one — a custom element written inside `<tbody>` is\n * discarded by the parser, so the widget goes around the table and the\n * template goes inside it, where the content model already allows it.\n */\nclass LbList extends HTMLElement implements HubRowHost {\n acceptRows(result: Projection) {\n applyRows(this, result);\n }\n}\n\ncustomElements.define(\"lb-list\", LbList);\n"]}
|
package/dist/lb-list.html
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<div lb-slot></div>
|
package/docs/lb-list.md
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# `lb-list`
|
|
2
|
-
|
|
3
|
-
The plain repeater: a row per tuple, in the order the rows arrive.
|
|
4
|
-
|
|
5
|
-
Apply the `lb-query` attribute to name the query. Write a
|
|
6
|
-
`<template lb-key="...">` inside the tag, naming the column that identifies a
|
|
7
|
-
row, and apply `lb-cell` to each element in it.
|
|
8
|
-
|
|
9
|
-
```html
|
|
10
|
-
<lb-list lb-query="people">
|
|
11
|
-
<template lb-key="id">
|
|
12
|
-
<div>
|
|
13
|
-
<span lb-cell="name"></span>
|
|
14
|
-
<span lb-cell="email"></span>
|
|
15
|
-
</div>
|
|
16
|
-
</template>
|
|
17
|
-
</lb-list>
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Status
|
|
21
|
-
|
|
22
|
-
Barebones starter to prove out the publish/consume chain and identify necessary
|
|
23
|
-
improvements through iterative use.
|
|
24
|
-
|
|
25
|
-
## Parameters
|
|
26
|
-
|
|
27
|
-
None.
|
|
28
|
-
|
|
29
|
-
## Binding
|
|
30
|
-
|
|
31
|
-
| Attribute | Read from | Names |
|
|
32
|
-
|------------|----------------------------------|----------------------------------|
|
|
33
|
-
| `lb-query` | the element itself | the query whose rows land here |
|
|
34
|
-
| `lb-key` | the row template | the column that identifies a row |
|
|
35
|
-
| `lb-cell` | elements inside the row template | the column each element shows |
|
|
36
|
-
|
|
37
|
-
For ordering and section headings, see [`lb-table`](./lb-table.md).
|
|
38
|
-
|
|
39
|
-
## Repeating table rows
|
|
40
|
-
|
|
41
|
-
Put `lb-list` around the `<table>` and the `<template>` inside `<tbody>`. A
|
|
42
|
-
custom element written inside `<tbody>` is thrown away by the HTML parser.
|
|
43
|
-
|
|
44
|
-
```html
|
|
45
|
-
<lb-list lb-query="ledger">
|
|
46
|
-
<table>
|
|
47
|
-
<tbody>
|
|
48
|
-
<template lb-key="id">
|
|
49
|
-
<tr><td lb-cell="date"></td><td lb-cell="amount"></td></tr>
|
|
50
|
-
</template>
|
|
51
|
-
</tbody>
|
|
52
|
-
</table>
|
|
53
|
-
</lb-list>
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
[`lb-table`](./lb-table.md) supplies the table itself.
|
|
57
|
-
|
|
58
|
-
## Styling an empty list
|
|
59
|
-
|
|
60
|
-
`lb-list` carries `data-rows` with the number of rows showing, for a
|
|
61
|
-
stylesheet to read.
|