@loadbare/app 0.23.0 → 0.25.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 +78 -1
- package/dist/build/assemble.js.map +1 -1
- package/dist/core/lb-constants.d.ts +8 -5
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +20 -11
- package/dist/core/lb-constants.js.map +1 -1
- package/dist/core/lb-types.d.ts +19 -3
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js +35 -5
- package/dist/core/lb-types.js.map +1 -1
- package/dist/hub/lb-apply.d.ts +31 -22
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +186 -52
- 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 +59 -28
- package/dist/hub/lb-hub.browser.js.map +1 -1
- package/dist/server/lb-server.d.ts +2 -2
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +29 -9
- package/dist/server/lb-server.js.map +1 -1
- package/docs/comparison.md +13 -11
- package/docs/miswarts.md +4 -3
- package/docs/reference/chrome.md +42 -0
- package/docs/reference/custom-elements.md +9 -4
- package/docs/reference/data-binding.md +143 -67
- package/docs/reference/names.md +11 -8
- package/docs/reference/page-files.md +11 -6
- package/docs/testing.md +3 -3
- package/package.json +1 -1
- package/skills/loadbare-app/SKILL.md +38 -15
- package/skills/loadbare-app/references/chrome.md +42 -0
- package/skills/loadbare-app/references/custom-elements.md +9 -4
- package/skills/loadbare-app/references/data-binding.md +143 -67
- package/skills/loadbare-app/references/miswarts.md +4 -3
- package/skills/loadbare-app/references/names.md +11 -8
- package/skills/loadbare-app/references/page-files.md +11 -6
- package/docs/temp-proposals.md +0 -251
package/dist/hub/lb-apply.js
CHANGED
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
* landing reaches in there as it reaches everywhere else, so a branch that
|
|
39
39
|
* returns is already current. Nothing but landing reaches in.
|
|
40
40
|
*/
|
|
41
|
-
import { AGGREGATES, ATTR_COLUMN, ATTR_FILTER, ATTR_ORDER, CHOICES_QUERY, LB_RESERVED_PREFIX, ATTR_COLUMN_VALUE, ATTR_COUNT, ATTR_GROUP, ATTR_GROUP_COLUMN, ATTR_GROUP_LEAVING, ATTR_GROUP_LIVE, ATTR_GROUP_VALUE, ATTR_KEY_VALUE, ATTR_MAX, ATTR_MIN, ATTR_QUERY, ATTR_QUERY_ORDER, ATTR_QUERY_ROW_COUNT,
|
|
41
|
+
import { AGGREGATES, ATTR_COLUMN, ATTR_FILTER, ATTR_ORDER, CHOICES_QUERY, LB_RESERVED_PREFIX, ATTR_COLUMN_VALUE, ATTR_COUNT, ATTR_GROUP, ATTR_GROUP_COLUMN, ATTR_GROUP_LEAVING, ATTR_GROUP_LIVE, ATTR_GROUP_VALUE, ATTR_KEY_VALUE, ATTR_MAX, ATTR_MIN, ATTR_QUERY, ATTR_QUERY_ORDER, ATTR_QUERY_ROW_COUNT, ATTR_MANUAL, MANUAL_FILTER, MANUAL_ORDER, ATTR_QUERY_UNPLACED_COUNT, ATTR_ROW_CHANGED, ATTR_ROW_CREATED, ATTR_ROW_LEAVING, ATTR_ROW_LIVE, ATTR_ROW_MOVED, ATTR_ROW_REQUESTED, ATTR_ROW_UNPLACED, ATTR_SHOW, ATTR_SUM, ATTR_URL_PARM, KIND_ROW, LIVE_GROUP, LIVE_ROW, ORDER_DESCENDING, REQUESTED_CHANGED, REQUESTED_CREATED, REQUESTED_MOVED, SHOW_NOT, ATTR_SELECT, ATTR_ROW_SELECTED, } from "../core/lb-constants.js";
|
|
42
42
|
import { compareRows, compareValues, isPatch, parseFilter, parseOrder, passes, readAs, } from "../core/lb-types.js";
|
|
43
43
|
import { attachPlaced, detachRemoved, LEAVING, PARKED, placed, upgradeAbsent, } from "./lb-lifecycle.js";
|
|
44
44
|
/** A DocumentFragment's nodeType. Tier 3 has no `Node` global to read it from. */
|
|
@@ -100,23 +100,58 @@ export function isFormAssociated(el) {
|
|
|
100
100
|
export function isControl(el) {
|
|
101
101
|
return (isNativeControl(el) || isCheckbox(el) || isRadio(el) || isFormAssociated(el));
|
|
102
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* A control that holds several values: a `<select multiple>`, or a checkbox
|
|
105
|
+
* carrying a `value`, one of a set naming one column. A checkbox with no
|
|
106
|
+
* `value` is a flag.
|
|
107
|
+
*/
|
|
108
|
+
function holdsSeveral(el) {
|
|
109
|
+
if (el.localName === "select")
|
|
110
|
+
return el.multiple;
|
|
111
|
+
return isCheckbox(el) && el.hasAttribute("value");
|
|
112
|
+
}
|
|
113
|
+
/** A landed value as the list of strings a control holding several shows. */
|
|
114
|
+
function listOf(value) {
|
|
115
|
+
if (Array.isArray(value))
|
|
116
|
+
return value.map((v) => String(v));
|
|
117
|
+
if (value === null || value === undefined || value === "")
|
|
118
|
+
return [];
|
|
119
|
+
return [String(value)];
|
|
120
|
+
}
|
|
103
121
|
/**
|
|
104
122
|
* What a control gathers: a checkbox `"true"` or `"false"`, so unchecked
|
|
105
|
-
* still says so,
|
|
123
|
+
* still says so, a `<select multiple>` the values of its selected options,
|
|
124
|
+
* and any other control its `value`, which a custom element may give as
|
|
125
|
+
* several.
|
|
106
126
|
*/
|
|
107
127
|
function controlValue(el) {
|
|
108
|
-
|
|
128
|
+
if (isCheckbox(el))
|
|
129
|
+
return String(el.checked);
|
|
130
|
+
if (el.localName === "select" && el.multiple) {
|
|
131
|
+
return [...el.selectedOptions].map((o) => o.value);
|
|
132
|
+
}
|
|
133
|
+
const value = el.value;
|
|
134
|
+
return Array.isArray(value) ? value.map((v) => String(v)) : el.value;
|
|
109
135
|
}
|
|
110
136
|
/**
|
|
111
137
|
* Gather a control into `values` under its column. Of the radio buttons
|
|
112
138
|
* naming one column, the one checked gives the value, and `""` stands when
|
|
113
|
-
* none is.
|
|
139
|
+
* none is. Of the checkboxes in a set naming one column, the checked ones
|
|
140
|
+
* give their values, and none checked gives none.
|
|
114
141
|
*/
|
|
115
142
|
export function gatherInto(values, column, el) {
|
|
116
143
|
if (isRadio(el) && !el.checked) {
|
|
117
144
|
values[column] ??= "";
|
|
118
145
|
return;
|
|
119
146
|
}
|
|
147
|
+
if (isCheckbox(el) && holdsSeveral(el)) {
|
|
148
|
+
const had = values[column];
|
|
149
|
+
const set = Array.isArray(had) ? had : [];
|
|
150
|
+
values[column] = set;
|
|
151
|
+
if (el.checked)
|
|
152
|
+
set.push(el.value);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
120
155
|
values[column] = controlValue(el);
|
|
121
156
|
}
|
|
122
157
|
/**
|
|
@@ -126,6 +161,12 @@ export function gatherInto(values, column, el) {
|
|
|
126
161
|
* text content is set. Every one carries the value as `lb-column-value`.
|
|
127
162
|
*/
|
|
128
163
|
function land(el, value) {
|
|
164
|
+
if (holdsSeveral(el))
|
|
165
|
+
return landSeveral(el, value);
|
|
166
|
+
// A custom element control takes several values as they are. Anything
|
|
167
|
+
// else holds one, and shows the first, as a URL's parm reads.
|
|
168
|
+
if (Array.isArray(value) && !isFormAssociated(el))
|
|
169
|
+
value = value[0];
|
|
129
170
|
// Untouched, whatever the server sent: the browser decides what a
|
|
130
171
|
// non-string looks like. See Row in core/lb-types.ts.
|
|
131
172
|
if (value === null || value === undefined) {
|
|
@@ -163,6 +204,26 @@ function land(el, value) {
|
|
|
163
204
|
}
|
|
164
205
|
setStamp(el, ATTR_COLUMN_VALUE, String(value));
|
|
165
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Set a control that holds several values: a checkbox in a set is checked
|
|
209
|
+
* when its `value` is among them, and a `<select multiple>` selects each
|
|
210
|
+
* option whose `value` is.
|
|
211
|
+
*/
|
|
212
|
+
function landSeveral(el, value) {
|
|
213
|
+
const list = listOf(value);
|
|
214
|
+
if (isCheckbox(el)) {
|
|
215
|
+
el.checked = list.includes(el.value);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
for (const option of el.options) {
|
|
219
|
+
option.selected = list.includes(option.value);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (list.length === 0)
|
|
223
|
+
el.removeAttribute(ATTR_COLUMN_VALUE);
|
|
224
|
+
else
|
|
225
|
+
setStamp(el, ATTR_COLUMN_VALUE, JSON.stringify(list));
|
|
226
|
+
}
|
|
166
227
|
/**
|
|
167
228
|
* Set an element's text, unless it already holds exactly that text. Writing
|
|
168
229
|
* the same text again replaces the text node, which is two mutations and a
|
|
@@ -632,6 +693,7 @@ function listFor(el, template) {
|
|
|
632
693
|
levels,
|
|
633
694
|
rowTemplate: at,
|
|
634
695
|
byKey: new Map(),
|
|
696
|
+
unentered: [],
|
|
635
697
|
root: {
|
|
636
698
|
id: "",
|
|
637
699
|
nodes: [],
|
|
@@ -660,7 +722,7 @@ function listFor(el, template) {
|
|
|
660
722
|
data: {},
|
|
661
723
|
placed: {},
|
|
662
724
|
hidden: false,
|
|
663
|
-
|
|
725
|
+
unplaced: false,
|
|
664
726
|
};
|
|
665
727
|
list.byKey.set(key, entry);
|
|
666
728
|
list.root.rows.push(entry);
|
|
@@ -700,11 +762,79 @@ function create(list, doc, key) {
|
|
|
700
762
|
data: {},
|
|
701
763
|
placed: {},
|
|
702
764
|
hidden: false,
|
|
703
|
-
|
|
765
|
+
unplaced: false,
|
|
704
766
|
};
|
|
705
767
|
list.byKey.set(key, entry);
|
|
706
768
|
return entry;
|
|
707
769
|
}
|
|
770
|
+
/**
|
|
771
|
+
* Put the rows not yet entered after every row: immediately after the list's
|
|
772
|
+
* first template, which the rows and groups land in front of, so placing
|
|
773
|
+
* them never moves these.
|
|
774
|
+
*/
|
|
775
|
+
function standLast(list) {
|
|
776
|
+
let at = list.root.slot;
|
|
777
|
+
for (const row of list.unentered) {
|
|
778
|
+
if (at.nextElementSibling !== row)
|
|
779
|
+
at.after(row);
|
|
780
|
+
at = row;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
/** Stamp a list's count: the rows it shows, and its rows not yet entered. */
|
|
784
|
+
function recount(el, list) {
|
|
785
|
+
const showing = rowsUnder(list.root).filter((entry) => !entry.hidden);
|
|
786
|
+
setStamp(el, ATTR_QUERY_ROW_COUNT, String(showing.length + list.unentered.length));
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Put a row not yet entered into a list: a live row copied from its row
|
|
790
|
+
* template, keyed by the empty key, its controls at their HTML defaults and
|
|
791
|
+
* its `lb-show` branches as for a row whose columns are all null. It sends
|
|
792
|
+
* nothing; the submit of the form around the list carries it. False when the
|
|
793
|
+
* element holds no row template.
|
|
794
|
+
*/
|
|
795
|
+
export function addUnentered(el) {
|
|
796
|
+
const template = lists.get(el)?.top ?? templateIn(el);
|
|
797
|
+
if (!template)
|
|
798
|
+
return false;
|
|
799
|
+
const list = listFor(el, template);
|
|
800
|
+
if (!list)
|
|
801
|
+
return false;
|
|
802
|
+
const doc = el.ownerDocument;
|
|
803
|
+
const source = list.rowTemplate.content.firstElementChild;
|
|
804
|
+
list.paths ??= compile(source);
|
|
805
|
+
// Imported rather than cloned, for the reason a live row is.
|
|
806
|
+
const row = doc.importNode(source, true);
|
|
807
|
+
upgradeAbsent(row, doc);
|
|
808
|
+
placed(row);
|
|
809
|
+
row.setAttribute(ATTR_ROW_LIVE, "");
|
|
810
|
+
row.setAttribute(ATTR_KEY_VALUE, "");
|
|
811
|
+
nameRadios(row, "");
|
|
812
|
+
for (const [show, els] of placesBy(row, list.paths).shows) {
|
|
813
|
+
for (const shown of els)
|
|
814
|
+
presentPlaced(shown, show.startsWith(SHOW_NOT));
|
|
815
|
+
}
|
|
816
|
+
list.unentered.push(row);
|
|
817
|
+
standLast(list);
|
|
818
|
+
recount(el, list);
|
|
819
|
+
attachPlaced();
|
|
820
|
+
return true;
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Take a row not yet entered out of its list, as a row leaves. False when
|
|
824
|
+
* the row is not one `--lb-new` put in a list.
|
|
825
|
+
*/
|
|
826
|
+
export function discardUnentered(row) {
|
|
827
|
+
const el = row.parentElement?.closest(`[${ATTR_QUERY}]`);
|
|
828
|
+
const list = el ? lists.get(el) : undefined;
|
|
829
|
+
const at = list?.unentered.indexOf(row) ?? -1;
|
|
830
|
+
if (!el || !list || at < 0)
|
|
831
|
+
return false;
|
|
832
|
+
list.unentered.splice(at, 1);
|
|
833
|
+
leaving(row, ATTR_ROW_LIVE, ATTR_ROW_LEAVING);
|
|
834
|
+
depart([row]);
|
|
835
|
+
recount(el, list);
|
|
836
|
+
return true;
|
|
837
|
+
}
|
|
708
838
|
/**
|
|
709
839
|
* Give each radio button of a new live row a group of its own. The browser
|
|
710
840
|
* groups radio buttons by `name`, and a row template repeats one `name` in
|
|
@@ -1136,10 +1266,12 @@ function differs(before, after) {
|
|
|
1136
1266
|
* the same rows again moves nothing. A row a filter the user chose does not
|
|
1137
1267
|
* pass is `hidden`.
|
|
1138
1268
|
*
|
|
1139
|
-
* A landing
|
|
1140
|
-
*
|
|
1141
|
-
*
|
|
1142
|
-
*
|
|
1269
|
+
* A landing that answers a request leaves alone each side `lb-manual` names
|
|
1270
|
+
* on the element, whole or a patch. Under `order`, nothing already showing
|
|
1271
|
+
* moves, and a new row goes last in its group. Under `filter`, a row the
|
|
1272
|
+
* answer changed or created stays showing. A row left where the order or
|
|
1273
|
+
* the filter would not leave it is stamped unplaced until the rows are next
|
|
1274
|
+
* placed.
|
|
1143
1275
|
*/
|
|
1144
1276
|
export function applyRows(el, template, keyColumn, result, options = {}) {
|
|
1145
1277
|
const list = listFor(el, template);
|
|
@@ -1224,39 +1356,39 @@ export function applyRows(el, template, keyColumn, result, options = {}) {
|
|
|
1224
1356
|
a.i - b.i)
|
|
1225
1357
|
.map(({ entry }) => entry);
|
|
1226
1358
|
const passing = filterFor(el, options.choices, options.types ?? {});
|
|
1359
|
+
const manual = options.requested ? manualOf(el) : NO_MANUAL;
|
|
1360
|
+
const touched = new Set([...changed, ...created]);
|
|
1361
|
+
// A row left out of place stays touched until the rows are placed again.
|
|
1362
|
+
for (const entry of shown)
|
|
1363
|
+
if (entry.unplaced)
|
|
1364
|
+
touched.add(entry);
|
|
1227
1365
|
let order;
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
// stands, and what is new goes last
|
|
1366
|
+
let out = new Set();
|
|
1367
|
+
if (manual.order) {
|
|
1368
|
+
// What already shows stays where it stands, and what is new goes last
|
|
1369
|
+
// in the group it belongs to.
|
|
1231
1370
|
for (const entry of created)
|
|
1232
1371
|
entry.placed = entry.data;
|
|
1233
1372
|
order = heldOrder(stayed, created, groupTerms(list, terms));
|
|
1234
|
-
|
|
1235
|
-
for (const entry of order)
|
|
1236
|
-
if (entry.unsorted)
|
|
1237
|
-
touched.add(entry);
|
|
1238
|
-
const out = movedRows(order, sort(order), touched);
|
|
1239
|
-
for (const entry of order) {
|
|
1240
|
-
const fails = !passing(entry.data);
|
|
1241
|
-
// A row the user just changed does not vanish from under them.
|
|
1242
|
-
const kept = touched.has(entry) && !entry.hidden;
|
|
1243
|
-
filterRow(entry, fails && !kept);
|
|
1244
|
-
// A row stands in the group it was placed in until it is placed again.
|
|
1245
|
-
const moves = regrouped(entry, list, terms);
|
|
1246
|
-
if (!moves)
|
|
1247
|
-
entry.placed = entry.data;
|
|
1248
|
-
entry.unsorted = out.has(entry) || (kept && fails) || moves;
|
|
1249
|
-
}
|
|
1373
|
+
out = movedRows(order, sort(order), touched);
|
|
1250
1374
|
}
|
|
1251
1375
|
else {
|
|
1252
1376
|
order = sort(whole
|
|
1253
1377
|
? [...arriving.keys()].map((key) => list.byKey.get(key))
|
|
1254
1378
|
: [...stayed, ...created]);
|
|
1255
|
-
|
|
1379
|
+
}
|
|
1380
|
+
for (const entry of order) {
|
|
1381
|
+
const fails = !passing(entry.data);
|
|
1382
|
+
// A row the answer touched does not vanish from under the user.
|
|
1383
|
+
const kept = manual.filter && touched.has(entry) && !entry.hidden && fails;
|
|
1384
|
+
const hides = fails && !kept;
|
|
1385
|
+
filterRow(entry, hides);
|
|
1386
|
+
// A row stands in the group it was placed in until it is placed again.
|
|
1387
|
+
const moves = manual.order && regrouped(entry, list, terms);
|
|
1388
|
+
if (!moves)
|
|
1256
1389
|
entry.placed = entry.data;
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
}
|
|
1390
|
+
// A row nobody sees stands nowhere the user would notice.
|
|
1391
|
+
entry.unplaced = !hides && (out.has(entry) || kept || moves);
|
|
1260
1392
|
}
|
|
1261
1393
|
const moved = movedRows(stayed, order, changed);
|
|
1262
1394
|
const retired = arrange(list, doc, order, terms);
|
|
@@ -1281,18 +1413,19 @@ export function applyRows(el, template, keyColumn, result, options = {}) {
|
|
|
1281
1413
|
}
|
|
1282
1414
|
}
|
|
1283
1415
|
stamps.done(doc);
|
|
1284
|
-
let
|
|
1416
|
+
let unplaced = 0;
|
|
1285
1417
|
for (const entry of order) {
|
|
1286
|
-
if (entry.el.hasAttribute(
|
|
1287
|
-
entry.el.toggleAttribute(
|
|
1418
|
+
if (entry.el.hasAttribute(ATTR_ROW_UNPLACED) !== entry.unplaced) {
|
|
1419
|
+
entry.el.toggleAttribute(ATTR_ROW_UNPLACED, entry.unplaced);
|
|
1288
1420
|
}
|
|
1289
|
-
if (entry.
|
|
1290
|
-
|
|
1421
|
+
if (entry.unplaced)
|
|
1422
|
+
unplaced++;
|
|
1291
1423
|
}
|
|
1292
1424
|
const rows = finish(list.root, options.types ?? {});
|
|
1293
1425
|
// Counted from what the hub holds, which is what the rows show.
|
|
1294
|
-
setStamp(el, ATTR_QUERY_ROW_COUNT, String(rows.length));
|
|
1295
|
-
|
|
1426
|
+
setStamp(el, ATTR_QUERY_ROW_COUNT, String(rows.length + list.unentered.length));
|
|
1427
|
+
standLast(list);
|
|
1428
|
+
setStamp(el, ATTR_QUERY_UNPLACED_COUNT, String(unplaced));
|
|
1296
1429
|
if (terms.length === 0)
|
|
1297
1430
|
el.removeAttribute(ATTR_QUERY_ORDER);
|
|
1298
1431
|
else
|
|
@@ -1304,6 +1437,14 @@ export function applyRows(el, template, keyColumn, result, options = {}) {
|
|
|
1304
1437
|
depart(departing);
|
|
1305
1438
|
host.lbRowsLanded?.();
|
|
1306
1439
|
}
|
|
1440
|
+
const NO_MANUAL = { order: false, filter: false };
|
|
1441
|
+
function manualOf(el) {
|
|
1442
|
+
const sides = (el.getAttribute(ATTR_MANUAL) ?? "").split(/\s+/);
|
|
1443
|
+
return {
|
|
1444
|
+
order: sides.includes(MANUAL_ORDER),
|
|
1445
|
+
filter: sides.includes(MANUAL_FILTER),
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1307
1448
|
/** The order's terms a list's groups break on, outermost first. */
|
|
1308
1449
|
function groupTerms(list, terms) {
|
|
1309
1450
|
return terms.slice(0, list.levels.length);
|
|
@@ -1533,13 +1674,6 @@ function fillArrivals(root, state) {
|
|
|
1533
1674
|
}
|
|
1534
1675
|
}
|
|
1535
1676
|
}
|
|
1536
|
-
/**
|
|
1537
|
-
* Whether a request from `origin` was sent from inside `el`'s list: `el` is
|
|
1538
|
-
* the nearest element carrying `lb-query` around it.
|
|
1539
|
-
*/
|
|
1540
|
-
function sentFrom(el, origin) {
|
|
1541
|
-
return origin?.parentElement?.closest(`[${ATTR_QUERY}]`) === el;
|
|
1542
|
-
}
|
|
1543
1677
|
/**
|
|
1544
1678
|
* Land response items. Every answer arrives through here, and is kept for
|
|
1545
1679
|
* `root`, so that what arrives after it is filled from it — see forget().
|
|
@@ -1563,7 +1697,6 @@ export function applyResponse(root, items, options = {}) {
|
|
|
1563
1697
|
landItem(el, item, {
|
|
1564
1698
|
...rowsOptions(state, item),
|
|
1565
1699
|
requested: options.requested,
|
|
1566
|
-
held: sentFrom(el, options.origin),
|
|
1567
1700
|
});
|
|
1568
1701
|
}
|
|
1569
1702
|
if (choices.size > 0)
|
|
@@ -1573,11 +1706,12 @@ export function applyResponse(root, items, options = {}) {
|
|
|
1573
1706
|
attachPlaced();
|
|
1574
1707
|
}
|
|
1575
1708
|
/**
|
|
1576
|
-
* Place an element's rows by the order again, from the answer
|
|
1577
|
-
* query, with no round trip: what the user asks for once an
|
|
1578
|
-
* rows out of
|
|
1709
|
+
* Place an element's rows by the order and the filter again, from the answer
|
|
1710
|
+
* kept for its query, with no round trip: what the user asks for once an
|
|
1711
|
+
* answer has left rows out of place. False when the element shows no rows
|
|
1712
|
+
* the hub holds.
|
|
1579
1713
|
*/
|
|
1580
|
-
export function
|
|
1714
|
+
export function placeAgain(root, el) {
|
|
1581
1715
|
const state = kept.get(root);
|
|
1582
1716
|
const item = state?.answers.get(el.getAttribute(ATTR_QUERY) ?? "");
|
|
1583
1717
|
if (!state || !item || item.kind === KIND_ROW || !state.landed.has(el)) {
|