@loadbare/app 0.25.0 → 0.26.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 +1 -2
- package/dist/build/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +11 -53
- package/dist/build/assemble.js.map +1 -1
- package/dist/build/cli.d.ts +4 -10
- package/dist/build/cli.d.ts.map +1 -1
- package/dist/build/cli.js +11 -33
- package/dist/build/cli.js.map +1 -1
- package/dist/build/declared.js +1 -1
- package/dist/build/declared.js.map +1 -1
- package/dist/core/lb-constants.d.ts +8 -18
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +32 -72
- package/dist/core/lb-constants.js.map +1 -1
- package/dist/core/lb-types.d.ts +12 -11
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js +7 -0
- package/dist/core/lb-types.js.map +1 -1
- package/dist/hub/lb-apply.d.ts +10 -37
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +139 -522
- 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 +115 -126
- package/dist/hub/lb-hub.browser.js.map +1 -1
- package/dist/hub/lb-lifecycle.d.ts +2 -2
- package/dist/hub/lb-lifecycle.d.ts.map +1 -1
- package/dist/hub/lb-lifecycle.js +9 -17
- package/dist/hub/lb-lifecycle.js.map +1 -1
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +3 -3
- package/dist/server/lb-express.js.map +1 -1
- package/dist/server/lb-server.d.ts +35 -19
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +141 -88
- package/dist/server/lb-server.js.map +1 -1
- package/docs/binding-cases.md +150 -0
- package/docs/comparison.md +18 -29
- package/docs/miswarts.md +2 -2
- package/docs/reference/css.md +2 -15
- package/docs/reference/custom-elements.md +6 -8
- package/docs/reference/data-binding.md +37 -180
- package/docs/reference/names.md +10 -26
- package/docs/reference/page-files.md +1 -12
- package/docs/reference/server.md +0 -1
- package/docs/testing.md +2 -5
- package/package.json +1 -1
- package/skills/loadbare-app/SKILL.md +52 -54
- package/skills/loadbare-app/references/css.md +2 -15
- package/skills/loadbare-app/references/custom-elements.md +6 -8
- package/skills/loadbare-app/references/data-binding.md +37 -180
- package/skills/loadbare-app/references/miswarts.md +2 -2
- package/skills/loadbare-app/references/names.md +10 -26
- package/skills/loadbare-app/references/page-files.md +1 -12
- package/skills/loadbare-app/references/server.md +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
|
-
import { ATTR_COLUMN, ATTR_COLUMN_VALUE, ATTR_KEY_VALUE, ATTR_PAGE, ATTR_PAGE_TITLE, ATTR_QUERY, ATTR_REQUEST, ATTR_REQUEST_ERROR, ATTR_REQUEST_PENDING, ATTR_REQUEST_REFUSED, ATTR_ROW_EDITING,
|
|
3
|
-
import { isRowRequest, nextLocation, pageNameFor, parmsRow, } from "../core/lb-types.js";
|
|
4
|
-
import { addUnentered, applyResponse, discardUnentered, applyRow, blankRow, focusIn, gatherInto,
|
|
2
|
+
import { ATTR_COLUMN, ATTR_COLUMN_VALUE, ATTR_KEY_VALUE, ATTR_PAGE, ATTR_PAGE_TITLE, ATTR_QUERY, ATTR_REQUEST, ATTR_REQUEST_ERROR, ATTR_REQUEST_PENDING, ATTR_REQUEST_REFUSED, ATTR_ROW_EDITING, ATTR_URL_LINK, ATTR_URL_PUSH, ATTR_URL_UNKNOWN, COMMAND_EDIT, COMMAND_NEW, COMMAND_RELEASE, COMMAND_PLACE, FILTERS_COLUMN_QUERY, FILTERS_QUERY, HUB_COLUMN_AS_OF, HUB_COLUMN_ERROR, HUB_COLUMN_LOAD_STATE, HUB_COLUMN_PENDING, HUB_COLUMN_SERVER_STATE, HUB_ERROR_NETWORK, HUB_ERROR_TIMEOUT, HUB_LOAD_FAILED, HUB_LOAD_LOADED, HUB_LOAD_LOADING, HUB_QUERY, HUB_SERVER_FAILING, HUB_SERVER_REACHABLE, HUB_SERVER_UNREACHABLE, HUB_TAG_NAME, KIND_ROW, KIND_ROWS, LB_DONE_EVENT_NAME, LB_ENDPOINT, LB_EVENT_NAME, LB_REFUSAL_STATUS, LB_REQUEST_TIMEOUT_MS, LB_COMMAND_PREFIX, LB_RESERVED_PREFIX, LIVE_ROW, REQUEST_READ, REQUEST_ROW_DELETE, REQUEST_ROW_INSERT, REQUEST_ROW_UPDATE, SORT_COLUMN_ORDER, SORT_COLUMN_QUERY, SORT_QUERY, URL_COLUMN_PAGE_LABEL, URL_COLUMN_PAGE_UNKNOWN, URL_COLUMN_PATH, URL_QUERY, } from "../core/lb-constants.js";
|
|
3
|
+
import { isRowRequest, nextLocation, pageNameFor, parmsRow, oneRow, } from "../core/lb-types.js";
|
|
4
|
+
import { addUnentered, applyResponse, discardUnentered, applyRow, blankRow, focusIn, gatherInto, forget, isControl, isFormAssociated, keptAnswer, placeAgain, templateIn, within, } from "./lb-apply.js";
|
|
5
5
|
import { attachPlaced, detachRemoved, placed, requestDone, upgradeAbsent, } from "./lb-lifecycle.js";
|
|
6
6
|
export { applyResponse, applyRow } from "./lb-apply.js";
|
|
7
7
|
/** What each request Loadbare provides cannot be issued without. */
|
|
@@ -55,8 +55,7 @@ function formOwnerOf(el) {
|
|
|
55
55
|
: null;
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
* The controls a request gathers
|
|
59
|
-
* "Gathering":
|
|
58
|
+
* The controls a request gathers:
|
|
60
59
|
*
|
|
61
60
|
* 1. the element carrying `lb-request` has `lb-column`: it alone;
|
|
62
61
|
* 2. the element is a `<form>`: the controls whose form owner it is;
|
|
@@ -194,24 +193,6 @@ const insertedFrom = new WeakMap();
|
|
|
194
193
|
const pointed = new WeakMap();
|
|
195
194
|
/** The pointed form each request gathered from, until it succeeds. */
|
|
196
195
|
const savedFrom = new WeakMap();
|
|
197
|
-
/**
|
|
198
|
-
* The `lb-select` checkboxes showing for a query, under the hub an element
|
|
199
|
-
* is in. One parked by `lb-show` is in template content, out of reach, so
|
|
200
|
-
* only a selection the user can see counts.
|
|
201
|
-
*/
|
|
202
|
-
function selectorsFor(el, query) {
|
|
203
|
-
const root = el.closest(HUB_TAG_NAME) ?? el.ownerDocument;
|
|
204
|
-
return [...root.querySelectorAll(`[${ATTR_SELECT}]`)].filter((box) => {
|
|
205
|
-
const live = liveRowOf(box);
|
|
206
|
-
return live !== null && queryOf(live)?.getAttribute(ATTR_QUERY) === query;
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
/** The keys of the rows a query's showing checkboxes select. */
|
|
210
|
-
function selectedKeys(boxes) {
|
|
211
|
-
return boxes
|
|
212
|
-
.filter((box) => box.checked)
|
|
213
|
-
.map((box) => liveRowOf(box).getAttribute(ATTR_KEY_VALUE));
|
|
214
|
-
}
|
|
215
196
|
/**
|
|
216
197
|
* Complete a request from where its element sits. Every request passes
|
|
217
198
|
* through here, whoever issued it; a field it already carries is its
|
|
@@ -281,7 +262,7 @@ function complete(request, el) {
|
|
|
281
262
|
}
|
|
282
263
|
// A query the hub serves has one row, keyed by the path, so a request for
|
|
283
264
|
// one from anywhere on the page is for that row.
|
|
284
|
-
if (request.query === URL_QUERY
|
|
265
|
+
if (request.query === URL_QUERY) {
|
|
285
266
|
request.key ??= location.pathname;
|
|
286
267
|
}
|
|
287
268
|
if (name === REQUEST_ROW_INSERT && group?.form) {
|
|
@@ -307,28 +288,9 @@ function complete(request, el) {
|
|
|
307
288
|
return false;
|
|
308
289
|
}
|
|
309
290
|
}
|
|
310
|
-
// A request that names no one row acts on the rows the user selected,
|
|
311
|
-
// when its query shows checkboxes to select them by. An insert adds a row
|
|
312
|
-
// and selects nothing. With none ticked, an update or a delete has
|
|
313
|
-
// nothing to act on, and is not sent, as for a row not yet entered.
|
|
314
|
-
if (request.key === undefined &&
|
|
315
|
-
request.keys === undefined &&
|
|
316
|
-
request.query !== undefined &&
|
|
317
|
-
!unentered &&
|
|
318
|
-
name !== REQUEST_ROW_INSERT) {
|
|
319
|
-
const boxes = selectorsFor(el, request.query);
|
|
320
|
-
if (boxes.length > 0) {
|
|
321
|
-
request.keys = selectedKeys(boxes);
|
|
322
|
-
if (request.keys.length === 0 &&
|
|
323
|
-
(name === REQUEST_ROW_UPDATE || name === REQUEST_ROW_DELETE)) {
|
|
324
|
-
return false;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
291
|
const needs = NEEDS[name];
|
|
329
292
|
if (needs) {
|
|
330
|
-
const missing = needs.filter((field) =>
|
|
331
|
-
!(field === "key" && request.keys !== undefined)) ||
|
|
293
|
+
const missing = needs.filter((field) => request[field] === undefined ||
|
|
332
294
|
(field === "values" &&
|
|
333
295
|
Object.keys(request.values).length === 0 &&
|
|
334
296
|
request.rows === undefined));
|
|
@@ -420,9 +382,18 @@ function pageHostFor(page) {
|
|
|
420
382
|
* browser is showing rides after it verbatim, so every round trip reaches
|
|
421
383
|
* the server with the query parms in hand.
|
|
422
384
|
*/
|
|
423
|
-
async function fetchItems(page, request) {
|
|
385
|
+
async function fetchItems(page, request, parms) {
|
|
424
386
|
const path = page.split("/").map(encodeURIComponent).join("/");
|
|
425
|
-
|
|
387
|
+
// Parms set for this request alone, as a bound row's key is for the lists
|
|
388
|
+
// inside it. An empty one is still sent: it names no row.
|
|
389
|
+
let query = location.search;
|
|
390
|
+
if (parms) {
|
|
391
|
+
const search = new URLSearchParams(location.search);
|
|
392
|
+
for (const [name, value] of Object.entries(parms))
|
|
393
|
+
search.set(name, value);
|
|
394
|
+
query = `?${search.toString()}`;
|
|
395
|
+
}
|
|
396
|
+
const url = `${LB_ENDPOINT}/${path}${query}`;
|
|
426
397
|
const controller = new AbortController();
|
|
427
398
|
const timeout = setTimeout(() => controller.abort(), LB_REQUEST_TIMEOUT_MS);
|
|
428
399
|
try {
|
|
@@ -516,7 +487,7 @@ function clear(form) {
|
|
|
516
487
|
*/
|
|
517
488
|
function point(form, row, key, live) {
|
|
518
489
|
clear(form);
|
|
519
|
-
if (row)
|
|
490
|
+
if (row !== null)
|
|
520
491
|
applyRow(form, row);
|
|
521
492
|
form.setAttribute(ATTR_KEY_VALUE, key);
|
|
522
493
|
live?.setAttribute(ATTR_ROW_EDITING, "");
|
|
@@ -599,7 +570,7 @@ class LbHub extends HTMLElement {
|
|
|
599
570
|
* lands, so each custom element in it is filled before it is attached.
|
|
600
571
|
*/
|
|
601
572
|
unplaced = [];
|
|
602
|
-
/** The hub's own state, as the query `lb-hub` serves it. */
|
|
573
|
+
/** The hub's own state, as the query `lb-hub-status` serves it. */
|
|
603
574
|
trips = 0;
|
|
604
575
|
loadState = HUB_LOAD_LOADING;
|
|
605
576
|
serverState = "";
|
|
@@ -621,10 +592,18 @@ class LbHub extends HTMLElement {
|
|
|
621
592
|
// The element that issued the request, not e.currentTarget (the hub
|
|
622
593
|
// itself, since the event bubbles).
|
|
623
594
|
const origin = e.target ?? undefined;
|
|
624
|
-
if (request.query
|
|
595
|
+
if (request.query === URL_QUERY) {
|
|
625
596
|
void this.answer(request, origin);
|
|
626
597
|
return;
|
|
627
598
|
}
|
|
599
|
+
if (request.query === SORT_QUERY) {
|
|
600
|
+
void this.chooseSort(request, origin);
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
if (request.query === FILTERS_QUERY) {
|
|
604
|
+
void this.chooseFilters(request, origin);
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
628
607
|
void this.send(request, origin);
|
|
629
608
|
});
|
|
630
609
|
// A form commits on submit. The submitter's own lb-request replaces the
|
|
@@ -665,14 +644,6 @@ class LbHub extends HTMLElement {
|
|
|
665
644
|
edited(e.target);
|
|
666
645
|
}, { capture: true });
|
|
667
646
|
}
|
|
668
|
-
// A selector selects its row, and is no column.
|
|
669
|
-
this.addEventListener("change", (e) => {
|
|
670
|
-
const box = e.target;
|
|
671
|
-
if (box?.hasAttribute(ATTR_SELECT)) {
|
|
672
|
-
select(box, box.checked);
|
|
673
|
-
}
|
|
674
|
-
});
|
|
675
|
-
// A control commits on change.
|
|
676
647
|
this.addEventListener("change", (e) => {
|
|
677
648
|
const el = e.target;
|
|
678
649
|
if (!el || !el.hasAttribute(ATTR_REQUEST) || !isControl(el))
|
|
@@ -781,10 +752,12 @@ class LbHub extends HTMLElement {
|
|
|
781
752
|
return;
|
|
782
753
|
}
|
|
783
754
|
point(form, row, key, live);
|
|
755
|
+
void this.readNested(form, key);
|
|
784
756
|
return;
|
|
785
757
|
}
|
|
786
758
|
case COMMAND_NEW:
|
|
787
759
|
point(form, null, "", null);
|
|
760
|
+
void this.readNested(form, "");
|
|
788
761
|
return;
|
|
789
762
|
case COMMAND_RELEASE:
|
|
790
763
|
release(form);
|
|
@@ -794,6 +767,36 @@ class LbHub extends HTMLElement {
|
|
|
794
767
|
`names no command Loadbare provides, ignoring`);
|
|
795
768
|
}
|
|
796
769
|
}
|
|
770
|
+
/**
|
|
771
|
+
* Make the lists nested in a form follow the row it is now bound to: drop
|
|
772
|
+
* the rows not yet entered there, and ask the server for each list's
|
|
773
|
+
* query again with the row's key as the parm its key column names. A new
|
|
774
|
+
* row's key is empty, so its lists answer with none. A form bound to
|
|
775
|
+
* another row, or a page moved on, before the answer returns lands
|
|
776
|
+
* nothing.
|
|
777
|
+
*/
|
|
778
|
+
async readNested(form, key) {
|
|
779
|
+
const lists = [...form.querySelectorAll(`[${ATTR_QUERY}]`)].filter((el) => queryOf(el) === form && templateIn(el) !== null);
|
|
780
|
+
if (lists.length === 0)
|
|
781
|
+
return;
|
|
782
|
+
const column = keptAnswer(this, form.getAttribute(ATTR_QUERY))?.key;
|
|
783
|
+
if (column === undefined)
|
|
784
|
+
return;
|
|
785
|
+
for (const list of lists) {
|
|
786
|
+
for (const row of list.querySelectorAll(`[${ATTR_KEY_VALUE}=""]`)) {
|
|
787
|
+
if (queryOf(row) === list)
|
|
788
|
+
discardUnentered(row);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
const queries = [...new Set(lists.map((el) => el.getAttribute(ATTR_QUERY)))];
|
|
792
|
+
const at = here();
|
|
793
|
+
const items = await this.roundTrip(this.page, { name: REQUEST_READ, queries }, undefined, { [column]: key });
|
|
794
|
+
if (items instanceof Error || here() !== at)
|
|
795
|
+
return;
|
|
796
|
+
if (form.getAttribute(ATTR_KEY_VALUE) !== key)
|
|
797
|
+
return;
|
|
798
|
+
applyResponse(this, items);
|
|
799
|
+
}
|
|
797
800
|
/**
|
|
798
801
|
* Place a list's rows by the order and the filter again, from the rows
|
|
799
802
|
* the hub holds: no round trip, since the user asks only to see what they
|
|
@@ -845,11 +848,6 @@ class LbHub extends HTMLElement {
|
|
|
845
848
|
else
|
|
846
849
|
landed = await this.follow(url, load, origin);
|
|
847
850
|
}
|
|
848
|
-
// The selected rows have been acted on, and the selection is done.
|
|
849
|
-
if (request.keys !== undefined && request.query !== undefined) {
|
|
850
|
-
for (const box of selectorsFor(this, request.query))
|
|
851
|
-
select(box, false);
|
|
852
|
-
}
|
|
853
851
|
// A pointed form has done what it was pointed for, and lets go. A form
|
|
854
852
|
// an insert was gathered from starts over, unless the answer landed the
|
|
855
853
|
// new row on it.
|
|
@@ -889,7 +887,7 @@ class LbHub extends HTMLElement {
|
|
|
889
887
|
* element that issued it carries `lb-request-pending` while it is in
|
|
890
888
|
* flight, and `lb-request-error` when it fails. The error on failure.
|
|
891
889
|
*/
|
|
892
|
-
async roundTrip(page, request, origin) {
|
|
890
|
+
async roundTrip(page, request, origin, parms) {
|
|
893
891
|
// A page load says how the page showing got its data, unless the URL
|
|
894
892
|
// has moved on by the time it returns, and a later load says instead.
|
|
895
893
|
const load = request === undefined;
|
|
@@ -900,7 +898,7 @@ class LbHub extends HTMLElement {
|
|
|
900
898
|
this.loadState = HUB_LOAD_LOADING;
|
|
901
899
|
this.landHub();
|
|
902
900
|
try {
|
|
903
|
-
const items = await fetchItems(page, request);
|
|
901
|
+
const items = await fetchItems(page, request, parms);
|
|
904
902
|
this.serverState = HUB_SERVER_REACHABLE;
|
|
905
903
|
this.error = "";
|
|
906
904
|
this.asOf = new Date().toISOString();
|
|
@@ -961,19 +959,15 @@ class LbHub extends HTMLElement {
|
|
|
961
959
|
if (!this.querySelector(`[${ATTR_QUERY}="${HUB_QUERY}"]`))
|
|
962
960
|
return;
|
|
963
961
|
applyResponse(this, [
|
|
964
|
-
{ query: HUB_QUERY, kind: KIND_ROW, key: URL_COLUMN_PATH, row },
|
|
962
|
+
{ query: HUB_QUERY, kind: KIND_ROW, key: URL_COLUMN_PATH, rows: [row] },
|
|
965
963
|
]);
|
|
966
964
|
}
|
|
967
965
|
/**
|
|
968
|
-
* Answer a request for
|
|
969
|
-
*
|
|
970
|
-
* `lb-hub` too, which nothing but the hub writes.
|
|
966
|
+
* Answer a request for `lb-url`, the one query the hub serves, with no
|
|
967
|
+
* round trip: write the URL and load the page it names.
|
|
971
968
|
*/
|
|
972
969
|
async answer(request, origin) {
|
|
973
|
-
if (request.
|
|
974
|
-
return done(origin, request, [this.choose(request.values ?? {})]);
|
|
975
|
-
}
|
|
976
|
-
if (request.query !== URL_QUERY || request.name !== REQUEST_ROW_UPDATE) {
|
|
970
|
+
if (request.name !== REQUEST_ROW_UPDATE) {
|
|
977
971
|
const message = `lb-hub: '${request.name}' for '${request.query}' is not a request ` +
|
|
978
972
|
`the hub answers, ignoring`;
|
|
979
973
|
console.error(message);
|
|
@@ -986,50 +980,64 @@ class LbHub extends HTMLElement {
|
|
|
986
980
|
done(origin, request, await this.navigate(origin));
|
|
987
981
|
}
|
|
988
982
|
/**
|
|
989
|
-
*
|
|
990
|
-
*
|
|
991
|
-
*
|
|
992
|
-
*
|
|
983
|
+
* Answer an order the user chose: land its row of `lb-sort`, which places
|
|
984
|
+
* the rows it holds again by the new order, and send the order to the
|
|
985
|
+
* server to keep for the next page. Re-sorting is the browser's, from the
|
|
986
|
+
* rows it holds, so the round trip only persists the change.
|
|
993
987
|
*/
|
|
994
|
-
|
|
995
|
-
const
|
|
996
|
-
|
|
997
|
-
...values,
|
|
998
|
-
[URL_COLUMN_PATH]: location.pathname,
|
|
999
|
-
};
|
|
988
|
+
async chooseSort(request, origin) {
|
|
989
|
+
const query = request.key ?? "";
|
|
990
|
+
const order = request.values?.[SORT_COLUMN_ORDER] ?? "";
|
|
1000
991
|
const item = {
|
|
1001
|
-
query:
|
|
1002
|
-
kind:
|
|
1003
|
-
key:
|
|
1004
|
-
|
|
992
|
+
query: SORT_QUERY,
|
|
993
|
+
kind: KIND_ROWS,
|
|
994
|
+
key: SORT_COLUMN_QUERY,
|
|
995
|
+
rows: [{ [SORT_COLUMN_QUERY]: query, [SORT_COLUMN_ORDER]: order }],
|
|
1005
996
|
};
|
|
1006
997
|
applyResponse(this, [item]);
|
|
1007
|
-
|
|
1008
|
-
|
|
998
|
+
const items = await this.roundTrip(this.page, {
|
|
999
|
+
name: REQUEST_ROW_UPDATE,
|
|
1000
|
+
query: SORT_QUERY,
|
|
1001
|
+
key: query,
|
|
1002
|
+
values: { [SORT_COLUMN_ORDER]: order },
|
|
1003
|
+
}, undefined);
|
|
1004
|
+
if (!(items instanceof Error))
|
|
1005
|
+
applyResponse(this, items, { requested: true });
|
|
1006
|
+
done(origin, request, [item]);
|
|
1009
1007
|
}
|
|
1010
1008
|
/**
|
|
1011
|
-
*
|
|
1012
|
-
* `lb-
|
|
1013
|
-
*
|
|
1014
|
-
*
|
|
1009
|
+
* Answer a filter the user chose: merge it into the query's row of
|
|
1010
|
+
* `lb-filters`, land it, and send the whole row to the server to keep for
|
|
1011
|
+
* the next page. Filtering is the browser's, so no round trip answers the
|
|
1012
|
+
* change; the round trip only persists it.
|
|
1015
1013
|
*/
|
|
1016
|
-
async
|
|
1017
|
-
const
|
|
1018
|
-
const values = {};
|
|
1019
|
-
|
|
1020
|
-
|
|
1014
|
+
async chooseFilters(request, origin) {
|
|
1015
|
+
const query = request.key ?? "";
|
|
1016
|
+
const values = request.values ?? {};
|
|
1017
|
+
const held = keptAnswer(this, FILTERS_QUERY)?.rows?.find((row) => row[FILTERS_COLUMN_QUERY] === query);
|
|
1018
|
+
const row = { ...held, ...values, [FILTERS_COLUMN_QUERY]: query };
|
|
1019
|
+
const item = {
|
|
1020
|
+
query: FILTERS_QUERY,
|
|
1021
|
+
kind: KIND_ROWS,
|
|
1022
|
+
key: FILTERS_COLUMN_QUERY,
|
|
1023
|
+
rows: [row],
|
|
1024
|
+
};
|
|
1025
|
+
applyResponse(this, [item]);
|
|
1026
|
+
const columns = {};
|
|
1027
|
+
for (const [column, value] of Object.entries(row)) {
|
|
1028
|
+
if (column !== FILTERS_COLUMN_QUERY && typeof value === "string") {
|
|
1029
|
+
columns[column] = value;
|
|
1030
|
+
}
|
|
1021
1031
|
}
|
|
1022
|
-
const at = here();
|
|
1023
1032
|
const items = await this.roundTrip(this.page, {
|
|
1024
1033
|
name: REQUEST_ROW_UPDATE,
|
|
1025
|
-
query:
|
|
1026
|
-
key:
|
|
1027
|
-
values,
|
|
1034
|
+
query: FILTERS_QUERY,
|
|
1035
|
+
key: query,
|
|
1036
|
+
values: columns,
|
|
1028
1037
|
}, undefined);
|
|
1029
|
-
if (items instanceof Error
|
|
1030
|
-
return;
|
|
1031
|
-
if (items.length > 0)
|
|
1038
|
+
if (!(items instanceof Error))
|
|
1032
1039
|
applyResponse(this, items, { requested: true });
|
|
1040
|
+
done(origin, request, [item]);
|
|
1033
1041
|
}
|
|
1034
1042
|
/**
|
|
1035
1043
|
* Write a row for `lb-url` into the URL. A new page pushes a history
|
|
@@ -1056,7 +1064,7 @@ class LbHub extends HTMLElement {
|
|
|
1056
1064
|
* asked for again that fails is not the request's failure.
|
|
1057
1065
|
*/
|
|
1058
1066
|
async follow(item, load, origin) {
|
|
1059
|
-
this.moveTo(item
|
|
1067
|
+
this.moveTo(oneRow(item) ?? {}, origin?.hasAttribute(ATTR_URL_PUSH) ?? false);
|
|
1060
1068
|
if (load.length === 0) {
|
|
1061
1069
|
const again = await this.navigate();
|
|
1062
1070
|
return again instanceof Error ? [item] : [item, ...again];
|
|
@@ -1159,10 +1167,9 @@ class LbHub extends HTMLElement {
|
|
|
1159
1167
|
this.unplaced.push(this.main);
|
|
1160
1168
|
forget(this);
|
|
1161
1169
|
this.entering = true;
|
|
1162
|
-
this.landChoices();
|
|
1163
1170
|
}
|
|
1164
1171
|
this.landUrl(template ? titleOf(template, page) : null);
|
|
1165
|
-
// An app that ships `<body hidden>`
|
|
1172
|
+
// An app that ships `<body hidden>` stays
|
|
1166
1173
|
// invisible until there is something coherent to show.
|
|
1167
1174
|
document.body.hidden = false;
|
|
1168
1175
|
if (!template) {
|
|
@@ -1174,24 +1181,6 @@ class LbHub extends HTMLElement {
|
|
|
1174
1181
|
}
|
|
1175
1182
|
return template;
|
|
1176
1183
|
}
|
|
1177
|
-
/**
|
|
1178
|
-
* The user's choices as a page begins: none made, so every column an
|
|
1179
|
-
* element under `lb-query="lb-choices"` shows lands empty. Choices hold
|
|
1180
|
-
* until the page changes, unless the server restores them.
|
|
1181
|
-
*/
|
|
1182
|
-
landChoices() {
|
|
1183
|
-
const row = {};
|
|
1184
|
-
for (const scope of this.querySelectorAll(`[${ATTR_QUERY}="${CHOICES_QUERY}"]`)) {
|
|
1185
|
-
for (const el of within(scope, `[${ATTR_COLUMN}]`)) {
|
|
1186
|
-
if (el !== scope)
|
|
1187
|
-
row[el.getAttribute(ATTR_COLUMN)] = "";
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
row[URL_COLUMN_PATH] = location.pathname;
|
|
1191
|
-
applyResponse(this, [
|
|
1192
|
-
{ query: CHOICES_QUERY, kind: KIND_ROW, key: URL_COLUMN_PATH, row },
|
|
1193
|
-
]);
|
|
1194
|
-
}
|
|
1195
1184
|
/**
|
|
1196
1185
|
* The hub's own query: one row, keyed by the path, whose other columns are
|
|
1197
1186
|
* the query parms.
|
|
@@ -1221,7 +1210,7 @@ class LbHub extends HTMLElement {
|
|
|
1221
1210
|
row[URL_COLUMN_PATH] = location.pathname;
|
|
1222
1211
|
row[URL_COLUMN_PAGE_LABEL] = label;
|
|
1223
1212
|
row[URL_COLUMN_PAGE_UNKNOWN] = label === null;
|
|
1224
|
-
applyResponse(this, [{ query: URL_QUERY, kind: KIND_ROW, key: URL_COLUMN_PATH, row }]);
|
|
1213
|
+
applyResponse(this, [{ query: URL_QUERY, kind: KIND_ROW, key: URL_COLUMN_PATH, rows: [row] }]);
|
|
1225
1214
|
}
|
|
1226
1215
|
}
|
|
1227
1216
|
customElements.define(HUB_TAG_NAME, LbHub);
|