@fieldia/core 0.24.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/README.md +77 -1
- package/package.json +1 -1
- package/schema/page.schema.json +951 -380
- package/src/index.d.mts +5 -2
- package/src/index.d.ts +5 -2
- package/src/index.js +14 -1
- package/src/index.js.map +1 -1
- package/src/index.mjs +6 -2
- package/src/index.mjs.map +1 -1
- package/src/lib/format/actions.d.mts +31 -1
- package/src/lib/format/actions.d.ts +31 -1
- package/src/lib/format/actions.js +1 -0
- package/src/lib/format/actions.js.map +1 -1
- package/src/lib/format/actions.mjs +1 -0
- package/src/lib/format/actions.mjs.map +1 -1
- package/src/lib/format/duplicate-kinds.d.mts +10 -0
- package/src/lib/format/duplicate-kinds.d.ts +10 -0
- package/src/lib/format/duplicate-kinds.js +14 -0
- package/src/lib/format/duplicate-kinds.js.map +1 -0
- package/src/lib/format/duplicate-kinds.mjs +10 -0
- package/src/lib/format/duplicate-kinds.mjs.map +1 -0
- package/src/lib/format/duplicates.d.mts +22 -0
- package/src/lib/format/duplicates.d.ts +22 -0
- package/src/lib/format/duplicates.js +24 -0
- package/src/lib/format/duplicates.js.map +1 -0
- package/src/lib/format/duplicates.mjs +21 -0
- package/src/lib/format/duplicates.mjs.map +1 -0
- package/src/lib/format/field.d.mts +236 -0
- package/src/lib/format/field.d.ts +236 -0
- package/src/lib/format/field.js +4 -3
- package/src/lib/format/field.js.map +1 -1
- package/src/lib/format/field.mjs +2 -1
- package/src/lib/format/field.mjs.map +1 -1
- package/src/lib/format/import-fields.d.mts +18 -0
- package/src/lib/format/import-fields.d.ts +18 -0
- package/src/lib/format/import-fields.js +26 -0
- package/src/lib/format/import-fields.js.map +1 -0
- package/src/lib/format/import-fields.mjs +21 -0
- package/src/lib/format/import-fields.mjs.map +1 -0
- package/src/lib/format/layout.d.mts +5453 -1522
- package/src/lib/format/layout.d.ts +5453 -1522
- package/src/lib/format/layout.js +90 -32
- package/src/lib/format/layout.js.map +1 -1
- package/src/lib/format/layout.mjs +89 -31
- package/src/lib/format/layout.mjs.map +1 -1
- package/src/lib/format/layout.types.d.mts +5 -2
- package/src/lib/format/layout.types.d.ts +5 -2
- package/src/lib/format/page.d.mts +961 -127
- package/src/lib/format/page.d.ts +961 -127
- package/src/lib/format/page.js +14 -0
- package/src/lib/format/page.js.map +1 -1
- package/src/lib/format/page.mjs +14 -0
- package/src/lib/format/page.mjs.map +1 -1
- package/src/lib/format/references.d.mts +29 -0
- package/src/lib/format/references.d.ts +29 -0
- package/src/lib/format/references.js +172 -24
- package/src/lib/format/references.js.map +1 -1
- package/src/lib/format/references.mjs +172 -24
- package/src/lib/format/references.mjs.map +1 -1
- package/src/lib/format/translate.js +8 -4
- package/src/lib/format/translate.js.map +1 -1
- package/src/lib/format/translate.mjs +8 -4
- package/src/lib/format/translate.mjs.map +1 -1
- package/src/lib/format/upgrade.d.mts +5 -3
- package/src/lib/format/upgrade.d.ts +5 -3
- package/src/lib/format/upgrade.js +5 -3
- package/src/lib/format/upgrade.js.map +1 -1
- package/src/lib/format/upgrade.mjs +5 -3
- package/src/lib/format/upgrade.mjs.map +1 -1
- package/src/lib/format/version.d.mts +2 -2
- package/src/lib/format/version.d.ts +2 -2
- package/src/lib/format/version.js +2 -2
- package/src/lib/format/version.js.map +1 -1
- package/src/lib/format/version.mjs +2 -2
- package/src/lib/format/version.mjs.map +1 -1
- package/src/lib/record/data-source.d.mts +121 -0
- package/src/lib/record/data-source.d.ts +121 -0
- package/src/lib/record/data-source.js +16 -0
- package/src/lib/record/data-source.js.map +1 -1
- package/src/lib/record/data-source.mjs +15 -0
- package/src/lib/record/data-source.mjs.map +1 -1
- package/src/lib/record/form.d.mts +45 -1
- package/src/lib/record/form.d.ts +45 -1
- package/src/lib/record/form.js +53 -0
- package/src/lib/record/form.js.map +1 -1
- package/src/lib/record/form.mjs +53 -0
- package/src/lib/record/form.mjs.map +1 -1
- package/src/lib/record/memory-data-source.d.mts +12 -5
- package/src/lib/record/memory-data-source.d.ts +12 -5
- package/src/lib/record/memory-data-source.js +61 -0
- package/src/lib/record/memory-data-source.js.map +1 -1
- package/src/lib/record/memory-data-source.mjs +62 -1
- package/src/lib/record/memory-data-source.mjs.map +1 -1
- package/src/lib/record/memory-duplicates.d.mts +19 -0
- package/src/lib/record/memory-duplicates.d.ts +19 -0
- package/src/lib/record/memory-duplicates.js +78 -0
- package/src/lib/record/memory-duplicates.js.map +1 -0
- package/src/lib/record/memory-duplicates.mjs +74 -0
- package/src/lib/record/memory-duplicates.mjs.map +1 -0
- package/src/lib/record/run.d.mts +31 -1
- package/src/lib/record/run.d.ts +31 -1
- package/src/lib/record/run.js +15 -1
- package/src/lib/record/run.js.map +1 -1
- package/src/lib/record/run.mjs +15 -1
- package/src/lib/record/run.mjs.map +1 -1
- package/src/lib/record/waits.d.mts +32 -0
- package/src/lib/record/waits.d.ts +32 -0
- package/src/lib/record/waits.js +89 -0
- package/src/lib/record/waits.js.map +1 -0
- package/src/lib/record/waits.mjs +85 -0
- package/src/lib/record/waits.mjs.map +1 -0
- package/src/runtime.d.mts +10 -7
- package/src/runtime.d.ts +10 -7
- package/src/runtime.js +8 -3
- package/src/runtime.js.map +1 -1
- package/src/runtime.mjs +2 -1
- package/src/runtime.mjs.map +1 -1
package/src/lib/record/form.js
CHANGED
|
@@ -20,6 +20,7 @@ const roles_1 = require("./roles");
|
|
|
20
20
|
const cells_1 = require("./cells");
|
|
21
21
|
const messages_1 = require("./messages");
|
|
22
22
|
const data_source_1 = require("./data-source");
|
|
23
|
+
const waits_1 = require("./waits");
|
|
23
24
|
const uploads_1 = require("./uploads");
|
|
24
25
|
const scheduler_1 = require("./scheduler");
|
|
25
26
|
const run_1 = require("./run");
|
|
@@ -164,6 +165,8 @@ function innerForm(options, within, place, outer) {
|
|
|
164
165
|
const editingNow = () => editing && !(started && state.can?.edit === false);
|
|
165
166
|
const about = () => ({ recordId: started ? state.recordId : (options.recordId ?? null), user: options.user, editing: editingNow(), context: options.context, parent: parentContext });
|
|
166
167
|
const computed = (0, compute_1.compileComputed)(page, today, about);
|
|
168
|
+
/** Each value worked out from others — by the page, or by the app as the lines change — and what it waits for. */
|
|
169
|
+
const workedOut = (0, waits_1.workedOutFrom)(page, !!options.dataSource?.onchange && page.data.kind === 'record', !!options.parent);
|
|
167
170
|
const setWhen = (0, set_when_1.compileSetWhen)(page, today, about);
|
|
168
171
|
/** The saved forms placed on the page, by the part's id: each one's answers sit under its name, as JSON. */
|
|
169
172
|
const parts = new Map();
|
|
@@ -323,6 +326,13 @@ function innerForm(options, within, place, outer) {
|
|
|
323
326
|
* for is still the one shown, as it was. A save's answer, a call's, an onchange's, a name looked up, a line's
|
|
324
327
|
* dialog, a record step's — none is laid on another record, nor over the values the person's Discard let go.
|
|
325
328
|
*/
|
|
329
|
+
/** The data source with a member a record's page asks of it, and the page's model: refused where either is missing. */
|
|
330
|
+
function recordSource(member) {
|
|
331
|
+
const source = options.dataSource;
|
|
332
|
+
if (page.data.kind !== 'record' || !source?.[member])
|
|
333
|
+
throw new Error(`The data source has no ${member}()`);
|
|
334
|
+
return [source, page.data.model];
|
|
335
|
+
}
|
|
326
336
|
function shownNow() {
|
|
327
337
|
const at = generation;
|
|
328
338
|
const reset = resets;
|
|
@@ -2026,6 +2036,13 @@ function innerForm(options, within, place, outer) {
|
|
|
2026
2036
|
leave: (how) => Promise.resolve(letGo(how)),
|
|
2027
2037
|
leaveNow: (how = {}) => (outer ? outer.leaveNow(how) : atOnce(how)),
|
|
2028
2038
|
recordShown: shownNow,
|
|
2039
|
+
relatedFilter(id) {
|
|
2040
|
+
const node = index.get(id)?.source;
|
|
2041
|
+
if (node?.type !== 'related' || state.recordId == null)
|
|
2042
|
+
return null;
|
|
2043
|
+
const related = node;
|
|
2044
|
+
return [{ field: related.field, op: '=', value: state.recordId }, ...(related.filter ? (0, filter_1.resolveFilter)(related.filter, context()) : [])];
|
|
2045
|
+
},
|
|
2029
2046
|
post,
|
|
2030
2047
|
setEditing(next) {
|
|
2031
2048
|
if (next === editing)
|
|
@@ -2038,6 +2055,10 @@ function innerForm(options, within, place, outer) {
|
|
|
2038
2055
|
loadChoices,
|
|
2039
2056
|
loadDefinitions,
|
|
2040
2057
|
addDefinition,
|
|
2058
|
+
waitsFor(name) {
|
|
2059
|
+
const from = workedOut.get(name);
|
|
2060
|
+
return from ? (0, waits_1.waitingOn)(page, from, state.values, state.unread) : null;
|
|
2061
|
+
},
|
|
2041
2062
|
problem(name) {
|
|
2042
2063
|
fieldDef(name);
|
|
2043
2064
|
// Only a field someone can see is asked anything, and required wherever it is shown so.
|
|
@@ -2116,6 +2137,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2116
2137
|
save: () => track(save()),
|
|
2117
2138
|
canUpload: () => typeof options.dataSource?.upload === 'function',
|
|
2118
2139
|
today,
|
|
2140
|
+
recordReads: (id, values, fields) => (0, env_1.recordContext)(values, fields ?? page.fields, { recordId: id, user: options.user, editing: false, context: options.context }),
|
|
2119
2141
|
canFindSlots: () => typeof options.dataSource?.slots === 'function',
|
|
2120
2142
|
findSlots(field, ask) {
|
|
2121
2143
|
const source = options.dataSource;
|
|
@@ -2136,6 +2158,28 @@ function innerForm(options, within, place, outer) {
|
|
|
2136
2158
|
values: (0, values_1.structuredCopy)(state.values),
|
|
2137
2159
|
});
|
|
2138
2160
|
},
|
|
2161
|
+
canFindDuplicates: () => !!(page.data.kind === 'record' && page.duplicates?.fields?.length && options.dataSource?.duplicates),
|
|
2162
|
+
async findDuplicates() {
|
|
2163
|
+
const [source, model] = recordSource('duplicates');
|
|
2164
|
+
const fields = (page.duplicates?.fields ?? []).filter((name) => page.fields[name] && !(0, values_1.isEmpty)(page.fields[name], state.values[name]));
|
|
2165
|
+
const recordId = state.recordId;
|
|
2166
|
+
// Nothing typed in any of them: nothing asked.
|
|
2167
|
+
if (!fields.length)
|
|
2168
|
+
return [];
|
|
2169
|
+
const { matches } = await source.duplicates({ model, recordId, values: (0, values_1.structuredCopy)(state.values), fields });
|
|
2170
|
+
// Never the record itself, whatever the source says; the likest first where it scores them.
|
|
2171
|
+
const others = (matches ?? []).filter((match) => match && String(match.id) !== String(recordId));
|
|
2172
|
+
return others.every((match) => typeof match.score === 'number') ? others.sort((a, b) => b.score - a.score) : others;
|
|
2173
|
+
},
|
|
2174
|
+
canMerge: () => !!(page.data.kind === 'record' && options.dataSource?.merge && options.dataSource.load),
|
|
2175
|
+
async readRecord(id) {
|
|
2176
|
+
const [source, model] = recordSource('load');
|
|
2177
|
+
return (0, data_source_1.loadedRecordOf)(await source.load({ model, id, fields: page.fields })).values;
|
|
2178
|
+
},
|
|
2179
|
+
async merge(request) {
|
|
2180
|
+
const [source, model] = recordSource('merge');
|
|
2181
|
+
return source.merge({ model, keepId: request.keepId, mergeIds: [...request.mergeIds], values: (0, values_1.structuredCopy)(request.values), fields: page.fields });
|
|
2182
|
+
},
|
|
2139
2183
|
upload: (field, files, how = {}) => uploads.start(field, files, how),
|
|
2140
2184
|
uploading: (field) => state.uploads.filter((upload) => upload.field === field && !upload.line),
|
|
2141
2185
|
cancelUpload(id) {
|
|
@@ -2812,6 +2856,12 @@ function indexLayout(root) {
|
|
|
2812
2856
|
else if (node.type === 'form') {
|
|
2813
2857
|
add(node, parent, 'form', { invisible: node.invisible, readonly: node.readonly });
|
|
2814
2858
|
}
|
|
2859
|
+
else if (node.type === 'related') {
|
|
2860
|
+
add(node, parent, 'other', { invisible: node.invisible });
|
|
2861
|
+
// A row's buttons: their roles here, hidden with the list, their condition read by it on each row's record.
|
|
2862
|
+
for (const button of node.rowButtons ?? [])
|
|
2863
|
+
add(button, node.id, 'button', {});
|
|
2864
|
+
}
|
|
2815
2865
|
else {
|
|
2816
2866
|
add(node, parent, 'other', { invisible: node.invisible });
|
|
2817
2867
|
}
|
|
@@ -2838,6 +2888,9 @@ function indexLayout(root) {
|
|
|
2838
2888
|
// A list holds no fields of its own record: only the buttons for what is selected.
|
|
2839
2889
|
for (const button of root.actions ?? [])
|
|
2840
2890
|
add(button, root.id, 'button', { invisible: button.invisible });
|
|
2891
|
+
// A row's buttons: their roles here, their condition read by the list on each row's record, which the form does not hold.
|
|
2892
|
+
for (const button of root.rowButtons ?? [])
|
|
2893
|
+
add(button, root.id, 'button', {});
|
|
2841
2894
|
}
|
|
2842
2895
|
else {
|
|
2843
2896
|
// The title and statusbar show fields too, so they are validated like
|