@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.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import { rolesAllow } from './roles.mjs';
|
|
|
14
14
|
import { compileFieldTone, compileTable } from './cells.mjs';
|
|
15
15
|
import { fill, MESSAGES } from './messages.mjs';
|
|
16
16
|
import { loadedRecordOf, saveProblemOf, } from './data-source.mjs';
|
|
17
|
+
import { waitingOn, workedOutFrom } from './waits.mjs';
|
|
17
18
|
import { createUploads, fileBytesIn, uploadGoing, withoutFileBytes } from './uploads.mjs';
|
|
18
19
|
import { hostScheduler } from './scheduler.mjs';
|
|
19
20
|
import { createMoments, createRunner } from './run.mjs';
|
|
@@ -158,6 +159,8 @@ function innerForm(options, within, place, outer) {
|
|
|
158
159
|
const editingNow = () => editing && !(started && state.can?.edit === false);
|
|
159
160
|
const about = () => ({ recordId: started ? state.recordId : (options.recordId ?? null), user: options.user, editing: editingNow(), context: options.context, parent: parentContext });
|
|
160
161
|
const computed = compileComputed(page, today, about);
|
|
162
|
+
/** Each value worked out from others — by the page, or by the app as the lines change — and what it waits for. */
|
|
163
|
+
const workedOut = workedOutFrom(page, !!options.dataSource?.onchange && page.data.kind === 'record', !!options.parent);
|
|
161
164
|
const setWhen = compileSetWhen(page, today, about);
|
|
162
165
|
/** The saved forms placed on the page, by the part's id: each one's answers sit under its name, as JSON. */
|
|
163
166
|
const parts = new Map();
|
|
@@ -317,6 +320,13 @@ function innerForm(options, within, place, outer) {
|
|
|
317
320
|
* 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
|
|
318
321
|
* dialog, a record step's — none is laid on another record, nor over the values the person's Discard let go.
|
|
319
322
|
*/
|
|
323
|
+
/** The data source with a member a record's page asks of it, and the page's model: refused where either is missing. */
|
|
324
|
+
function recordSource(member) {
|
|
325
|
+
const source = options.dataSource;
|
|
326
|
+
if (page.data.kind !== 'record' || !source?.[member])
|
|
327
|
+
throw new Error(`The data source has no ${member}()`);
|
|
328
|
+
return [source, page.data.model];
|
|
329
|
+
}
|
|
320
330
|
function shownNow() {
|
|
321
331
|
const at = generation;
|
|
322
332
|
const reset = resets;
|
|
@@ -2020,6 +2030,13 @@ function innerForm(options, within, place, outer) {
|
|
|
2020
2030
|
leave: (how) => Promise.resolve(letGo(how)),
|
|
2021
2031
|
leaveNow: (how = {}) => (outer ? outer.leaveNow(how) : atOnce(how)),
|
|
2022
2032
|
recordShown: shownNow,
|
|
2033
|
+
relatedFilter(id) {
|
|
2034
|
+
const node = index.get(id)?.source;
|
|
2035
|
+
if (node?.type !== 'related' || state.recordId == null)
|
|
2036
|
+
return null;
|
|
2037
|
+
const related = node;
|
|
2038
|
+
return [{ field: related.field, op: '=', value: state.recordId }, ...(related.filter ? resolveFilter(related.filter, context()) : [])];
|
|
2039
|
+
},
|
|
2023
2040
|
post,
|
|
2024
2041
|
setEditing(next) {
|
|
2025
2042
|
if (next === editing)
|
|
@@ -2032,6 +2049,10 @@ function innerForm(options, within, place, outer) {
|
|
|
2032
2049
|
loadChoices,
|
|
2033
2050
|
loadDefinitions,
|
|
2034
2051
|
addDefinition,
|
|
2052
|
+
waitsFor(name) {
|
|
2053
|
+
const from = workedOut.get(name);
|
|
2054
|
+
return from ? waitingOn(page, from, state.values, state.unread) : null;
|
|
2055
|
+
},
|
|
2035
2056
|
problem(name) {
|
|
2036
2057
|
fieldDef(name);
|
|
2037
2058
|
// Only a field someone can see is asked anything, and required wherever it is shown so.
|
|
@@ -2110,6 +2131,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2110
2131
|
save: () => track(save()),
|
|
2111
2132
|
canUpload: () => typeof options.dataSource?.upload === 'function',
|
|
2112
2133
|
today,
|
|
2134
|
+
recordReads: (id, values, fields) => recordContext(values, fields ?? page.fields, { recordId: id, user: options.user, editing: false, context: options.context }),
|
|
2113
2135
|
canFindSlots: () => typeof options.dataSource?.slots === 'function',
|
|
2114
2136
|
findSlots(field, ask) {
|
|
2115
2137
|
const source = options.dataSource;
|
|
@@ -2130,6 +2152,28 @@ function innerForm(options, within, place, outer) {
|
|
|
2130
2152
|
values: structuredCopy(state.values),
|
|
2131
2153
|
});
|
|
2132
2154
|
},
|
|
2155
|
+
canFindDuplicates: () => !!(page.data.kind === 'record' && page.duplicates?.fields?.length && options.dataSource?.duplicates),
|
|
2156
|
+
async findDuplicates() {
|
|
2157
|
+
const [source, model] = recordSource('duplicates');
|
|
2158
|
+
const fields = (page.duplicates?.fields ?? []).filter((name) => page.fields[name] && !isEmpty(page.fields[name], state.values[name]));
|
|
2159
|
+
const recordId = state.recordId;
|
|
2160
|
+
// Nothing typed in any of them: nothing asked.
|
|
2161
|
+
if (!fields.length)
|
|
2162
|
+
return [];
|
|
2163
|
+
const { matches } = await source.duplicates({ model, recordId, values: structuredCopy(state.values), fields });
|
|
2164
|
+
// Never the record itself, whatever the source says; the likest first where it scores them.
|
|
2165
|
+
const others = (matches ?? []).filter((match) => match && String(match.id) !== String(recordId));
|
|
2166
|
+
return others.every((match) => typeof match.score === 'number') ? others.sort((a, b) => b.score - a.score) : others;
|
|
2167
|
+
},
|
|
2168
|
+
canMerge: () => !!(page.data.kind === 'record' && options.dataSource?.merge && options.dataSource.load),
|
|
2169
|
+
async readRecord(id) {
|
|
2170
|
+
const [source, model] = recordSource('load');
|
|
2171
|
+
return loadedRecordOf(await source.load({ model, id, fields: page.fields })).values;
|
|
2172
|
+
},
|
|
2173
|
+
async merge(request) {
|
|
2174
|
+
const [source, model] = recordSource('merge');
|
|
2175
|
+
return source.merge({ model, keepId: request.keepId, mergeIds: [...request.mergeIds], values: structuredCopy(request.values), fields: page.fields });
|
|
2176
|
+
},
|
|
2133
2177
|
upload: (field, files, how = {}) => uploads.start(field, files, how),
|
|
2134
2178
|
uploading: (field) => state.uploads.filter((upload) => upload.field === field && !upload.line),
|
|
2135
2179
|
cancelUpload(id) {
|
|
@@ -2806,6 +2850,12 @@ function indexLayout(root) {
|
|
|
2806
2850
|
else if (node.type === 'form') {
|
|
2807
2851
|
add(node, parent, 'form', { invisible: node.invisible, readonly: node.readonly });
|
|
2808
2852
|
}
|
|
2853
|
+
else if (node.type === 'related') {
|
|
2854
|
+
add(node, parent, 'other', { invisible: node.invisible });
|
|
2855
|
+
// A row's buttons: their roles here, hidden with the list, their condition read by it on each row's record.
|
|
2856
|
+
for (const button of node.rowButtons ?? [])
|
|
2857
|
+
add(button, node.id, 'button', {});
|
|
2858
|
+
}
|
|
2809
2859
|
else {
|
|
2810
2860
|
add(node, parent, 'other', { invisible: node.invisible });
|
|
2811
2861
|
}
|
|
@@ -2832,6 +2882,9 @@ function indexLayout(root) {
|
|
|
2832
2882
|
// A list holds no fields of its own record: only the buttons for what is selected.
|
|
2833
2883
|
for (const button of root.actions ?? [])
|
|
2834
2884
|
add(button, root.id, 'button', { invisible: button.invisible });
|
|
2885
|
+
// A row's buttons: their roles here, their condition read by the list on each row's record, which the form does not hold.
|
|
2886
|
+
for (const button of root.rowButtons ?? [])
|
|
2887
|
+
add(button, root.id, 'button', {});
|
|
2835
2888
|
}
|
|
2836
2889
|
else {
|
|
2837
2890
|
// The title and statusbar show fields too, so they are validated like
|