@fieldia/core 0.27.0 → 0.28.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 +24 -5
- package/package.json +1 -1
- package/schema/page.schema.json +4 -2
- package/src/index.d.mts +2 -0
- package/src/index.d.ts +2 -0
- package/src/index.js +6 -2
- package/src/index.js.map +1 -1
- package/src/index.mjs +2 -0
- package/src/index.mjs.map +1 -1
- package/src/lib/format/layout.d.mts +23 -2
- package/src/lib/format/layout.d.ts +23 -2
- package/src/lib/format/layout.js +1 -1
- package/src/lib/format/layout.js.map +1 -1
- package/src/lib/format/layout.mjs +1 -1
- package/src/lib/format/layout.mjs.map +1 -1
- package/src/lib/format/page.d.mts +8 -1
- package/src/lib/format/page.d.ts +8 -1
- package/src/lib/format/upgrade.d.mts +2 -1
- package/src/lib/format/upgrade.d.ts +2 -1
- package/src/lib/format/upgrade.js +2 -1
- package/src/lib/format/upgrade.js.map +1 -1
- package/src/lib/format/upgrade.mjs +2 -1
- 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/check.js +3 -1
- package/src/lib/record/check.js.map +1 -1
- package/src/lib/record/check.mjs +3 -1
- package/src/lib/record/check.mjs.map +1 -1
- package/src/lib/record/day-words.d.mts +10 -0
- package/src/lib/record/day-words.d.ts +10 -0
- package/src/lib/record/day-words.js +23 -0
- package/src/lib/record/day-words.js.map +1 -0
- package/src/lib/record/day-words.mjs +20 -0
- package/src/lib/record/day-words.mjs.map +1 -0
- package/src/lib/record/form-limits.d.mts +5 -8
- package/src/lib/record/form-limits.d.ts +5 -8
- package/src/lib/record/form-limits.js +20 -21
- package/src/lib/record/form-limits.js.map +1 -1
- package/src/lib/record/form-limits.mjs +21 -22
- package/src/lib/record/form-limits.mjs.map +1 -1
- package/src/lib/record/form.d.mts +9 -7
- package/src/lib/record/form.d.ts +9 -7
- package/src/lib/record/form.js +70 -6
- package/src/lib/record/form.js.map +1 -1
- package/src/lib/record/form.mjs +70 -6
- package/src/lib/record/form.mjs.map +1 -1
- package/src/lib/record/limits.js +4 -2
- package/src/lib/record/limits.js.map +1 -1
- package/src/lib/record/limits.mjs +4 -2
- package/src/lib/record/limits.mjs.map +1 -1
- package/src/lib/record/locales/ar.js +1 -0
- package/src/lib/record/locales/ar.js.map +1 -1
- package/src/lib/record/locales/ar.mjs +1 -0
- package/src/lib/record/locales/ar.mjs.map +1 -1
- package/src/lib/record/locales/de.js +1 -0
- package/src/lib/record/locales/de.js.map +1 -1
- package/src/lib/record/locales/de.mjs +1 -0
- package/src/lib/record/locales/de.mjs.map +1 -1
- package/src/lib/record/locales/fr.js +1 -0
- package/src/lib/record/locales/fr.js.map +1 -1
- package/src/lib/record/locales/fr.mjs +1 -0
- package/src/lib/record/locales/fr.mjs.map +1 -1
- package/src/lib/record/messages.d.mts +10 -2
- package/src/lib/record/messages.d.ts +10 -2
- package/src/lib/record/messages.js +3 -4
- package/src/lib/record/messages.js.map +1 -1
- package/src/lib/record/messages.mjs +3 -4
- package/src/lib/record/messages.mjs.map +1 -1
- package/src/lib/record/option-rules.d.mts +7 -1
- package/src/lib/record/option-rules.d.ts +7 -1
- package/src/lib/record/option-rules.js +9 -3
- package/src/lib/record/option-rules.js.map +1 -1
- package/src/lib/record/option-rules.mjs +9 -3
- package/src/lib/record/option-rules.mjs.map +1 -1
- package/src/lib/record/record-words.d.mts +13 -1
- package/src/lib/record/record-words.d.ts +13 -1
- package/src/lib/record/record-words.js +29 -1
- package/src/lib/record/record-words.js.map +1 -1
- package/src/lib/record/record-words.mjs +27 -1
- package/src/lib/record/record-words.mjs.map +1 -1
- package/src/lib/record/waits.d.mts +7 -5
- package/src/lib/record/waits.d.ts +7 -5
- package/src/lib/record/waits.js +7 -10
- package/src/lib/record/waits.js.map +1 -1
- package/src/lib/record/waits.mjs +7 -10
- package/src/lib/record/waits.mjs.map +1 -1
- package/src/runtime.d.mts +1 -1
- package/src/runtime.d.ts +1 -1
- package/src/runtime.js +7 -3
- package/src/runtime.js.map +1 -1
- package/src/runtime.mjs +4 -1
- package/src/runtime.mjs.map +1 -1
package/src/lib/record/form.mjs
CHANGED
|
@@ -85,7 +85,9 @@ function innerForm(options, within, place, outer) {
|
|
|
85
85
|
const page = zonedPage(options.page);
|
|
86
86
|
const scheduler = options.scheduler ?? hostScheduler;
|
|
87
87
|
// A field's label small inside a message's sentence where the page asks (`lowerLabels`).
|
|
88
|
-
|
|
88
|
+
// …and a day written in the page's order where it says one (`look.dateOrder`): "can't be after Oct 20, 2026".
|
|
89
|
+
const dateOrder = page.look?.dateOrder;
|
|
90
|
+
const messages = page.lowerLabels || dateOrder ? { ...(options.messages ?? MESSAGES.en), lowerLabels: page.lowerLabels, dateOrder } : (options.messages ?? MESSAGES.en);
|
|
89
91
|
/** One of the form's sentences in the words the page names its records by (`records`), else as the language says it plainly. */
|
|
90
92
|
const named = (noun, plain) => recordSentence(page.records, noun, plain, messages.locale);
|
|
91
93
|
/** The app's functions the page's expressions call: a name none may call is refused now. */
|
|
@@ -550,6 +552,7 @@ function innerForm(options, within, place, outer) {
|
|
|
550
552
|
let edit = false;
|
|
551
553
|
function set(patch) {
|
|
552
554
|
const was = state.values;
|
|
555
|
+
const choicesWas = state.choices;
|
|
553
556
|
const shown = state.errors;
|
|
554
557
|
const edited = edit;
|
|
555
558
|
edit = false;
|
|
@@ -558,6 +561,12 @@ function innerForm(options, within, place, outer) {
|
|
|
558
561
|
// A line's choices go with their line.
|
|
559
562
|
if (state.values !== was)
|
|
560
563
|
state = { ...state, choices: choicesNow(state.choices) };
|
|
564
|
+
// What was said taken out is said while it is true: an option offered again, or picked again, is said no more.
|
|
565
|
+
if ((state.values !== was || state.choices !== choicesWas) && Object.keys(state.takenOut).length) {
|
|
566
|
+
const takenOut = takenOutTrue();
|
|
567
|
+
if (takenOut !== state.takenOut)
|
|
568
|
+
state = { ...state, takenOut };
|
|
569
|
+
}
|
|
561
570
|
// What waits for an app's answer, as the values shown last settled.
|
|
562
571
|
if (patch.values !== undefined && patch.asked === undefined && !sameAsked(state.asked, askedNow))
|
|
563
572
|
state = { ...state, asked: Object.fromEntries(askedNow) };
|
|
@@ -858,7 +867,9 @@ function innerForm(options, within, place, outer) {
|
|
|
858
867
|
const written = line
|
|
859
868
|
? (values[field] ?? []).map((l) => (l.key === line.key ? { ...l, values: { ...l.values, [line.column]: without(l.values[line.column]) } } : l))
|
|
860
869
|
: without(values[field]);
|
|
861
|
-
|
|
870
|
+
const path = line ? `${field}.${line.key}.${line.column}` : field;
|
|
871
|
+
takenOutOf.set(path, lost.map((value, i) => ({ value, label: labels[i] })));
|
|
872
|
+
writeValues({ ...values, [field]: written }, {}, false, { [path]: takenOutWords(labels) });
|
|
862
873
|
afterEdit(field);
|
|
863
874
|
changed(field, 'step', { keep: true });
|
|
864
875
|
}
|
|
@@ -967,6 +978,38 @@ function innerForm(options, within, place, outer) {
|
|
|
967
978
|
* "Garden is no longer offered, so it was taken out".
|
|
968
979
|
*/
|
|
969
980
|
const takenOutWords = (labels) => fillMessage(messages, labels.length > 1 ? messages.notOfferedNowMany : messages.notOfferedNow, { name: listedLabels(labels.map((label) => (label.includes('{') ? wordsNow(label) : label))) });
|
|
981
|
+
/** The options each note of `takenOut` names, by its path: a note goes once none of them is still out. */
|
|
982
|
+
const takenOutOf = new Map();
|
|
983
|
+
/**
|
|
984
|
+
* The notes of options taken out (`FormState.takenOut`) that are still true for the values and the choices now: one
|
|
985
|
+
* whose options are all offered again — the type that offers them picked again, the treatment ticked again above, the
|
|
986
|
+
* app's list asked again offering it — or picked again goes; one with some still out names those. A list being asked
|
|
987
|
+
* again says nothing yet. The same notes, when none changed.
|
|
988
|
+
*/
|
|
989
|
+
function takenOutTrue() {
|
|
990
|
+
let kept = null;
|
|
991
|
+
for (const path of Object.keys(state.takenOut)) {
|
|
992
|
+
const options = takenOutOf.get(path);
|
|
993
|
+
const held = valueAt(state.values, path);
|
|
994
|
+
if (!options || held === gone || state.choices[path]?.loading)
|
|
995
|
+
continue;
|
|
996
|
+
const offered = form.offered(path);
|
|
997
|
+
if (!offered)
|
|
998
|
+
continue;
|
|
999
|
+
const chosen = [held].flat();
|
|
1000
|
+
const left = options.filter((option) => !chosen.includes(option.value) && !offered.some((one) => one.value === option.value));
|
|
1001
|
+
if (left.length === options.length)
|
|
1002
|
+
continue;
|
|
1003
|
+
kept ??= { ...state.takenOut };
|
|
1004
|
+
if (left.length) {
|
|
1005
|
+
takenOutOf.set(path, left);
|
|
1006
|
+
kept[path] = takenOutWords(left.map((option) => option.label));
|
|
1007
|
+
}
|
|
1008
|
+
else
|
|
1009
|
+
delete kept[path];
|
|
1010
|
+
}
|
|
1011
|
+
return kept ?? state.takenOut;
|
|
1012
|
+
}
|
|
970
1013
|
/** A line's field as its value is checked: its choices from the app's list, those loaded for its line. */
|
|
971
1014
|
function checkedLine(path, def) {
|
|
972
1015
|
const loaded = def.type === 'selection' && optionsList(def.optionsFrom) ? state.choices[path]?.options : null;
|
|
@@ -1152,6 +1195,23 @@ function innerForm(options, within, place, outer) {
|
|
|
1152
1195
|
const name = node.field;
|
|
1153
1196
|
return unticked(node) ?? checkInput(page.fields[name], node.source, state.values[name], messages) ?? checkValue(checked(name), state.values[name], nodeState(node.id).required, messages, today(), node.source.widget) ?? ruleCheck(node).error;
|
|
1154
1197
|
}
|
|
1198
|
+
/** What a check would refuse now (`collectErrors`), for the state it was found for. */
|
|
1199
|
+
let refusals = null;
|
|
1200
|
+
/**
|
|
1201
|
+
* Whether the form refuses the answer at a path — a field's, or a table line's cell (`field.lineKey.subfield`) — as a
|
|
1202
|
+
* check would now: one it shows that holds a value its rules do not take (under its least, past today, a pattern not
|
|
1203
|
+
* met, an answer rule's error). An empty answer is none, required or not: there is nothing worked out from it to wait.
|
|
1204
|
+
*/
|
|
1205
|
+
function refusedAt(path) {
|
|
1206
|
+
if (refusals?.state !== state)
|
|
1207
|
+
refusals = { state, errors: collectErrors() };
|
|
1208
|
+
if (!refusals.errors[path])
|
|
1209
|
+
return false;
|
|
1210
|
+
const parts = path.split('.');
|
|
1211
|
+
const def = page.fields[parts[0]];
|
|
1212
|
+
const held = valueAt(state.values, path);
|
|
1213
|
+
return held !== gone && !isEmpty((parts.length > 2 && def?.type === 'one2many' ? def.fields[parts[parts.length - 1]] : def), held);
|
|
1214
|
+
}
|
|
1155
1215
|
/** Warnings for the visible fields of the page, without publishing them. */
|
|
1156
1216
|
function collectWarnings() {
|
|
1157
1217
|
const warnings = {};
|
|
@@ -1420,6 +1480,8 @@ function innerForm(options, within, place, outer) {
|
|
|
1420
1480
|
if (out) {
|
|
1421
1481
|
settled = out.values;
|
|
1422
1482
|
takenOut = { ...takenOut, ...out.said };
|
|
1483
|
+
for (const [path, gone] of Object.entries(out.gone))
|
|
1484
|
+
takenOutOf.set(path, gone);
|
|
1423
1485
|
}
|
|
1424
1486
|
if (Object.keys(takenOut).length || Object.keys(state.takenOut).length)
|
|
1425
1487
|
extra = { takenOut, ...extra };
|
|
@@ -2707,11 +2769,13 @@ function innerForm(options, within, place, outer) {
|
|
|
2707
2769
|
return changesOptions(cell ? (lines?.type === 'one2many' ? lines.fields[cell.column] : undefined) : page.fields[name]);
|
|
2708
2770
|
},
|
|
2709
2771
|
waitsFor(name) {
|
|
2710
|
-
|
|
2711
|
-
if (found)
|
|
2712
|
-
return { field: name, asking: true, ...(found.failed !== undefined ? { failed: found.failed } : {}) };
|
|
2772
|
+
// What it is worked out from first: an answer that does not read, or that the form refuses, is never asked about.
|
|
2713
2773
|
const from = workedOut.get(name);
|
|
2714
|
-
|
|
2774
|
+
const waits = from ? waitingOn(page, from, state.values, state.unread, refusedAt) : null;
|
|
2775
|
+
if (waits)
|
|
2776
|
+
return waits;
|
|
2777
|
+
const found = state.asked[name];
|
|
2778
|
+
return found ? { field: name, asking: true, ...(found.failed !== undefined ? { failed: found.failed } : {}) } : null;
|
|
2715
2779
|
},
|
|
2716
2780
|
problem(name) {
|
|
2717
2781
|
fieldDef(name);
|