@loadbare/widgets 0.3.0 → 0.3.2

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.
@@ -7,8 +7,9 @@ import { ATTR_ACTION, ATTR_VALUE, LB_EVENT_NAME, } from "@loadbare/app/constants
7
7
  *
8
8
  * The other direction of the same cell: `lb-action` names what a change
9
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.
10
+ * the reserved `lb-row-update` reaches the server with the list and key this
11
+ * input sits in, and `values` holding this input's cell alone. Without
12
+ * `lb-action` a change sends nothing.
12
13
  *
13
14
  * The developer has to ask, because committing per field is one editing model
14
15
  * and not the only one. An input inside an `lb-row-insert` or `lb-row-update`
@@ -1 +1 @@
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
+ {"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;;;;;;;;;;;;;;;GAeG;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-row-update` reaches the server with the list and key this\n * input sits in, and `values` holding this input's cell alone. Without\n * `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,5 +1,7 @@
1
1
  import type { ListHost, Row } from "@loadbare/app/types";
2
2
  export declare class LbOptions extends HTMLElement implements ListHost {
3
+ static observedAttributes: string[];
4
+ attributeChangedCallback(_name: string, _old: string, value: string): void;
3
5
  /**
4
6
  * The hub reconciles the list and calls this for placement only, so this
5
7
  * widget carries no copy of what a row is or when one arrives.
@@ -1 +1 @@
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
+ {"version":3,"file":"lb-options.browser.d.ts","sourceRoot":"","sources":["../widgets/lb-options.browser.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAc,QAAQ,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AA6BrE,qBAAa,SAAU,SAAQ,WAAY,YAAW,QAAQ;IAC5D,MAAM,CAAC,kBAAkB,WAAgB;IAEzC,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAKnE;;;OAGG;IACH,UAAU,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAUtE,0EAA0E;IAC1E,YAAY,IAAI,IAAI;IAepB,wEAAwE;IACxE,OAAO,CAAC,QAAQ;IAUhB,iBAAiB;CAoBlB"}
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="dom" />
2
- import { ATTR_ACTION, ATTR_KEY_VALUE, LB_EVENT_NAME, } from "@loadbare/app/constants";
2
+ import { ATTR_ACTION, ATTR_KEY_VALUE, ATTR_QUERY_PARM, ATTR_VALUE, LB_EVENT_NAME, } from "@loadbare/app/constants";
3
3
  /**
4
4
  * A `<select>` whose choices come from a list, and the case that shows why
5
5
  * placement belongs to the widget rather than to the framework.
@@ -16,6 +16,10 @@ import { ATTR_ACTION, ATTR_KEY_VALUE, LB_EVENT_NAME, } from "@loadbare/app/const
16
16
  * the hub stamps on each option is copied to its `value`, and the developer
17
17
  * names the key column once, with `lb-key` on the template.
18
18
  *
19
+ * It receives a value like any other cell, which selects the option whose
20
+ * key it is. A picker that writes a query parm sends nothing: the hub reads
21
+ * its choice off the bubbling `change` and lands the URL's back on it.
22
+ *
19
23
  * Exported because `lb-picker` is this widget with its row template
20
24
  * supplied by the definition instead of by the page. Nothing about the
21
25
  * behavior changes, so nothing about the behavior is written twice.
@@ -23,6 +27,12 @@ import { ATTR_ACTION, ATTR_KEY_VALUE, LB_EVENT_NAME, } from "@loadbare/app/const
23
27
  /** Named on the row template: the column whose distinct values become groups. */
24
28
  const GROUP_COLUMN = "data-group";
25
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
+ }
26
36
  /**
27
37
  * The hub reconciles the list and calls this for placement only, so this
28
38
  * widget carries no copy of what a row is or when one arrives.
@@ -48,6 +58,10 @@ export class LbOptions extends HTMLElement {
48
58
  if (!group.querySelector("option"))
49
59
  group.remove();
50
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;
51
65
  }
52
66
  /** The group with this label, or a new one in front of the template. */
53
67
  groupFor(select, template, label) {
@@ -68,6 +82,8 @@ export class LbOptions extends HTMLElement {
68
82
  return;
69
83
  }
70
84
  const name = this.getAttribute(ATTR_ACTION);
85
+ if (!name && this.hasAttribute(ATTR_QUERY_PARM))
86
+ return;
71
87
  if (!name) {
72
88
  console.error(`lb-options: no ${ATTR_ACTION}, nothing to send`);
73
89
  return;
@@ -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,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"]}
1
+ {"version":3,"file":"lb-options.browser.js","sourceRoot":"","sources":["../widgets/lb-options.browser.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference lib=\"dom\" />\n\nimport {\n ATTR_ACTION,\n ATTR_KEY_VALUE,\n ATTR_QUERY_PARM,\n ATTR_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 * It receives a value like any other cell, which selects the option whose\n * key it is. A picker that writes a query parm sends nothing: the hub reads\n * its choice off the bubbling `change` and lands the URL's back on it.\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 static observedAttributes = [ATTR_VALUE];\n\n attributeChangedCallback(_name: string, _old: string, value: string) {\n const select = this.querySelector(\"select\");\n if (select) select.value = value;\n }\n\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 // A value that landed before its option did selects it now.\n const value = this.getAttribute(ATTR_VALUE);\n if (value !== null) this.querySelector(\"select\")!.value = value;\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 && this.hasAttribute(ATTR_QUERY_PARM)) return;\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"]}
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="dom" />
2
- import { ATTR_ACTION, ATTR_VALUE, LB_EVENT_NAME, } from "@loadbare/app/constants";
2
+ import { ATTR_ACTION, ATTR_QUERY_PARM, 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
5
  * and on change it sends the action the developer declared for it — the
@@ -8,6 +8,9 @@ import { ATTR_ACTION, ATTR_VALUE, LB_EVENT_NAME, } from "@loadbare/app/constants
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.
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.
11
14
  */
12
15
  class LbSelect extends HTMLElement {
13
16
  static observedAttributes = [ATTR_VALUE];
@@ -27,6 +30,8 @@ class LbSelect extends HTMLElement {
27
30
  return;
28
31
  }
29
32
  const name = this.getAttribute(ATTR_ACTION);
33
+ if (!name && this.hasAttribute(ATTR_QUERY_PARM))
34
+ return;
30
35
  if (!name) {
31
36
  console.error(`lb-select: no ${ATTR_ACTION}, nothing to send`);
32
37
  return;
@@ -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,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"]}
1
+ {"version":3,"file":"lb-select.browser.js","sourceRoot":"","sources":["../widgets/lb-select.browser.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,OAAO,EACL,WAAW,EACX,eAAe,EACf,UAAU,EACV,aAAa,GACd,MAAM,yBAAyB,CAAC;AAGjC;;;;;;;;;;;GAWG;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,IAAI,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC;gBAAE,OAAO;YACxD,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_QUERY_PARM,\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 *\n * A select that writes a query parm sends nothing: the hub reads its choice\n * off the bubbling `change` and puts it in the address bar.\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 && this.hasAttribute(ATTR_QUERY_PARM)) return;\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/docs/lb-input.md CHANGED
@@ -10,7 +10,7 @@ Requires attributes `lb-cell` and `exp-label`.
10
10
  <div lb-list="people">
11
11
  <template lb-key="id">
12
12
  <div>
13
- <lb-input lb-cell="name" exp-label="Name" lb-action="lb-cell-change"></lb-input>
13
+ <lb-input lb-cell="name" exp-label="Name" lb-action="lb-row-update"></lb-input>
14
14
  <lb-input lb-cell="email" exp-label="Email" exp-readonly></lb-input>
15
15
  </div>
16
16
  </template>
@@ -36,9 +36,10 @@ off drops the attribute.
36
36
  |-------------|----------------------------------------------------|
37
37
  | `lb-action` | what a change sends; nothing is sent without it |
38
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.
39
+ `lb-action="lb-row-update"` saves the input's own cell: the hub sends an
40
+ `lb-row-update` whose `values` holds that one cell, and the list's `rowUpdate`
41
+ answers it the same way it answers a form. Any other name sends that action,
42
+ carrying the input's value. The hub adds the binding below to either.
42
43
 
43
44
  ## Binding
44
45
 
@@ -34,6 +34,7 @@ improvements through iterative use.
34
34
  |-------------|--------------------|--------------------------------------|
35
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-query-parm` | the element itself | the query parm a change writes |
37
38
  | `lb-key` | the row template | the column that identifies a row |
38
39
  | `data-group` | the row template | the column that sections the options |
39
40
  | `lb-cell` | the `<option>` | the column the option shows |
@@ -51,7 +52,13 @@ Choosing an option notifies the hub, which fires a request to the server: the
51
52
  action named by `lb-action`, carrying the key of the chosen row. Declare that
52
53
  action on the server.
53
54
 
54
- `lb-action` is required. Without it, a change does nothing.
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.
59
+
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.
55
62
 
56
63
  While the request is in flight the hub marks the `lb-options` with
57
64
  `lb-pending`, and on failure with `lb-error`. Both are there for a
package/docs/lb-picker.md CHANGED
@@ -42,11 +42,12 @@ options ungrouped.
42
42
  |-------------|--------------------|-------------------------------------|
43
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
+ | `lb-query-parm` | the element itself | the query parm a change writes |
45
46
 
46
47
  ## Choosing
47
48
 
48
49
  The same as [`lb-options`](./lb-options.md): grouping, the key as the option's
49
- value, `lb-row-count`, and the action on change.
50
+ value, `lb-row-count`, and the action or query parm on change.
50
51
 
51
52
  Write `lb-options` and the template instead for a row that needs a second
52
53
  element, or an option built from two columns.
package/docs/lb-select.md CHANGED
@@ -31,6 +31,7 @@ improvements through iterative use.
31
31
  |-------------|--------------------|-----------------------------|
32
32
  | `lb-cell` | the element itself | the column the select shows |
33
33
  | `lb-action` | the element itself | the action a change sends |
34
+ | `lb-query-parm` | the element itself | the query parm a change writes |
34
35
 
35
36
  ## Choosing
36
37
 
@@ -38,7 +39,9 @@ Choosing an option notifies the hub, which fires a request to the server: the
38
39
  action named by `lb-action`, carrying the chosen option's value. Declare that
39
40
  action on the server.
40
41
 
41
- `lb-action` is required. Without it, a change does nothing.
42
+ `lb-action` is required, unless the select carries `lb-query-parm`, in which
43
+ case the hub writes the choice into the URL and the select sends nothing.
44
+ Without either, a change does nothing.
42
45
 
43
46
  While the request is in flight the hub marks the `lb-select` with
44
47
  `lb-pending`, and on failure with `lb-error`. Both are there for a
@@ -32,7 +32,7 @@ to the hub's `lb-navigation` query, which the hub lands on every navigation.
32
32
  | Cell | Holds |
33
33
  |--------------|-----------------------------------------------------------|
34
34
  | `page-label` | the text of the nav anchor for the path, or empty if none |
35
- | `page-uri` | the path as the browser has it |
35
+ | `page-uri` | the path and query string as the browser has them |
36
36
 
37
37
  The widget shows both. It does not yet choose between "the page *Members*
38
38
  is not in this app" and "the URL */members* is not in this app"; that is the
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@loadbare/widgets",
3
3
  "description": "The basic widget set for @loadbare/app: input, select, options, picker, table, unknown-page",
4
- "version": "0.3.0",
4
+ "version": "0.3.2",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",
@@ -49,10 +49,10 @@
49
49
  "node": ">=22"
50
50
  },
51
51
  "peerDependencies": {
52
- "@loadbare/app": "^0.7.0"
52
+ "@loadbare/app": ">=0.8.2 <1.0.0"
53
53
  },
54
54
  "devDependencies": {
55
- "@loadbare/app": "^0.7.0",
55
+ "@loadbare/app": ">=0.8.2 <1.0.0",
56
56
  "@types/jsdom": "^21.1.7",
57
57
  "@types/node": "^22.0.0",
58
58
  "jsdom": "^25.0.0",