@wildwinter/app-shell 0.56.0 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/dist/controls.css +52 -0
- package/dist/controls.css.map +1 -1
- package/dist/index.cjs +22 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +22 -1
- package/dist/index.js.map +1 -1
- package/dist/value-rows.cjs +200 -10
- package/dist/value-rows.cjs.map +1 -1
- package/dist/value-rows.d.cts +36 -1
- package/dist/value-rows.d.ts +36 -1
- package/dist/value-rows.js +197 -12
- package/dist/value-rows.js.map +1 -1
- package/package.json +1 -1
package/dist/value-rows.cjs
CHANGED
|
@@ -902,13 +902,198 @@ function extensionFold() {
|
|
|
902
902
|
};
|
|
903
903
|
return fold;
|
|
904
904
|
}
|
|
905
|
+
var isObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
906
|
+
function isRecordMap(decl) {
|
|
907
|
+
return decl.type === "map" && decl.of?.type === "record";
|
|
908
|
+
}
|
|
909
|
+
function recordMapKeyLabel(decl, key, adapter) {
|
|
910
|
+
const kind = decl.key?.type === "reference" ? decl.key.to ?? "" : void 0;
|
|
911
|
+
if (kind === void 0 || !adapter) return key;
|
|
912
|
+
return adapter.nodes(kind).find((n) => n.id === key)?.label ?? adapter.label?.(kind, key) ?? key;
|
|
913
|
+
}
|
|
914
|
+
function valueText(decl, value, adapter) {
|
|
915
|
+
if (!isSet(decl, value)) return "";
|
|
916
|
+
if (decl.type === "pick" && typeof value === "string") {
|
|
917
|
+
const choice = extensions.pickChoices(decl, {
|
|
918
|
+
...adapter ? { nodes: (kind) => adapter.nodes(kind) } : {},
|
|
919
|
+
...adapter?.list ? { list: (name) => adapter.list(name) } : {}
|
|
920
|
+
}).find((c) => c.value === value);
|
|
921
|
+
return choice?.label ?? value;
|
|
922
|
+
}
|
|
923
|
+
if (decl.type === "reference" && typeof value === "string" && decl.to !== void 0 && adapter) {
|
|
924
|
+
return adapter.nodes(decl.to).find((n) => n.id === value)?.label ?? adapter.label?.(decl.to, value) ?? value;
|
|
925
|
+
}
|
|
926
|
+
if (decl.type === "boolean") return value === true ? decl.label ?? decl.name : "";
|
|
927
|
+
if (Array.isArray(value)) return value.map(String).join(", ");
|
|
928
|
+
if (isObject2(value)) return JSON.stringify(value);
|
|
929
|
+
return String(value);
|
|
930
|
+
}
|
|
931
|
+
function recordMapSummary(decl, value, adapter) {
|
|
932
|
+
if (!isObject2(value)) return [];
|
|
933
|
+
const fields = decl.of?.fields ?? [];
|
|
934
|
+
return Object.entries(value).map(([key, record]) => {
|
|
935
|
+
const name = recordMapKeyLabel(decl, key, adapter);
|
|
936
|
+
const words = isObject2(record) ? fields.map((f) => valueText(f, record[f.name], adapter)).filter((w) => w !== "") : [];
|
|
937
|
+
return words.length > 0 ? `${name}: ${words.join(", ")}` : name;
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
function recordMapGrid(initial) {
|
|
941
|
+
let opts = initial;
|
|
942
|
+
const root = el("div", "shell-recordgrid");
|
|
943
|
+
const table = el("table", "shell-recordgrid-table");
|
|
944
|
+
const head = el("thead");
|
|
945
|
+
const body = el("tbody");
|
|
946
|
+
table.append(head, body);
|
|
947
|
+
const adder = el("div", "shell-recordgrid-add");
|
|
948
|
+
const empty = el("p", "shell-recordgrid-empty");
|
|
949
|
+
root.append(table, empty, adder);
|
|
950
|
+
const cells = /* @__PURE__ */ new Map();
|
|
951
|
+
let addKey = "";
|
|
952
|
+
const editable = () => opts.onCommit !== void 0 && opts.disabled !== true;
|
|
953
|
+
const held = () => isObject2(opts.value) ? opts.value : {};
|
|
954
|
+
const commit = (next) => {
|
|
955
|
+
const v = Object.keys(next).length > 0 ? next : void 0;
|
|
956
|
+
opts = { ...opts, value: v };
|
|
957
|
+
draw();
|
|
958
|
+
opts.onCommit?.(v);
|
|
959
|
+
};
|
|
960
|
+
const cell = (key, field, record) => {
|
|
961
|
+
const id = `${key}\0${field.name}`;
|
|
962
|
+
const o = {
|
|
963
|
+
decl: field,
|
|
964
|
+
value: record[field.name],
|
|
965
|
+
...opts.adapter ? { adapter: opts.adapter } : {},
|
|
966
|
+
...opts.disabled !== void 0 ? { disabled: opts.disabled } : {},
|
|
967
|
+
...opts.controlClassName !== void 0 ? { controlClassName: opts.controlClassName } : {},
|
|
968
|
+
...editable() ? {
|
|
969
|
+
onCommit: (v) => {
|
|
970
|
+
const map = held();
|
|
971
|
+
const rec = { ...isObject2(map[key]) ? map[key] : {} };
|
|
972
|
+
if (v === void 0) delete rec[field.name];
|
|
973
|
+
else rec[field.name] = v;
|
|
974
|
+
commit({ ...map, [key]: rec });
|
|
975
|
+
}
|
|
976
|
+
} : {},
|
|
977
|
+
className: "shell-recordgrid-cell"
|
|
978
|
+
};
|
|
979
|
+
const had = cells.get(id);
|
|
980
|
+
if (had && had.el.dataset["type"] === field.type) {
|
|
981
|
+
fieldRowFocus.settle(had.el);
|
|
982
|
+
had.update(o);
|
|
983
|
+
fieldRowFocus.restore(had.el);
|
|
984
|
+
return had.el;
|
|
985
|
+
}
|
|
986
|
+
const handle = valueRow(o);
|
|
987
|
+
handle.el.dataset["type"] = field.type;
|
|
988
|
+
cells.set(id, handle);
|
|
989
|
+
return handle.el;
|
|
990
|
+
};
|
|
991
|
+
const draw = () => {
|
|
992
|
+
const decl = opts.decl;
|
|
993
|
+
const fields = decl.of?.fields ?? [];
|
|
994
|
+
const map = held();
|
|
995
|
+
const keys = Object.keys(map);
|
|
996
|
+
const adapter = opts.adapter;
|
|
997
|
+
const keyKind = decl.key?.type === "reference" ? decl.key.to ?? "" : void 0;
|
|
998
|
+
const keyNodes = keyKind !== void 0 && adapter ? adapter.nodes(keyKind) : void 0;
|
|
999
|
+
const hr = el("tr");
|
|
1000
|
+
hr.append(el("th", "shell-recordgrid-keyhead", recordMapKeyHeading(decl)));
|
|
1001
|
+
for (const f of fields) {
|
|
1002
|
+
const th = el("th", void 0, f.label ?? f.name);
|
|
1003
|
+
const help = f.help ?? f.purpose;
|
|
1004
|
+
if (help) th.dataset["tip"] = help;
|
|
1005
|
+
hr.append(th);
|
|
1006
|
+
}
|
|
1007
|
+
hr.append(el("th", "shell-recordgrid-tools"));
|
|
1008
|
+
head.replaceChildren(hr);
|
|
1009
|
+
const used = /* @__PURE__ */ new Set();
|
|
1010
|
+
const rows = [];
|
|
1011
|
+
for (const key of keys) {
|
|
1012
|
+
const record = isObject2(map[key]) ? map[key] : {};
|
|
1013
|
+
const tr = el("tr");
|
|
1014
|
+
tr.dataset["key"] = key;
|
|
1015
|
+
const name = recordMapKeyLabel(decl, key, adapter);
|
|
1016
|
+
const keyCell = el("th", "shell-recordgrid-key", name);
|
|
1017
|
+
keyCell.setAttribute("scope", "row");
|
|
1018
|
+
if (keyNodes !== void 0 && adapter && keyNodes.every((n) => n.id !== key) && adapter.label?.(keyKind, key) === void 0) {
|
|
1019
|
+
keyCell.append(el("span", "shell-recordgrid-gone", " (not here any more)"));
|
|
1020
|
+
}
|
|
1021
|
+
tr.append(keyCell);
|
|
1022
|
+
for (const f of fields) {
|
|
1023
|
+
used.add(`${key}\0${f.name}`);
|
|
1024
|
+
const td = el("td");
|
|
1025
|
+
td.append(cell(key, f, record));
|
|
1026
|
+
tr.append(td);
|
|
1027
|
+
}
|
|
1028
|
+
const tools = el("td", "shell-recordgrid-tools");
|
|
1029
|
+
if (editable()) tools.append(iconBtn("close", `Remove ${name}`, () => {
|
|
1030
|
+
const next = { ...held() };
|
|
1031
|
+
delete next[key];
|
|
1032
|
+
commit(next);
|
|
1033
|
+
}));
|
|
1034
|
+
tr.append(tools);
|
|
1035
|
+
rows.push(tr);
|
|
1036
|
+
}
|
|
1037
|
+
for (const id of [...cells.keys()]) if (!used.has(id)) cells.delete(id);
|
|
1038
|
+
body.replaceChildren(...rows);
|
|
1039
|
+
table.hidden = keys.length === 0;
|
|
1040
|
+
empty.textContent = keys.length === 0 ? "None yet." : "";
|
|
1041
|
+
empty.hidden = keys.length !== 0;
|
|
1042
|
+
adder.replaceChildren();
|
|
1043
|
+
if (!editable()) return;
|
|
1044
|
+
const add = (k) => {
|
|
1045
|
+
if (k === "" || Object.prototype.hasOwnProperty.call(held(), k)) return;
|
|
1046
|
+
commit({ ...held(), [k]: extensions.defaultOf(decl.of) });
|
|
1047
|
+
};
|
|
1048
|
+
if (keyNodes !== void 0) {
|
|
1049
|
+
const pick = el("select", `field shell-valuerow-key${opts.controlClassName ? ` ${opts.controlClassName}` : ""}`);
|
|
1050
|
+
const first = el("option", void 0, "Add...");
|
|
1051
|
+
first.value = "";
|
|
1052
|
+
pick.append(first);
|
|
1053
|
+
for (const n of keyNodes.filter((n2) => !Object.prototype.hasOwnProperty.call(map, n2.id))) {
|
|
1054
|
+
const opt = el("option", void 0, n.label);
|
|
1055
|
+
opt.value = n.id;
|
|
1056
|
+
pick.append(opt);
|
|
1057
|
+
}
|
|
1058
|
+
pick.disabled = pick.options.length === 1;
|
|
1059
|
+
pick.addEventListener("change", () => add(pick.value));
|
|
1060
|
+
adder.append(pick);
|
|
1061
|
+
} else {
|
|
1062
|
+
const input = el("input", "field shell-valuerow-key");
|
|
1063
|
+
input.type = "text";
|
|
1064
|
+
input.placeholder = "New key";
|
|
1065
|
+
input.value = addKey;
|
|
1066
|
+
input.addEventListener("input", () => {
|
|
1067
|
+
addKey = input.value;
|
|
1068
|
+
});
|
|
1069
|
+
const go = () => {
|
|
1070
|
+
const k = input.value.trim();
|
|
1071
|
+
addKey = "";
|
|
1072
|
+
add(k);
|
|
1073
|
+
};
|
|
1074
|
+
input.addEventListener("keydown", (e) => {
|
|
1075
|
+
if (e.key === "Enter") go();
|
|
1076
|
+
});
|
|
1077
|
+
adder.append(input, iconBtn("add", "Add", go));
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
draw();
|
|
1081
|
+
return { el: root, update: (o) => {
|
|
1082
|
+
opts = o;
|
|
1083
|
+
draw();
|
|
1084
|
+
} };
|
|
1085
|
+
}
|
|
1086
|
+
function recordMapKeyHeading(decl) {
|
|
1087
|
+
const to = decl.key?.type === "reference" ? decl.key.to : void 0;
|
|
1088
|
+
return to ? to.charAt(0).toUpperCase() + to.slice(1) : "Key";
|
|
1089
|
+
}
|
|
905
1090
|
|
|
906
1091
|
// src/value-rows.ts
|
|
907
1092
|
function nodeLabel(adapter, kind, id) {
|
|
908
1093
|
return adapter.nodes(kind).find((n) => n.id === id)?.label ?? adapter.label?.(kind, id);
|
|
909
1094
|
}
|
|
910
1095
|
var SCALARS = /* @__PURE__ */ new Set(["text", "multiline", "number", "boolean", "enum", "flags", "quality", "string"]);
|
|
911
|
-
var
|
|
1096
|
+
var isObject3 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
912
1097
|
var nameOf = (decl) => decl.label ?? decl.name;
|
|
913
1098
|
var asDecl = (d, name) => ({ ...d, name: d.name ?? name });
|
|
914
1099
|
function ownProblems(decl, value, adapter) {
|
|
@@ -1147,31 +1332,31 @@ function structuredRow(initial) {
|
|
|
1147
1332
|
}, controls));
|
|
1148
1333
|
};
|
|
1149
1334
|
if (decl.type === "record") {
|
|
1150
|
-
const value =
|
|
1335
|
+
const value = isObject3(o.value) ? o.value : void 0;
|
|
1151
1336
|
const required = value === void 0 ? [] : extensions.valueProblems(decl, value).filter((p) => p.code === "required");
|
|
1152
1337
|
for (const f of decl.fields ?? []) {
|
|
1153
1338
|
const missing = required.find((p) => p.path === f.name);
|
|
1154
1339
|
child(`f:${f.name}`, f, value?.[f.name], (v) => {
|
|
1155
|
-
const next = { ...
|
|
1340
|
+
const next = { ...isObject3(opts.value) ? opts.value : {} };
|
|
1156
1341
|
if (v === void 0) delete next[f.name];
|
|
1157
1342
|
else next[f.name] = v;
|
|
1158
1343
|
commit(Object.keys(next).length > 0 ? next : void 0);
|
|
1159
1344
|
}, void 0, void 0, missing ? "Required." : void 0);
|
|
1160
1345
|
}
|
|
1161
1346
|
} else if (decl.type === "map" && decl.of) {
|
|
1162
|
-
const value =
|
|
1347
|
+
const value = isObject3(o.value) ? o.value : {};
|
|
1163
1348
|
const keyNodes = decl.key?.type === "reference" && o.adapter ? o.adapter.nodes(decl.key.to ?? "") : void 0;
|
|
1164
1349
|
const keyKind = decl.key?.to ?? "";
|
|
1165
1350
|
const keyLabel = (k) => (o.adapter && keyNodes !== void 0 ? nodeLabel(o.adapter, keyKind, k) : void 0) ?? k;
|
|
1166
1351
|
for (const k of Object.keys(value)) {
|
|
1167
1352
|
const remove = editable() ? [iconBtn("close", `Remove ${keyLabel(k)}`, () => {
|
|
1168
|
-
const next = { ...
|
|
1353
|
+
const next = { ...isObject3(opts.value) ? opts.value : {} };
|
|
1169
1354
|
delete next[k];
|
|
1170
1355
|
commit(Object.keys(next).length > 0 ? next : void 0);
|
|
1171
1356
|
})] : [];
|
|
1172
1357
|
const gone = keyNodes !== void 0 && o.adapter && nodeLabel(o.adapter, keyKind, k) === void 0 ? `${k} is not here any more.` : void 0;
|
|
1173
1358
|
child(`k:${k}`, asDecl(decl.of, k), value[k], (v) => {
|
|
1174
|
-
const next = { ...
|
|
1359
|
+
const next = { ...isObject3(opts.value) ? opts.value : {} };
|
|
1175
1360
|
next[k] = v === void 0 ? extensions.defaultOf(decl.of) : v;
|
|
1176
1361
|
commit(next);
|
|
1177
1362
|
}, remove, keyLabel(k), gone);
|
|
@@ -1215,7 +1400,7 @@ function structuredRow(initial) {
|
|
|
1215
1400
|
}
|
|
1216
1401
|
} else if (decl.type === "list" && decl.of) {
|
|
1217
1402
|
const value = Array.isArray(o.value) ? o.value : [];
|
|
1218
|
-
const idOf = (item, i) => decl.id !== void 0 &&
|
|
1403
|
+
const idOf = (item, i) => decl.id !== void 0 && isObject3(item) && typeof item[decl.id] === "string" ? `id:${item[decl.id]}` : `i:${i}`;
|
|
1219
1404
|
value.forEach((item, i) => {
|
|
1220
1405
|
const set = (next) => commit(next.length > 0 ? next : void 0);
|
|
1221
1406
|
const controls = editable() ? [
|
|
@@ -1229,7 +1414,7 @@ function structuredRow(initial) {
|
|
|
1229
1414
|
}, i === value.length - 1),
|
|
1230
1415
|
iconBtn("close", "Remove", () => set(value.filter((_, j) => j !== i)), false, true)
|
|
1231
1416
|
] : [];
|
|
1232
|
-
const itemLabel = decl.id !== void 0 &&
|
|
1417
|
+
const itemLabel = decl.id !== void 0 && isObject3(item) && typeof item[decl.id] === "string" ? item[decl.id] : `${i + 1}`;
|
|
1233
1418
|
child(idOf(item, i), asDecl(decl.of, itemLabel), item, (v) => {
|
|
1234
1419
|
const next = [...Array.isArray(opts.value) ? opts.value : []];
|
|
1235
1420
|
next[i] = v === void 0 ? extensions.defaultOf(decl.of) : v;
|
|
@@ -1240,8 +1425,8 @@ function structuredRow(initial) {
|
|
|
1240
1425
|
if (editable()) {
|
|
1241
1426
|
adder.append(iconBtn("add", "Add", () => {
|
|
1242
1427
|
const fresh = extensions.defaultOf(decl.of);
|
|
1243
|
-
if (decl.id !== void 0 &&
|
|
1244
|
-
const taken = new Set(value.map((x) =>
|
|
1428
|
+
if (decl.id !== void 0 && isObject3(fresh)) {
|
|
1429
|
+
const taken = new Set(value.map((x) => isObject3(x) ? x[decl.id] : void 0));
|
|
1245
1430
|
let n = value.length + 1;
|
|
1246
1431
|
while (taken.has(`item-${n}`)) n++;
|
|
1247
1432
|
fresh[decl.id] = `item-${n}`;
|
|
@@ -1292,8 +1477,13 @@ function valueRows() {
|
|
|
1292
1477
|
|
|
1293
1478
|
exports.extensionFold = extensionFold;
|
|
1294
1479
|
exports.foldCaption = foldCaption;
|
|
1480
|
+
exports.isRecordMap = isRecordMap;
|
|
1295
1481
|
exports.isSet = isSet;
|
|
1482
|
+
exports.recordMapGrid = recordMapGrid;
|
|
1483
|
+
exports.recordMapKeyLabel = recordMapKeyLabel;
|
|
1484
|
+
exports.recordMapSummary = recordMapSummary;
|
|
1296
1485
|
exports.valueRow = valueRow;
|
|
1297
1486
|
exports.valueRows = valueRows;
|
|
1487
|
+
exports.valueText = valueText;
|
|
1298
1488
|
//# sourceMappingURL=value-rows.cjs.map
|
|
1299
1489
|
//# sourceMappingURL=value-rows.cjs.map
|