@loadbare/app 0.10.0 → 0.12.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/dist/build/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +51 -38
- package/dist/build/assemble.js.map +1 -1
- package/dist/build/expand.d.ts +6 -1
- package/dist/build/expand.d.ts.map +1 -1
- package/dist/build/expand.js +92 -7
- package/dist/build/expand.js.map +1 -1
- package/dist/core/lb-constants.d.ts +4 -0
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +16 -0
- package/dist/core/lb-constants.js.map +1 -1
- package/dist/core/lb-types.d.ts +11 -0
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js.map +1 -1
- package/dist/hub/lb-apply.d.ts +7 -1
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +136 -18
- package/dist/hub/lb-apply.js.map +1 -1
- package/dist/hub/lb-hub.browser.d.ts.map +1 -1
- package/dist/hub/lb-hub.browser.js +121 -37
- package/dist/hub/lb-hub.browser.js.map +1 -1
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +17 -0
- package/dist/server/lb-server.js.map +1 -1
- package/docs/TECHREF-1.0.md +151 -10
- package/docs/comparison.md +30 -5
- package/docs/reference/chrome.md +15 -0
- package/docs/reference/custom-elements.md +163 -9
- package/docs/reference/data-binding.md +45 -2
- package/docs/reference/page-files.md +59 -2
- package/docs/what-does-loadbare-extend.md +124 -0
- package/package.json +1 -1
- package/skills/loadbare-app/SKILL.md +86 -10
- package/skills/loadbare-app/references/TECHREF-1.0.md +151 -10
- package/skills/loadbare-app/references/chrome.md +15 -0
- package/skills/loadbare-app/references/custom-elements.md +163 -9
- package/skills/loadbare-app/references/data-binding.md +45 -2
- package/skills/loadbare-app/references/page-files.md +59 -2
|
@@ -25,6 +25,9 @@ export const ATTR_COLUMN = "lb-column";
|
|
|
25
25
|
// branch in the document so the hub holds nothing. Landing reaches inside it,
|
|
26
26
|
// so a branch that returns is already current; nothing else does.
|
|
27
27
|
export const ATTR_SHOW = "lb-show";
|
|
28
|
+
// Written before the column in ATTR_SHOW, it reverses the condition:
|
|
29
|
+
// `lb-show="!chosen"` is present while `chosen` is null or false.
|
|
30
|
+
export const SHOW_NOT = "!";
|
|
28
31
|
// Stamped by the hub on every element it sets from a column: the value it
|
|
29
32
|
// set, so a stylesheet can select on it and a custom element that is not a
|
|
30
33
|
// control can render it.
|
|
@@ -33,6 +36,14 @@ export const ATTR_COLUMN_VALUE = "lb-column-value";
|
|
|
33
36
|
// itself: the row's key. The key column is the query's, declared by the
|
|
34
37
|
// server and carried by every response item.
|
|
35
38
|
export const ATTR_KEY_VALUE = "lb-key-value";
|
|
39
|
+
// Stamped by the hub on each live row, and on nothing else: the element it
|
|
40
|
+
// cloned from a row template. A key alone does not say so, because an
|
|
41
|
+
// element a `row` lands on carries one too, and a live row may name a query
|
|
42
|
+
// of its own, so neither the key nor `lb-query` tells the two apart.
|
|
43
|
+
export const ATTR_ROW_LIVE = "lb-row-live";
|
|
44
|
+
// Selects a live row. Anything that walks rows — reconciliation, gathering,
|
|
45
|
+
// a custom element's own placement — finds them with this and nothing else.
|
|
46
|
+
export const LIVE_ROW = `[${ATTR_ROW_LIVE}]`;
|
|
36
47
|
// Stamped by the hub on an element with a row template: how many live rows
|
|
37
48
|
// it holds, so "no members yet" is a stylesheet rule rather than a
|
|
38
49
|
// conditional in every widget.
|
|
@@ -44,6 +55,11 @@ export const ATTR_REQUEST = "lb-request";
|
|
|
44
55
|
// Every request travels as one bubbling event of this name, whoever issued
|
|
45
56
|
// it, so an ancestor may stop it.
|
|
46
57
|
export const LB_EVENT_NAME = "lb-request";
|
|
58
|
+
// The counterpart of LB_EVENT_NAME: raised on the element that committed,
|
|
59
|
+
// once what the request brought back has landed or the round trip has
|
|
60
|
+
// failed. It bubbles, so the element that cares about the outcome need not
|
|
61
|
+
// be the one that committed.
|
|
62
|
+
export const LB_DONE_EVENT_NAME = "lb-request-done";
|
|
47
63
|
// Stamped on the element that issued a request while its round trip is in
|
|
48
64
|
// flight, alongside aria-busy. A commit on an element carrying it is ignored.
|
|
49
65
|
export const ATTR_REQUEST_PENDING = "lb-request-pending";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-constants.js","sourceRoot":"","sources":["../../core/lb-constants.ts"],"names":[],"mappings":"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","sourcesContent":["// Every Loadbare attribute name lives here. Nothing writes one as a string\n// literal — not a widget, not a page host, not a test. See\n// docs/reference/data-binding.md.\n\n// --- Landing (docs/reference/data-binding.md) ----------------------------\n// Everything is a query. The server answers every request with response\n// items, one per query, and each carries the query's kind and key. The\n// markup names a query and the columns it shows; it never repeats what the\n// server already declared.\n\n// Names the query whose rows land in an element's content. A nested one\n// begins a new query: what is inside it is that query's.\nexport const ATTR_QUERY = \"lb-query\";\n\n// The column an element shows, read from the nearest ancestor row. On a\n// control the hub sets its value, and gathers it back under this name when\n// the control's request is issued.\nexport const ATTR_COLUMN = \"lb-column\";\n\n// The column that decides whether an element is present: `null` and `false`\n// are off, and every other value is on. It reads from the nearest ancestor\n// row, as ATTR_COLUMN does.\n//\n// An element that is off is moved into a `<template>` carrying this attribute\n// and the same column, standing where the element stood, and moved back out\n// when the column turns on. The builder ships every such element already\n// inside its template. A developer writes this attribute on an element and\n// never on a template: the template form is Loadbare's, and holds the absent\n// branch in the document so the hub holds nothing. Landing reaches inside it,\n// so a branch that returns is already current; nothing else does.\nexport const ATTR_SHOW = \"lb-show\";\n\n// Stamped by the hub on every element it sets from a column: the value it\n// set, so a stylesheet can select on it and a custom element that is not a\n// control can render it.\nexport const ATTR_COLUMN_VALUE = \"lb-column-value\";\n\n// Stamped by the hub on each live row, and on an element a `row` lands on\n// itself: the row's key. The key column is the query's, declared by the\n// server and carried by every response item.\nexport const ATTR_KEY_VALUE = \"lb-key-value\";\n\n// Stamped by the hub on an element with a row template: how many live rows\n// it holds, so \"no members yet\" is a stylesheet rule rather than a\n// conditional in every widget.\nexport const ATTR_QUERY_ROW_COUNT = \"lb-query-row-count\";\n\n// --- Requests (docs/reference/data-binding.md) --------------------------\n// Names the request an element issues when it commits: a form on submit, a\n// control on change, anything else on click.\nexport const ATTR_REQUEST = \"lb-request\";\n\n// Every request travels as one bubbling event of this name, whoever issued\n// it, so an ancestor may stop it.\nexport const LB_EVENT_NAME = \"lb-request\";\n\n// Stamped on the element that issued a request while its round trip is in\n// flight, alongside aria-busy. A commit on an element carrying it is ignored.\nexport const ATTR_REQUEST_PENDING = \"lb-request-pending\";\n\n// Stamped on the element that issued a request when its round trip failed —\n// a non-2xx response, a thrown application error, or a timeout, all alike.\n// Cleared at the start of that element's next request.\nexport const ATTR_REQUEST_ERROR = \"lb-request-error\";\n\n// --- Reserved names ----------------------------------------------------\n// Loadbare owns every name beginning with this prefix: attribute names, the\n// values of its attributes, query names, request names, the column names of\n// its own queries, custom element names and event names. The server refuses\n// a page that declares a query or a request so named.\nexport const LB_RESERVED_PREFIX = \"lb-\";\n\n// The requests Loadbare provides. Each runs the page's crud declaration for\n// the request's query.\nexport const REQUEST_ROW_INSERT = \"lb-row-insert\";\nexport const REQUEST_ROW_UPDATE = \"lb-row-update\";\nexport const REQUEST_ROW_DELETE = \"lb-row-delete\";\n\nexport const LB_ROW_REQUESTS = [\n REQUEST_ROW_INSERT,\n REQUEST_ROW_UPDATE,\n REQUEST_ROW_DELETE,\n] as const;\n\n// --- Kinds --------------------------------------------------------------\nexport const KIND_ROW = \"row\";\nexport const KIND_ROWS = \"rows\";\n\n// --- The URL (docs/reference/chrome.md) ---------------------------------\n// The query the hub serves: one row, keyed by the path, whose other columns\n// are the query parms.\nexport const URL_QUERY = \"lb-url\";\nexport const URL_COLUMN_PATH = \"lb-path\";\nexport const URL_COLUMN_PAGE_LABEL = \"lb-page-label\";\nexport const URL_COLUMN_PAGE_UNKNOWN = \"lb-page-unknown\";\n\n// On an `<a>`: a plain primary click sets `lb-path` from the `href`.\nexport const ATTR_URL_LINK = \"lb-url-link\";\n\n// On an element with `lb-request`: its update of `lb-url` pushes a history\n// entry instead of replacing one.\nexport const ATTR_URL_PUSH = \"lb-url-push\";\n\n// On a `<dialog>`: the hub calls showModal() when `lb-page-unknown` is true.\nexport const ATTR_URL_UNKNOWN = \"lb-url-unknown\";\n\n// --- Build time (docs/reference/custom-elements.md) ---------------------\n// Marks the element in an element file whose children receive the rest of\n// the custom element's content. An attribute rather than an element because\n// HTML content models discard foreign elements inside <select> and <table>.\nexport const ATTR_EXP_SLOT = \"lb-exp-slot\";\n\n// One name in two positions. In an element file it marks a destination; on a\n// `<template>` in the custom element's content it names the destination\n// that template is for, and the template's content moves into it.\nexport const ATTR_EXP_TEMPLATE = \"lb-exp-template\";\n\n// A page ships as <template lb-page=\"<stub>\">.\nexport const ATTR_PAGE = \"lb-page\";\n\n// Stamped by the builder beside ATTR_PAGE: the text content of the page\n// file's `<title>`, which the builder removes.\nexport const ATTR_PAGE_TITLE = \"lb-page-title\";\n\n// The attributes a developer writes. The builder refuses any other `lb-`\n// attribute in markup: the rest are stamps, written by the hub or the\n// builder and never by hand or by script.\nexport const DEVELOPER_ATTRIBUTES = [\n ATTR_QUERY,\n ATTR_COLUMN,\n ATTR_SHOW,\n ATTR_REQUEST,\n ATTR_URL_LINK,\n ATTR_URL_PUSH,\n ATTR_URL_UNKNOWN,\n] as const;\n\n// The hub's own tag. It is exempt from the rule every other LB-* tag\n// follows: expansion does not require it to have a definition (build/expand.ts),\n// and the client build does not require it to have a script of its own\n// (build/elements.ts) — it is the framework's live element, always present,\n// registered once by the package's main entry point, not a widget an\n// application discovers or declares.\nexport const HUB_TAG_NAME = \"lb-hub\";\n\n// --- Wire --------------------------------------------------------------\n// One path, one method: everything the hub asks of the server is a POST\n// below this path. The page's stub follows it, and the query string the\n// browser is showing rides after that verbatim, so `/accounts?acct=23` asks\n// as `POST /lb/accounts?acct=23` and the server always knows what URL the\n// browser is showing. Navigation is therefore the only GET an application\n// serves, which is what leaves the page name space unrestricted.\nexport const LB_ENDPOINT = \"/lb\";\n\n// A client-side deadline for one round trip. fetch() has no deadline of its\n// own; past this the request is aborted and treated as a failure.\nexport const LB_REQUEST_TIMEOUT_MS = 10_000;\n"]}
|
|
1
|
+
{"version":3,"file":"lb-constants.js","sourceRoot":"","sources":["../../core/lb-constants.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,2DAA2D;AAC3D,kCAAkC;AAElC,4EAA4E;AAC5E,wEAAwE;AACxE,uEAAuE;AACvE,2EAA2E;AAC3E,2BAA2B;AAE3B,wEAAwE;AACxE,yDAAyD;AACzD,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC;AAErC,wEAAwE;AACxE,2EAA2E;AAC3E,mCAAmC;AACnC,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC;AAEvC,4EAA4E;AAC5E,2EAA2E;AAC3E,4BAA4B;AAC5B,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,yEAAyE;AACzE,2EAA2E;AAC3E,6EAA6E;AAC7E,8EAA8E;AAC9E,kEAAkE;AAClE,MAAM,CAAC,MAAM,SAAS,GAAG,SAAS,CAAC;AAEnC,qEAAqE;AACrE,kEAAkE;AAClE,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,CAAC;AAE5B,0EAA0E;AAC1E,2EAA2E;AAC3E,yBAAyB;AACzB,MAAM,CAAC,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAEnD,0EAA0E;AAC1E,wEAAwE;AACxE,6CAA6C;AAC7C,MAAM,CAAC,MAAM,cAAc,GAAG,cAAc,CAAC;AAE7C,2EAA2E;AAC3E,sEAAsE;AACtE,4EAA4E;AAC5E,qEAAqE;AACrE,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC;AAE3C,4EAA4E;AAC5E,4EAA4E;AAC5E,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,aAAa,GAAG,CAAC;AAE7C,2EAA2E;AAC3E,mEAAmE;AACnE,+BAA+B;AAC/B,MAAM,CAAC,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAEzD,2EAA2E;AAC3E,2EAA2E;AAC3E,6CAA6C;AAC7C,MAAM,CAAC,MAAM,YAAY,GAAG,YAAY,CAAC;AAEzC,2EAA2E;AAC3E,kCAAkC;AAClC,MAAM,CAAC,MAAM,aAAa,GAAG,YAAY,CAAC;AAE1C,0EAA0E;AAC1E,sEAAsE;AACtE,2EAA2E;AAC3E,6BAA6B;AAC7B,MAAM,CAAC,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEpD,0EAA0E;AAC1E,8EAA8E;AAC9E,MAAM,CAAC,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAEzD,4EAA4E;AAC5E,2EAA2E;AAC3E,uDAAuD;AACvD,MAAM,CAAC,MAAM,kBAAkB,GAAG,kBAAkB,CAAC;AAErD,0EAA0E;AAC1E,4EAA4E;AAC5E,4EAA4E;AAC5E,4EAA4E;AAC5E,sDAAsD;AACtD,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,CAAC;AAExC,4EAA4E;AAC5E,uBAAuB;AACvB,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAClD,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAClD,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAElD,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,kBAAkB;IAClB,kBAAkB;IAClB,kBAAkB;CACV,CAAC;AAEX,2EAA2E;AAC3E,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,CAAC;AAC9B,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC;AAEhC,2EAA2E;AAC3E,4EAA4E;AAC5E,uBAAuB;AACvB,MAAM,CAAC,MAAM,SAAS,GAAG,QAAQ,CAAC;AAClC,MAAM,CAAC,MAAM,eAAe,GAAG,SAAS,CAAC;AACzC,MAAM,CAAC,MAAM,qBAAqB,GAAG,eAAe,CAAC;AACrD,MAAM,CAAC,MAAM,uBAAuB,GAAG,iBAAiB,CAAC;AAEzD,qEAAqE;AACrE,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC;AAE3C,2EAA2E;AAC3E,kCAAkC;AAClC,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC;AAE3C,6EAA6E;AAC7E,MAAM,CAAC,MAAM,gBAAgB,GAAG,gBAAgB,CAAC;AAEjD,2EAA2E;AAC3E,0EAA0E;AAC1E,4EAA4E;AAC5E,4EAA4E;AAC5E,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC;AAE3C,6EAA6E;AAC7E,wEAAwE;AACxE,kEAAkE;AAClE,MAAM,CAAC,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAEnD,+CAA+C;AAC/C,MAAM,CAAC,MAAM,SAAS,GAAG,SAAS,CAAC;AAEnC,wEAAwE;AACxE,+CAA+C;AAC/C,MAAM,CAAC,MAAM,eAAe,GAAG,eAAe,CAAC;AAE/C,yEAAyE;AACzE,sEAAsE;AACtE,0CAA0C;AAC1C,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,UAAU;IACV,WAAW;IACX,SAAS;IACT,YAAY;IACZ,aAAa;IACb,aAAa;IACb,gBAAgB;CACR,CAAC;AAEX,qEAAqE;AACrE,iFAAiF;AACjF,uEAAuE;AACvE,4EAA4E;AAC5E,qEAAqE;AACrE,qCAAqC;AACrC,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AAErC,0EAA0E;AAC1E,wEAAwE;AACxE,wEAAwE;AACxE,4EAA4E;AAC5E,0EAA0E;AAC1E,0EAA0E;AAC1E,iEAAiE;AACjE,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,CAAC;AAEjC,4EAA4E;AAC5E,kEAAkE;AAClE,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC","sourcesContent":["// Every Loadbare attribute name lives here. Nothing writes one as a string\n// literal — not a widget, not a page host, not a test. See\n// docs/reference/data-binding.md.\n\n// --- Landing (docs/reference/data-binding.md) ----------------------------\n// Everything is a query. The server answers every request with response\n// items, one per query, and each carries the query's kind and key. The\n// markup names a query and the columns it shows; it never repeats what the\n// server already declared.\n\n// Names the query whose rows land in an element's content. A nested one\n// begins a new query: what is inside it is that query's.\nexport const ATTR_QUERY = \"lb-query\";\n\n// The column an element shows, read from the nearest ancestor row. On a\n// control the hub sets its value, and gathers it back under this name when\n// the control's request is issued.\nexport const ATTR_COLUMN = \"lb-column\";\n\n// The column that decides whether an element is present: `null` and `false`\n// are off, and every other value is on. It reads from the nearest ancestor\n// row, as ATTR_COLUMN does.\n//\n// An element that is off is moved into a `<template>` carrying this attribute\n// and the same column, standing where the element stood, and moved back out\n// when the column turns on. The builder ships every such element already\n// inside its template. A developer writes this attribute on an element and\n// never on a template: the template form is Loadbare's, and holds the absent\n// branch in the document so the hub holds nothing. Landing reaches inside it,\n// so a branch that returns is already current; nothing else does.\nexport const ATTR_SHOW = \"lb-show\";\n\n// Written before the column in ATTR_SHOW, it reverses the condition:\n// `lb-show=\"!chosen\"` is present while `chosen` is null or false.\nexport const SHOW_NOT = \"!\";\n\n// Stamped by the hub on every element it sets from a column: the value it\n// set, so a stylesheet can select on it and a custom element that is not a\n// control can render it.\nexport const ATTR_COLUMN_VALUE = \"lb-column-value\";\n\n// Stamped by the hub on each live row, and on an element a `row` lands on\n// itself: the row's key. The key column is the query's, declared by the\n// server and carried by every response item.\nexport const ATTR_KEY_VALUE = \"lb-key-value\";\n\n// Stamped by the hub on each live row, and on nothing else: the element it\n// cloned from a row template. A key alone does not say so, because an\n// element a `row` lands on carries one too, and a live row may name a query\n// of its own, so neither the key nor `lb-query` tells the two apart.\nexport const ATTR_ROW_LIVE = \"lb-row-live\";\n\n// Selects a live row. Anything that walks rows — reconciliation, gathering,\n// a custom element's own placement — finds them with this and nothing else.\nexport const LIVE_ROW = `[${ATTR_ROW_LIVE}]`;\n\n// Stamped by the hub on an element with a row template: how many live rows\n// it holds, so \"no members yet\" is a stylesheet rule rather than a\n// conditional in every widget.\nexport const ATTR_QUERY_ROW_COUNT = \"lb-query-row-count\";\n\n// --- Requests (docs/reference/data-binding.md) --------------------------\n// Names the request an element issues when it commits: a form on submit, a\n// control on change, anything else on click.\nexport const ATTR_REQUEST = \"lb-request\";\n\n// Every request travels as one bubbling event of this name, whoever issued\n// it, so an ancestor may stop it.\nexport const LB_EVENT_NAME = \"lb-request\";\n\n// The counterpart of LB_EVENT_NAME: raised on the element that committed,\n// once what the request brought back has landed or the round trip has\n// failed. It bubbles, so the element that cares about the outcome need not\n// be the one that committed.\nexport const LB_DONE_EVENT_NAME = \"lb-request-done\";\n\n// Stamped on the element that issued a request while its round trip is in\n// flight, alongside aria-busy. A commit on an element carrying it is ignored.\nexport const ATTR_REQUEST_PENDING = \"lb-request-pending\";\n\n// Stamped on the element that issued a request when its round trip failed —\n// a non-2xx response, a thrown application error, or a timeout, all alike.\n// Cleared at the start of that element's next request.\nexport const ATTR_REQUEST_ERROR = \"lb-request-error\";\n\n// --- Reserved names ----------------------------------------------------\n// Loadbare owns every name beginning with this prefix: attribute names, the\n// values of its attributes, query names, request names, the column names of\n// its own queries, custom element names and event names. The server refuses\n// a page that declares a query or a request so named.\nexport const LB_RESERVED_PREFIX = \"lb-\";\n\n// The requests Loadbare provides. Each runs the page's crud declaration for\n// the request's query.\nexport const REQUEST_ROW_INSERT = \"lb-row-insert\";\nexport const REQUEST_ROW_UPDATE = \"lb-row-update\";\nexport const REQUEST_ROW_DELETE = \"lb-row-delete\";\n\nexport const LB_ROW_REQUESTS = [\n REQUEST_ROW_INSERT,\n REQUEST_ROW_UPDATE,\n REQUEST_ROW_DELETE,\n] as const;\n\n// --- Kinds --------------------------------------------------------------\nexport const KIND_ROW = \"row\";\nexport const KIND_ROWS = \"rows\";\n\n// --- The URL (docs/reference/chrome.md) ---------------------------------\n// The query the hub serves: one row, keyed by the path, whose other columns\n// are the query parms.\nexport const URL_QUERY = \"lb-url\";\nexport const URL_COLUMN_PATH = \"lb-path\";\nexport const URL_COLUMN_PAGE_LABEL = \"lb-page-label\";\nexport const URL_COLUMN_PAGE_UNKNOWN = \"lb-page-unknown\";\n\n// On an `<a>`: a plain primary click sets `lb-path` from the `href`.\nexport const ATTR_URL_LINK = \"lb-url-link\";\n\n// On an element with `lb-request`: its update of `lb-url` pushes a history\n// entry instead of replacing one.\nexport const ATTR_URL_PUSH = \"lb-url-push\";\n\n// On a `<dialog>`: the hub calls showModal() when `lb-page-unknown` is true.\nexport const ATTR_URL_UNKNOWN = \"lb-url-unknown\";\n\n// --- Build time (docs/reference/custom-elements.md) ---------------------\n// Marks the element in an element file whose children receive the rest of\n// the custom element's content. An attribute rather than an element because\n// HTML content models discard foreign elements inside <select> and <table>.\nexport const ATTR_EXP_SLOT = \"lb-exp-slot\";\n\n// One name in two positions. In an element file it marks a destination; on a\n// `<template>` in the custom element's content it names the destination\n// that template is for, and the template's content moves into it.\nexport const ATTR_EXP_TEMPLATE = \"lb-exp-template\";\n\n// A page ships as <template lb-page=\"<stub>\">.\nexport const ATTR_PAGE = \"lb-page\";\n\n// Stamped by the builder beside ATTR_PAGE: the text content of the page\n// file's `<title>`, which the builder removes.\nexport const ATTR_PAGE_TITLE = \"lb-page-title\";\n\n// The attributes a developer writes. The builder refuses any other `lb-`\n// attribute in markup: the rest are stamps, written by the hub or the\n// builder and never by hand or by script.\nexport const DEVELOPER_ATTRIBUTES = [\n ATTR_QUERY,\n ATTR_COLUMN,\n ATTR_SHOW,\n ATTR_REQUEST,\n ATTR_URL_LINK,\n ATTR_URL_PUSH,\n ATTR_URL_UNKNOWN,\n] as const;\n\n// The hub's own tag. It is exempt from the rule every other LB-* tag\n// follows: expansion does not require it to have a definition (build/expand.ts),\n// and the client build does not require it to have a script of its own\n// (build/elements.ts) — it is the framework's live element, always present,\n// registered once by the package's main entry point, not a widget an\n// application discovers or declares.\nexport const HUB_TAG_NAME = \"lb-hub\";\n\n// --- Wire --------------------------------------------------------------\n// One path, one method: everything the hub asks of the server is a POST\n// below this path. The page's stub follows it, and the query string the\n// browser is showing rides after that verbatim, so `/accounts?acct=23` asks\n// as `POST /lb/accounts?acct=23` and the server always knows what URL the\n// browser is showing. Navigation is therefore the only GET an application\n// serves, which is what leaves the page name space unrestricted.\nexport const LB_ENDPOINT = \"/lb\";\n\n// A client-side deadline for one round trip. fetch() has no deadline of its\n// own; past this the request is aborted and treated as a failure.\nexport const LB_REQUEST_TIMEOUT_MS = 10_000;\n"]}
|
package/dist/core/lb-types.d.ts
CHANGED
|
@@ -63,6 +63,17 @@ export interface HubRequest {
|
|
|
63
63
|
key?: string;
|
|
64
64
|
values?: Record<string, string>;
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* The detail of the `lb-request-done` event: the request as the hub
|
|
68
|
+
* completed and sent it, and what came of it. `items` is every response
|
|
69
|
+
* item that landed because of it, in the order it landed. `error` is set
|
|
70
|
+
* when the request's own round trip failed, and then nothing landed.
|
|
71
|
+
*/
|
|
72
|
+
export interface HubRequestDone {
|
|
73
|
+
request: HubRequest;
|
|
74
|
+
items: ResponseItem[];
|
|
75
|
+
error?: Error;
|
|
76
|
+
}
|
|
66
77
|
/** The fields a handler receives: the request, less its name. */
|
|
67
78
|
export type RequestFields = Omit<HubRequest, "name">;
|
|
68
79
|
/** Whether a name is one of the requests Loadbare provides. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-types.d.ts","sourceRoot":"","sources":["../../core/lb-types.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,QAAQ,EACR,SAAS,EAIV,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;GAOG;AACH,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1C;;;;;;;;;GASG;AACH,MAAM,WAAW,KAAK;IACpB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,0EAA0E;AAC1E,MAAM,MAAM,IAAI,GAAG,OAAO,QAAQ,GAAG,OAAO,SAAS,CAAC;AAEtD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,GAAG,EAAE,GAAG,KAAK,CAAC;AAEvC,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,IAAI,KAAK,CAE3D;AAED,qEAAqE;AACrE,MAAM,MAAM,SAAS,GAAG,GAAG,GAAG,UAAU,CAAC;AAEzC;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAEhD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,IAAI,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACb,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG,YAAY,EAAE,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,iEAAiE;AACjE,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAErD,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,eAAe,CAOjB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGhD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,eAAe,EACtB,GAAG,EAAE,GAAG,GACP;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAc1D;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ;IACvB;;;;OAIG;IACH,UAAU,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAExE,4EAA4E;IAC5E,YAAY,CAAC,IAAI,IAAI,CAAC;CACvB"}
|
|
1
|
+
{"version":3,"file":"lb-types.d.ts","sourceRoot":"","sources":["../../core/lb-types.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,QAAQ,EACR,SAAS,EAIV,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;GAOG;AACH,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1C;;;;;;;;;GASG;AACH,MAAM,WAAW,KAAK;IACpB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,0EAA0E;AAC1E,MAAM,MAAM,IAAI,GAAG,OAAO,QAAQ,GAAG,OAAO,SAAS,CAAC;AAEtD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,GAAG,EAAE,GAAG,KAAK,CAAC;AAEvC,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,IAAI,KAAK,CAE3D;AAED,qEAAqE;AACrE,MAAM,MAAM,SAAS,GAAG,GAAG,GAAG,UAAU,CAAC;AAEzC;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAEhD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,IAAI,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACb,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG,YAAY,EAAE,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,UAAU,CAAC;IACpB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,iEAAiE;AACjE,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAErD,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,eAAe,CAOjB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGhD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,eAAe,EACtB,GAAG,EAAE,GAAG,GACP;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAc1D;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ;IACvB;;;;OAIG;IACH,UAAU,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAExE,4EAA4E;IAC5E,YAAY,CAAC,IAAI,IAAI,CAAC;CACvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-types.js","sourceRoot":"","sources":["../../core/lb-types.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,qDAAqD;AAErD,OAAO,EAGL,kBAAkB,EAClB,eAAe,EACf,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAoC3B,MAAM,UAAU,OAAO,CAAC,MAAkB;IACxC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;
|
|
1
|
+
{"version":3,"file":"lb-types.js","sourceRoot":"","sources":["../../core/lb-types.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,qDAAqD;AAErD,OAAO,EAGL,kBAAkB,EAClB,eAAe,EACf,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAoC3B,MAAM,UAAU,OAAO,CAAC,MAAkB;IACxC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAuDD,+DAA+D;AAC/D,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAQ,eAAqC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAuB,EACvB,KAA6B;IAE7B,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,IAAI,KAAK,KAAK,EAAE;YAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;;YAC/B,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAC5C,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AACtC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,KAAsB,EACtB,GAAQ;IAER,MAAM,KAAK,GAA2B,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAChD,IAAI,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC;YAAE,SAAS;QAClD,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,CAAC,eAAe,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,MAAM,KAAK,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5B,OAAO;QACL,IAAI,EAAE,IAAI;QACV,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,eAAe,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC;QACnE,KAAK;KACN,CAAC;AACJ,CAAC","sourcesContent":["/// <reference lib=\"dom\" />\n\n// The wire vocabulary. Shared by browser and server.\n\nimport {\n KIND_ROW,\n KIND_ROWS,\n LB_RESERVED_PREFIX,\n LB_ROW_REQUESTS,\n URL_COLUMN_PATH,\n} from \"./lb-constants.js\";\n\n/**\n * One row: its columns, by name.\n *\n * A value is whatever the server serialized. The hub knows nothing about its\n * type and enforces nothing: it hands each value to the browser untouched,\n * and the browser renders it as it sees fit. What a number, a date or a null\n * should look like is the application's decision, made in the query.\n */\nexport type Row = Record<string, unknown>;\n\n/**\n * Part of a `rows` query's answer — see docs/reference/server.md, \"Queries\".\n *\n * Rows named here are added or updated, keys in `drop` are removed, and\n * anything unnamed is left alone: its contents, and its place in whatever\n * order the widget is keeping. Add and remove are one result rather than two\n * because the interesting cases are both at once — a row whose sort key\n * changed has to move, a swap is one out and one in — and two messages would\n * paint the intermediate state.\n */\nexport interface Patch {\n rows?: Row[];\n drop?: unknown[];\n}\n\n/** A query's kind, declared by the server and carried by every answer. */\nexport type Kind = typeof KIND_ROW | typeof KIND_ROWS;\n\n/**\n * What a `rows` query answers with: all rows, which is also their order, or\n * a patch. `Array.isArray` tells the two apart.\n */\nexport type RowsResult = Row[] | Patch;\n\nexport function isPatch(result: RowsResult): result is Patch {\n return !Array.isArray(result);\n}\n\n/** What any one query answers with, as the application writes it. */\nexport type HubResult = Row | RowsResult;\n\n/**\n * What application code hands the engine: answers by query name. The engine\n * turns each into a response item, adding the kind and key the query\n * declared.\n */\nexport type HubData = Record<string, HubResult>;\n\n/**\n * One query's answer on the wire: its name, its kind, its key's column name,\n * and exactly one of a row, all rows, or a patch. A patch answers a `rows`\n * query only.\n */\nexport interface ResponseItem {\n query: string;\n kind: Kind;\n key: string;\n row?: Row;\n rows?: Row[];\n patch?: Patch;\n}\n\n/** Every response the server sends: a page load and a request alike. */\nexport type HubResponse = ResponseItem[];\n\n/**\n * Every request has one shape. The name picks the handler; the rest is\n * present when the hub found it.\n */\nexport interface HubRequest {\n name: string;\n query?: string;\n key?: string;\n values?: Record<string, string>;\n}\n\n/**\n * The detail of the `lb-request-done` event: the request as the hub\n * completed and sent it, and what came of it. `items` is every response\n * item that landed because of it, in the order it landed. `error` is set\n * when the request's own round trip failed, and then nothing landed.\n */\nexport interface HubRequestDone {\n request: HubRequest;\n items: ResponseItem[];\n error?: Error;\n}\n\n/** The fields a handler receives: the request, less its name. */\nexport type RequestFields = Omit<HubRequest, \"name\">;\n\n/** Whether a name is one of the requests Loadbare provides. */\nexport function isRowRequest(name: string): boolean {\n return (LB_ROW_REQUESTS as readonly string[]).includes(name);\n}\n\n/**\n * A query string with some parms set, and the rest as they were. An empty\n * value takes its parm out, since empty is every parm's default and a\n * default is left out of the URL.\n *\n * One rule on both sides of the wire: the hub applies it to the address bar,\n * and the server to the query string it loads the page at after a request\n * returned `lb-url`. The two must agree, or what the page shows would differ\n * from what the address bar says.\n */\nexport function withQueryParms(\n search: URLSearchParams,\n parms: Record<string, string>,\n): URLSearchParams {\n const next = new URLSearchParams(search);\n for (const [name, value] of Object.entries(parms)) {\n if (value === \"\") next.delete(name);\n else next.set(name, value);\n }\n return next;\n}\n\n/**\n * A path names a page by its stub, and nothing finer. The bare path resolves\n * to `index`, the same convention a static file server follows.\n */\nexport function pageNameFor(path: string): string {\n const name = path.replace(/^\\/+|\\/+$/g, \"\");\n return name === \"\" ? \"index\" : name;\n}\n\n/**\n * Where a row for `lb-url` leads, from a page and its query parms.\n *\n * A column whose name begins with `lb-` is Loadbare's, and never a query\n * parm; every other column is one. An `lb-path` naming another page enters\n * it with only the parms the row names. Otherwise the named parms are set,\n * or taken out when empty, and the rest are kept.\n *\n * One rule on both sides of the wire: the server loads the page it names,\n * and the hub writes the URL it names. The two must agree, or what the page\n * shows would differ from what the address bar says.\n */\nexport function nextLocation(\n page: string,\n parms: URLSearchParams,\n row: Row,\n): { page: string; parms: URLSearchParams; moved: boolean } {\n const named: Record<string, string> = {};\n for (const [name, value] of Object.entries(row)) {\n if (name.startsWith(LB_RESERVED_PREFIX)) continue;\n named[name] = value === null || value === undefined ? \"\" : String(value);\n }\n const path = row[URL_COLUMN_PATH];\n const next = typeof path === \"string\" ? pageNameFor(path) : page;\n const moved = next !== page;\n return {\n page: next,\n parms: withQueryParms(moved ? new URLSearchParams() : parms, named),\n moved,\n };\n}\n\n/**\n * The two hooks a custom element holding rows may implement, both optional.\n *\n * The hub reconciles every row template itself, so a custom element supplies\n * placement and scaffolding and nothing else. They are methods on an element\n * the hub does not own, so they carry the `lb` prefix Loadbare reserves for\n * exactly that — see docs/reference/custom-elements.md.\n */\nexport interface RowsHost {\n /**\n * Where a live row belongs, called with the row detached on its first\n * appearance. Without it a row lands immediately before the template, so\n * rows accumulate in the order they arrive.\n */\n lbPlaceRow?(el: Element, row: Row, template: HTMLTemplateElement): void;\n\n /** Called once after a whole result has landed, for derived scaffolding. */\n lbRowsLanded?(): void;\n}\n"]}
|
package/dist/hub/lb-apply.d.ts
CHANGED
|
@@ -53,7 +53,13 @@ export declare function templateIn(el: Element): HTMLTemplateElement | null;
|
|
|
53
53
|
*/
|
|
54
54
|
export declare function applyRows(el: Element, template: HTMLTemplateElement, keyColumn: string, result: RowsResult): void;
|
|
55
55
|
/**
|
|
56
|
-
*
|
|
56
|
+
* Drop what landing kept for a root. The hub calls it when it replaces the
|
|
57
|
+
* page, since another page's query of the same name is another query.
|
|
58
|
+
*/
|
|
59
|
+
export declare function forget(root: ParentNode): void;
|
|
60
|
+
/**
|
|
61
|
+
* Land response items. Every answer arrives through here, and is kept for
|
|
62
|
+
* `root`, so that what arrives after it is filled from it — see forget().
|
|
57
63
|
*
|
|
58
64
|
* `quiet` names queries that may have nowhere to land without a warning: the
|
|
59
65
|
* hub's own `lb-url` lands wherever a chrome or page names it, and nowhere
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-apply.d.ts","sourceRoot":"","sources":["../../hub/lb-apply.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lb-apply.d.ts","sourceRoot":"","sources":["../../hub/lb-apply.ts"],"names":[],"mappings":"AA6CA,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,GAAG,EAER,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAW7B,mEAAmE;AACnE,MAAM,WAAW,OAAQ,SAAQ,OAAO;IACtC,KAAK,EAAE,MAAM,CAAC;CACf;AAiBD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAGrD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,OAAO,CAEpD;AAuDD;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE,CAMjE;AA8DD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,UAAQ,GAAG,IAAI,CAmBvE;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,mBAAmB,GAAG,IAAI,CAKlE;AA+BD;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CACvB,EAAE,EAAE,OAAO,EACX,QAAQ,EAAE,mBAAmB,EAC7B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,GACjB,IAAI,CAmEN;AA6CD;;;GAGG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAE7C;AAwED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,YAAY,EAAE,EACrB,KAAK,GAAE,SAAS,MAAM,EAAO,GAC5B,IAAI,CAiBN"}
|
package/dist/hub/lb-apply.js
CHANGED
|
@@ -16,12 +16,20 @@
|
|
|
16
16
|
* operation, and the only difference is how much of the page the root
|
|
17
17
|
* covers.
|
|
18
18
|
*
|
|
19
|
+
* An answer is kept after it lands. An element that names a query and
|
|
20
|
+
* arrives after that query's answer — a picker in a new live row, a ghost
|
|
21
|
+
* row a custom element built — is filled from the kept answer at the end of
|
|
22
|
+
* the next landing, so a query need not land again, and need not land after
|
|
23
|
+
* the one whose rows hold it, just to fill what is new. A custom element
|
|
24
|
+
* shipped inside an absent branch has not upgraded, so it is filled again
|
|
25
|
+
* once it has, and places its rows itself.
|
|
26
|
+
*
|
|
19
27
|
* A condition is the one other thing landing moves. An element whose
|
|
20
28
|
* `lb-show` column is off stands in the document inside a template, and
|
|
21
29
|
* landing reaches in there as it reaches everywhere else, so a branch that
|
|
22
30
|
* returns is already current. Nothing but landing reaches in.
|
|
23
31
|
*/
|
|
24
|
-
import { ATTR_COLUMN, ATTR_COLUMN_VALUE, ATTR_KEY_VALUE, ATTR_QUERY, ATTR_QUERY_ROW_COUNT, ATTR_SHOW, KIND_ROW, } from "../core/lb-constants.js";
|
|
32
|
+
import { ATTR_COLUMN, ATTR_COLUMN_VALUE, ATTR_KEY_VALUE, ATTR_QUERY, ATTR_QUERY_ROW_COUNT, ATTR_ROW_LIVE, ATTR_SHOW, KIND_ROW, LIVE_ROW, SHOW_NOT, } from "../core/lb-constants.js";
|
|
25
33
|
import { isPatch, } from "../core/lb-types.js";
|
|
26
34
|
/** The template holding an absent branch — see ATTR_SHOW. */
|
|
27
35
|
const PARKED = `template[${ATTR_SHOW}]`;
|
|
@@ -210,6 +218,9 @@ export function applyRow(root, row, liveRow = false) {
|
|
|
210
218
|
for (const el of below(root, `[${ATTR_SHOW}="${column}"]`)) {
|
|
211
219
|
present(el, on);
|
|
212
220
|
}
|
|
221
|
+
for (const el of below(root, `[${ATTR_SHOW}="${SHOW_NOT}${column}"]`)) {
|
|
222
|
+
present(el, !on);
|
|
223
|
+
}
|
|
213
224
|
}
|
|
214
225
|
}
|
|
215
226
|
/**
|
|
@@ -223,14 +234,27 @@ export function templateIn(el) {
|
|
|
223
234
|
return template ?? null;
|
|
224
235
|
}
|
|
225
236
|
/**
|
|
226
|
-
* The live rows
|
|
227
|
-
* query is that query's.
|
|
237
|
+
* The live rows of the element's query, in document order. A row of a nested
|
|
238
|
+
* query is that query's. An element a `row` query landed on carries a key and
|
|
239
|
+
* is not a live row, so a `row` shown in a table's head or foot is not one of
|
|
240
|
+
* the table's rows.
|
|
241
|
+
*/
|
|
242
|
+
function liveRows(el) {
|
|
243
|
+
return [...el.querySelectorAll(LIVE_ROW)].filter((row) => inScope(el, row));
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* The live rows already showing, by the key each carries. Two live rows with
|
|
247
|
+
* one key are a mistake nothing downstream can recover from: only one of them
|
|
248
|
+
* would ever be matched, and the other would stay until the page goes.
|
|
228
249
|
*/
|
|
229
250
|
function showing(el) {
|
|
230
251
|
const rows = new Map();
|
|
231
|
-
for (const row of el
|
|
232
|
-
|
|
233
|
-
|
|
252
|
+
for (const row of liveRows(el)) {
|
|
253
|
+
const key = row.getAttribute(ATTR_KEY_VALUE);
|
|
254
|
+
if (rows.has(key)) {
|
|
255
|
+
console.error(`lb-hub: <${el.localName}> shows two rows with the key '${key}'`);
|
|
256
|
+
}
|
|
257
|
+
rows.set(key, row);
|
|
234
258
|
}
|
|
235
259
|
return rows;
|
|
236
260
|
}
|
|
@@ -251,6 +275,7 @@ export function applyRows(el, template, keyColumn, result) {
|
|
|
251
275
|
const shown = showing(el);
|
|
252
276
|
const host = el;
|
|
253
277
|
const whole = !isPatch(result);
|
|
278
|
+
const landed = new Set();
|
|
254
279
|
const place = (row, data) => {
|
|
255
280
|
if (host.lbPlaceRow)
|
|
256
281
|
host.lbPlaceRow(row, data, template);
|
|
@@ -260,11 +285,16 @@ export function applyRows(el, template, keyColumn, result) {
|
|
|
260
285
|
const upsert = (data) => {
|
|
261
286
|
if (data[keyColumn] === undefined) {
|
|
262
287
|
console.error(`lb-hub: a row for <${el.localName}> has no '${keyColumn}' column`);
|
|
263
|
-
return
|
|
288
|
+
return;
|
|
264
289
|
}
|
|
265
290
|
// The key is stored on the row as an attribute and read back from
|
|
266
291
|
// there, so it is compared as the string the attribute holds.
|
|
267
292
|
const key = String(data[keyColumn]);
|
|
293
|
+
if (landed.has(key)) {
|
|
294
|
+
console.error(`lb-hub: two rows for <${el.localName}> have the key '${key}'; ` +
|
|
295
|
+
`the last one shows`);
|
|
296
|
+
}
|
|
297
|
+
landed.add(key);
|
|
268
298
|
let row = shown.get(key);
|
|
269
299
|
const fresh = row === undefined;
|
|
270
300
|
if (!row) {
|
|
@@ -273,6 +303,7 @@ export function applyRows(el, template, keyColumn, result) {
|
|
|
273
303
|
// until inserted, and a form-associated control would be filled as if
|
|
274
304
|
// it were not one.
|
|
275
305
|
row = el.ownerDocument.importNode(template.content.firstElementChild, true);
|
|
306
|
+
row.setAttribute(ATTR_ROW_LIVE, "");
|
|
276
307
|
row.setAttribute(ATTR_KEY_VALUE, key);
|
|
277
308
|
shown.set(key, row);
|
|
278
309
|
}
|
|
@@ -282,17 +313,12 @@ export function applyRows(el, template, keyColumn, result) {
|
|
|
282
313
|
applyRow(row, data, true);
|
|
283
314
|
if (fresh || whole)
|
|
284
315
|
place(row, data);
|
|
285
|
-
return key;
|
|
286
316
|
};
|
|
287
317
|
if (Array.isArray(result)) {
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
const key = upsert(data);
|
|
291
|
-
if (key !== null)
|
|
292
|
-
arrived.add(key);
|
|
293
|
-
}
|
|
318
|
+
for (const data of result)
|
|
319
|
+
upsert(data);
|
|
294
320
|
for (const [key, row] of shown)
|
|
295
|
-
if (!
|
|
321
|
+
if (!landed.has(key))
|
|
296
322
|
row.remove();
|
|
297
323
|
}
|
|
298
324
|
else {
|
|
@@ -305,7 +331,7 @@ export function applyRows(el, template, keyColumn, result) {
|
|
|
305
331
|
// and a patch report the same fact the same way. Only here is the count
|
|
306
332
|
// known: the server answers with rows and says nothing about how many
|
|
307
333
|
// survived reconciliation.
|
|
308
|
-
el.setAttribute(ATTR_QUERY_ROW_COUNT, String(
|
|
334
|
+
el.setAttribute(ATTR_QUERY_ROW_COUNT, String(liveRows(el).length));
|
|
309
335
|
// Derived scaffolding — a section heading, an <optgroup> — goes when its
|
|
310
336
|
// last row does, and only the custom element knows it exists.
|
|
311
337
|
host.lbRowsLanded?.();
|
|
@@ -333,15 +359,103 @@ function landItem(el, item) {
|
|
|
333
359
|
const result = item.patch ?? item.rows ?? [];
|
|
334
360
|
applyRows(el, template, item.key, result);
|
|
335
361
|
}
|
|
362
|
+
const kept = new WeakMap();
|
|
363
|
+
function keptFor(root) {
|
|
364
|
+
let found = kept.get(root);
|
|
365
|
+
if (!found) {
|
|
366
|
+
found = { answers: new Map(), landed: new WeakSet() };
|
|
367
|
+
kept.set(root, found);
|
|
368
|
+
}
|
|
369
|
+
return found;
|
|
370
|
+
}
|
|
336
371
|
/**
|
|
337
|
-
*
|
|
372
|
+
* Drop what landing kept for a root. The hub calls it when it replaces the
|
|
373
|
+
* page, since another page's query of the same name is another query.
|
|
374
|
+
*/
|
|
375
|
+
export function forget(root) {
|
|
376
|
+
kept.delete(root);
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* All rows after a patch, as the elements it landed on now show them: a row
|
|
380
|
+
* it names takes the columns it sends and keeps its place, a new one goes
|
|
381
|
+
* last, and a dropped one goes.
|
|
382
|
+
*/
|
|
383
|
+
function patched(rows, key, patch) {
|
|
384
|
+
const byKey = new Map(rows.map((row) => [String(row[key]), row]));
|
|
385
|
+
for (const row of patch.rows ?? []) {
|
|
386
|
+
const k = String(row[key]);
|
|
387
|
+
byKey.set(k, { ...byKey.get(k), ...row });
|
|
388
|
+
}
|
|
389
|
+
for (const k of patch.drop ?? [])
|
|
390
|
+
byKey.delete(String(k));
|
|
391
|
+
return [...byKey.values()];
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Keep an answer as all of the query's rows. A patch is applied to the
|
|
395
|
+
* answer already kept; with none, it is not the whole of anything, and
|
|
396
|
+
* nothing is kept.
|
|
397
|
+
*/
|
|
398
|
+
function keep(answers, item) {
|
|
399
|
+
if (!item.patch) {
|
|
400
|
+
answers.set(item.query, item);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const before = answers.get(item.query);
|
|
404
|
+
if (!before)
|
|
405
|
+
return;
|
|
406
|
+
const { patch: _, ...whole } = item;
|
|
407
|
+
answers.set(item.query, {
|
|
408
|
+
...whole,
|
|
409
|
+
rows: patched(before.rows ?? [], item.key, item.patch),
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* A custom element whose class is defined and has not yet run on it: one the
|
|
414
|
+
* builder shipped inside an absent branch's template, where nothing upgrades.
|
|
415
|
+
* It has none of the hooks landing calls, so rows that land on it are not
|
|
416
|
+
* placed by it. A landing on it does not count, and once `lb-show` brings it
|
|
417
|
+
* into the document and it upgrades, it is filled again from the kept answer.
|
|
418
|
+
*
|
|
419
|
+
* An element with no class, such as one defined by an element file alone,
|
|
420
|
+
* never upgrades, and is never waiting.
|
|
421
|
+
*/
|
|
422
|
+
function waiting(el) {
|
|
423
|
+
const registry = globalThis
|
|
424
|
+
.customElements;
|
|
425
|
+
const upgraded = registry?.get(el.localName);
|
|
426
|
+
return upgraded !== undefined && !(el instanceof upgraded);
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Fill every element that names a kept query and has had nothing land on it.
|
|
430
|
+
* Filling one can build more — a live row holding a picker of its own — so
|
|
431
|
+
* it goes round until a pass finds nothing new.
|
|
432
|
+
*/
|
|
433
|
+
function fillArrivals(root, { answers, landed }) {
|
|
434
|
+
for (let filled = true; filled;) {
|
|
435
|
+
filled = false;
|
|
436
|
+
for (const [query, item] of answers) {
|
|
437
|
+
for (const el of everywhere(root, `[${ATTR_QUERY}="${query}"]`)) {
|
|
438
|
+
if (landed.has(el) || waiting(el))
|
|
439
|
+
continue;
|
|
440
|
+
landed.add(el);
|
|
441
|
+
landItem(el, item);
|
|
442
|
+
filled = true;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Land response items. Every answer arrives through here, and is kept for
|
|
449
|
+
* `root`, so that what arrives after it is filled from it — see forget().
|
|
338
450
|
*
|
|
339
451
|
* `quiet` names queries that may have nowhere to land without a warning: the
|
|
340
452
|
* hub's own `lb-url` lands wherever a chrome or page names it, and nowhere
|
|
341
453
|
* otherwise.
|
|
342
454
|
*/
|
|
343
455
|
export function applyResponse(root, items, quiet = []) {
|
|
456
|
+
const kept = keptFor(root);
|
|
344
457
|
for (const item of items) {
|
|
458
|
+
keep(kept.answers, item);
|
|
345
459
|
const targets = everywhere(root, `[${ATTR_QUERY}="${item.query}"]`);
|
|
346
460
|
if (targets.length === 0) {
|
|
347
461
|
if (!quiet.includes(item.query)) {
|
|
@@ -349,8 +463,12 @@ export function applyResponse(root, items, quiet = []) {
|
|
|
349
463
|
}
|
|
350
464
|
continue;
|
|
351
465
|
}
|
|
352
|
-
for (const el of targets)
|
|
466
|
+
for (const el of targets) {
|
|
467
|
+
if (!waiting(el))
|
|
468
|
+
kept.landed.add(el);
|
|
353
469
|
landItem(el, item);
|
|
470
|
+
}
|
|
354
471
|
}
|
|
472
|
+
fillArrivals(root, kept);
|
|
355
473
|
}
|
|
356
474
|
//# sourceMappingURL=lb-apply.js.map
|
package/dist/hub/lb-apply.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-apply.js","sourceRoot":"","sources":["../../hub/lb-apply.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,UAAU,EACV,oBAAoB,EACpB,SAAS,EACT,QAAQ,GACT,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,OAAO,GAKR,MAAM,qBAAqB,CAAC;AAE7B,6DAA6D;AAC7D,MAAM,MAAM,GAAG,YAAY,SAAS,GAAG,CAAC;AAExC,kFAAkF;AAClF,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB,0EAA0E;AAC1E,MAAM,eAAe,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAOtD;;;;;;GAMG;AACH,SAAS,eAAe,CAAC,EAAW;IAClC,IAAI,EAAE,CAAC,SAAS,KAAK,QAAQ,IAAI,EAAE,CAAC,SAAS,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IAC1E,OAAO,CACL,EAAE,CAAC,SAAS,KAAK,OAAO;QACxB,CAAC,eAAe,CAAC,QAAQ,CAAE,EAAuB,CAAC,IAAI,CAAC,CACzD,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAW;IAC1C,MAAM,IAAI,GAAG,EAAE,CAAC,WAA2C,CAAC;IAC5D,OAAO,IAAI,CAAC,cAAc,KAAK,IAAI,IAAI,OAAO,IAAI,EAAE,CAAC;AACvD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,EAAW;IACnC,OAAO,eAAe,CAAC,EAAE,CAAC,IAAI,gBAAgB,CAAC,EAAE,CAAC,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,SAAS,IAAI,CAAC,EAAW,EAAE,KAAc;IACvC,kEAAkE;IAClE,sDAAsD;IACtD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,IAAI,SAAS,CAAC,EAAE,CAAC;YAAE,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC;aAC5B,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,SAAS,KAAK,OAAO,EAAE,CAAC;YACjE,EAAE,CAAC,WAAW,GAAG,EAAE,CAAC;QACtB,CAAC;QACD,EAAE,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC;QACtC,OAAO;IACT,CAAC;IACD,IAAI,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;QAClB,EAAE,CAAC,KAAK,GAAG,KAAe,CAAC;IAC7B,CAAC;SAAM,IAAI,EAAE,CAAC,SAAS,KAAK,OAAO,EAAE,CAAC;QACpC,OAAO,CAAC,IAAI,CACV,iDAAkD,EAAuB,CAAC,IAAI,cAAc,EAC5F,EAAE,CACH,CAAC;QACF,OAAO;IACT,CAAC;SAAM,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACvC,EAAE,CAAC,WAAW,GAAG,KAAe,CAAC;IACnC,CAAC;IACD,EAAE,CAAC,YAAY,CAAC,iBAAiB,EAAE,KAAe,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,OAAO,CAAC,IAAgC,EAAE,EAAW;IAC5D,KAAK,IAAI,IAAI,GAAG,EAAE,CAAC,aAAa,EAAE,IAAI,IAAI,IAAI,KAAK,IAAI,GAAG,CAAC;QACzD,IAAI,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC;YAAE,OAAO,KAAK,CAAC;QAChD,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,MAAM,CAAC,IAAa,EAAE,QAAgB;IACpD,MAAM,KAAK,GAAc,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjD,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,KAAK,CAAC,IAAgC,EAAE,QAAgB;IAC/D,MAAM,KAAK,GAAc,EAAE,CAAC;IAC5B,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjD,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,IAAI,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAE,MAA8B,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CAAC,IAAgB,EAAE,QAAgB;IACpD,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC;IACnD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,MAAM,OAAO,GAAI,MAA8B,CAAC,OAAO,CAAC;QACxD,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,OAAO,CAAC,EAAW,EAAE,EAAW;IACvC,IAAI,EAAE,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,MAAM,GAAI,EAA0B,CAAC,OAAO,CAAC,iBAAiB,CAAC;QACrE,IAAI,EAAE,IAAI,MAAM;YAAE,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACzC,OAAO;IACT,CAAC;IACD,IAAI,EAAE,IAAI,EAAE,CAAC,UAAU,EAAE,QAAQ,KAAK,aAAa;QAAE,OAAO;IAC5D,MAAM,MAAM,GAAG,EAAE,CAAC,aAAa,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IAC1D,MAAM,CAAC,YAAY,CAAC,SAAS,EAAE,EAAE,CAAC,YAAY,CAAC,SAAS,CAAE,CAAC,CAAC;IAC5D,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACvB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAC5B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAa,EAAE,GAAQ,EAAE,OAAO,GAAG,KAAK;IAC/D,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAClD,MAAM,QAAQ,GAAG,IAAI,WAAW,KAAK,MAAM,IAAI,CAAC;QAChD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACtC,IAAI,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;YAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7D,KAAK,MAAM,EAAE,IAAI,OAAO;YAAE,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC1C,yEAAyE;QACzE,2DAA2D;QAC3D,EAAE;QACF,oEAAoE;QACpE,qEAAqE;QACrE,MAAM,EAAE,GAAG,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,CAAC;QAC7C,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,EAAE,IAAI,SAAS,KAAK,MAAM,IAAI,CAAC,EAAE,CAAC;YAC3D,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,EAAW;IACpC,MAAM,QAAQ,GAAG,MAAM,CAAC,EAAE,EAAE,iBAAiB,SAAS,IAAI,CAAC,CAAC,IAAI,CAC9D,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAChB,CAAC;IACF,OAAQ,QAA4C,IAAI,IAAI,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,SAAS,OAAO,CAAC,EAAW;IAC1B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAmB,CAAC;IACxC,KAAK,MAAM,GAAG,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;QAC7D,IAAI,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,cAAc,CAAE,EAAE,GAAG,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,SAAS,CACvB,EAAW,EACX,QAA6B,EAC7B,SAAiB,EACjB,MAAkB;IAElB,MAAM,KAAK,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1B,MAAM,IAAI,GAAG,EAAwB,CAAC;IACtC,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/B,MAAM,KAAK,GAAG,CAAC,GAAY,EAAE,IAAS,EAAQ,EAAE;QAC9C,IAAI,IAAI,CAAC,UAAU;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;;YACrD,QAAQ,CAAC,aAAc,CAAC,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC3D,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,CAAC,IAAS,EAAiB,EAAE;QAC1C,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,SAAS,EAAE,CAAC;YAClC,OAAO,CAAC,KAAK,CACX,sBAAsB,EAAE,CAAC,SAAS,aAAa,SAAS,UAAU,CACnE,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QACD,kEAAkE;QAClE,8DAA8D;QAC9D,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QACpC,IAAI,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACzB,MAAM,KAAK,GAAG,GAAG,KAAK,SAAS,CAAC;QAChC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,sEAAsE;YACtE,mEAAmE;YACnE,sEAAsE;YACtE,mBAAmB;YACnB,GAAG,GAAG,EAAE,CAAC,aAAa,CAAC,UAAU,CAC/B,QAAQ,CAAC,OAAO,CAAC,iBAAkB,EACnC,IAAI,CACL,CAAC;YACF,GAAG,CAAC,YAAY,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;YACtC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACtB,CAAC;QACD,uEAAuE;QACvE,qEAAqE;QACrE,yCAAyC;QACzC,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1B,IAAI,KAAK,IAAI,KAAK;YAAE,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACrC,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IAEF,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;YAC1B,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;YACzB,IAAI,GAAG,KAAK,IAAI;gBAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,CAAC;QACD,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,KAAK;YAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,GAAG,CAAC,MAAM,EAAE,CAAC;IACtE,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,IAAI,EAAE;YAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACnD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,IAAI,EAAE;YAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;IACxE,CAAC;IAED,yEAAyE;IACzE,wEAAwE;IACxE,sEAAsE;IACtE,2BAA2B;IAC3B,EAAE,CAAC,YAAY,CAAC,oBAAoB,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEhE,yEAAyE;IACzE,8DAA8D;IAC9D,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;AACxB,CAAC;AAED,kEAAkE;AAClE,SAAS,QAAQ,CAAC,EAAW,EAAE,IAAkB;IAC/C,MAAM,QAAQ,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;IAChC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC;QAC3B,IAAI,QAAQ,EAAE,CAAC;YACb,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;YACzC,OAAO;QACT,CAAC;QACD,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAClB,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACtC,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO;IACT,CAAC;IACD,0EAA0E;IAC1E,oDAAoD;IACpD,IAAI,CAAC,QAAQ;QAAE,OAAO;IACtB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;IAC7C,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAgB,EAChB,KAAqB,EACrB,QAA2B,EAAE;IAE7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,UAAU,KAAK,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;QACpE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAChC,OAAO,CAAC,IAAI,CAAC,0BAA0B,IAAI,CAAC,KAAK,aAAa,CAAC,CAAC;YAClE,CAAC;YACD,SAAS;QACX,CAAC;QACD,KAAK,MAAM,EAAE,IAAI,OAAO;YAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC","sourcesContent":["/// <reference lib=\"dom\" />\n\n/**\n * Landing — see docs/reference/data-binding.md.\n *\n * The hub lands a response item's rows on every element whose `lb-query`\n * names its query. The item says the query's kind and key, so the markup\n * says neither:\n *\n * kind row template the hub\n * row no lands the row on the element itself\n * row yes lands one live row\n * rows yes lands one live row per row\n * rows no lands nothing\n *\n * Filling a live row and filling an element a `row` lands on are the same\n * operation, and the only difference is how much of the page the root\n * covers.\n *\n * A condition is the one other thing landing moves. An element whose\n * `lb-show` column is off stands in the document inside a template, and\n * landing reaches in there as it reaches everywhere else, so a branch that\n * returns is already current. Nothing but landing reaches in.\n */\n\nimport {\n ATTR_COLUMN,\n ATTR_COLUMN_VALUE,\n ATTR_KEY_VALUE,\n ATTR_QUERY,\n ATTR_QUERY_ROW_COUNT,\n ATTR_SHOW,\n KIND_ROW,\n} from \"../core/lb-constants.js\";\nimport {\n isPatch,\n type ResponseItem,\n type Row,\n type RowsHost,\n type RowsResult,\n} from \"../core/lb-types.js\";\n\n/** The template holding an absent branch — see ATTR_SHOW. */\nconst PARKED = `template[${ATTR_SHOW}]`;\n\n/** A DocumentFragment's nodeType. Tier 3 has no `Node` global to read it from. */\nconst FRAGMENT_NODE = 11;\n\n/** The input types whose state is not their `value`, or cannot be set. */\nconst UNLANDED_INPUTS = [\"checkbox\", \"radio\", \"file\"];\n\n/** A control's value, whether it is native or a custom element. */\nexport interface Control extends Element {\n value: string;\n}\n\n/**\n * A native control whose state is its `value`: a `<select>`, a\n * `<textarea>`, or an `<input>` of any type but checkbox, radio and file.\n *\n * Told apart by tag name and type rather than by class, so it holds for an\n * element from any document.\n */\nfunction isNativeControl(el: Element): boolean {\n if (el.localName === \"select\" || el.localName === \"textarea\") return true;\n return (\n el.localName === \"input\" &&\n !UNLANDED_INPUTS.includes((el as HTMLInputElement).type)\n );\n}\n\n/**\n * A form-associated custom element with a `value` property. HTML marks one\n * with `static formAssociated = true` on its class, and a control has a value.\n */\nexport function isFormAssociated(el: Element): boolean {\n const ctor = el.constructor as { formAssociated?: unknown };\n return ctor.formAssociated === true && \"value\" in el;\n}\n\n/**\n * A control: an `<input>`, `<select>` or `<textarea>`, or a form-associated\n * custom element with a `value` property. Landing and gathering draw the\n * same line, so a value read back is the one that landed there.\n */\nexport function isControl(el: Element): el is Control {\n return isNativeControl(el) || isFormAssociated(el);\n}\n\n/**\n * Set an element from a column. A control's `value` is set. A custom element\n * that is not a control keeps its content, which the builder placed there\n * from its element file, and renders the stamp instead. Any other element's\n * text content is set. Every one carries the value as `lb-column-value`.\n */\nfunction land(el: Element, value: unknown): void {\n // Untouched, whatever the server sent: the browser decides what a\n // non-string looks like. See Row in core/lb-types.ts.\n if (value === null || value === undefined) {\n if (isControl(el)) el.value = \"\";\n else if (!el.localName.includes(\"-\") && el.localName !== \"input\") {\n el.textContent = \"\";\n }\n el.removeAttribute(ATTR_COLUMN_VALUE);\n return;\n }\n if (isControl(el)) {\n el.value = value as string;\n } else if (el.localName === \"input\") {\n console.warn(\n `lb-hub: a value does not land on <input type=\"${(el as HTMLInputElement).type}\">, ignoring`,\n el,\n );\n return;\n } else if (!el.localName.includes(\"-\")) {\n el.textContent = value as string;\n }\n el.setAttribute(ATTR_COLUMN_VALUE, value as string);\n}\n\n/**\n * Whether an element under `root` belongs to root's query, rather than to a\n * query nested inside it.\n *\n * A descendant is root's unless something between it and root carries\n * `lb-query`. The element's own `lb-query` does not count: it names what the\n * element holds, and the row it reads from is its ancestors'. So a picker\n * carrying `lb-query` and `lb-column` is set from the row around it. Root's\n * own attribute does not count either: root is what is being filled.\n *\n * Root may be the content of a template holding an absent branch. The walk\n * up from an element inside it stops at the top of that content, which has\n * no parent, and the template it stands for was in scope already.\n */\nfunction inScope(root: Element | DocumentFragment, el: Element): boolean {\n for (let node = el.parentElement; node && node !== root;) {\n if (node.hasAttribute(ATTR_QUERY)) return false;\n node = node.parentElement;\n }\n return true;\n}\n\n/**\n * Root if it matches, then every descendant in root's query that does.\n * Gathering finds columns through here. It does not reach into an absent\n * branch, so a live row is read back from the places a row lands that are\n * showing.\n */\nexport function within(root: Element, selector: string): Element[] {\n const found: Element[] = root.matches(selector) ? [root] : [];\n for (const el of root.querySelectorAll(selector)) {\n if (inScope(root, el)) found.push(el);\n }\n return found;\n}\n\n/**\n * Every descendant in root's query that matches, absent branches included.\n *\n * `querySelectorAll` does not enter template content, so an element that is\n * off would otherwise miss everything landed while it was away, and return\n * stale or with its rows empty. A template holding one is in root's query by\n * the ordinary rule, and its content is searched as if it stood there.\n */\nfunction below(root: Element | DocumentFragment, selector: string): Element[] {\n const found: Element[] = [];\n for (const el of root.querySelectorAll(selector)) {\n if (inScope(root, el)) found.push(el);\n }\n for (const parked of root.querySelectorAll(PARKED)) {\n if (inScope(root, parked)) {\n found.push(...below((parked as HTMLTemplateElement).content, selector));\n }\n }\n return found;\n}\n\n/**\n * Every element in root that matches, absent branches included. A query is\n * looked up this way before any row is known, so nothing is filtered out.\n */\nfunction everywhere(root: ParentNode, selector: string): Element[] {\n const found = [...root.querySelectorAll(selector)];\n for (const parked of root.querySelectorAll(PARKED)) {\n const content = (parked as HTMLTemplateElement).content;\n found.push(...everywhere(content, selector));\n }\n return found;\n}\n\n/**\n * Put an element in the document or take it out, as its `lb-show` column\n * says.\n *\n * Off is inside a template that stands where the element stood, carrying the\n * same `lb-show`. The element is moved rather than rebuilt, so a custom\n * element keeps its instance and a control keeps what was typed into it. On\n * moves it back and the template goes.\n *\n * Landing reaches into that template, so both it and the element it holds\n * arrive here. The template decides; the element it holds, whose parent is\n * the template's content, is left to it.\n */\nfunction present(el: Element, on: boolean): void {\n if (el.localName === \"template\") {\n const branch = (el as HTMLTemplateElement).content.firstElementChild;\n if (on && branch) el.replaceWith(branch);\n return;\n }\n if (on || el.parentNode?.nodeType === FRAGMENT_NODE) return;\n const parked = el.ownerDocument.createElement(\"template\");\n parked.setAttribute(ATTR_SHOW, el.getAttribute(ATTR_SHOW)!);\n el.replaceWith(parked);\n parked.content.append(el);\n}\n\n/**\n * Fill from one row: every `lb-column` and `lb-show` in root's query.\n *\n * A live row is its own row, so its root counts when it carries\n * `lb-column`: `<option>`'s content model is text, so an option row has to\n * be the column it displays. An element a `row` lands on is not its own\n * ancestor, so its own `lb-column` reads from the row around it and is left\n * alone here.\n */\nexport function applyRow(root: Element, row: Row, liveRow = false): void {\n for (const [column, value] of Object.entries(row)) {\n const selector = `[${ATTR_COLUMN}=\"${column}\"]`;\n const columns = below(root, selector);\n if (liveRow && root.matches(selector)) columns.unshift(root);\n for (const el of columns) land(el, value);\n // Only null and false are off. A string is never read, so \"false\" is on,\n // and the query spells a condition as a boolean or a null.\n //\n // Never the root's own: a row template's root carries none, and the\n // element a row lands on reads its condition from the row around it.\n const on = value !== null && value !== false;\n for (const el of below(root, `[${ATTR_SHOW}=\"${column}\"]`)) {\n present(el, on);\n }\n }\n}\n\n/**\n * The row template: the first `<template>` among the element's descendants,\n * outside any nested `lb-query`. A template holding an absent branch is not a\n * row template: rows land before the row template, so one inside the first\n * live row comes first in document order.\n */\nexport function templateIn(el: Element): HTMLTemplateElement | null {\n const template = within(el, `template:not([${ATTR_SHOW}])`).find(\n (t) => t !== el,\n );\n return (template as HTMLTemplateElement | undefined) ?? null;\n}\n\n/**\n * The live rows already showing, by the key each carries. A row of a nested\n * query is that query's.\n */\nfunction showing(el: Element): Map<string, Element> {\n const rows = new Map<string, Element>();\n for (const row of el.querySelectorAll(`[${ATTR_KEY_VALUE}]`)) {\n if (inScope(el, row)) rows.set(row.getAttribute(ATTR_KEY_VALUE)!, row);\n }\n return rows;\n}\n\n/**\n * Land rows through a row template.\n *\n * All rows decide membership and order: every row is placed in the order\n * given, and a row whose key did not arrive is gone. A patch disturbs only\n * what it names — a row it did not mention keeps its contents and its\n * position.\n *\n * A custom element that carries `lbPlaceRow` decides where a row goes,\n * because only it knows whether it sorts or groups. Without it a row lands\n * immediately before the template, so rows accumulate in the order they\n * arrive and the template stays put as the insertion marker.\n */\nexport function applyRows(\n el: Element,\n template: HTMLTemplateElement,\n keyColumn: string,\n result: RowsResult,\n): void {\n const shown = showing(el);\n const host = el as Element & RowsHost;\n const whole = !isPatch(result);\n\n const place = (row: Element, data: Row): void => {\n if (host.lbPlaceRow) host.lbPlaceRow(row, data, template);\n else template.parentElement!.insertBefore(row, template);\n };\n\n const upsert = (data: Row): string | null => {\n if (data[keyColumn] === undefined) {\n console.error(\n `lb-hub: a row for <${el.localName}> has no '${keyColumn}' column`,\n );\n return null;\n }\n // The key is stored on the row as an attribute and read back from\n // there, so it is compared as the string the attribute holds.\n const key = String(data[keyColumn]);\n let row = shown.get(key);\n const fresh = row === undefined;\n if (!row) {\n // Imported rather than cloned: template content belongs to a document\n // with no custom element definitions, so a clone would not upgrade\n // until inserted, and a form-associated control would be filled as if\n // it were not one.\n row = el.ownerDocument.importNode(\n template.content.firstElementChild!,\n true,\n );\n row.setAttribute(ATTR_KEY_VALUE, key);\n shown.set(key, row);\n }\n // Fill before insertion. The attributes are already there when the row\n // upgrades, which is the same thing that makes a first landing and a\n // refresh one operation everywhere else.\n applyRow(row, data, true);\n if (fresh || whole) place(row, data);\n return key;\n };\n\n if (Array.isArray(result)) {\n const arrived = new Set<string>();\n for (const data of result) {\n const key = upsert(data);\n if (key !== null) arrived.add(key);\n }\n for (const [key, row] of shown) if (!arrived.has(key)) row.remove();\n } else {\n for (const data of result.rows ?? []) upsert(data);\n for (const key of result.drop ?? []) shown.get(String(key))?.remove();\n }\n\n // Counted from the DOM rather than from either branch above, so all rows\n // and a patch report the same fact the same way. Only here is the count\n // known: the server answers with rows and says nothing about how many\n // survived reconciliation.\n el.setAttribute(ATTR_QUERY_ROW_COUNT, String(showing(el).size));\n\n // Derived scaffolding — a section heading, an <optgroup> — goes when its\n // last row does, and only the custom element knows it exists.\n host.lbRowsLanded?.();\n}\n\n/** Land one response item on one element that names its query. */\nfunction landItem(el: Element, item: ResponseItem): void {\n const template = templateIn(el);\n if (item.kind === KIND_ROW) {\n const row = item.row ?? {};\n if (template) {\n applyRows(el, template, item.key, [row]);\n return;\n }\n applyRow(el, row);\n const key = row[item.key];\n if (key !== undefined && key !== null) {\n el.setAttribute(ATTR_KEY_VALUE, String(key));\n }\n return;\n }\n // A `rows` query with no row template lands nothing: an insert form names\n // the query it adds to, and shows none of its rows.\n if (!template) return;\n const result = item.patch ?? item.rows ?? [];\n applyRows(el, template, item.key, result);\n}\n\n/**\n * Land response items. Every answer arrives through here.\n *\n * `quiet` names queries that may have nowhere to land without a warning: the\n * hub's own `lb-url` lands wherever a chrome or page names it, and nowhere\n * otherwise.\n */\nexport function applyResponse(\n root: ParentNode,\n items: ResponseItem[],\n quiet: readonly string[] = [],\n): void {\n for (const item of items) {\n const targets = everywhere(root, `[${ATTR_QUERY}=\"${item.query}\"]`);\n if (targets.length === 0) {\n if (!quiet.includes(item.query)) {\n console.warn(`lb-hub: nothing names '${item.query}', skipping`);\n }\n continue;\n }\n for (const el of targets) landItem(el, item);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"lb-apply.js","sourceRoot":"","sources":["../../hub/lb-apply.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference lib=\"dom\" />\n\n/**\n * Landing — see docs/reference/data-binding.md.\n *\n * The hub lands a response item's rows on every element whose `lb-query`\n * names its query. The item says the query's kind and key, so the markup\n * says neither:\n *\n * kind row template the hub\n * row no lands the row on the element itself\n * row yes lands one live row\n * rows yes lands one live row per row\n * rows no lands nothing\n *\n * Filling a live row and filling an element a `row` lands on are the same\n * operation, and the only difference is how much of the page the root\n * covers.\n *\n * An answer is kept after it lands. An element that names a query and\n * arrives after that query's answer — a picker in a new live row, a ghost\n * row a custom element built — is filled from the kept answer at the end of\n * the next landing, so a query need not land again, and need not land after\n * the one whose rows hold it, just to fill what is new. A custom element\n * shipped inside an absent branch has not upgraded, so it is filled again\n * once it has, and places its rows itself.\n *\n * A condition is the one other thing landing moves. An element whose\n * `lb-show` column is off stands in the document inside a template, and\n * landing reaches in there as it reaches everywhere else, so a branch that\n * returns is already current. Nothing but landing reaches in.\n */\n\nimport {\n ATTR_COLUMN,\n ATTR_COLUMN_VALUE,\n ATTR_KEY_VALUE,\n ATTR_QUERY,\n ATTR_QUERY_ROW_COUNT,\n ATTR_ROW_LIVE,\n ATTR_SHOW,\n KIND_ROW,\n LIVE_ROW,\n SHOW_NOT,\n} from \"../core/lb-constants.js\";\nimport {\n isPatch,\n type Patch,\n type ResponseItem,\n type Row,\n type RowsHost,\n type RowsResult,\n} from \"../core/lb-types.js\";\n\n/** The template holding an absent branch — see ATTR_SHOW. */\nconst PARKED = `template[${ATTR_SHOW}]`;\n\n/** A DocumentFragment's nodeType. Tier 3 has no `Node` global to read it from. */\nconst FRAGMENT_NODE = 11;\n\n/** The input types whose state is not their `value`, or cannot be set. */\nconst UNLANDED_INPUTS = [\"checkbox\", \"radio\", \"file\"];\n\n/** A control's value, whether it is native or a custom element. */\nexport interface Control extends Element {\n value: string;\n}\n\n/**\n * A native control whose state is its `value`: a `<select>`, a\n * `<textarea>`, or an `<input>` of any type but checkbox, radio and file.\n *\n * Told apart by tag name and type rather than by class, so it holds for an\n * element from any document.\n */\nfunction isNativeControl(el: Element): boolean {\n if (el.localName === \"select\" || el.localName === \"textarea\") return true;\n return (\n el.localName === \"input\" &&\n !UNLANDED_INPUTS.includes((el as HTMLInputElement).type)\n );\n}\n\n/**\n * A form-associated custom element with a `value` property. HTML marks one\n * with `static formAssociated = true` on its class, and a control has a value.\n */\nexport function isFormAssociated(el: Element): boolean {\n const ctor = el.constructor as { formAssociated?: unknown };\n return ctor.formAssociated === true && \"value\" in el;\n}\n\n/**\n * A control: an `<input>`, `<select>` or `<textarea>`, or a form-associated\n * custom element with a `value` property. Landing and gathering draw the\n * same line, so a value read back is the one that landed there.\n */\nexport function isControl(el: Element): el is Control {\n return isNativeControl(el) || isFormAssociated(el);\n}\n\n/**\n * Set an element from a column. A control's `value` is set. A custom element\n * that is not a control keeps its content, which the builder placed there\n * from its element file, and renders the stamp instead. Any other element's\n * text content is set. Every one carries the value as `lb-column-value`.\n */\nfunction land(el: Element, value: unknown): void {\n // Untouched, whatever the server sent: the browser decides what a\n // non-string looks like. See Row in core/lb-types.ts.\n if (value === null || value === undefined) {\n if (isControl(el)) el.value = \"\";\n else if (!el.localName.includes(\"-\") && el.localName !== \"input\") {\n el.textContent = \"\";\n }\n el.removeAttribute(ATTR_COLUMN_VALUE);\n return;\n }\n if (isControl(el)) {\n el.value = value as string;\n } else if (el.localName === \"input\") {\n console.warn(\n `lb-hub: a value does not land on <input type=\"${(el as HTMLInputElement).type}\">, ignoring`,\n el,\n );\n return;\n } else if (!el.localName.includes(\"-\")) {\n el.textContent = value as string;\n }\n el.setAttribute(ATTR_COLUMN_VALUE, value as string);\n}\n\n/**\n * Whether an element under `root` belongs to root's query, rather than to a\n * query nested inside it.\n *\n * A descendant is root's unless something between it and root carries\n * `lb-query`. The element's own `lb-query` does not count: it names what the\n * element holds, and the row it reads from is its ancestors'. So a picker\n * carrying `lb-query` and `lb-column` is set from the row around it. Root's\n * own attribute does not count either: root is what is being filled.\n *\n * Root may be the content of a template holding an absent branch. The walk\n * up from an element inside it stops at the top of that content, which has\n * no parent, and the template it stands for was in scope already.\n */\nfunction inScope(root: Element | DocumentFragment, el: Element): boolean {\n for (let node = el.parentElement; node && node !== root;) {\n if (node.hasAttribute(ATTR_QUERY)) return false;\n node = node.parentElement;\n }\n return true;\n}\n\n/**\n * Root if it matches, then every descendant in root's query that does.\n * Gathering finds columns through here. It does not reach into an absent\n * branch, so a live row is read back from the places a row lands that are\n * showing.\n */\nexport function within(root: Element, selector: string): Element[] {\n const found: Element[] = root.matches(selector) ? [root] : [];\n for (const el of root.querySelectorAll(selector)) {\n if (inScope(root, el)) found.push(el);\n }\n return found;\n}\n\n/**\n * Every descendant in root's query that matches, absent branches included.\n *\n * `querySelectorAll` does not enter template content, so an element that is\n * off would otherwise miss everything landed while it was away, and return\n * stale or with its rows empty. A template holding one is in root's query by\n * the ordinary rule, and its content is searched as if it stood there.\n */\nfunction below(root: Element | DocumentFragment, selector: string): Element[] {\n const found: Element[] = [];\n for (const el of root.querySelectorAll(selector)) {\n if (inScope(root, el)) found.push(el);\n }\n for (const parked of root.querySelectorAll(PARKED)) {\n if (inScope(root, parked)) {\n found.push(...below((parked as HTMLTemplateElement).content, selector));\n }\n }\n return found;\n}\n\n/**\n * Every element in root that matches, absent branches included. A query is\n * looked up this way before any row is known, so nothing is filtered out.\n */\nfunction everywhere(root: ParentNode, selector: string): Element[] {\n const found = [...root.querySelectorAll(selector)];\n for (const parked of root.querySelectorAll(PARKED)) {\n const content = (parked as HTMLTemplateElement).content;\n found.push(...everywhere(content, selector));\n }\n return found;\n}\n\n/**\n * Put an element in the document or take it out, as its `lb-show` column\n * says.\n *\n * Off is inside a template that stands where the element stood, carrying the\n * same `lb-show`. The element is moved rather than rebuilt, so a custom\n * element keeps its instance and a control keeps what was typed into it. On\n * moves it back and the template goes.\n *\n * Landing reaches into that template, so both it and the element it holds\n * arrive here. The template decides; the element it holds, whose parent is\n * the template's content, is left to it.\n */\nfunction present(el: Element, on: boolean): void {\n if (el.localName === \"template\") {\n const branch = (el as HTMLTemplateElement).content.firstElementChild;\n if (on && branch) el.replaceWith(branch);\n return;\n }\n if (on || el.parentNode?.nodeType === FRAGMENT_NODE) return;\n const parked = el.ownerDocument.createElement(\"template\");\n parked.setAttribute(ATTR_SHOW, el.getAttribute(ATTR_SHOW)!);\n el.replaceWith(parked);\n parked.content.append(el);\n}\n\n/**\n * Fill from one row: every `lb-column` and `lb-show` in root's query.\n *\n * A live row is its own row, so its root counts when it carries\n * `lb-column`: `<option>`'s content model is text, so an option row has to\n * be the column it displays. An element a `row` lands on is not its own\n * ancestor, so its own `lb-column` reads from the row around it and is left\n * alone here.\n */\nexport function applyRow(root: Element, row: Row, liveRow = false): void {\n for (const [column, value] of Object.entries(row)) {\n const selector = `[${ATTR_COLUMN}=\"${column}\"]`;\n const columns = below(root, selector);\n if (liveRow && root.matches(selector)) columns.unshift(root);\n for (const el of columns) land(el, value);\n // Only null and false are off. A string is never read, so \"false\" is on,\n // and the query spells a condition as a boolean or a null.\n //\n // Never the root's own: a row template's root carries none, and the\n // element a row lands on reads its condition from the row around it.\n const on = value !== null && value !== false;\n for (const el of below(root, `[${ATTR_SHOW}=\"${column}\"]`)) {\n present(el, on);\n }\n for (const el of below(root, `[${ATTR_SHOW}=\"${SHOW_NOT}${column}\"]`)) {\n present(el, !on);\n }\n }\n}\n\n/**\n * The row template: the first `<template>` among the element's descendants,\n * outside any nested `lb-query`. A template holding an absent branch is not a\n * row template: rows land before the row template, so one inside the first\n * live row comes first in document order.\n */\nexport function templateIn(el: Element): HTMLTemplateElement | null {\n const template = within(el, `template:not([${ATTR_SHOW}])`).find(\n (t) => t !== el,\n );\n return (template as HTMLTemplateElement | undefined) ?? null;\n}\n\n/**\n * The live rows of the element's query, in document order. A row of a nested\n * query is that query's. An element a `row` query landed on carries a key and\n * is not a live row, so a `row` shown in a table's head or foot is not one of\n * the table's rows.\n */\nfunction liveRows(el: Element): Element[] {\n return [...el.querySelectorAll(LIVE_ROW)].filter((row) => inScope(el, row));\n}\n\n/**\n * The live rows already showing, by the key each carries. Two live rows with\n * one key are a mistake nothing downstream can recover from: only one of them\n * would ever be matched, and the other would stay until the page goes.\n */\nfunction showing(el: Element): Map<string, Element> {\n const rows = new Map<string, Element>();\n for (const row of liveRows(el)) {\n const key = row.getAttribute(ATTR_KEY_VALUE)!;\n if (rows.has(key)) {\n console.error(\n `lb-hub: <${el.localName}> shows two rows with the key '${key}'`,\n );\n }\n rows.set(key, row);\n }\n return rows;\n}\n\n/**\n * Land rows through a row template.\n *\n * All rows decide membership and order: every row is placed in the order\n * given, and a row whose key did not arrive is gone. A patch disturbs only\n * what it names — a row it did not mention keeps its contents and its\n * position.\n *\n * A custom element that carries `lbPlaceRow` decides where a row goes,\n * because only it knows whether it sorts or groups. Without it a row lands\n * immediately before the template, so rows accumulate in the order they\n * arrive and the template stays put as the insertion marker.\n */\nexport function applyRows(\n el: Element,\n template: HTMLTemplateElement,\n keyColumn: string,\n result: RowsResult,\n): void {\n const shown = showing(el);\n const host = el as Element & RowsHost;\n const whole = !isPatch(result);\n const landed = new Set<string>();\n\n const place = (row: Element, data: Row): void => {\n if (host.lbPlaceRow) host.lbPlaceRow(row, data, template);\n else template.parentElement!.insertBefore(row, template);\n };\n\n const upsert = (data: Row): void => {\n if (data[keyColumn] === undefined) {\n console.error(\n `lb-hub: a row for <${el.localName}> has no '${keyColumn}' column`,\n );\n return;\n }\n // The key is stored on the row as an attribute and read back from\n // there, so it is compared as the string the attribute holds.\n const key = String(data[keyColumn]);\n if (landed.has(key)) {\n console.error(\n `lb-hub: two rows for <${el.localName}> have the key '${key}'; ` +\n `the last one shows`,\n );\n }\n landed.add(key);\n let row = shown.get(key);\n const fresh = row === undefined;\n if (!row) {\n // Imported rather than cloned: template content belongs to a document\n // with no custom element definitions, so a clone would not upgrade\n // until inserted, and a form-associated control would be filled as if\n // it were not one.\n row = el.ownerDocument.importNode(\n template.content.firstElementChild!,\n true,\n );\n row.setAttribute(ATTR_ROW_LIVE, \"\");\n row.setAttribute(ATTR_KEY_VALUE, key);\n shown.set(key, row);\n }\n // Fill before insertion. The attributes are already there when the row\n // upgrades, which is the same thing that makes a first landing and a\n // refresh one operation everywhere else.\n applyRow(row, data, true);\n if (fresh || whole) place(row, data);\n };\n\n if (Array.isArray(result)) {\n for (const data of result) upsert(data);\n for (const [key, row] of shown) if (!landed.has(key)) row.remove();\n } else {\n for (const data of result.rows ?? []) upsert(data);\n for (const key of result.drop ?? []) shown.get(String(key))?.remove();\n }\n\n // Counted from the DOM rather than from either branch above, so all rows\n // and a patch report the same fact the same way. Only here is the count\n // known: the server answers with rows and says nothing about how many\n // survived reconciliation.\n el.setAttribute(ATTR_QUERY_ROW_COUNT, String(liveRows(el).length));\n\n // Derived scaffolding — a section heading, an <optgroup> — goes when its\n // last row does, and only the custom element knows it exists.\n host.lbRowsLanded?.();\n}\n\n/** Land one response item on one element that names its query. */\nfunction landItem(el: Element, item: ResponseItem): void {\n const template = templateIn(el);\n if (item.kind === KIND_ROW) {\n const row = item.row ?? {};\n if (template) {\n applyRows(el, template, item.key, [row]);\n return;\n }\n applyRow(el, row);\n const key = row[item.key];\n if (key !== undefined && key !== null) {\n el.setAttribute(ATTR_KEY_VALUE, String(key));\n }\n return;\n }\n // A `rows` query with no row template lands nothing: an insert form names\n // the query it adds to, and shows none of its rows.\n if (!template) return;\n const result = item.patch ?? item.rows ?? [];\n applyRows(el, template, item.key, result);\n}\n\n/**\n * What landing keeps for one root: the last whole answer to each query, and\n * every element an answer has landed on.\n */\ninterface Kept {\n answers: Map<string, ResponseItem>;\n landed: WeakSet<Element>;\n}\n\nconst kept = new WeakMap<ParentNode, Kept>();\n\nfunction keptFor(root: ParentNode): Kept {\n let found = kept.get(root);\n if (!found) {\n found = { answers: new Map(), landed: new WeakSet() };\n kept.set(root, found);\n }\n return found;\n}\n\n/**\n * Drop what landing kept for a root. The hub calls it when it replaces the\n * page, since another page's query of the same name is another query.\n */\nexport function forget(root: ParentNode): void {\n kept.delete(root);\n}\n\n/**\n * All rows after a patch, as the elements it landed on now show them: a row\n * it names takes the columns it sends and keeps its place, a new one goes\n * last, and a dropped one goes.\n */\nfunction patched(rows: Row[], key: string, patch: Patch): Row[] {\n const byKey = new Map(rows.map((row) => [String(row[key]), row]));\n for (const row of patch.rows ?? []) {\n const k = String(row[key]);\n byKey.set(k, { ...byKey.get(k), ...row });\n }\n for (const k of patch.drop ?? []) byKey.delete(String(k));\n return [...byKey.values()];\n}\n\n/**\n * Keep an answer as all of the query's rows. A patch is applied to the\n * answer already kept; with none, it is not the whole of anything, and\n * nothing is kept.\n */\nfunction keep(answers: Map<string, ResponseItem>, item: ResponseItem): void {\n if (!item.patch) {\n answers.set(item.query, item);\n return;\n }\n const before = answers.get(item.query);\n if (!before) return;\n const { patch: _, ...whole } = item;\n answers.set(item.query, {\n ...whole,\n rows: patched(before.rows ?? [], item.key, item.patch),\n });\n}\n\n/**\n * A custom element whose class is defined and has not yet run on it: one the\n * builder shipped inside an absent branch's template, where nothing upgrades.\n * It has none of the hooks landing calls, so rows that land on it are not\n * placed by it. A landing on it does not count, and once `lb-show` brings it\n * into the document and it upgrades, it is filled again from the kept answer.\n *\n * An element with no class, such as one defined by an element file alone,\n * never upgrades, and is never waiting.\n */\nfunction waiting(el: Element): boolean {\n const registry = (globalThis as { customElements?: CustomElementRegistry })\n .customElements;\n const upgraded = registry?.get(el.localName);\n return upgraded !== undefined && !(el instanceof upgraded);\n}\n\n/**\n * Fill every element that names a kept query and has had nothing land on it.\n * Filling one can build more — a live row holding a picker of its own — so\n * it goes round until a pass finds nothing new.\n */\nfunction fillArrivals(root: ParentNode, { answers, landed }: Kept): void {\n for (let filled = true; filled;) {\n filled = false;\n for (const [query, item] of answers) {\n for (const el of everywhere(root, `[${ATTR_QUERY}=\"${query}\"]`)) {\n if (landed.has(el) || waiting(el)) continue;\n landed.add(el);\n landItem(el, item);\n filled = true;\n }\n }\n }\n}\n\n/**\n * Land response items. Every answer arrives through here, and is kept for\n * `root`, so that what arrives after it is filled from it — see forget().\n *\n * `quiet` names queries that may have nowhere to land without a warning: the\n * hub's own `lb-url` lands wherever a chrome or page names it, and nowhere\n * otherwise.\n */\nexport function applyResponse(\n root: ParentNode,\n items: ResponseItem[],\n quiet: readonly string[] = [],\n): void {\n const kept = keptFor(root);\n for (const item of items) {\n keep(kept.answers, item);\n const targets = everywhere(root, `[${ATTR_QUERY}=\"${item.query}\"]`);\n if (targets.length === 0) {\n if (!quiet.includes(item.query)) {\n console.warn(`lb-hub: nothing names '${item.query}', skipping`);\n }\n continue;\n }\n for (const el of targets) {\n if (!waiting(el)) kept.landed.add(el);\n landItem(el, item);\n }\n }\n fillArrivals(root, kept);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lb-hub.browser.d.ts","sourceRoot":"","sources":["../../hub/lb-hub.browser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lb-hub.browser.d.ts","sourceRoot":"","sources":["../../hub/lb-hub.browser.ts"],"names":[],"mappings":"AAkDA,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
|