@fieldia/core 0.26.1 → 0.27.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 +307 -3
- package/package.json +1 -1
- package/schema/page.schema.json +227 -23
- package/src/index.d.mts +8 -2
- package/src/index.d.ts +8 -2
- package/src/index.js +22 -1
- package/src/index.js.map +1 -1
- package/src/index.mjs +9 -2
- package/src/index.mjs.map +1 -1
- package/src/lib/expression/app-functions.d.mts +47 -0
- package/src/lib/expression/app-functions.d.ts +47 -0
- package/src/lib/expression/app-functions.js +64 -0
- package/src/lib/expression/app-functions.js.map +1 -0
- package/src/lib/expression/app-functions.mjs +58 -0
- package/src/lib/expression/app-functions.mjs.map +1 -0
- package/src/lib/expression/dates.d.mts +21 -0
- package/src/lib/expression/dates.d.ts +21 -0
- package/src/lib/expression/dates.js +73 -0
- package/src/lib/expression/dates.js.map +1 -0
- package/src/lib/expression/dates.mjs +67 -0
- package/src/lib/expression/dates.mjs.map +1 -0
- package/src/lib/expression/evaluator.js +12 -6
- package/src/lib/expression/evaluator.js.map +1 -1
- package/src/lib/expression/evaluator.mjs +12 -6
- package/src/lib/expression/evaluator.mjs.map +1 -1
- package/src/lib/expression/expression.d.mts +7 -3
- package/src/lib/expression/expression.d.ts +7 -3
- package/src/lib/expression/expression.js +8 -2
- package/src/lib/expression/expression.js.map +1 -1
- package/src/lib/expression/expression.mjs +7 -2
- package/src/lib/expression/expression.mjs.map +1 -1
- package/src/lib/expression/functions-part.d.mts +7 -0
- package/src/lib/expression/functions-part.d.ts +7 -0
- package/src/lib/expression/functions-part.js +13 -0
- package/src/lib/expression/functions-part.js.map +1 -0
- package/src/lib/expression/functions-part.mjs +8 -0
- package/src/lib/expression/functions-part.mjs.map +1 -0
- package/src/lib/expression/functions.d.mts +60 -3
- package/src/lib/expression/functions.d.ts +60 -3
- package/src/lib/expression/functions.js +109 -15
- package/src/lib/expression/functions.js.map +1 -1
- package/src/lib/expression/functions.mjs +103 -16
- package/src/lib/expression/functions.mjs.map +1 -1
- package/src/lib/expression/kinds.d.mts +31 -0
- package/src/lib/expression/kinds.d.ts +31 -0
- package/src/lib/expression/kinds.js +159 -0
- package/src/lib/expression/kinds.js.map +1 -0
- package/src/lib/expression/kinds.mjs +151 -0
- package/src/lib/expression/kinds.mjs.map +1 -0
- package/src/lib/expression/modifier.d.mts +4 -3
- package/src/lib/expression/modifier.d.ts +4 -3
- package/src/lib/expression/modifier.js +4 -3
- package/src/lib/expression/modifier.js.map +1 -1
- package/src/lib/expression/modifier.mjs +4 -3
- package/src/lib/expression/modifier.mjs.map +1 -1
- package/src/lib/expression/more-functions.d.mts +8 -0
- package/src/lib/expression/more-functions.d.ts +8 -0
- package/src/lib/expression/more-functions.js +235 -0
- package/src/lib/expression/more-functions.js.map +1 -0
- package/src/lib/expression/more-functions.mjs +230 -0
- package/src/lib/expression/more-functions.mjs.map +1 -0
- package/src/lib/expression/parser.d.mts +4 -1
- package/src/lib/expression/parser.d.ts +4 -1
- package/src/lib/expression/parser.js +6 -3
- package/src/lib/expression/parser.js.map +1 -1
- package/src/lib/expression/parser.mjs +6 -3
- package/src/lib/expression/parser.mjs.map +1 -1
- package/src/lib/expression/reads.d.mts +2 -2
- package/src/lib/expression/reads.d.ts +2 -2
- package/src/lib/expression/reads.js +6 -4
- package/src/lib/expression/reads.js.map +1 -1
- package/src/lib/expression/reads.mjs +6 -4
- package/src/lib/expression/reads.mjs.map +1 -1
- package/src/lib/format/actions.d.mts +16 -16
- package/src/lib/format/actions.d.ts +16 -16
- package/src/lib/format/check-page.d.mts +7 -2
- package/src/lib/format/check-page.d.ts +7 -2
- package/src/lib/format/check-page.js +10 -4
- package/src/lib/format/check-page.js.map +1 -1
- package/src/lib/format/check-page.mjs +10 -4
- package/src/lib/format/check-page.mjs.map +1 -1
- package/src/lib/format/choice-options.d.mts +34 -0
- package/src/lib/format/choice-options.d.ts +34 -0
- package/src/lib/format/choice-options.js +60 -0
- package/src/lib/format/choice-options.js.map +1 -0
- package/src/lib/format/choice-options.mjs +51 -0
- package/src/lib/format/choice-options.mjs.map +1 -0
- package/src/lib/format/endings-checks.d.mts +8 -0
- package/src/lib/format/endings-checks.d.ts +8 -0
- package/src/lib/format/endings-checks.js +23 -0
- package/src/lib/format/endings-checks.js.map +1 -0
- package/src/lib/format/endings-checks.mjs +20 -0
- package/src/lib/format/endings-checks.mjs.map +1 -0
- package/src/lib/format/endings.d.mts +40 -0
- package/src/lib/format/endings.d.ts +40 -0
- package/src/lib/format/endings.js +15 -0
- package/src/lib/format/endings.js.map +1 -0
- package/src/lib/format/endings.mjs +12 -0
- package/src/lib/format/endings.mjs.map +1 -0
- package/src/lib/format/field.d.mts +151 -16
- package/src/lib/format/field.d.ts +151 -16
- package/src/lib/format/field.js +64 -11
- package/src/lib/format/field.js.map +1 -1
- package/src/lib/format/field.mjs +63 -10
- package/src/lib/format/field.mjs.map +1 -1
- package/src/lib/format/head-checks.d.mts +2 -0
- package/src/lib/format/head-checks.d.ts +2 -0
- package/src/lib/format/head-checks.js +2 -59
- package/src/lib/format/head-checks.js.map +1 -1
- package/src/lib/format/head-checks.mjs +1 -58
- package/src/lib/format/head-checks.mjs.map +1 -1
- package/src/lib/format/later-checks.d.mts +17 -0
- package/src/lib/format/later-checks.d.ts +17 -0
- package/src/lib/format/later-checks.js +20 -0
- package/src/lib/format/later-checks.js.map +1 -0
- package/src/lib/format/later-checks.mjs +17 -0
- package/src/lib/format/later-checks.mjs.map +1 -0
- package/src/lib/format/layout.d.mts +241 -188
- package/src/lib/format/layout.d.ts +241 -188
- package/src/lib/format/layout.js +33 -28
- package/src/lib/format/layout.js.map +1 -1
- package/src/lib/format/layout.mjs +13 -3
- package/src/lib/format/layout.mjs.map +1 -1
- package/src/lib/format/layout.types.d.mts +2 -1
- package/src/lib/format/layout.types.d.ts +2 -1
- package/src/lib/format/limits-checks.d.mts +14 -0
- package/src/lib/format/limits-checks.d.ts +14 -0
- package/src/lib/format/limits-checks.js +36 -0
- package/src/lib/format/limits-checks.js.map +1 -0
- package/src/lib/format/limits-checks.mjs +33 -0
- package/src/lib/format/limits-checks.mjs.map +1 -0
- package/src/lib/format/limits.d.mts +24 -0
- package/src/lib/format/limits.d.ts +24 -0
- package/src/lib/format/limits.js +35 -0
- package/src/lib/format/limits.js.map +1 -0
- package/src/lib/format/limits.mjs +32 -0
- package/src/lib/format/limits.mjs.map +1 -0
- package/src/lib/format/list-checks.d.mts +44 -0
- package/src/lib/format/list-checks.d.ts +44 -0
- package/src/lib/format/list-checks.js +340 -0
- package/src/lib/format/list-checks.js.map +1 -0
- package/src/lib/format/list-checks.mjs +336 -0
- package/src/lib/format/list-checks.mjs.map +1 -0
- package/src/lib/format/map-checks.d.mts +33 -0
- package/src/lib/format/map-checks.d.ts +33 -0
- package/src/lib/format/map-checks.js +180 -0
- package/src/lib/format/map-checks.js.map +1 -0
- package/src/lib/format/map-checks.mjs +175 -0
- package/src/lib/format/map-checks.mjs.map +1 -0
- package/src/lib/format/mapchecks-part.d.mts +5 -0
- package/src/lib/format/mapchecks-part.d.ts +5 -0
- package/src/lib/format/mapchecks-part.js +11 -0
- package/src/lib/format/mapchecks-part.js.map +1 -0
- package/src/lib/format/mapchecks-part.mjs +6 -0
- package/src/lib/format/mapchecks-part.mjs.map +1 -0
- package/src/lib/format/option-checks.d.mts +16 -0
- package/src/lib/format/option-checks.d.ts +16 -0
- package/src/lib/format/option-checks.js +39 -0
- package/src/lib/format/option-checks.js.map +1 -0
- package/src/lib/format/option-checks.mjs +36 -0
- package/src/lib/format/option-checks.mjs.map +1 -0
- package/src/lib/format/options-part.d.mts +6 -0
- package/src/lib/format/options-part.d.ts +6 -0
- package/src/lib/format/options-part.js +11 -0
- package/src/lib/format/options-part.js.map +1 -0
- package/src/lib/format/options-part.mjs +7 -0
- package/src/lib/format/options-part.mjs.map +1 -0
- package/src/lib/format/page.d.mts +147 -71
- package/src/lib/format/page.d.ts +147 -71
- package/src/lib/format/page.js +5 -0
- package/src/lib/format/page.js.map +1 -1
- package/src/lib/format/page.mjs +5 -0
- package/src/lib/format/page.mjs.map +1 -1
- package/src/lib/format/part-checks.d.mts +20 -0
- package/src/lib/format/part-checks.d.ts +20 -0
- package/src/lib/format/part-checks.js +3 -0
- package/src/lib/format/part-checks.js.map +1 -0
- package/src/lib/format/part-checks.mjs +2 -0
- package/src/lib/format/part-checks.mjs.map +1 -0
- package/src/lib/format/references.d.mts +71 -65
- package/src/lib/format/references.d.ts +71 -65
- package/src/lib/format/references.js +194 -631
- package/src/lib/format/references.js.map +1 -1
- package/src/lib/format/references.mjs +194 -631
- package/src/lib/format/references.mjs.map +1 -1
- package/src/lib/format/seats-checks.d.mts +3 -0
- package/src/lib/format/seats-checks.d.ts +3 -0
- package/src/lib/format/seats-checks.js +13 -0
- package/src/lib/format/seats-checks.js.map +1 -0
- package/src/lib/format/seats-checks.mjs +10 -0
- package/src/lib/format/seats-checks.mjs.map +1 -0
- package/src/lib/format/sheet-head-checks.d.mts +4 -0
- package/src/lib/format/sheet-head-checks.d.ts +4 -0
- package/src/lib/format/sheet-head-checks.js +68 -0
- package/src/lib/format/sheet-head-checks.js.map +1 -0
- package/src/lib/format/sheet-head-checks.mjs +65 -0
- package/src/lib/format/sheet-head-checks.mjs.map +1 -0
- package/src/lib/format/signature-checks.d.mts +7 -0
- package/src/lib/format/signature-checks.d.ts +7 -0
- package/src/lib/format/signature-checks.js +32 -0
- package/src/lib/format/signature-checks.js.map +1 -0
- package/src/lib/format/signature-checks.mjs +29 -0
- package/src/lib/format/signature-checks.mjs.map +1 -0
- package/src/lib/format/signature-options.d.mts +24 -0
- package/src/lib/format/signature-options.d.ts +24 -0
- package/src/lib/format/signature-options.js +30 -0
- package/src/lib/format/signature-options.js.map +1 -0
- package/src/lib/format/signature-options.mjs +26 -0
- package/src/lib/format/signature-options.mjs.map +1 -0
- package/src/lib/format/slots-checks.d.mts +4 -0
- package/src/lib/format/slots-checks.d.ts +4 -0
- package/src/lib/format/slots-checks.js +21 -0
- package/src/lib/format/slots-checks.js.map +1 -0
- package/src/lib/format/slots-checks.mjs +18 -0
- package/src/lib/format/slots-checks.mjs.map +1 -0
- package/src/lib/format/statusbar-checks.d.mts +8 -0
- package/src/lib/format/statusbar-checks.d.ts +8 -0
- package/src/lib/format/statusbar-checks.js +35 -0
- package/src/lib/format/statusbar-checks.js.map +1 -0
- package/src/lib/format/statusbar-checks.mjs +32 -0
- package/src/lib/format/statusbar-checks.mjs.map +1 -0
- package/src/lib/format/table-checks.d.mts +21 -0
- package/src/lib/format/table-checks.d.ts +21 -0
- package/src/lib/format/table-checks.js +86 -0
- package/src/lib/format/table-checks.js.map +1 -0
- package/src/lib/format/table-checks.mjs +82 -0
- package/src/lib/format/table-checks.mjs.map +1 -0
- package/src/lib/format/translate.js +2 -2
- package/src/lib/format/translate.js.map +1 -1
- package/src/lib/format/translate.mjs +2 -2
- package/src/lib/format/translate.mjs.map +1 -1
- package/src/lib/format/upgrade.d.mts +6 -3
- package/src/lib/format/upgrade.d.ts +6 -3
- package/src/lib/format/upgrade.js +6 -3
- package/src/lib/format/upgrade.js.map +1 -1
- package/src/lib/format/upgrade.mjs +6 -3
- package/src/lib/format/upgrade.mjs.map +1 -1
- package/src/lib/format/validate.d.mts +18 -2
- package/src/lib/format/validate.d.ts +18 -2
- package/src/lib/format/validate.js +36 -3
- package/src/lib/format/validate.js.map +1 -1
- package/src/lib/format/validate.mjs +35 -3
- package/src/lib/format/validate.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/format/words.d.mts +6 -2
- package/src/lib/format/words.d.ts +6 -2
- package/src/lib/format/words.js +13 -7
- package/src/lib/format/words.js.map +1 -1
- package/src/lib/format/words.mjs +13 -7
- package/src/lib/format/words.mjs.map +1 -1
- package/src/lib/parts.d.mts +45 -0
- package/src/lib/parts.d.ts +45 -0
- package/src/lib/parts.js +60 -0
- package/src/lib/parts.js.map +1 -0
- package/src/lib/parts.mjs +53 -0
- package/src/lib/parts.mjs.map +1 -0
- package/src/lib/record/cells.d.mts +3 -3
- package/src/lib/record/cells.d.ts +3 -3
- package/src/lib/record/cells.js +16 -15
- package/src/lib/record/cells.js.map +1 -1
- package/src/lib/record/cells.mjs +16 -15
- package/src/lib/record/cells.mjs.map +1 -1
- package/src/lib/record/compute.d.mts +16 -5
- package/src/lib/record/compute.d.ts +16 -5
- package/src/lib/record/compute.js +57 -16
- package/src/lib/record/compute.js.map +1 -1
- package/src/lib/record/compute.mjs +57 -16
- package/src/lib/record/compute.mjs.map +1 -1
- package/src/lib/record/data-source.d.mts +140 -3
- package/src/lib/record/data-source.d.ts +140 -3
- package/src/lib/record/data-source.js.map +1 -1
- package/src/lib/record/data-source.mjs.map +1 -1
- package/src/lib/record/drafts.d.mts +19 -0
- package/src/lib/record/drafts.d.ts +19 -0
- package/src/lib/record/drafts.js +35 -0
- package/src/lib/record/drafts.js.map +1 -0
- package/src/lib/record/drafts.mjs +31 -0
- package/src/lib/record/drafts.mjs.map +1 -0
- package/src/lib/record/env.d.mts +5 -1
- package/src/lib/record/env.d.ts +5 -1
- package/src/lib/record/env.js +9 -1
- package/src/lib/record/env.js.map +1 -1
- package/src/lib/record/env.mjs +8 -1
- package/src/lib/record/env.mjs.map +1 -1
- package/src/lib/record/form-limits.d.mts +81 -0
- package/src/lib/record/form-limits.d.ts +81 -0
- package/src/lib/record/form-limits.js +231 -0
- package/src/lib/record/form-limits.js.map +1 -0
- package/src/lib/record/form-limits.mjs +224 -0
- package/src/lib/record/form-limits.mjs.map +1 -0
- package/src/lib/record/form.d.mts +173 -4
- package/src/lib/record/form.d.ts +173 -4
- package/src/lib/record/form.js +792 -88
- package/src/lib/record/form.js.map +1 -1
- package/src/lib/record/form.mjs +792 -88
- package/src/lib/record/form.mjs.map +1 -1
- package/src/lib/record/limits-part.d.mts +6 -0
- package/src/lib/record/limits-part.d.ts +6 -0
- package/src/lib/record/limits-part.js +12 -0
- package/src/lib/record/limits-part.js.map +1 -0
- package/src/lib/record/limits-part.mjs +7 -0
- package/src/lib/record/limits-part.mjs.map +1 -0
- package/src/lib/record/line-choices.d.mts +30 -0
- package/src/lib/record/line-choices.d.ts +30 -0
- package/src/lib/record/line-choices.js +42 -0
- package/src/lib/record/line-choices.js.map +1 -0
- package/src/lib/record/line-choices.mjs +36 -0
- package/src/lib/record/line-choices.mjs.map +1 -0
- package/src/lib/record/locales/ar.js +10 -0
- package/src/lib/record/locales/ar.js.map +1 -1
- package/src/lib/record/locales/ar.mjs +10 -0
- package/src/lib/record/locales/ar.mjs.map +1 -1
- package/src/lib/record/locales/de.js +10 -0
- package/src/lib/record/locales/de.js.map +1 -1
- package/src/lib/record/locales/de.mjs +10 -0
- package/src/lib/record/locales/de.mjs.map +1 -1
- package/src/lib/record/locales/fr.js +10 -0
- package/src/lib/record/locales/fr.js.map +1 -1
- package/src/lib/record/locales/fr.mjs +10 -0
- package/src/lib/record/locales/fr.mjs.map +1 -1
- package/src/lib/record/memory-data-source.d.mts +44 -3
- package/src/lib/record/memory-data-source.d.ts +44 -3
- package/src/lib/record/memory-data-source.js +102 -40
- package/src/lib/record/memory-data-source.js.map +1 -1
- package/src/lib/record/memory-data-source.mjs +101 -40
- package/src/lib/record/memory-data-source.mjs.map +1 -1
- package/src/lib/record/memory-kept.d.mts +20 -0
- package/src/lib/record/memory-kept.d.ts +20 -0
- package/src/lib/record/memory-kept.js +46 -0
- package/src/lib/record/memory-kept.js.map +1 -0
- package/src/lib/record/memory-kept.mjs +43 -0
- package/src/lib/record/memory-kept.mjs.map +1 -0
- package/src/lib/record/memory-later.d.mts +19 -0
- package/src/lib/record/memory-later.d.ts +19 -0
- package/src/lib/record/memory-later.js +39 -0
- package/src/lib/record/memory-later.js.map +1 -0
- package/src/lib/record/memory-later.mjs +35 -0
- package/src/lib/record/memory-later.mjs.map +1 -0
- package/src/lib/record/memory-part.d.mts +7 -0
- package/src/lib/record/memory-part.d.ts +7 -0
- package/src/lib/record/memory-part.js +13 -0
- package/src/lib/record/memory-part.js.map +1 -0
- package/src/lib/record/memory-part.mjs +8 -0
- package/src/lib/record/memory-part.mjs.map +1 -0
- package/src/lib/record/messages.d.mts +17 -0
- package/src/lib/record/messages.d.ts +17 -0
- package/src/lib/record/messages.js +10 -0
- package/src/lib/record/messages.js.map +1 -1
- package/src/lib/record/messages.mjs +10 -0
- package/src/lib/record/messages.mjs.map +1 -1
- package/src/lib/record/option-rules.d.mts +50 -0
- package/src/lib/record/option-rules.d.ts +50 -0
- package/src/lib/record/option-rules.js +137 -0
- package/src/lib/record/option-rules.js.map +1 -0
- package/src/lib/record/option-rules.mjs +134 -0
- package/src/lib/record/option-rules.mjs.map +1 -0
- package/src/lib/record/rules.d.mts +2 -2
- package/src/lib/record/rules.d.ts +2 -2
- package/src/lib/record/rules.js +3 -3
- package/src/lib/record/rules.js.map +1 -1
- package/src/lib/record/rules.mjs +3 -3
- package/src/lib/record/rules.mjs.map +1 -1
- package/src/lib/record/run.js +9 -7
- package/src/lib/record/run.js.map +1 -1
- package/src/lib/record/run.mjs +9 -7
- package/src/lib/record/run.mjs.map +1 -1
- package/src/lib/record/search.d.mts +12 -2
- package/src/lib/record/search.d.ts +12 -2
- package/src/lib/record/search.js +29 -4
- package/src/lib/record/search.js.map +1 -1
- package/src/lib/record/search.mjs +29 -4
- package/src/lib/record/search.mjs.map +1 -1
- package/src/lib/record/set-when.d.mts +3 -3
- package/src/lib/record/set-when.d.ts +3 -3
- package/src/lib/record/set-when.js +9 -9
- package/src/lib/record/set-when.js.map +1 -1
- package/src/lib/record/set-when.mjs +9 -9
- package/src/lib/record/set-when.mjs.map +1 -1
- package/src/lib/record/signed.d.mts +21 -0
- package/src/lib/record/signed.d.ts +21 -0
- package/src/lib/record/signed.js +62 -0
- package/src/lib/record/signed.js.map +1 -0
- package/src/lib/record/signed.mjs +57 -0
- package/src/lib/record/signed.mjs.map +1 -0
- package/src/lib/record/taking.d.mts +60 -0
- package/src/lib/record/taking.d.ts +60 -0
- package/src/lib/record/taking.js +58 -0
- package/src/lib/record/taking.js.map +1 -0
- package/src/lib/record/taking.mjs +50 -0
- package/src/lib/record/taking.mjs.map +1 -0
- package/src/lib/record/value-map.js +11 -4
- package/src/lib/record/value-map.js.map +1 -1
- package/src/lib/record/value-map.mjs +11 -4
- package/src/lib/record/value-map.mjs.map +1 -1
- package/src/lib/record/values.d.mts +20 -0
- package/src/lib/record/values.d.ts +20 -0
- package/src/lib/record/values.js.map +1 -1
- package/src/lib/record/values.mjs.map +1 -1
- package/src/lib/record/waits.d.mts +6 -1
- package/src/lib/record/waits.d.ts +6 -1
- package/src/lib/record/waits.js +2 -2
- package/src/lib/record/waits.js.map +1 -1
- package/src/lib/record/waits.mjs +2 -2
- package/src/lib/record/waits.mjs.map +1 -1
- package/src/runtime.d.mts +16 -5
- package/src/runtime.d.ts +16 -5
- package/src/runtime.js +41 -3
- package/src/runtime.js.map +1 -1
- package/src/runtime.mjs +13 -0
- package/src/runtime.mjs.map +1 -1
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ list of records — that your app implements;
|
|
|
28
28
|
The page format is also a JSON Schema, for editors and other languages:
|
|
29
29
|
`@fieldia/core/page.schema.json`.
|
|
30
30
|
|
|
31
|
-
A page says which format it is written in (`"fieldia": "0.
|
|
31
|
+
A page says which format it is written in (`"fieldia": "0.6"`, `FORMAT_VERSION`; a page in 0.1, 0.2, 0.3, 0.4 or 0.5 is read as it is).
|
|
32
32
|
One in an older format is brought up to date as it is read — by `checkPage`,
|
|
33
33
|
`validatePage` and `createForm`, so by the viewer and the designer too — and
|
|
34
34
|
`upgradePage` does it for the pages your app keeps. One newer than this Fieldia
|
|
@@ -78,6 +78,120 @@ and null when it shows its value; the viewer draws it as a dash, said in words
|
|
|
78
78
|
("Fix line 1 of Order lines to see Untaxed amount"). An answer your onchange
|
|
79
79
|
gives meanwhile is kept, and shown once the line reads.
|
|
80
80
|
|
|
81
|
+
## Functions, and your app's own
|
|
82
|
+
|
|
83
|
+
A value worked out (`compute`), a value set by a condition (`setWhen`) and every
|
|
84
|
+
condition call functions by name. Fieldia's own: `round`, `roundMoney`, `min`,
|
|
85
|
+
`max`, `abs`, `sum`, `count`, `average` on numbers; `today`, `days`, `age`,
|
|
86
|
+
`years`, `months`, `addDays`, `addMonths`, `addYears` on dates; `contains`,
|
|
87
|
+
`startsWith`, `endsWith`, `upper`, `lower`, `join`, `len` on words; `if`,
|
|
88
|
+
`empty`, `firstOf` to choose.
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
"age": { "type": "integer", "label": "Age", "compute": "age(birth_date)" },
|
|
92
|
+
"second_due": { "type": "date", "label": "Second payment", "compute": "addMonths(first_due, 1)" },
|
|
93
|
+
"per_meter": { "type": "monetary", "label": "Price per m²", "compute": "roundMoney(price / area, currency_id)", "currencyField": "currency_id" },
|
|
94
|
+
"code": { "type": "char", "label": "Code", "compute": "upper(join('-', city, firstOf(nickname, name)))" }
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- `age(birth, on?)` counts whole years to today on the page's clock (its `timeZone`),
|
|
98
|
+
or to a day given; `years(from, to)` and `months(from, to)` count whole ones,
|
|
99
|
+
negative when the second day comes first.
|
|
100
|
+
- `addMonths(day, n)` ends on a shorter month's last day — 31 January and a month
|
|
101
|
+
is 28 February — and `months` counts the same way, so the two agree. A date and
|
|
102
|
+
time keeps its time. `addYears` moves 29 February to the 28th.
|
|
103
|
+
- `contains`, `startsWith` and `endsWith` read text whatever its case; `contains`
|
|
104
|
+
also finds a value in a list (choices ticked, links).
|
|
105
|
+
- `join(between, values…)` joins the values — lists opened — with the words given
|
|
106
|
+
first, leaving the empty ones out: `join(' ', first_name, last_name)`.
|
|
107
|
+
- `average` takes values or a list, or a field of the lines where a condition holds,
|
|
108
|
+
as `sum` does: `average(lines, 'price', "qty > 0")`.
|
|
109
|
+
- `roundMoney(amount, currency)` rounds to the currency's places — three for a
|
|
110
|
+
Jordanian dinar, none for a yen — the currency a code (`'JOD'`) or a link to one,
|
|
111
|
+
read by its name.
|
|
112
|
+
- `empty(value)` holds for nothing, words of only spaces, an empty list; zero and
|
|
113
|
+
no are values. `firstOf(a, b, …)` is the first that is not empty.
|
|
114
|
+
|
|
115
|
+
Each is read as the page is. **Refused**, by `validatePage`, `checkPage` and so as a
|
|
116
|
+
form is made: a function there is not, or one given too many or too few values.
|
|
117
|
+
**A warning**, never a refusal: a value of a kind a function does not take —
|
|
118
|
+
`age(notes)`: "age takes a date as its first value; "notes" is text". `validatePage`
|
|
119
|
+
passes such a page and says it in `warnings` beside it (`pageWarnings(page)` gives
|
|
120
|
+
them alone; `kindProblems` to your own tools); the designer says it as a fix to make;
|
|
121
|
+
the viewer runs the page as it always did, the call giving nothing — a function given
|
|
122
|
+
a value it cannot use gives nothing, never an error. A page that passed before still
|
|
123
|
+
passes.
|
|
124
|
+
|
|
125
|
+
**Your app's own.** Give the form `functions` — the viewer and the React, Vue and
|
|
126
|
+
Angular components take the same option — and a page calls them by name, beside
|
|
127
|
+
Fieldia's. A plain function will do; say what it takes and gives, and the page's
|
|
128
|
+
checks and the designer read it:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
createForm({
|
|
132
|
+
page,
|
|
133
|
+
functions: {
|
|
134
|
+
vatRate: { takes: [1, 1], values: ['text'], gives: 'number', tip: 'The VAT rate of a country: vatRate(country)', call: (country) => RATES[country] ?? 0 },
|
|
135
|
+
// One that asks your server: a promise.
|
|
136
|
+
districtPrice: { asks: true, call: (district) => api.get(`/districts/${district}/price`).then((r) => r.perMeter) },
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
A number one gives is tidied as Fieldia's own arithmetic tidies one — `price * 1.1`
|
|
142
|
+
of 100 is 110, never 110.00000000000001 — and one that is no number (NaN, Infinity) is
|
|
143
|
+
nothing. A function that asks your server gives a promise. What is worked out from it waits
|
|
144
|
+
while it is asked — empty meanwhile, `form.getState().asked` says so by field
|
|
145
|
+
(`{ total: {} }`), `form.waitsFor('total')` gives `{ field: 'total', asking: true }`,
|
|
146
|
+
and the viewer shows an ellipsis, said in words ("Working out Total…") — then its
|
|
147
|
+
answer is worked into everything after it. It is asked once for the same values,
|
|
148
|
+
for the record shown: another record shown, its answer is laid on none. A save waits
|
|
149
|
+
for every answer; one that failed (`{ failed: 'why' }`) is asked again as the person
|
|
150
|
+
saves, and stops the save under its field when it fails again ("Total could not be
|
|
151
|
+
worked out: …"). A condition calling one does not hold while it is asked. A name a
|
|
152
|
+
page cannot call — Fieldia's own, a word of the language — is refused as the form is
|
|
153
|
+
made. `validatePage(page, { functions })` and `checkPage(page, { functions })` read
|
|
154
|
+
pages that call them.
|
|
155
|
+
|
|
156
|
+
## Options that follow the answers
|
|
157
|
+
|
|
158
|
+
An option of a choice may show only while a condition holds, written as a part's
|
|
159
|
+
`invisible`; and a choice may offer the options picked in an earlier one
|
|
160
|
+
(`optionsFrom: { "field": … }`), its own options after them:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
"features": { "type": "selection", "label": "Features", "multiple": true, "options": [
|
|
164
|
+
{ "value": "garden", "label": "Garden", "invisible": "unit_type != 'villa'" },
|
|
165
|
+
{ "value": "lift", "label": "Lift", "invisible": "unit_type == 'villa'" },
|
|
166
|
+
{ "value": "security", "label": "24-hour security" }
|
|
167
|
+
] },
|
|
168
|
+
"again": { "type": "selection", "label": "Which would you have again?",
|
|
169
|
+
"options": [{ "value": "none", "label": "None of them" }],
|
|
170
|
+
"optionsFrom": { "field": "treatments" } },
|
|
171
|
+
"try_next": { "type": "selection", "label": "Which would you like to try?", "multiple": true,
|
|
172
|
+
"options": [], "optionsFrom": { "field": "treatments", "picked": false } }
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`form.offered(field)` gives the options a choice offers now: its own — or those its
|
|
176
|
+
app's list gave, or those picked where they are carried from (`picked: false`: those
|
|
177
|
+
left unpicked; a "None of these" is never carried) — less those whose condition hides
|
|
178
|
+
them. A chosen option a change no longer offers is taken out of the answer, never
|
|
179
|
+
silently kept: `form.getState().takenOut` says so by path ("Garden is no longer
|
|
180
|
+
offered, so it was taken out"), until that value is written again — the same words
|
|
181
|
+
for a choice its app's list no longer offers, a page's or a line's. One a record held
|
|
182
|
+
as it was opened stays chosen, and the choice says it is no longer offered. The
|
|
183
|
+
page's checks read each option's condition and refuse a choice carried from itself,
|
|
184
|
+
from a field that is no choice, or round a circle.
|
|
185
|
+
|
|
186
|
+
A table line's choice takes conditions on its options too, read on the line's own
|
|
187
|
+
values and the record it is on (`parent.`): a room's "Garden room" only on the
|
|
188
|
+
ground floor, a "Pool house" only for a villa. `form.offered("rooms.k1.use")` gives
|
|
189
|
+
a line's by its cell's path; an option a change of the line, or of the record, hides
|
|
190
|
+
is taken out and said by that path (`takenOut["rooms.k1.use"]`). A line's choice
|
|
191
|
+
carries no options from a question: its own, or one of the app's lists — the same
|
|
192
|
+
`optionsFrom` as a page's own choice (`{ list, dependsOn }`, its `dependsOn` naming
|
|
193
|
+
the line's columns or `parent.` and a field).
|
|
194
|
+
|
|
81
195
|
## A public form's bot check
|
|
82
196
|
|
|
83
197
|
A page of responses anyone may send — a sign-up, a contact form — can check that
|
|
@@ -115,6 +229,46 @@ viewer draws the hidden box and tells the form when it is filled
|
|
|
115
229
|
(`form.botCheck({ honeypot: true })`). A page without `botCheck` sends what it
|
|
116
230
|
always did.
|
|
117
231
|
|
|
232
|
+
## What a page says once it is sent, and answers in its words
|
|
233
|
+
|
|
234
|
+
A page of responses' `endings` say what it shows once its answers are sent: the
|
|
235
|
+
first whose `when` holds on the answers as they were sent — with what the data
|
|
236
|
+
source answered, the id it gave the response and the values it added — else the
|
|
237
|
+
one with no `when`, shown otherwise, which comes last. `{id}` in its words is
|
|
238
|
+
that reference:
|
|
239
|
+
|
|
240
|
+
```json
|
|
241
|
+
"endings": [
|
|
242
|
+
{ "when": "overall <= 2", "title": "We are sorry, {name}", "text": "{branch} will call you today.", "again": false },
|
|
243
|
+
{ "title": "Thank you, {name}", "text": "Your booking is {id}.", "again": "Book another" }
|
|
244
|
+
]
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
async submit({ pageId, values }) {
|
|
249
|
+
const kept = await store(pageId, values);
|
|
250
|
+
return { id: kept.reference, values: { confirmed_at: kept.confirmedAt } }; // what the ending reads
|
|
251
|
+
}
|
|
252
|
+
form.getState().sent; // { id: 'B-1043', values: { …the answers sent, confirmed_at } } until reset()
|
|
253
|
+
form.ending(); // the ending for them, or null
|
|
254
|
+
form.endingWords('Your booking is {id}.'); // "Your booking is B-1043."
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
`{field}` shows an answer in the page's own words too — a field's label and
|
|
258
|
+
help, each choice's words, a step's title and words, a wizard's and a button's
|
|
259
|
+
words — as in a text's and an alert's: `form.words('How was your visit to
|
|
260
|
+
{branch}?')` is "How was your visit to Zamalek?", and the form's messages name
|
|
261
|
+
the field so ("How was your visit to Zamalek? is required"). How a value reads
|
|
262
|
+
there is the viewer's (`valueWords` in `createForm`'s options: a choice by its
|
|
263
|
+
label, money with its currency, a day in the page's order); without one, a
|
|
264
|
+
choice's label, a link's name, Yes or No, anything else as held. After a bar,
|
|
265
|
+
the words said while the answer is empty — `{branch|this branch}` reads "this
|
|
266
|
+
branch" until one is chosen, translated with the page's other words; without
|
|
267
|
+
them an empty answer says nothing. A `{name}` that is no field of the page is
|
|
268
|
+
refused by the page check. `createMemoryDataSource`
|
|
269
|
+
answers each response as a server would with ``submitted: (request, count) =>
|
|
270
|
+
({ id: `B-${1042 + count}` })``.
|
|
271
|
+
|
|
118
272
|
## Files stored as they are picked
|
|
119
273
|
|
|
120
274
|
Give the data source `upload(file, request)` and a file or picture field sends
|
|
@@ -185,6 +339,20 @@ each date and time field that names none, as the form and the viewer read it;
|
|
|
185
339
|
they use. `checkPage` and `validatePage` refuse a zone the browser does not know.
|
|
186
340
|
Without `timeZone`, the browser's own zone, as before.
|
|
187
341
|
|
|
342
|
+
A selection's choices may come from the app's own lists (`optionsFrom: { list,
|
|
343
|
+
dependsOn }`), through the data source's `options({ list, values })`, asked again
|
|
344
|
+
when a field in `dependsOn` changes — by the person, a step or the app: a choice
|
|
345
|
+
the new answer does not offer is then taken out and said (`state.takenOut`, as a
|
|
346
|
+
condition's is, until its value changes again); a record that comes in with one
|
|
347
|
+
keeps it, and its save says why. On a table's line they are that line's:
|
|
348
|
+
asked with the line's values and the record it is on as `parent`
|
|
349
|
+
(`dependsOn` naming the line's columns, or `parent.` and a field), shared by the
|
|
350
|
+
lines whose columns hold the same values, and kept by the cell's path in
|
|
351
|
+
`state.choices` (`form.loadChoices("tool_ids.k1.tool")`), taken out and said the
|
|
352
|
+
same way, by the cell's path, as a column they change with changes on that line. `form.lineChoices(field, key, column,
|
|
353
|
+
values)` asks a line's for a row about to be pasted on it. The memory source's
|
|
354
|
+
`lists` functions take the request after the values.
|
|
355
|
+
|
|
188
356
|
A date and time drawn as booking slots (`"widget": "slots"`) asks the data
|
|
189
357
|
source's `slots(request)` for its free times: the days asked (`from`, `to`, in
|
|
190
358
|
`timeZone`), the schedule its node names (`options.schedule`), the booking's
|
|
@@ -240,13 +408,22 @@ source's work, and `saveRefused({ kind: 'rule', message })` refuses it.
|
|
|
240
408
|
source for a screen of your own, `form.canFindDuplicates()` and
|
|
241
409
|
`form.canMerge()` say whether it can; the viewer says each match under the field
|
|
242
410
|
it matched on, with Open it, "It's someone new" and Merge (`@fieldia/viewer`).
|
|
411
|
+
"It's someone new" is kept on the app's server where the source has both
|
|
412
|
+
`notDuplicates({ model, recordId }) → ids` (asked as a saved record is shown,
|
|
413
|
+
before its matches are said) and `markNotDuplicate({ model, recordId, matchId })`
|
|
414
|
+
(as it is pressed on a saved record, or a new one is saved after it): a pair,
|
|
415
|
+
answered from either side, so another device lets that record be too; without
|
|
416
|
+
both, the person's browser keeps it (the viewer's `preferences`), as before.
|
|
417
|
+
`form.keepsNotDuplicates()`, `form.notDuplicates()` and
|
|
418
|
+
`form.markNotDuplicate(matchId)` reach them.
|
|
243
419
|
`checkPage` and `validatePage` refuse a field the page has not, one named twice,
|
|
244
420
|
and one of a kind records are not compared by (`DUPLICATE_KINDS`). Without
|
|
245
421
|
`duplicates` in the source, nothing is asked. For demos and tests,
|
|
246
422
|
`alikeRecords()` answers from the memory source's records — the same value as
|
|
247
423
|
people write them apart (a phone's spaces, an email's case, a link's record) or,
|
|
248
|
-
in the name field, a name close enough (`nameLikeness`) —
|
|
249
|
-
merges as a backend would
|
|
424
|
+
in the name field, a name close enough (`nameLikeness`) — its `merge`
|
|
425
|
+
merges as a backend would, and its `notDuplicates` and `markNotDuplicate` keep
|
|
426
|
+
what was let be as pairs (`notDuplicatePairs`, an option to start from):
|
|
250
427
|
|
|
251
428
|
```ts
|
|
252
429
|
import { alikeRecords, createMemoryDataSource } from '@fieldia/core';
|
|
@@ -254,6 +431,133 @@ import { alikeRecords, createMemoryDataSource } from '@fieldia/core';
|
|
|
254
431
|
const dataSource = createMemoryDataSource({ records, duplicates: alikeRecords() });
|
|
255
432
|
```
|
|
256
433
|
|
|
434
|
+
## A signature that signs the answers
|
|
435
|
+
|
|
436
|
+
A signature (`"widget": "signature"` on a binary field) may sign answers: `signs`
|
|
437
|
+
names them — `"form"`, every answer shown on the page, or the fields by name —
|
|
438
|
+
and `signerField` the field holding the signer's name (a text, or a link's
|
|
439
|
+
record; left out, the name typed on the pad, else the form's `user`):
|
|
440
|
+
|
|
441
|
+
```json
|
|
442
|
+
{ "type": "field", "id": "f-signature", "field": "signature", "widget": "signature",
|
|
443
|
+
"options": { "signs": ["patient", "treatment", "areas", "risks_read"], "signerField": "patient" } }
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Signed, it keeps beside its picture who, when and a fingerprint of the answers
|
|
447
|
+
(`FileValue.signed`, a `SignatureEvidence`), once what is typed in them is read
|
|
448
|
+
— an answer still to give, or text that does not read, is said, and nothing is
|
|
449
|
+
signed:
|
|
450
|
+
|
|
451
|
+
```json
|
|
452
|
+
{ "name": "signature.png", "type": "image/png", "size": 5120, "data": "iVBOR…",
|
|
453
|
+
"signed": { "format": "fieldia-signature/1", "by": "Mona Adel", "at": "2026-10-19T08:42:00.000Z",
|
|
454
|
+
"fields": ["patient", "treatment", "areas", "risks_read"],
|
|
455
|
+
"sha256": "2e3b5fa2ca4b2012eeb1fe7895af5c05dda2c3f5a76246b78fff3b530dbae16d" } }
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
While a signature with evidence is kept, the form locks the answers it names —
|
|
459
|
+
whatever the page's conditions say (`form.node(id).readonly`,
|
|
460
|
+
`form.fieldReadonly(name)`) — until the person takes it away with Change
|
|
461
|
+
answers, asked first, and signs again. `signedBy(fields, values)` says which
|
|
462
|
+
signature locks which answer; `evidenceOf(value)` reads one. The evidence
|
|
463
|
+
travels in the value: a store that keeps a signature as bytes keeps `signed`
|
|
464
|
+
beside them (a save storing the picture keeps it, as it keeps a typed name's
|
|
465
|
+
`text`). `form.now()`, `form.user()` and `form.shownFields()` are what a
|
|
466
|
+
signature reads — the clock, the person, the answers shown.
|
|
467
|
+
|
|
468
|
+
**The canonical form, `fieldia-signature/1`.** The fingerprint is the SHA-256,
|
|
469
|
+
in small hexadecimal letters, of the UTF-8 bytes of this JSON, written as
|
|
470
|
+
RFC 8785 (JCS) writes JSON — no spaces, each object's keys in the order of their
|
|
471
|
+
UTF-16 code units, numbers as JavaScript writes them:
|
|
472
|
+
|
|
473
|
+
```json
|
|
474
|
+
{"answers":{"areas":["forehead"],"patient":"Mona Adel","risks_read":true,"treatment":"botox"},"at":"2026-10-19T08:42:00.000Z","by":"Mona Adel","field":"signature","format":"fieldia-signature/1","page":"clinic-consent"}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
— the text of the evidence above, whose SHA-256 is its `sha256`.
|
|
478
|
+
|
|
479
|
+
`answers` holds each field the evidence names, as the person saw it as they
|
|
480
|
+
signed: nothing (null, an empty text, no choice) is `null`; yes and no `true`
|
|
481
|
+
and `false`; a number itself; a text, a choice's value, a date and a time their
|
|
482
|
+
text as kept; a date and time kept with its zone, its instant in UTC with
|
|
483
|
+
milliseconds; a link its record's id (several, their ids in order); a reference
|
|
484
|
+
`{ "id", "model" }`; a file `{ "name", "size", "type" }`; a table's lines an
|
|
485
|
+
array of each line's own fields; anything else its JSON by these rules. `by` is
|
|
486
|
+
left out when no one is named; `page` is the page's id and `field` the
|
|
487
|
+
signature's. So an app checks a record it keeps in any language — or with the
|
|
488
|
+
evidence part's own functions (@fieldia/widgets' `evidencePart`, the part a
|
|
489
|
+
page whose signature signs answers loads), in a browser or on a server:
|
|
490
|
+
|
|
491
|
+
```ts
|
|
492
|
+
import { evidencePart } from '@fieldia/widgets';
|
|
493
|
+
|
|
494
|
+
const { answersMatch, signatureText, sha256Hex } = await evidencePart.load();
|
|
495
|
+
const same = await answersMatch(record.signature.signed, page, 'signature', record); // true while no signed answer changed
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
Only a signature the page has sign answers locks them (`signsAnswers(node)`):
|
|
499
|
+
a page whose signature no longer says `signs` treats it as a picture, whatever
|
|
500
|
+
its value carries.
|
|
501
|
+
|
|
502
|
+
`checkPage` and `validatePage` refuse `signs` that is neither `"form"` nor a
|
|
503
|
+
list of the page's fields, a signature signing itself, and a `signerField` that
|
|
504
|
+
holds no name. Two people signing in turn is the app's (a flow's) to order: each
|
|
505
|
+
signature signs its answers.
|
|
506
|
+
|
|
507
|
+
## Opening, closing and limiting a form
|
|
508
|
+
|
|
509
|
+
A page of responses may say when it takes answers, and how many; any choice's
|
|
510
|
+
options may say how many may pick each:
|
|
511
|
+
|
|
512
|
+
```json
|
|
513
|
+
{
|
|
514
|
+
"limits": { "opens": "2026-11-01T09:00", "closes": "2026-11-20", "maxResponses": 200, "onePerPerson": true, "message": "See you in December." },
|
|
515
|
+
"fields": { "session": { "type": "selection", "label": "Session", "options": [{ "value": "am", "label": "Morning", "seats": 30 }] } }
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
`opens` and `closes` are read on the page's clock (`timeZone`): a day and a
|
|
520
|
+
time, or a day alone — opening at its first minute, closing at its end. The
|
|
521
|
+
data source's `limits({ pageId, model, recordId, fields, user })` answers
|
|
522
|
+
`{ responses, mine, perChoice }`: the responses sent so far, this person's, and
|
|
523
|
+
for each choice field asked how many hold each option (the record edited not
|
|
524
|
+
counted). The form asks it as it opens — `state.limits.state` is `asking`
|
|
525
|
+
meanwhile where the page counts responses — after a record loads, after every
|
|
526
|
+
send, as it starts again, and just before its answers go. `state.limits` tells
|
|
527
|
+
whether it takes answers now (`open`, `soon`, `closed`, `full`, `sent`), when it
|
|
528
|
+
opens and closes, the responses it takes still, and each choice's places
|
|
529
|
+
(`seats[field][value]`: `{ seats, taken, left }`); `takingWords(page, limits,
|
|
530
|
+
messages)` says why it takes none. A full option the person picks is refused,
|
|
531
|
+
said under its field ("Afternoon workshop is full"); one that filled up while
|
|
532
|
+
held, or a page closed since it was opened, stops the send. The app's own
|
|
533
|
+
`setValues` is never refused: the check says it. Without `limits` in the
|
|
534
|
+
source, the page keeps to its dates alone. The memory source counts its own
|
|
535
|
+
responses over its `limits` option's starting counts, and
|
|
536
|
+
`answeredElsewhere(pageId, values)` takes one as someone else would.
|
|
537
|
+
|
|
538
|
+
## Save and resume on another device
|
|
539
|
+
|
|
540
|
+
A page with `continueLater` keeps its answers on the app's server as they are
|
|
541
|
+
typed — and as a wizard's step is entered — through the data source's
|
|
542
|
+
`saveDraft({ pageId, model, recordId, key, draft, later })`, which answers with
|
|
543
|
+
the draft's key (and, on Continue later, the `link` to carry on from and words
|
|
544
|
+
of its own); `loadDraft({ pageId, key })` gives the draft back. A draft keeps
|
|
545
|
+
the answers, the wizard's step and the steps passed over, text typed that does
|
|
546
|
+
not read (`unread`, as typed, with why) and the fields answered. The form's
|
|
547
|
+
`resume` (the key from the link) opens it — at its step, or the nearest before
|
|
548
|
+
it that still applies, its unread text said again — and a key that names none
|
|
549
|
+
leaves `state.resume.missing`. `form.continueLater()` reads what is typed and
|
|
550
|
+
keeps the draft (`later: true`); `form.canContinueLater()` says whether the
|
|
551
|
+
page and its source can; the `draft` event tells each draft kept. A send
|
|
552
|
+
carries the draft's key (`SubmitRequest.draft`, `SaveRequest.draft`). Without
|
|
553
|
+
the two members, drafts stay in the browser's store (`drafts.store`), as before
|
|
554
|
+
— with their wizard step now; with them, that store keeps only the key.
|
|
555
|
+
|
|
556
|
+
```ts
|
|
557
|
+
const form = createForm({ page, dataSource, resume: new URLSearchParams(location.search).get('resume') ?? undefined });
|
|
558
|
+
form.on('draft', ({ key, later, saved }) => later && console.log('Continue from', saved.link));
|
|
559
|
+
```
|
|
560
|
+
|
|
257
561
|
## What a page's records are called
|
|
258
562
|
|
|
259
563
|
A page may say what its records are, so Fieldia's own sentences name them as
|
package/package.json
CHANGED