@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.
- package/README.md +9 -5
- package/dist/control.d.ts +54 -0
- package/dist/control.d.ts.map +1 -0
- package/dist/control.js +100 -0
- package/dist/control.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lb-confirm.browser.d.ts +2 -0
- package/dist/lb-confirm.browser.d.ts.map +1 -0
- package/dist/lb-confirm.browser.js +36 -0
- package/dist/lb-confirm.browser.js.map +1 -0
- package/dist/lb-confirm.html +27 -0
- package/dist/lb-form.browser.d.ts +2 -0
- package/dist/lb-form.browser.d.ts.map +1 -0
- package/dist/lb-form.browser.js +28 -0
- package/dist/lb-form.browser.js.map +1 -0
- package/dist/lb-form.html +7 -0
- package/dist/lb-input.browser.js +9 -37
- package/dist/lb-input.browser.js.map +1 -1
- package/dist/lb-input.html +1 -1
- package/dist/lb-insert-dialog.browser.d.ts +2 -0
- package/dist/lb-insert-dialog.browser.d.ts.map +1 -0
- package/dist/lb-insert-dialog.browser.js +59 -0
- package/dist/lb-insert-dialog.browser.js.map +1 -0
- package/dist/lb-insert-dialog.html +25 -0
- package/dist/lb-master.html +51 -0
- package/dist/lb-options.browser.d.ts +38 -11
- package/dist/lb-options.browser.d.ts.map +1 -1
- package/dist/lb-options.browser.js +50 -80
- package/dist/lb-options.browser.js.map +1 -1
- package/dist/lb-options.html +1 -1
- package/dist/lb-picker.browser.js +9 -11
- package/dist/lb-picker.browser.js.map +1 -1
- package/dist/lb-picker.html +2 -2
- package/dist/lb-select.browser.js +7 -39
- package/dist/lb-select.browser.js.map +1 -1
- package/dist/lb-select.html +1 -1
- package/dist/lb-table.browser.js +63 -90
- package/dist/lb-table.browser.js.map +1 -1
- package/dist/lb-table.html +4 -3
- package/dist/lb-unknown-page.html +2 -4
- package/docs/lb-confirm.md +45 -0
- package/docs/lb-form.md +33 -0
- package/docs/lb-input.md +42 -40
- package/docs/lb-insert-dialog.md +50 -0
- package/docs/lb-master.md +53 -0
- package/docs/lb-options.md +54 -34
- package/docs/lb-picker.md +24 -24
- package/docs/lb-select.md +37 -19
- package/docs/lb-table.md +63 -29
- package/docs/lb-unknown-page.md +11 -13
- package/package.json +4 -4
|
@@ -1,16 +1,43 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type { RowsHost } from "@loadbare/app/types";
|
|
2
|
+
import { WrappedControl } from "./control.js";
|
|
3
|
+
/**
|
|
4
|
+
* A `<select>` whose choices are the rows of the query it names.
|
|
5
|
+
*
|
|
6
|
+
* The hub places the options by the query's order, and a group template
|
|
7
|
+
* holding an `<optgroup>` puts them in groups. What landing does not give an
|
|
8
|
+
* `<optgroup>` is its label, so the widget sets it from the `lb-group-value`
|
|
9
|
+
* the hub stamps on it.
|
|
10
|
+
*
|
|
11
|
+
* The identity of a row is the value of its option, so the `lb-key-value` the
|
|
12
|
+
* hub stamps on each option is copied to its `value`. The key column is the
|
|
13
|
+
* one the server declared for the query; the page never names it.
|
|
14
|
+
*
|
|
15
|
+
* It is two things at once, and the hub keeps them apart: its own `lb-query`
|
|
16
|
+
* names the rows it offers, and its `lb-column` is a column of the row around
|
|
17
|
+
* it, which is what it holds and what it sends. As a control it is
|
|
18
|
+
* form-associated, with a `value` and a `change` of its own, so the hub lands
|
|
19
|
+
* that column on `value` and commits `lb-request` on `change`.
|
|
20
|
+
*
|
|
21
|
+
* The value is held here as well as on the select. A `<select>` does not hold
|
|
22
|
+
* a value it has no option for: it selects nothing and forgets it. The value
|
|
23
|
+
* and the choices are two answers that land in either order, so the held
|
|
24
|
+
* value is put on the select again whenever rows land.
|
|
25
|
+
*
|
|
26
|
+
* Exported because `lb-picker` is this widget with its row template supplied
|
|
27
|
+
* by the definition instead of by the page.
|
|
28
|
+
*/
|
|
29
|
+
export declare class LbOptions extends WrappedControl implements RowsHost {
|
|
30
|
+
protected inner: string;
|
|
5
31
|
/**
|
|
6
|
-
* The
|
|
7
|
-
*
|
|
32
|
+
* The value last set or chosen, which the options may not include yet.
|
|
33
|
+
* Null when nothing chose one, so the select's own default stands.
|
|
8
34
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
35
|
+
private chosen;
|
|
36
|
+
get value(): string;
|
|
37
|
+
set value(value: string);
|
|
38
|
+
formResetCallback(): void;
|
|
39
|
+
protected changed(): void;
|
|
40
|
+
/** Everything derived from the rows, once the whole answer has landed. */
|
|
11
41
|
lbRowsLanded(): void;
|
|
12
|
-
/** The group with this label, or a new one in front of the template. */
|
|
13
|
-
private groupFor;
|
|
14
|
-
connectedCallback(): void;
|
|
15
42
|
}
|
|
16
43
|
//# sourceMappingURL=lb-options.browser.d.ts.map
|
|
@@ -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":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,qBAAa,SAAU,SAAQ,cAAe,YAAW,QAAQ;IAC/D,SAAS,CAAC,KAAK,SAAY;IAE3B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAuB;IAErC,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAGtB;IAED,iBAAiB,IAAI,IAAI;IAKzB,SAAS,CAAC,OAAO,IAAI,IAAI;IAKzB,0EAA0E;IAC1E,YAAY,IAAI,IAAI;CAcrB"}
|
|
@@ -1,97 +1,67 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
|
-
import {
|
|
2
|
+
import { ATTR_GROUP_VALUE, ATTR_KEY_VALUE, LIVE_GROUP, LIVE_ROW, } from "@loadbare/app/constants";
|
|
3
|
+
import { WrappedControl } from "./control.js";
|
|
3
4
|
/**
|
|
4
|
-
* A `<select>` whose choices
|
|
5
|
-
* placement belongs to the widget rather than to the framework.
|
|
5
|
+
* A `<select>` whose choices are the rows of the query it names.
|
|
6
6
|
*
|
|
7
|
-
* The
|
|
8
|
-
* `<optgroup
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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.
|
|
7
|
+
* The hub places the options by the query's order, and a group template
|
|
8
|
+
* holding an `<optgroup>` puts them in groups. What landing does not give an
|
|
9
|
+
* `<optgroup>` is its label, so the widget sets it from the `lb-group-value`
|
|
10
|
+
* the hub stamps on it.
|
|
14
11
|
*
|
|
15
|
-
* The identity of a row is the value of
|
|
16
|
-
*
|
|
17
|
-
*
|
|
12
|
+
* The identity of a row is the value of its option, so the `lb-key-value` the
|
|
13
|
+
* hub stamps on each option is copied to its `value`. The key column is the
|
|
14
|
+
* one the server declared for the query; the page never names it.
|
|
18
15
|
*
|
|
19
|
-
* It
|
|
20
|
-
*
|
|
21
|
-
*
|
|
16
|
+
* It is two things at once, and the hub keeps them apart: its own `lb-query`
|
|
17
|
+
* names the rows it offers, and its `lb-column` is a column of the row around
|
|
18
|
+
* it, which is what it holds and what it sends. As a control it is
|
|
19
|
+
* form-associated, with a `value` and a `change` of its own, so the hub lands
|
|
20
|
+
* that column on `value` and commits `lb-request` on `change`.
|
|
22
21
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
22
|
+
* The value is held here as well as on the select. A `<select>` does not hold
|
|
23
|
+
* a value it has no option for: it selects nothing and forgets it. The value
|
|
24
|
+
* and the choices are two answers that land in either order, so the held
|
|
25
|
+
* value is put on the select again whenever rows land.
|
|
26
|
+
*
|
|
27
|
+
* Exported because `lb-picker` is this widget with its row template supplied
|
|
28
|
+
* by the definition instead of by the page.
|
|
26
29
|
*/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export class LbOptions extends HTMLElement {
|
|
30
|
-
static observedAttributes = [ATTR_VALUE];
|
|
31
|
-
attributeChangedCallback(_name, _old, value) {
|
|
32
|
-
const select = this.querySelector("select");
|
|
33
|
-
if (select)
|
|
34
|
-
select.value = value;
|
|
35
|
-
}
|
|
30
|
+
export class LbOptions extends WrappedControl {
|
|
31
|
+
inner = "select";
|
|
36
32
|
/**
|
|
37
|
-
* The
|
|
38
|
-
*
|
|
33
|
+
* The value last set or chosen, which the options may not include yet.
|
|
34
|
+
* Null when nothing chose one, so the select's own default stands.
|
|
39
35
|
*/
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
this.groupFor(select, template, String(row[groupColumn] ?? "")).append(el);
|
|
36
|
+
chosen = null;
|
|
37
|
+
get value() {
|
|
38
|
+
return this.chosen ?? super.value;
|
|
39
|
+
}
|
|
40
|
+
set value(value) {
|
|
41
|
+
this.chosen = value === null || value === undefined ? null : String(value);
|
|
42
|
+
super.value = value;
|
|
48
43
|
}
|
|
49
|
-
|
|
44
|
+
formResetCallback() {
|
|
45
|
+
this.chosen = null;
|
|
46
|
+
super.formResetCallback();
|
|
47
|
+
}
|
|
48
|
+
changed() {
|
|
49
|
+
this.chosen = this.control?.value ?? null;
|
|
50
|
+
super.changed();
|
|
51
|
+
}
|
|
52
|
+
/** Everything derived from the rows, once the whole answer has landed. */
|
|
50
53
|
lbRowsLanded() {
|
|
51
|
-
|
|
52
|
-
// page has to say so twice.
|
|
53
|
-
for (const option of this.querySelectorAll(`option[${ATTR_KEY_VALUE}]`)) {
|
|
54
|
+
for (const option of this.querySelectorAll(`option${LIVE_ROW}`)) {
|
|
54
55
|
option.setAttribute("value", option.getAttribute(ATTR_KEY_VALUE));
|
|
55
56
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (!group.querySelector("option"))
|
|
59
|
-
group.remove();
|
|
60
|
-
}
|
|
61
|
-
// A value that landed before its option did selects it now.
|
|
62
|
-
const value = this.getAttribute(ATTR_VALUE);
|
|
63
|
-
if (value !== null)
|
|
64
|
-
this.querySelector("select").value = value;
|
|
65
|
-
}
|
|
66
|
-
/** The group with this label, or a new one in front of the template. */
|
|
67
|
-
groupFor(select, template, label) {
|
|
68
|
-
for (const group of select.querySelectorAll("optgroup")) {
|
|
69
|
-
if (group.label === label)
|
|
70
|
-
return group;
|
|
57
|
+
for (const group of this.querySelectorAll(`optgroup${LIVE_GROUP}`)) {
|
|
58
|
+
group.label = group.getAttribute(ATTR_GROUP_VALUE) ?? "";
|
|
71
59
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
select.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
connectedCallback() {
|
|
78
|
-
this.addEventListener("change", () => {
|
|
79
|
-
const select = this.querySelector("select");
|
|
80
|
-
if (!select) {
|
|
81
|
-
console.error(`lb-options: change with no <select>, ignoring`);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const name = this.getAttribute(ATTR_ACTION);
|
|
85
|
-
if (!name && this.hasAttribute(ATTR_QUERY_PARM))
|
|
86
|
-
return;
|
|
87
|
-
if (!name) {
|
|
88
|
-
console.error(`lb-options: no ${ATTR_ACTION}, nothing to send`);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
// The hub scopes it, so a choice inside a row says which row.
|
|
92
|
-
const detail = { action: name, value: select.value };
|
|
93
|
-
this.dispatchEvent(new CustomEvent(LB_EVENT_NAME, { bubbles: true, detail }));
|
|
94
|
-
});
|
|
60
|
+
// After the values above, since the select matches against them.
|
|
61
|
+
const select = this.querySelector("select");
|
|
62
|
+
if (select && this.chosen !== null)
|
|
63
|
+
select.value = this.chosen;
|
|
64
|
+
this.formValue();
|
|
95
65
|
}
|
|
96
66
|
}
|
|
97
67
|
customElements.define("lb-options", LbOptions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-options.browser.js","sourceRoot":"","sources":["../widgets/lb-options.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"lb-options.browser.js","sourceRoot":"","sources":["../widgets/lb-options.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,UAAU,EACV,QAAQ,GACT,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,MAAM,OAAO,SAAU,SAAQ,cAAc;IACjC,KAAK,GAAG,QAAQ,CAAC;IAE3B;;;OAGG;IACK,MAAM,GAAkB,IAAI,CAAC;IAErC,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC;IACpC,CAAC;IAED,IAAI,KAAK,CAAC,KAAa;QACrB,IAAI,CAAC,MAAM,GAAG,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3E,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;IACtB,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC5B,CAAC;IAES,OAAO;QACf,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,KAAK,IAAI,IAAI,CAAC;QAC1C,KAAK,CAAC,OAAO,EAAE,CAAC;IAClB,CAAC;IAED,0EAA0E;IAC1E,YAAY;QACV,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,gBAAgB,CAAC,SAAS,QAAQ,EAAE,CAAC,EAAE,CAAC;YAChE,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,cAAc,CAAE,CAAC,CAAC;QACrE,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,gBAAgB,CACvC,WAAW,UAAU,EAAE,CACxB,EAAE,CAAC;YACF,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;QAC3D,CAAC;QACD,iEAAiE;QACjE,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,MAAM,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI;YAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;QAC/D,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;CACF;AAED,cAAc,CAAC,MAAM,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC","sourcesContent":["/// <reference lib=\"dom\" />\n\nimport {\n ATTR_GROUP_VALUE,\n ATTR_KEY_VALUE,\n LIVE_GROUP,\n LIVE_ROW,\n} from \"@loadbare/app/constants\";\nimport type { RowsHost } from \"@loadbare/app/types\";\nimport { WrappedControl } from \"./control.js\";\n\n/**\n * A `<select>` whose choices are the rows of the query it names.\n *\n * The hub places the options by the query's order, and a group template\n * holding an `<optgroup>` puts them in groups. What landing does not give an\n * `<optgroup>` is its label, so the widget sets it from the `lb-group-value`\n * the hub stamps on it.\n *\n * The identity of a row is the value of its option, so the `lb-key-value` the\n * hub stamps on each option is copied to its `value`. The key column is the\n * one the server declared for the query; the page never names it.\n *\n * It is two things at once, and the hub keeps them apart: its own `lb-query`\n * names the rows it offers, and its `lb-column` is a column of the row around\n * it, which is what it holds and what it sends. As a control it is\n * form-associated, with a `value` and a `change` of its own, so the hub lands\n * that column on `value` and commits `lb-request` on `change`.\n *\n * The value is held here as well as on the select. A `<select>` does not hold\n * a value it has no option for: it selects nothing and forgets it. The value\n * and the choices are two answers that land in either order, so the held\n * value is put on the select again whenever rows land.\n *\n * Exported because `lb-picker` is this widget with its row template supplied\n * by the definition instead of by the page.\n */\n\nexport class LbOptions extends WrappedControl implements RowsHost {\n protected inner = \"select\";\n\n /**\n * The value last set or chosen, which the options may not include yet.\n * Null when nothing chose one, so the select's own default stands.\n */\n private chosen: string | null = null;\n\n get value(): string {\n return this.chosen ?? super.value;\n }\n\n set value(value: string) {\n this.chosen = value === null || value === undefined ? null : String(value);\n super.value = value;\n }\n\n formResetCallback(): void {\n this.chosen = null;\n super.formResetCallback();\n }\n\n protected changed(): void {\n this.chosen = this.control?.value ?? null;\n super.changed();\n }\n\n /** Everything derived from the rows, once the whole answer has landed. */\n lbRowsLanded(): void {\n for (const option of this.querySelectorAll(`option${LIVE_ROW}`)) {\n option.setAttribute(\"value\", option.getAttribute(ATTR_KEY_VALUE)!);\n }\n for (const group of this.querySelectorAll<HTMLOptGroupElement>(\n `optgroup${LIVE_GROUP}`,\n )) {\n group.label = group.getAttribute(ATTR_GROUP_VALUE) ?? \"\";\n }\n // After the values above, since the select matches against them.\n const select = this.querySelector(\"select\");\n if (select && this.chosen !== null) select.value = this.chosen;\n this.formValue();\n }\n}\n\ncustomElements.define(\"lb-options\", LbOptions);\n"]}
|
package/dist/lb-options.html
CHANGED
|
@@ -6,19 +6,17 @@ import { LbOptions } from "./lb-options.browser.js";
|
|
|
6
6
|
*
|
|
7
7
|
* A row template is markup, so writing it is the page author's job — until
|
|
8
8
|
* every row is one option and the only thing that varies is which columns it
|
|
9
|
-
* reads.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* reads. That is a word, and words are parameters: `exp-column` fills the
|
|
10
|
+
* placeholder in the template the definition already has, at build time, and
|
|
11
|
+
* the page writes one tag with no children.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* not.
|
|
13
|
+
* A page author who wants a second element in the row, an option built from
|
|
14
|
+
* two columns, or options in groups, writes `lb-options` and the templates
|
|
15
|
+
* themselves.
|
|
17
16
|
*
|
|
18
|
-
* The behavior is inherited whole, because it is the same behavior
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* requires one constructor per name.
|
|
17
|
+
* The behavior is inherited whole, because it is the same behavior. A
|
|
18
|
+
* distinct class exists only because `customElements.define` requires one
|
|
19
|
+
* constructor per name.
|
|
22
20
|
*/
|
|
23
21
|
class LbPicker extends LbOptions {
|
|
24
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-picker.browser.js","sourceRoot":"","sources":["../widgets/lb-picker.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEpD
|
|
1
|
+
{"version":3,"file":"lb-picker.browser.js","sourceRoot":"","sources":["../widgets/lb-picker.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEpD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,QAAS,SAAQ,SAAS;CAAG;AAEnC,cAAc,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC","sourcesContent":["/// <reference lib=\"dom\" />\n\nimport { LbOptions } from \"./lb-options.browser.js\";\n\n/**\n * The same `<select>` as `lb-options`, with the row template supplied by\n * the definition instead of by the page.\n *\n * A row template is markup, so writing it is the page author's job — until\n * every row is one option and the only thing that varies is which columns it\n * reads. That is a word, and words are parameters: `exp-column` fills the\n * placeholder in the template the definition already has, at build time, and\n * the page writes one tag with no children.\n *\n * A page author who wants a second element in the row, an option built from\n * two columns, or options in groups, writes `lb-options` and the templates\n * themselves.\n *\n * The behavior is inherited whole, because it is the same behavior. A\n * distinct class exists only because `customElements.define` requires one\n * constructor per name.\n */\nclass LbPicker extends LbOptions {}\n\ncustomElements.define(\"lb-picker\", LbPicker);\n"]}
|
package/dist/lb-picker.html
CHANGED
|
@@ -1,46 +1,14 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
|
-
import {
|
|
2
|
+
import { WrappedControl } from "./control.js";
|
|
3
3
|
/**
|
|
4
|
-
* A
|
|
5
|
-
*
|
|
6
|
-
* request vocabulary and the binding vocabulary running in both directions on
|
|
7
|
-
* one widget.
|
|
4
|
+
* A `<select>` with its label and the options the page writes, as one
|
|
5
|
+
* control, like `lb-input` and for the same reasons.
|
|
8
6
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* A select that writes a query parm sends nothing: the hub reads its choice
|
|
13
|
-
* off the bubbling `change` and puts it in the address bar.
|
|
7
|
+
* Inside `lb-query="lb-url"` with `lb-request="lb-row-update"` it writes a
|
|
8
|
+
* query parm, and the hub answers that itself.
|
|
14
9
|
*/
|
|
15
|
-
class LbSelect extends
|
|
16
|
-
|
|
17
|
-
attributeChangedCallback(_name, _old, value) {
|
|
18
|
-
const select = this.querySelector("select");
|
|
19
|
-
if (!select) {
|
|
20
|
-
console.error(`lb-select: no <select> to receive the value`);
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
select.value = value;
|
|
24
|
-
}
|
|
25
|
-
connectedCallback() {
|
|
26
|
-
this.addEventListener("change", () => {
|
|
27
|
-
const select = this.querySelector("select");
|
|
28
|
-
if (!select) {
|
|
29
|
-
console.error(`lb-select: change with no <select>, ignoring`);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
const name = this.getAttribute(ATTR_ACTION);
|
|
33
|
-
if (!name && this.hasAttribute(ATTR_QUERY_PARM))
|
|
34
|
-
return;
|
|
35
|
-
if (!name) {
|
|
36
|
-
console.error(`lb-select: no ${ATTR_ACTION}, nothing to send`);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
// The hub scopes it, so a choice inside a row says which row.
|
|
40
|
-
const detail = { action: name, value: select.value };
|
|
41
|
-
this.dispatchEvent(new CustomEvent(LB_EVENT_NAME, { bubbles: true, detail }));
|
|
42
|
-
});
|
|
43
|
-
}
|
|
10
|
+
class LbSelect extends WrappedControl {
|
|
11
|
+
inner = "select";
|
|
44
12
|
}
|
|
45
13
|
customElements.define("lb-select", LbSelect);
|
|
46
14
|
//# sourceMappingURL=lb-select.browser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-select.browser.js","sourceRoot":"","sources":["../widgets/lb-select.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,OAAO,
|
|
1
|
+
{"version":3,"file":"lb-select.browser.js","sourceRoot":"","sources":["../widgets/lb-select.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,QAAS,SAAQ,cAAc;IACzB,KAAK,GAAG,QAAQ,CAAC;CAC5B;AAED,cAAc,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC","sourcesContent":["/// <reference lib=\"dom\" />\n\nimport { WrappedControl } from \"./control.js\";\n\n/**\n * A `<select>` with its label and the options the page writes, as one\n * control, like `lb-input` and for the same reasons.\n *\n * Inside `lb-query=\"lb-url\"` with `lb-request=\"lb-row-update\"` it writes a\n * query parm, and the hub answers that itself.\n */\nclass LbSelect extends WrappedControl {\n protected inner = \"select\";\n}\n\ncustomElements.define(\"lb-select\", LbSelect);\n"]}
|
package/dist/lb-select.html
CHANGED
package/dist/lb-table.browser.js
CHANGED
|
@@ -1,116 +1,89 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
|
-
import {
|
|
2
|
+
import { ATTR_COLUMN, ATTR_ROW_REQUESTED, LB_DONE_EVENT_NAME, REQUEST_ROW_INSERT, REQUESTED_CREATED, REQUESTED_MOVED, } from "@loadbare/app/constants";
|
|
3
3
|
/**
|
|
4
|
-
* A table that owns its own scaffolding
|
|
5
|
-
* widget is for once the hub reconciles lists on its own.
|
|
4
|
+
* A table that owns its own scaffolding.
|
|
6
5
|
*
|
|
7
|
-
* A plain `<table lb-
|
|
6
|
+
* A plain `<table lb-query>` with a row template inside it needs no widget at
|
|
8
7
|
* all. This one supplies the table, and the developer writes what only the
|
|
9
8
|
* page knows: the heading row and the row template. That division is forced
|
|
10
9
|
* rather than chosen — a `<thead>` written inside a custom element is not
|
|
11
10
|
* inside a table, so the parser drops the tag and keeps the text. A
|
|
12
11
|
* `<template>` survives anywhere, so the heading row arrives wrapped and
|
|
13
|
-
* expansion unwraps it into the `lb-template` destination beside the slot.
|
|
12
|
+
* expansion unwraps it into the `lb-exp-template` destination beside the slot.
|
|
14
13
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
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.
|
|
14
|
+
* Placement and groups are the hub's, by the query's order and the page's
|
|
15
|
+
* group templates. What this widget adds at run time is the ghost row, and
|
|
16
|
+
* scrolling to the row the page's own request created or moved.
|
|
21
17
|
*
|
|
22
|
-
* The `foot` destination
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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.
|
|
18
|
+
* The `foot` destination needs no code here. A `<tfoot>` is a place on the
|
|
19
|
+
* page, not a row the hub delivers, so what lands in it is an aggregate or a
|
|
20
|
+
* `row` query of its own, named on its `<tr>`.
|
|
27
21
|
*/
|
|
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";
|
|
32
22
|
/**
|
|
33
|
-
* The
|
|
23
|
+
* The ghost row's `<tbody>`, a blank row for entering a new one, between the
|
|
24
|
+
* rows and the footer. The page writes it as the `ghost` template, so it
|
|
25
|
+
* ships in the document like the heading and the footer, and the hub lands
|
|
26
|
+
* on whatever in it names a query. It is not a live row: the hub never
|
|
27
|
+
* reconciles it and never counts it.
|
|
34
28
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
29
|
+
* A `<form>` cannot wrap a `<tr>`, so the widget makes one outside the table
|
|
30
|
+
* and joins the row's controls and buttons to it with the HTML `form`
|
|
31
|
+
* attribute. Sending is the page's: a button in the row carrying
|
|
32
|
+
* `lb-row-insert` submits that form, and the hub gathers what the form owns.
|
|
33
|
+
* The hub resets the form once the insert succeeds.
|
|
38
34
|
*/
|
|
39
|
-
const
|
|
35
|
+
const GHOST = "data-ghost";
|
|
36
|
+
/** Ghost row forms need ids for the `form` attribute; any unique one will do. */
|
|
37
|
+
let ghostForms = 0;
|
|
40
38
|
class LbTable extends HTMLElement {
|
|
41
39
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
40
|
+
* A ghost row is for entering the next row, so once one is saved the
|
|
41
|
+
* cursor goes back to the start of it. `lb-request-done` comes after the
|
|
42
|
+
* response items have landed, so nothing the hub places afterwards takes
|
|
43
|
+
* the focus away again. A refused insert leaves the cursor where the user
|
|
44
|
+
* left it.
|
|
47
45
|
*/
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
: undefined;
|
|
59
|
-
template.parentElement.insertBefore(el, after ?? end);
|
|
46
|
+
constructor() {
|
|
47
|
+
super();
|
|
48
|
+
this.addEventListener(LB_DONE_EVENT_NAME, (e) => {
|
|
49
|
+
const { request, error } = e.detail;
|
|
50
|
+
if (error || request.name !== REQUEST_ROW_INSERT)
|
|
51
|
+
return;
|
|
52
|
+
const row = e.target.closest(`[${GHOST}]`);
|
|
53
|
+
if (row)
|
|
54
|
+
firstControl(row)?.focus();
|
|
55
|
+
});
|
|
60
56
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
connectedCallback() {
|
|
58
|
+
const ghost = this.querySelector(`tbody[${GHOST}]`);
|
|
59
|
+
if (!ghost?.firstElementChild || this.ghostForm())
|
|
60
|
+
return;
|
|
61
|
+
const form = document.createElement("form");
|
|
62
|
+
form.id = `ghost-row-${++ghostForms}`;
|
|
63
|
+
form.hidden = true;
|
|
64
|
+
this.append(form);
|
|
65
|
+
for (const el of ghost.querySelectorAll(`[${ATTR_COLUMN}], button`)) {
|
|
66
|
+
el.setAttribute("form", form.id);
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
|
-
/**
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
* if there is none after it. Without grouping there is one section and it
|
|
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.
|
|
75
|
-
*/
|
|
76
|
-
section(heading) {
|
|
77
|
-
const template = this.querySelector("template");
|
|
78
|
-
const rows = [];
|
|
79
|
-
let node = heading
|
|
80
|
-
? heading.nextElementSibling
|
|
81
|
-
: template.parentElement.firstElementChild;
|
|
82
|
-
while (node && node !== template && !node.hasAttribute(SECTION_ROW)) {
|
|
83
|
-
if (node.hasAttribute(ATTR_KEY_VALUE))
|
|
84
|
-
rows.push(node);
|
|
85
|
-
node = node.nextElementSibling;
|
|
86
|
-
}
|
|
87
|
-
return { rows, end: node ?? template };
|
|
69
|
+
/** The row this page's request created or moved, brought into view. */
|
|
70
|
+
lbRowsLanded() {
|
|
71
|
+
this.querySelector(`[${ATTR_ROW_REQUESTED}="${REQUESTED_CREATED}"], ` +
|
|
72
|
+
`[${ATTR_ROW_REQUESTED}="${REQUESTED_MOVED}"]`)?.scrollIntoView({ block: "nearest" });
|
|
88
73
|
}
|
|
89
|
-
/** The
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
for (const heading of body.querySelectorAll(`tr[${SECTION_ROW}]`)) {
|
|
93
|
-
if (heading.getAttribute(SECTION_ROW) === label)
|
|
94
|
-
return heading;
|
|
95
|
-
}
|
|
96
|
-
const heading = document.createElement("tr");
|
|
97
|
-
heading.setAttribute(SECTION_ROW, label);
|
|
98
|
-
const cell = document.createElement("th");
|
|
99
|
-
cell.setAttribute("scope", "rowgroup");
|
|
100
|
-
// How wide the table is, from the row the developer wrote. Nothing
|
|
101
|
-
// else knows, and nothing had to be told twice.
|
|
102
|
-
cell.colSpan = template.content.firstElementChild?.childElementCount ?? 1;
|
|
103
|
-
cell.textContent = label;
|
|
104
|
-
heading.append(cell);
|
|
105
|
-
body.insertBefore(heading, template);
|
|
106
|
-
return heading;
|
|
74
|
+
/** The ghost row's form, once this table has made it. */
|
|
75
|
+
ghostForm() {
|
|
76
|
+
return this.querySelector(":scope > form");
|
|
107
77
|
}
|
|
108
78
|
}
|
|
109
|
-
/**
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
79
|
+
/**
|
|
80
|
+
* The first control in a row the user can type into or choose from. A
|
|
81
|
+
* control a widget wraps is found inside it, since the wrapper takes no focus.
|
|
82
|
+
*/
|
|
83
|
+
function firstControl(row) {
|
|
84
|
+
return [
|
|
85
|
+
...row.querySelectorAll('input:not([type="hidden"]), select, textarea'),
|
|
86
|
+
].find((el) => !el.matches(":disabled") && el.checkVisibility?.() !== false);
|
|
114
87
|
}
|
|
115
88
|
customElements.define("lb-table", LbTable);
|
|
116
89
|
//# sourceMappingURL=lb-table.browser.js.map
|