@fieldia/core 0.30.1 → 0.31.1
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 +373 -1
- package/package.json +1 -1
- package/schema/page.schema.json +758 -1
- package/src/index.d.mts +13 -2
- package/src/index.d.ts +13 -2
- package/src/index.js +45 -2
- package/src/index.js.map +1 -1
- package/src/index.mjs +18 -2
- package/src/index.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/actions.js +3 -0
- package/src/lib/format/actions.js.map +1 -1
- package/src/lib/format/actions.mjs +3 -0
- package/src/lib/format/actions.mjs.map +1 -1
- package/src/lib/format/address-search.js +3 -0
- package/src/lib/format/address-search.js.map +1 -1
- package/src/lib/format/address-search.mjs +3 -0
- package/src/lib/format/address-search.mjs.map +1 -1
- package/src/lib/format/duplicates.js +3 -0
- package/src/lib/format/duplicates.js.map +1 -1
- package/src/lib/format/duplicates.mjs +3 -0
- package/src/lib/format/duplicates.mjs.map +1 -1
- package/src/lib/format/endings.js +3 -0
- package/src/lib/format/endings.js.map +1 -1
- package/src/lib/format/endings.mjs +3 -0
- package/src/lib/format/endings.mjs.map +1 -1
- package/src/lib/format/field.d.mts +669 -0
- package/src/lib/format/field.d.ts +669 -0
- package/src/lib/format/field.js +42 -4
- package/src/lib/format/field.js.map +1 -1
- package/src/lib/format/field.mjs +42 -4
- package/src/lib/format/field.mjs.map +1 -1
- package/src/lib/format/flow-checks.d.mts +14 -0
- package/src/lib/format/flow-checks.d.ts +14 -0
- package/src/lib/format/flow-checks.js +61 -0
- package/src/lib/format/flow-checks.js.map +1 -0
- package/src/lib/format/flow-checks.mjs +58 -0
- package/src/lib/format/flow-checks.mjs.map +1 -0
- package/src/lib/format/flow.d.mts +43 -0
- package/src/lib/format/flow.d.ts +43 -0
- package/src/lib/format/flow.js +22 -0
- package/src/lib/format/flow.js.map +1 -0
- package/src/lib/format/flow.mjs +19 -0
- package/src/lib/format/flow.mjs.map +1 -0
- package/src/lib/format/from-schema.d.mts +70 -0
- package/src/lib/format/from-schema.d.ts +70 -0
- package/src/lib/format/from-schema.js +599 -0
- package/src/lib/format/from-schema.js.map +1 -0
- package/src/lib/format/from-schema.mjs +591 -0
- package/src/lib/format/from-schema.mjs.map +1 -0
- package/src/lib/format/jitless.d.mts +1 -0
- package/src/lib/format/jitless.d.ts +1 -0
- package/src/lib/format/jitless.js +27 -0
- package/src/lib/format/jitless.js.map +1 -0
- package/src/lib/format/jitless.mjs +24 -0
- package/src/lib/format/jitless.mjs.map +1 -0
- package/src/lib/format/json-schema.js +3 -0
- package/src/lib/format/json-schema.js.map +1 -1
- package/src/lib/format/json-schema.mjs +3 -0
- package/src/lib/format/json-schema.mjs.map +1 -1
- package/src/lib/format/json.js +3 -0
- package/src/lib/format/json.js.map +1 -1
- package/src/lib/format/json.mjs +3 -0
- package/src/lib/format/json.mjs.map +1 -1
- package/src/lib/format/languages-checks.d.mts +2 -1
- package/src/lib/format/languages-checks.d.ts +2 -1
- package/src/lib/format/languages-checks.js +8 -2
- package/src/lib/format/languages-checks.js.map +1 -1
- package/src/lib/format/languages-checks.mjs +8 -2
- package/src/lib/format/languages-checks.mjs.map +1 -1
- package/src/lib/format/later-checks.d.mts +3 -1
- package/src/lib/format/later-checks.d.ts +3 -1
- package/src/lib/format/later-checks.js +3 -1
- package/src/lib/format/later-checks.js.map +1 -1
- package/src/lib/format/later-checks.mjs +3 -1
- package/src/lib/format/later-checks.mjs.map +1 -1
- package/src/lib/format/layout.d.mts +13192 -7550
- package/src/lib/format/layout.d.ts +13192 -7550
- package/src/lib/format/layout.js +61 -3
- package/src/lib/format/layout.js.map +1 -1
- package/src/lib/format/layout.mjs +59 -1
- package/src/lib/format/layout.mjs.map +1 -1
- package/src/lib/format/layout.types.d.mts +3 -1
- package/src/lib/format/layout.types.d.ts +3 -1
- package/src/lib/format/limits.js +3 -0
- package/src/lib/format/limits.js.map +1 -1
- package/src/lib/format/limits.mjs +3 -0
- package/src/lib/format/limits.mjs.map +1 -1
- package/src/lib/format/list-checks.d.mts +1 -1
- package/src/lib/format/list-checks.d.ts +1 -1
- package/src/lib/format/list-checks.js +67 -22
- package/src/lib/format/list-checks.js.map +1 -1
- package/src/lib/format/list-checks.mjs +67 -22
- package/src/lib/format/list-checks.mjs.map +1 -1
- package/src/lib/format/list-rows-checks.d.mts +11 -0
- package/src/lib/format/list-rows-checks.d.ts +11 -0
- package/src/lib/format/list-rows-checks.js +62 -0
- package/src/lib/format/list-rows-checks.js.map +1 -0
- package/src/lib/format/list-rows-checks.mjs +59 -0
- package/src/lib/format/list-rows-checks.mjs.map +1 -0
- package/src/lib/format/media-options.d.mts +84 -0
- package/src/lib/format/media-options.d.ts +84 -0
- package/src/lib/format/media-options.js +86 -0
- package/src/lib/format/media-options.js.map +1 -0
- package/src/lib/format/media-options.mjs +80 -0
- package/src/lib/format/media-options.mjs.map +1 -0
- package/src/lib/format/outbox-checks.d.mts +11 -0
- package/src/lib/format/outbox-checks.d.ts +11 -0
- package/src/lib/format/outbox-checks.js +12 -0
- package/src/lib/format/outbox-checks.js.map +1 -0
- package/src/lib/format/outbox-checks.mjs +9 -0
- package/src/lib/format/outbox-checks.mjs.map +1 -0
- package/src/lib/format/page.d.mts +3057 -966
- package/src/lib/format/page.d.ts +3057 -966
- package/src/lib/format/page.js +15 -0
- package/src/lib/format/page.js.map +1 -1
- package/src/lib/format/page.mjs +15 -0
- package/src/lib/format/page.mjs.map +1 -1
- package/src/lib/format/part-checks.d.mts +3 -0
- package/src/lib/format/part-checks.d.ts +3 -0
- package/src/lib/format/payment-checks.d.mts +13 -0
- package/src/lib/format/payment-checks.d.ts +13 -0
- package/src/lib/format/payment-checks.js +30 -0
- package/src/lib/format/payment-checks.js.map +1 -0
- package/src/lib/format/payment-checks.mjs +27 -0
- package/src/lib/format/payment-checks.mjs.map +1 -0
- package/src/lib/format/payment.d.mts +37 -0
- package/src/lib/format/payment.d.ts +37 -0
- package/src/lib/format/payment.js +15 -0
- package/src/lib/format/payment.js.map +1 -0
- package/src/lib/format/payment.mjs +12 -0
- package/src/lib/format/payment.mjs.map +1 -0
- package/src/lib/format/phone.js +3 -0
- package/src/lib/format/phone.js.map +1 -1
- package/src/lib/format/phone.mjs +3 -0
- package/src/lib/format/phone.mjs.map +1 -1
- package/src/lib/format/picker-options.d.mts +12 -0
- package/src/lib/format/picker-options.d.ts +12 -0
- package/src/lib/format/picker-options.js +21 -3
- package/src/lib/format/picker-options.js.map +1 -1
- package/src/lib/format/picker-options.mjs +20 -2
- package/src/lib/format/picker-options.mjs.map +1 -1
- package/src/lib/format/prefill-checks.d.mts +13 -0
- package/src/lib/format/prefill-checks.d.ts +13 -0
- package/src/lib/format/prefill-checks.js +35 -0
- package/src/lib/format/prefill-checks.js.map +1 -0
- package/src/lib/format/prefill-checks.mjs +32 -0
- package/src/lib/format/prefill-checks.mjs.map +1 -0
- package/src/lib/format/prefill.d.mts +33 -0
- package/src/lib/format/prefill.d.ts +33 -0
- package/src/lib/format/prefill.js +20 -0
- package/src/lib/format/prefill.js.map +1 -0
- package/src/lib/format/prefill.mjs +17 -0
- package/src/lib/format/prefill.mjs.map +1 -0
- package/src/lib/format/quiz.d.mts +58 -0
- package/src/lib/format/quiz.d.ts +58 -0
- package/src/lib/format/quiz.js +24 -0
- package/src/lib/format/quiz.js.map +1 -0
- package/src/lib/format/quiz.mjs +21 -0
- package/src/lib/format/quiz.mjs.map +1 -0
- package/src/lib/format/records.js +3 -0
- package/src/lib/format/records.js.map +1 -1
- package/src/lib/format/records.mjs +3 -0
- package/src/lib/format/records.mjs.map +1 -1
- package/src/lib/format/references.d.mts +1 -1
- package/src/lib/format/references.d.ts +1 -1
- package/src/lib/format/references.js +56 -7
- package/src/lib/format/references.js.map +1 -1
- package/src/lib/format/references.mjs +57 -8
- package/src/lib/format/references.mjs.map +1 -1
- package/src/lib/format/schedule-checks.d.mts +8 -0
- package/src/lib/format/schedule-checks.d.ts +8 -0
- package/src/lib/format/schedule-checks.js +16 -0
- package/src/lib/format/schedule-checks.js.map +1 -0
- package/src/lib/format/schedule-checks.mjs +13 -0
- package/src/lib/format/schedule-checks.mjs.map +1 -0
- package/src/lib/format/signature-checks.d.mts +8 -0
- package/src/lib/format/signature-checks.d.ts +8 -0
- package/src/lib/format/signature-checks.js +21 -0
- package/src/lib/format/signature-checks.js.map +1 -1
- package/src/lib/format/signature-checks.mjs +20 -0
- package/src/lib/format/signature-checks.mjs.map +1 -1
- package/src/lib/format/signature-options.js +3 -0
- package/src/lib/format/signature-options.js.map +1 -1
- package/src/lib/format/signature-options.mjs +3 -0
- package/src/lib/format/signature-options.mjs.map +1 -1
- package/src/lib/format/slots-options.js +3 -0
- package/src/lib/format/slots-options.js.map +1 -1
- package/src/lib/format/slots-options.mjs +3 -0
- package/src/lib/format/slots-options.mjs.map +1 -1
- package/src/lib/format/templates-checks.d.mts +6 -0
- package/src/lib/format/templates-checks.d.ts +6 -0
- package/src/lib/format/templates-checks.js +16 -0
- package/src/lib/format/templates-checks.js.map +1 -0
- package/src/lib/format/templates-checks.mjs +13 -0
- package/src/lib/format/templates-checks.mjs.map +1 -0
- package/src/lib/format/translate.d.mts +2 -0
- package/src/lib/format/translate.d.ts +2 -0
- package/src/lib/format/translate.js +6 -2
- package/src/lib/format/translate.js.map +1 -1
- package/src/lib/format/translate.mjs +4 -2
- package/src/lib/format/translate.mjs.map +1 -1
- package/src/lib/format/typed-options.d.mts +43 -0
- package/src/lib/format/typed-options.d.ts +43 -0
- package/src/lib/format/typed-options.js +50 -0
- package/src/lib/format/typed-options.js.map +1 -0
- package/src/lib/format/typed-options.mjs +46 -0
- package/src/lib/format/typed-options.mjs.map +1 -0
- package/src/lib/format/upgrade.d.mts +5 -1
- package/src/lib/format/upgrade.d.ts +5 -1
- package/src/lib/format/upgrade.js +5 -1
- package/src/lib/format/upgrade.js.map +1 -1
- package/src/lib/format/upgrade.mjs +5 -1
- package/src/lib/format/upgrade.mjs.map +1 -1
- package/src/lib/format/validate.js +3 -0
- package/src/lib/format/validate.js.map +1 -1
- package/src/lib/format/validate.mjs +3 -0
- package/src/lib/format/validate.mjs.map +1 -1
- package/src/lib/format/verify-checks.d.mts +10 -0
- package/src/lib/format/verify-checks.d.ts +10 -0
- package/src/lib/format/verify-checks.js +44 -0
- package/src/lib/format/verify-checks.js.map +1 -0
- package/src/lib/format/verify-checks.mjs +41 -0
- package/src/lib/format/verify-checks.mjs.map +1 -0
- 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/week.d.mts +18 -0
- package/src/lib/format/week.d.ts +18 -0
- package/src/lib/format/week.js +58 -0
- package/src/lib/format/week.js.map +1 -0
- package/src/lib/format/week.mjs +55 -0
- package/src/lib/format/week.mjs.map +1 -0
- package/src/lib/record/approvals-part.d.mts +1 -1
- package/src/lib/record/approvals-part.d.ts +1 -1
- package/src/lib/record/approvals-part.js +2 -2
- package/src/lib/record/approvals-part.js.map +1 -1
- package/src/lib/record/approvals-part.mjs +1 -1
- package/src/lib/record/approvals-part.mjs.map +1 -1
- package/src/lib/record/data-source.d.mts +348 -5
- package/src/lib/record/data-source.d.ts +348 -5
- package/src/lib/record/data-source.js.map +1 -1
- package/src/lib/record/data-source.mjs.map +1 -1
- package/src/lib/record/day-words.d.mts +22 -1
- package/src/lib/record/day-words.d.ts +22 -1
- package/src/lib/record/day-words.js +29 -2
- package/src/lib/record/day-words.js.map +1 -1
- package/src/lib/record/day-words.mjs +24 -2
- package/src/lib/record/day-words.mjs.map +1 -1
- package/src/lib/record/drafts.d.mts +9 -0
- package/src/lib/record/drafts.d.ts +9 -0
- package/src/lib/record/drafts.js +20 -0
- package/src/lib/record/drafts.js.map +1 -1
- package/src/lib/record/drafts.mjs +19 -0
- package/src/lib/record/drafts.mjs.map +1 -1
- package/src/lib/record/files-part.d.mts +1 -0
- package/src/lib/record/files-part.d.ts +1 -0
- package/src/lib/record/files-part.js +7 -1
- package/src/lib/record/files-part.js.map +1 -1
- package/src/lib/record/files-part.mjs +5 -1
- package/src/lib/record/files-part.mjs.map +1 -1
- package/src/lib/record/form-approvals.d.mts +12 -17
- package/src/lib/record/form-approvals.d.ts +12 -17
- package/src/lib/record/form-approvals.js +19 -9
- package/src/lib/record/form-approvals.js.map +1 -1
- package/src/lib/record/form-approvals.mjs +18 -8
- package/src/lib/record/form-approvals.mjs.map +1 -1
- package/src/lib/record/form-limits.d.mts +4 -28
- package/src/lib/record/form-limits.d.ts +4 -28
- package/src/lib/record/form-limits.js +8 -5
- package/src/lib/record/form-limits.js.map +1 -1
- package/src/lib/record/form-limits.mjs +8 -5
- package/src/lib/record/form-limits.mjs.map +1 -1
- package/src/lib/record/form.d.mts +118 -3
- package/src/lib/record/form.d.ts +118 -3
- package/src/lib/record/form.js +283 -75
- package/src/lib/record/form.js.map +1 -1
- package/src/lib/record/form.mjs +285 -77
- package/src/lib/record/form.mjs.map +1 -1
- package/src/lib/record/history-steps.d.mts +38 -0
- package/src/lib/record/history-steps.d.ts +38 -0
- package/src/lib/record/history-steps.js +63 -0
- package/src/lib/record/history-steps.js.map +1 -0
- package/src/lib/record/history-steps.mjs +58 -0
- package/src/lib/record/history-steps.mjs.map +1 -0
- package/src/lib/record/history.d.mts +16 -0
- package/src/lib/record/history.d.ts +16 -0
- package/src/lib/record/history.js +3 -0
- package/src/lib/record/history.js.map +1 -0
- package/src/lib/record/history.mjs +2 -0
- package/src/lib/record/history.mjs.map +1 -0
- package/src/lib/record/input-rules.d.mts +2 -0
- package/src/lib/record/input-rules.d.ts +2 -0
- package/src/lib/record/input-rules.js +4 -1
- package/src/lib/record/input-rules.js.map +1 -1
- package/src/lib/record/input-rules.mjs +2 -0
- package/src/lib/record/input-rules.mjs.map +1 -1
- package/src/lib/record/joins.d.mts +143 -0
- package/src/lib/record/joins.d.ts +143 -0
- package/src/lib/record/joins.js +28 -0
- package/src/lib/record/joins.js.map +1 -0
- package/src/lib/record/joins.mjs +24 -0
- package/src/lib/record/joins.mjs.map +1 -0
- package/src/lib/record/limits.js +7 -4
- package/src/lib/record/limits.js.map +1 -1
- package/src/lib/record/limits.mjs +8 -5
- package/src/lib/record/limits.mjs.map +1 -1
- package/src/lib/record/link-values.d.mts +31 -0
- package/src/lib/record/link-values.d.ts +31 -0
- package/src/lib/record/link-values.js +132 -0
- package/src/lib/record/link-values.js.map +1 -0
- package/src/lib/record/link-values.mjs +125 -0
- package/src/lib/record/link-values.mjs.map +1 -0
- package/src/lib/record/live-later.d.mts +15 -0
- package/src/lib/record/live-later.d.ts +15 -0
- package/src/lib/record/live-later.js +10 -0
- package/src/lib/record/live-later.js.map +1 -0
- package/src/lib/record/live-later.mjs +7 -0
- package/src/lib/record/live-later.mjs.map +1 -0
- package/src/lib/record/live-part.d.mts +6 -0
- package/src/lib/record/live-part.d.ts +6 -0
- package/src/lib/record/live-part.js +12 -0
- package/src/lib/record/live-part.js.map +1 -0
- package/src/lib/record/live-part.mjs +7 -0
- package/src/lib/record/live-part.mjs.map +1 -0
- package/src/lib/record/live.d.mts +8 -0
- package/src/lib/record/live.d.ts +8 -0
- package/src/lib/record/live.js +133 -0
- package/src/lib/record/live.js.map +1 -0
- package/src/lib/record/live.mjs +129 -0
- package/src/lib/record/live.mjs.map +1 -0
- package/src/lib/record/locales/all.d.mts +5 -5
- package/src/lib/record/locales/all.d.ts +5 -5
- package/src/lib/record/locales/all.js +3 -3
- package/src/lib/record/locales/all.js.map +1 -1
- package/src/lib/record/locales/all.mjs +3 -3
- package/src/lib/record/locales/all.mjs.map +1 -1
- package/src/lib/record/locales/ar.js +8 -0
- package/src/lib/record/locales/ar.js.map +1 -1
- package/src/lib/record/locales/ar.mjs +8 -0
- package/src/lib/record/locales/ar.mjs.map +1 -1
- package/src/lib/record/locales/de.js +8 -0
- package/src/lib/record/locales/de.js.map +1 -1
- package/src/lib/record/locales/de.mjs +8 -0
- package/src/lib/record/locales/de.mjs.map +1 -1
- package/src/lib/record/locales/es.d.mts +8 -0
- package/src/lib/record/locales/es.d.ts +8 -0
- package/src/lib/record/locales/es.js +111 -0
- package/src/lib/record/locales/es.js.map +1 -0
- package/src/lib/record/locales/es.mjs +108 -0
- package/src/lib/record/locales/es.mjs.map +1 -0
- package/src/lib/record/locales/fa.d.mts +5 -0
- package/src/lib/record/locales/fa.d.ts +5 -0
- package/src/lib/record/locales/fa.js +117 -0
- package/src/lib/record/locales/fa.js.map +1 -0
- package/src/lib/record/locales/fa.mjs +114 -0
- package/src/lib/record/locales/fa.mjs.map +1 -0
- package/src/lib/record/locales/fr.js +8 -0
- package/src/lib/record/locales/fr.js.map +1 -1
- package/src/lib/record/locales/fr.mjs +8 -0
- package/src/lib/record/locales/fr.mjs.map +1 -1
- package/src/lib/record/locales/later.d.mts +8 -0
- package/src/lib/record/locales/later.d.ts +8 -0
- package/src/lib/record/locales/later.js +16 -0
- package/src/lib/record/locales/later.js.map +1 -0
- package/src/lib/record/locales/later.mjs +13 -0
- package/src/lib/record/locales/later.mjs.map +1 -0
- package/src/lib/record/locales/tr.d.mts +6 -0
- package/src/lib/record/locales/tr.d.ts +6 -0
- package/src/lib/record/locales/tr.js +118 -0
- package/src/lib/record/locales/tr.js.map +1 -0
- package/src/lib/record/locales/tr.mjs +115 -0
- package/src/lib/record/locales/tr.mjs.map +1 -0
- package/src/lib/record/locales/ur.d.mts +9 -0
- package/src/lib/record/locales/ur.d.ts +9 -0
- package/src/lib/record/locales/ur.js +118 -0
- package/src/lib/record/locales/ur.js.map +1 -0
- package/src/lib/record/locales/ur.mjs +115 -0
- package/src/lib/record/locales/ur.mjs.map +1 -0
- package/src/lib/record/memory-approvals.d.mts +4 -1
- package/src/lib/record/memory-approvals.d.ts +4 -1
- package/src/lib/record/memory-approvals.js +29 -0
- package/src/lib/record/memory-approvals.js.map +1 -1
- package/src/lib/record/memory-approvals.mjs +29 -0
- package/src/lib/record/memory-approvals.mjs.map +1 -1
- package/src/lib/record/memory-codes.d.mts +37 -0
- package/src/lib/record/memory-codes.d.ts +37 -0
- package/src/lib/record/memory-codes.js +50 -0
- package/src/lib/record/memory-codes.js.map +1 -0
- package/src/lib/record/memory-codes.mjs +47 -0
- package/src/lib/record/memory-codes.mjs.map +1 -0
- package/src/lib/record/memory-data-source.d.mts +56 -2
- package/src/lib/record/memory-data-source.d.ts +56 -2
- package/src/lib/record/memory-data-source.js +80 -2
- package/src/lib/record/memory-data-source.js.map +1 -1
- package/src/lib/record/memory-data-source.mjs +80 -2
- package/src/lib/record/memory-data-source.mjs.map +1 -1
- package/src/lib/record/memory-kept.d.mts +3 -1
- package/src/lib/record/memory-kept.d.ts +3 -1
- package/src/lib/record/memory-kept.js +7 -2
- package/src/lib/record/memory-kept.js.map +1 -1
- package/src/lib/record/memory-kept.mjs +7 -2
- package/src/lib/record/memory-kept.mjs.map +1 -1
- package/src/lib/record/memory-later.d.mts +3 -1
- package/src/lib/record/memory-later.d.ts +3 -1
- package/src/lib/record/memory-later.js +9 -5
- package/src/lib/record/memory-later.js.map +1 -1
- package/src/lib/record/memory-later.mjs +8 -4
- package/src/lib/record/memory-later.mjs.map +1 -1
- package/src/lib/record/memory-live.d.mts +8 -0
- package/src/lib/record/memory-live.d.ts +8 -0
- package/src/lib/record/memory-live.js +70 -0
- package/src/lib/record/memory-live.js.map +1 -0
- package/src/lib/record/memory-live.mjs +67 -0
- package/src/lib/record/memory-live.mjs.map +1 -0
- package/src/lib/record/memory-views.d.mts +11 -0
- package/src/lib/record/memory-views.d.ts +11 -0
- package/src/lib/record/memory-views.js +68 -0
- package/src/lib/record/memory-views.js.map +1 -0
- package/src/lib/record/memory-views.mjs +65 -0
- package/src/lib/record/memory-views.mjs.map +1 -0
- package/src/lib/record/messages.d.mts +21 -3
- package/src/lib/record/messages.d.ts +21 -3
- package/src/lib/record/messages.js +15 -2
- package/src/lib/record/messages.js.map +1 -1
- package/src/lib/record/messages.mjs +15 -2
- package/src/lib/record/messages.mjs.map +1 -1
- package/src/lib/record/order.d.mts +21 -0
- package/src/lib/record/order.d.ts +21 -0
- package/src/lib/record/order.js +129 -0
- package/src/lib/record/order.js.map +1 -0
- package/src/lib/record/order.mjs +122 -0
- package/src/lib/record/order.mjs.map +1 -0
- package/src/lib/record/outbox-form.d.mts +79 -0
- package/src/lib/record/outbox-form.d.ts +79 -0
- package/src/lib/record/outbox-form.js +291 -0
- package/src/lib/record/outbox-form.js.map +1 -0
- package/src/lib/record/outbox-form.mjs +286 -0
- package/src/lib/record/outbox-form.mjs.map +1 -0
- package/src/lib/record/outbox-part.d.mts +6 -0
- package/src/lib/record/outbox-part.d.ts +6 -0
- package/src/lib/record/outbox-part.js +13 -0
- package/src/lib/record/outbox-part.js.map +1 -0
- package/src/lib/record/outbox-part.mjs +7 -0
- package/src/lib/record/outbox-part.mjs.map +1 -0
- package/src/lib/record/pay-part.d.mts +6 -0
- package/src/lib/record/pay-part.d.ts +6 -0
- package/src/lib/record/pay-part.js +11 -0
- package/src/lib/record/pay-part.js.map +1 -0
- package/src/lib/record/pay-part.mjs +7 -0
- package/src/lib/record/pay-part.mjs.map +1 -0
- package/src/lib/record/payment-form.d.mts +25 -0
- package/src/lib/record/payment-form.d.ts +25 -0
- package/src/lib/record/payment-form.js +98 -0
- package/src/lib/record/payment-form.js.map +1 -0
- package/src/lib/record/payment-form.mjs +95 -0
- package/src/lib/record/payment-form.mjs.map +1 -0
- package/src/lib/record/prefill-part.d.mts +7 -0
- package/src/lib/record/prefill-part.d.ts +7 -0
- package/src/lib/record/prefill-part.js +15 -0
- package/src/lib/record/prefill-part.js.map +1 -0
- package/src/lib/record/prefill-part.mjs +8 -0
- package/src/lib/record/prefill-part.mjs.map +1 -0
- package/src/lib/record/press-undo.d.mts +42 -0
- package/src/lib/record/press-undo.d.ts +42 -0
- package/src/lib/record/press-undo.js +115 -0
- package/src/lib/record/press-undo.js.map +1 -0
- package/src/lib/record/press-undo.mjs +110 -0
- package/src/lib/record/press-undo.mjs.map +1 -0
- package/src/lib/record/quiz.d.mts +36 -0
- package/src/lib/record/quiz.d.ts +36 -0
- package/src/lib/record/quiz.js +90 -0
- package/src/lib/record/quiz.js.map +1 -0
- package/src/lib/record/quiz.mjs +87 -0
- package/src/lib/record/quiz.mjs.map +1 -0
- package/src/lib/record/record-words.js +1 -1
- package/src/lib/record/record-words.js.map +1 -1
- package/src/lib/record/record-words.mjs +1 -1
- package/src/lib/record/record-words.mjs.map +1 -1
- package/src/lib/record/run.d.mts +54 -2
- package/src/lib/record/run.d.ts +54 -2
- package/src/lib/record/run.js +30 -6
- package/src/lib/record/run.js.map +1 -1
- package/src/lib/record/run.mjs +28 -5
- package/src/lib/record/run.mjs.map +1 -1
- package/src/lib/record/survey-kit.d.mts +37 -0
- package/src/lib/record/survey-kit.d.ts +37 -0
- package/src/lib/record/survey-kit.js +110 -0
- package/src/lib/record/survey-kit.js.map +1 -0
- package/src/lib/record/survey-kit.mjs +107 -0
- package/src/lib/record/survey-kit.mjs.map +1 -0
- package/src/lib/record/survey.d.mts +16 -0
- package/src/lib/record/survey.d.ts +16 -0
- package/src/lib/record/survey.js +17 -0
- package/src/lib/record/survey.js.map +1 -0
- package/src/lib/record/survey.mjs +13 -0
- package/src/lib/record/survey.mjs.map +1 -0
- package/src/lib/record/surveyflow-part.d.mts +9 -0
- package/src/lib/record/surveyflow-part.d.ts +9 -0
- package/src/lib/record/surveyflow-part.js +20 -0
- package/src/lib/record/surveyflow-part.js.map +1 -0
- package/src/lib/record/surveyflow-part.mjs +10 -0
- package/src/lib/record/surveyflow-part.mjs.map +1 -0
- package/src/lib/record/timing.d.mts +76 -0
- package/src/lib/record/timing.d.ts +76 -0
- package/src/lib/record/timing.js +112 -0
- package/src/lib/record/timing.js.map +1 -0
- package/src/lib/record/timing.mjs +104 -0
- package/src/lib/record/timing.mjs.map +1 -0
- package/src/lib/record/undo-later.d.mts +9 -0
- package/src/lib/record/undo-later.d.ts +9 -0
- package/src/lib/record/undo-later.js +10 -0
- package/src/lib/record/undo-later.js.map +1 -0
- package/src/lib/record/undo-later.mjs +7 -0
- package/src/lib/record/undo-later.mjs.map +1 -0
- package/src/lib/record/undo-part.d.mts +8 -0
- package/src/lib/record/undo-part.d.ts +8 -0
- package/src/lib/record/undo-part.js +17 -0
- package/src/lib/record/undo-part.js.map +1 -0
- package/src/lib/record/undo-part.mjs +9 -0
- package/src/lib/record/undo-part.mjs.map +1 -0
- package/src/lib/record/upload-to-later.d.mts +10 -0
- package/src/lib/record/upload-to-later.d.ts +10 -0
- package/src/lib/record/upload-to-later.js +15 -0
- package/src/lib/record/upload-to-later.js.map +1 -0
- package/src/lib/record/upload-to-later.mjs +12 -0
- package/src/lib/record/upload-to-later.mjs.map +1 -0
- package/src/lib/record/uploads.d.mts +5 -0
- package/src/lib/record/uploads.d.ts +5 -0
- package/src/lib/record/uploads.js.map +1 -1
- package/src/lib/record/uploads.mjs.map +1 -1
- package/src/lib/record/values.d.mts +6 -0
- package/src/lib/record/values.d.ts +6 -0
- package/src/lib/record/values.js.map +1 -1
- package/src/lib/record/values.mjs.map +1 -1
- package/src/lib/record/verify-form.d.mts +24 -0
- package/src/lib/record/verify-form.d.ts +24 -0
- package/src/lib/record/verify-form.js +65 -0
- package/src/lib/record/verify-form.js.map +1 -0
- package/src/lib/record/verify-form.mjs +62 -0
- package/src/lib/record/verify-form.mjs.map +1 -0
- package/src/lib/record/verify-part.d.mts +6 -0
- package/src/lib/record/verify-part.d.ts +6 -0
- package/src/lib/record/verify-part.js +11 -0
- package/src/lib/record/verify-part.js.map +1 -0
- package/src/lib/record/verify-part.mjs +7 -0
- package/src/lib/record/verify-part.mjs.map +1 -0
- package/src/lib/record/words-chunks.d.mts +37 -0
- package/src/lib/record/words-chunks.d.ts +37 -0
- package/src/lib/record/words-chunks.js +94 -0
- package/src/lib/record/words-chunks.js.map +1 -0
- package/src/lib/record/words-chunks.mjs +86 -0
- package/src/lib/record/words-chunks.mjs.map +1 -0
- package/src/runtime.d.mts +28 -6
- package/src/runtime.d.ts +28 -6
- package/src/runtime.js +35 -6
- package/src/runtime.js.map +1 -1
- package/src/runtime.mjs +15 -3
- package/src/runtime.mjs.map +1 -1
package/src/lib/record/form.mjs
CHANGED
|
@@ -22,13 +22,14 @@ import { askKey, cellOf, listedColumns, notOffered } from './line-choices.mjs';
|
|
|
22
22
|
import { loadedRecordOf, saveProblemOf, } from './data-source.mjs';
|
|
23
23
|
import { formLimitsPart, hasLimits } from './taking.mjs';
|
|
24
24
|
import { formLanguagesPart, hasLanguages } from './languages.mjs';
|
|
25
|
-
import {
|
|
26
|
-
import { draftRead, resumeStep } from './drafts.mjs';
|
|
25
|
+
import { draftRead, resumeStep, timedStore } from './drafts.mjs';
|
|
27
26
|
import { waitingOn, workedOutFrom } from './waits.mjs';
|
|
28
27
|
import { createUploads, fileBytesIn, uploadGoing, withoutFileBytes } from './uploads.mjs';
|
|
29
28
|
import { hostScheduler } from './scheduler.mjs';
|
|
30
29
|
import { signedBy } from './signed.mjs';
|
|
31
|
-
import {
|
|
30
|
+
import { formJoins } from './joins.mjs';
|
|
31
|
+
import { pressUndoPart } from './undo-later.mjs';
|
|
32
|
+
import { createMoments, createRunner, takenNow } from './run.mjs';
|
|
32
33
|
import { emptyValue, initialValues, isEmpty, lineKind, structuredCopy, } from './values.mjs';
|
|
33
34
|
/**
|
|
34
35
|
* Whether leaving the record now would lose the person's work: changes not
|
|
@@ -89,7 +90,9 @@ function innerForm(options, within, place, outer) {
|
|
|
89
90
|
// A field's label small inside a message's sentence where the page asks (`lowerLabels`).
|
|
90
91
|
// …and a day written in the page's order where it says one (`look.dateOrder`): "can't be after Oct 20, 2026".
|
|
91
92
|
const dateOrder = page.look?.dateOrder;
|
|
92
|
-
|
|
93
|
+
// The page's ask over the language's messages, each read through to them as it is said: a language whose words come in a
|
|
94
|
+
// chunk of their own fills its table in place once they have come (words-chunks.ts).
|
|
95
|
+
const messages = page.lowerLabels || dateOrder ? Object.assign(Object.create(options.messages ?? MESSAGES.en), { lowerLabels: page.lowerLabels, dateOrder }) : (options.messages ?? MESSAGES.en);
|
|
93
96
|
/** One of the form's sentences in the words the page names its records by (`records`), else as the language says it plainly. */
|
|
94
97
|
const named = (noun, plain) => recordSentence(page.records, noun, plain, messages.locale);
|
|
95
98
|
/** The app's functions the page's expressions call: a name none may call is refused now. */
|
|
@@ -186,7 +189,23 @@ function innerForm(options, within, place, outer) {
|
|
|
186
189
|
placeholderOf('#title', page.layout.title);
|
|
187
190
|
/** Whether any rule only warns: without one, there are never warnings to look for. */
|
|
188
191
|
const warns = [...nodeRules.values()].some((rules) => rules.some((compiled) => compiled.rule.level === 'warning'));
|
|
189
|
-
const
|
|
192
|
+
const layoutSteps = page.layout.type === 'wizard' ? page.layout.children.map((step) => step.id) : [];
|
|
193
|
+
/** The wizard's steps in the order shown: the page's, or the order drawn for this respondent (`state.order`). */
|
|
194
|
+
let steps = layoutSteps;
|
|
195
|
+
/**
|
|
196
|
+
* The parts that join this form where its page or its source asks (`formJoins`: an offline outbox, the fields its link
|
|
197
|
+
* fills, proof by a code, a payment, a survey's flow, a record's approvals, the record made live), each made as it
|
|
198
|
+
* comes — at once where it has — and kept in their order: holes until then. While one has not come, a send waits for it
|
|
199
|
+
* (`joining`); a draft laid before one came is laid on it as it does (`laid`).
|
|
200
|
+
*/
|
|
201
|
+
const joins = [];
|
|
202
|
+
const joinParts = formJoins(page, options).filter((part) => !outer || part.name !== 'approvals');
|
|
203
|
+
let joining;
|
|
204
|
+
let laid = null;
|
|
205
|
+
/** A part's side of this form, by its name, once it came. */
|
|
206
|
+
const joined = (name) => (joins[joinParts.findIndex((part) => part.name === name)] ?? null);
|
|
207
|
+
/** Whether a part that joins the form says so of a part of the page, or of a field. */
|
|
208
|
+
const anyJoin = (says) => joins.some((join) => join && says(join));
|
|
190
209
|
const storeKey = () => `fieldia:draft:${page.id}:${state.recordId ?? 'new'}`;
|
|
191
210
|
/** Today, on the clock of the page's time zone (the browser's own when it names none). */
|
|
192
211
|
const today = () => dayIn(options.now?.() ?? new Date(), page.timeZone);
|
|
@@ -364,6 +383,9 @@ function innerForm(options, within, place, outer) {
|
|
|
364
383
|
limits: null,
|
|
365
384
|
resume: null,
|
|
366
385
|
approval: null,
|
|
386
|
+
order: null,
|
|
387
|
+
timers: {},
|
|
388
|
+
timeUp: [],
|
|
367
389
|
};
|
|
368
390
|
started = true;
|
|
369
391
|
/**
|
|
@@ -456,6 +478,8 @@ function innerForm(options, within, place, outer) {
|
|
|
456
478
|
filesChanged();
|
|
457
479
|
},
|
|
458
480
|
track,
|
|
481
|
+
// Through the parts that join the form: a photo taken without signal kept as its bytes, on a page with an outbox.
|
|
482
|
+
through,
|
|
459
483
|
});
|
|
460
484
|
/**
|
|
461
485
|
* Files on their way changed, here or in a saved form placed here: the
|
|
@@ -553,6 +577,88 @@ function innerForm(options, within, place, outer) {
|
|
|
553
577
|
const listed = listedColumns(page.fields);
|
|
554
578
|
/** Whether the values set now are a change someone made (`writeValues`), after which a line's choice no longer offered is cleared. */
|
|
555
579
|
let edit = false;
|
|
580
|
+
/**
|
|
581
|
+
* The one kit the parts that work inside the form are given of it (joins.ts): the parts that join it, its limits, the
|
|
582
|
+
* undo part's press.
|
|
583
|
+
*/
|
|
584
|
+
const inner = {
|
|
585
|
+
get form() {
|
|
586
|
+
return form;
|
|
587
|
+
},
|
|
588
|
+
page,
|
|
589
|
+
fields,
|
|
590
|
+
options,
|
|
591
|
+
messages,
|
|
592
|
+
scheduler,
|
|
593
|
+
state: () => state,
|
|
594
|
+
set,
|
|
595
|
+
reading,
|
|
596
|
+
sending: () => (page.data.kind === 'responses' ? answersSent() : sentValues()),
|
|
597
|
+
baseline: () => baseline,
|
|
598
|
+
same: (name, one, other) => sameValue(fields[name], one, other),
|
|
599
|
+
conflictOf,
|
|
600
|
+
store,
|
|
601
|
+
settled: savesSettled,
|
|
602
|
+
track,
|
|
603
|
+
shownNow,
|
|
604
|
+
generation: () => generation,
|
|
605
|
+
emit,
|
|
606
|
+
replaced,
|
|
607
|
+
clock,
|
|
608
|
+
started: () => started,
|
|
609
|
+
listened: () => listeners.size > 0,
|
|
610
|
+
disposed: () => disposed,
|
|
611
|
+
// A clock started is kept at once where drafts are kept here: a reload a moment after Start finds it.
|
|
612
|
+
keep: () => (onServer() ? scheduleDraft() : keepHere(draftNow())),
|
|
613
|
+
order: (list) => void (steps = list ?? layoutSteps),
|
|
614
|
+
refuse(name, message) {
|
|
615
|
+
// Said until the field changes from what it holds, as a refused save's words are.
|
|
616
|
+
serverErrors[name] = { message, value: JSON.stringify(state.values[name] ?? null) };
|
|
617
|
+
set({ errors: { ...state.errors, [name]: message } });
|
|
618
|
+
},
|
|
619
|
+
reload: () => load(true),
|
|
620
|
+
};
|
|
621
|
+
/** The person's changes to the record shown, as made (history.ts); and whether one is being taken back or made again now. */
|
|
622
|
+
let noted = [];
|
|
623
|
+
let replaying = false;
|
|
624
|
+
/**
|
|
625
|
+
* A person's change written to `field` (on `line`), noted for Undo with what it held `before`: typing on joins it
|
|
626
|
+
* (`joins`). The undo part takes them as they come, asked for with the first: in by the time the person presses Ctrl+Z.
|
|
627
|
+
*/
|
|
628
|
+
const noteChange = (field, line, before, joins) => {
|
|
629
|
+
if (replaying)
|
|
630
|
+
return;
|
|
631
|
+
noted.push([field, line, before ?? null, state.values[field] ?? null, joins]);
|
|
632
|
+
const part = pressUndoPart.loaded;
|
|
633
|
+
if (part)
|
|
634
|
+
part.takeNoted(noted, inner.same);
|
|
635
|
+
// Kept while it comes; where it cannot, the oldest let go.
|
|
636
|
+
else if (noted.length > 500)
|
|
637
|
+
noted.shift();
|
|
638
|
+
else
|
|
639
|
+
pressUndoPart.load().catch(() => undefined);
|
|
640
|
+
};
|
|
641
|
+
/**
|
|
642
|
+
* Undo (`back`) or Redo, once the undo part has come: the change taken, written as the person's own change back —
|
|
643
|
+
* the page's `change` steps and the source's onchange run as for any — once its field still holds what the change
|
|
644
|
+
* left and may be changed.
|
|
645
|
+
*/
|
|
646
|
+
function replay(back) {
|
|
647
|
+
const step = editing ? pressUndoPart.loaded?.takeStep(noted, inner.same, back, (one, left) => !form.fieldReadonly(one.field) && sameValue(fields[one.field], state.values[one.field], left)) : null;
|
|
648
|
+
if (!step)
|
|
649
|
+
return null;
|
|
650
|
+
const { value, ...taken } = step;
|
|
651
|
+
replaying = true;
|
|
652
|
+
try {
|
|
653
|
+
writeValues({ ...state.values, [taken.field]: structuredCopy(value) });
|
|
654
|
+
afterEdit(taken.field);
|
|
655
|
+
changed(taken.field, 'person', taken.line === undefined ? {} : { line: taken.line });
|
|
656
|
+
}
|
|
657
|
+
finally {
|
|
658
|
+
replaying = false;
|
|
659
|
+
}
|
|
660
|
+
return taken;
|
|
661
|
+
}
|
|
556
662
|
function set(patch) {
|
|
557
663
|
const was = state.values;
|
|
558
664
|
const choicesWas = state.choices;
|
|
@@ -586,6 +692,10 @@ function innerForm(options, within, place, outer) {
|
|
|
586
692
|
part.inner.show(inside(state.errors, part.name));
|
|
587
693
|
for (const listener of [...listeners])
|
|
588
694
|
listener(state);
|
|
695
|
+
// What the parts that join the form follow of it: the record shown, watched for changes made elsewhere and for who
|
|
696
|
+
// else has it open, asked again as that changes.
|
|
697
|
+
for (const join of joins)
|
|
698
|
+
join?.changed?.();
|
|
589
699
|
// Choices that change with a value that just changed are asked for again.
|
|
590
700
|
if (state.values !== was) {
|
|
591
701
|
// After someone's change, a choice its new choices do not offer is taken out and said, as a line's is.
|
|
@@ -625,6 +735,7 @@ function innerForm(options, within, place, outer) {
|
|
|
625
735
|
/** Another record is shown: nothing of the last one's is the person's work any more, and its files on their way stop. */
|
|
626
736
|
function newGeneration() {
|
|
627
737
|
generation++;
|
|
738
|
+
noted = [];
|
|
628
739
|
// Another record's lines ask the source anew.
|
|
629
740
|
lineAsks = new Map();
|
|
630
741
|
answers = new Map();
|
|
@@ -737,6 +848,9 @@ function innerForm(options, within, place, outer) {
|
|
|
737
848
|
return;
|
|
738
849
|
told = step;
|
|
739
850
|
emit('step', { step });
|
|
851
|
+
// A step with a time limit: its clock starts as it is first entered.
|
|
852
|
+
for (const join of joins)
|
|
853
|
+
join?.entered?.(step);
|
|
740
854
|
const list = page.on?.show?.[step];
|
|
741
855
|
if (list)
|
|
742
856
|
void track(moment(`on.show.${step}`, list, chain, true));
|
|
@@ -1108,13 +1222,13 @@ function innerForm(options, within, place, outer) {
|
|
|
1108
1222
|
if (!node)
|
|
1109
1223
|
throw new Error(`The page has no element "${id}"`);
|
|
1110
1224
|
const { context: ctx, env } = reading();
|
|
1111
|
-
const hidden = (at) => (at ? !rolesAllow(at.roles, userRoles) || at.invisible.evaluate(ctx, env) : false);
|
|
1225
|
+
const hidden = (at) => (at ? anyJoin((join) => join.hides?.(at.id)) || !rolesAllow(at.roles, userRoles) || at.invisible.evaluate(ctx, env) : false);
|
|
1112
1226
|
let invisible = hidden(node);
|
|
1113
1227
|
for (let parent = node.parent; !invisible && parent; parent = index.get(parent)?.parent ?? null) {
|
|
1114
1228
|
invisible = hidden(index.get(parent));
|
|
1115
1229
|
}
|
|
1116
|
-
// A section read-only right now makes everything inside it read-only too.
|
|
1117
|
-
let readonly = node.readonly.evaluate(ctx, env);
|
|
1230
|
+
// A section read-only right now makes everything inside it read-only too; a page's, or a step's, time run out too.
|
|
1231
|
+
let readonly = node.readonly.evaluate(ctx, env) || anyJoin((join) => join.locks?.(id));
|
|
1118
1232
|
for (let parent = node.parent; !readonly && parent; parent = index.get(parent)?.parent ?? null) {
|
|
1119
1233
|
readonly = index.get(parent)?.readonly.evaluate(ctx, env) ?? false;
|
|
1120
1234
|
}
|
|
@@ -1132,7 +1246,7 @@ function innerForm(options, within, place, outer) {
|
|
|
1132
1246
|
if (answer !== undefined && signedNow().has(answer))
|
|
1133
1247
|
readonly = true;
|
|
1134
1248
|
// A record waiting for approval: locked but for the fields the page leaves open (the approvals part).
|
|
1135
|
-
if (answer !== undefined &&
|
|
1249
|
+
if (answer !== undefined && anyJoin((join) => join.locksField?.(answer)))
|
|
1136
1250
|
readonly = true;
|
|
1137
1251
|
return {
|
|
1138
1252
|
invisible,
|
|
@@ -1274,6 +1388,9 @@ function innerForm(options, within, place, outer) {
|
|
|
1274
1388
|
const errors = {};
|
|
1275
1389
|
const seen = new Set();
|
|
1276
1390
|
for (const node of visibleFieldNodes(root)) {
|
|
1391
|
+
// The answers of a time run out go as they stood: their checks refuse nothing.
|
|
1392
|
+
if (anyJoin((join) => join.locks?.(node.id)))
|
|
1393
|
+
continue;
|
|
1277
1394
|
const name = node.field;
|
|
1278
1395
|
seen.add(name);
|
|
1279
1396
|
const end = rangeEnd(node);
|
|
@@ -1550,8 +1667,10 @@ function innerForm(options, within, place, outer) {
|
|
|
1550
1667
|
* form continues one by its key). A page without either keeps its drafts where it always did.
|
|
1551
1668
|
*/
|
|
1552
1669
|
const onServer = () => !!options.dataSource?.saveDraft && !!options.dataSource.loadDraft && (page.continueLater === true || !!options.resume);
|
|
1553
|
-
/**
|
|
1554
|
-
const
|
|
1670
|
+
/** Where drafts are kept in the browser: the app's store, else a timed page's tab (`timedStore`); none else. */
|
|
1671
|
+
const draftStore = options.drafts?.store ?? timedStore(page);
|
|
1672
|
+
/** Whether drafts are kept: in a store, or on the server. */
|
|
1673
|
+
const draftsOn = () => !!draftStore || onServer();
|
|
1555
1674
|
/** The key of the draft kept on the server for the record shown; null until the source gave one. */
|
|
1556
1675
|
let serverKey = null;
|
|
1557
1676
|
/** Counted each time the draft is let go (sent, discarded, another record shown): a keeping answered after is dropped. */
|
|
@@ -1582,6 +1701,7 @@ function innerForm(options, within, place, outer) {
|
|
|
1582
1701
|
return {
|
|
1583
1702
|
values,
|
|
1584
1703
|
...(steps.length ? { step: state.step, skipped: [...state.skipped] } : {}),
|
|
1704
|
+
...Object.assign({}, ...joins.map((join) => join?.kept?.())),
|
|
1585
1705
|
...(Object.keys(state.unread).length ? { unread: structuredCopy(state.unread) } : {}),
|
|
1586
1706
|
answered: Object.keys(fields).filter((name) => state.dirty.includes(name)),
|
|
1587
1707
|
savedAt: new Date(clock()).toISOString(),
|
|
@@ -1590,7 +1710,7 @@ function innerForm(options, within, place, outer) {
|
|
|
1590
1710
|
/** A draft in the browser's store, where the app gave one: a store that is full keeps none. */
|
|
1591
1711
|
function keepHere(kept) {
|
|
1592
1712
|
try {
|
|
1593
|
-
|
|
1713
|
+
draftStore?.setItem(storeKey(), JSON.stringify(kept));
|
|
1594
1714
|
}
|
|
1595
1715
|
catch {
|
|
1596
1716
|
// The draft is a convenience: the form goes on without it.
|
|
@@ -1637,6 +1757,10 @@ function innerForm(options, within, place, outer) {
|
|
|
1637
1757
|
/** A draft laid on the form: its answers as a starting point, its wizard step (or the nearest before it that applies), its text that does not read. */
|
|
1638
1758
|
function applyDraft(draft) {
|
|
1639
1759
|
found = null;
|
|
1760
|
+
// Its draw and its clocks, by the survey flow's part: now, or once it came.
|
|
1761
|
+
laid = draft;
|
|
1762
|
+
for (const join of joins)
|
|
1763
|
+
join?.lay?.(draft);
|
|
1640
1764
|
writeValues(withDraft(draft.values ?? {}), { draft: null }, true);
|
|
1641
1765
|
const known = (draft.skipped ?? []).filter((id) => steps.includes(id));
|
|
1642
1766
|
// Text typed that did not read stays as typed, said as it was, where its field is still on the page.
|
|
@@ -1659,12 +1783,15 @@ function innerForm(options, within, place, outer) {
|
|
|
1659
1783
|
serverKey = null;
|
|
1660
1784
|
if (state.resume)
|
|
1661
1785
|
set({ resume: null });
|
|
1662
|
-
|
|
1786
|
+
draftStore?.removeItem(storeKey());
|
|
1663
1787
|
}
|
|
1664
|
-
/**
|
|
1665
|
-
|
|
1788
|
+
/**
|
|
1789
|
+
* A draft found: laid at once (`restore: "auto"`, a draft continued from its key over nothing typed, one kept in a
|
|
1790
|
+
* timed page's tab, or one whose clock started — let go, it would start again), else offered.
|
|
1791
|
+
*/
|
|
1792
|
+
function offer(draft, auto = !options.drafts || options.drafts.restore === 'auto') {
|
|
1666
1793
|
// What the person typed while it was asked for is theirs: the draft is offered, never laid over it.
|
|
1667
|
-
if (auto && !state.dirty.length && !Object.keys(state.unread).length)
|
|
1794
|
+
if ((auto || draft.timers) && !state.dirty.length && !Object.keys(state.unread).length)
|
|
1668
1795
|
return applyDraft(draft);
|
|
1669
1796
|
found = draft;
|
|
1670
1797
|
set({ draft: { savedAt: draft.savedAt } });
|
|
@@ -1678,7 +1805,7 @@ function innerForm(options, within, place, outer) {
|
|
|
1678
1805
|
const source = options.dataSource;
|
|
1679
1806
|
const given = resumeKey;
|
|
1680
1807
|
resumeKey = undefined;
|
|
1681
|
-
const kept = given ? null : draftRead(
|
|
1808
|
+
const kept = given ? null : draftRead(draftStore?.getItem(storeKey()) ?? null);
|
|
1682
1809
|
if (!given && !kept)
|
|
1683
1810
|
return;
|
|
1684
1811
|
if (kept && !('key' in kept))
|
|
@@ -1696,7 +1823,7 @@ function innerForm(options, within, place, outer) {
|
|
|
1696
1823
|
set({ status: 'ready' });
|
|
1697
1824
|
if (!draft) {
|
|
1698
1825
|
// Sent, or gone: the form starts empty, and says so where it was asked to continue it.
|
|
1699
|
-
|
|
1826
|
+
draftStore?.removeItem(storeKey());
|
|
1700
1827
|
if (given)
|
|
1701
1828
|
set({ resume: { key, missing: true } });
|
|
1702
1829
|
return;
|
|
@@ -1731,27 +1858,8 @@ function innerForm(options, within, place, outer) {
|
|
|
1731
1858
|
function limitsMade(part) {
|
|
1732
1859
|
if (limitsKit || disposed)
|
|
1733
1860
|
return limitsKit;
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
dataSource: options.dataSource,
|
|
1737
|
-
user: options.user,
|
|
1738
|
-
messages,
|
|
1739
|
-
scheduler,
|
|
1740
|
-
state: () => state,
|
|
1741
|
-
set,
|
|
1742
|
-
clock,
|
|
1743
|
-
started: () => started,
|
|
1744
|
-
shownNow,
|
|
1745
|
-
generation: () => generation,
|
|
1746
|
-
replaced: () => named(messages.recordReplacedNoun, messages.recordReplaced),
|
|
1747
|
-
listened: () => listeners.size > 0,
|
|
1748
|
-
disposed: () => disposed,
|
|
1749
|
-
refuse(name, message) {
|
|
1750
|
-
// Said until the field changes from what it holds, as a refused save's words are.
|
|
1751
|
-
serverErrors[name] = { message, value: JSON.stringify(state.values[name] ?? null) };
|
|
1752
|
-
set({ errors: { ...state.errors, [name]: message } });
|
|
1753
|
-
},
|
|
1754
|
-
});
|
|
1861
|
+
// The one kit, made at the limits' own moment: once the form knows it keeps them (joins.ts).
|
|
1862
|
+
const kit = part.formLimits(inner);
|
|
1755
1863
|
limitsKit = kit;
|
|
1756
1864
|
kit.show();
|
|
1757
1865
|
if (kit.asks() && (state.recordId == null || loadedOnce))
|
|
@@ -1772,15 +1880,6 @@ function innerForm(options, within, place, outer) {
|
|
|
1772
1880
|
}
|
|
1773
1881
|
/** A choice of a full option, picked by the person, refused under its field: the field keeps what it held. */
|
|
1774
1882
|
const refusedFull = (name, value) => !!limitsKit?.refusedFull(name, value);
|
|
1775
|
-
// ---- a record's approvals (the approvals part: form-approvals.ts) ---------------------------------------------------
|
|
1776
|
-
/** The approvals made for this form from the approvals part, once it came: none before, nor on a page without them. */
|
|
1777
|
-
let approvalKit = null;
|
|
1778
|
-
/** Whether the form asks for approvals: a page with `approval` (a record's own, as its check says), its source telling them. */
|
|
1779
|
-
const keepsApprovals = !outer && !!page.approval && !!options.dataSource?.approvals;
|
|
1780
|
-
/** The approvals, made from their part once it came; null without them, or where it could not come. */
|
|
1781
|
-
const approvalsCode = () => keepsApprovals ? formApprovalsPart.load().then((part) => (approvalKit ??= part.formApprovals({ form, dataSource: options.dataSource, messages, set, reload: () => load(true) })), () => null) : Promise.resolve(null);
|
|
1782
|
-
/** Ask the record shown's approvals, once their part came; nothing on a page without them. */
|
|
1783
|
-
const askApprovals = () => approvalsCode().then((kit) => kit?.ask());
|
|
1784
1883
|
function scheduleAutosave() {
|
|
1785
1884
|
const autosave = options.autosave;
|
|
1786
1885
|
// A record the person may not change is never saved unasked: it would only be refused.
|
|
@@ -1857,9 +1956,24 @@ function innerForm(options, within, place, outer) {
|
|
|
1857
1956
|
}
|
|
1858
1957
|
return shown;
|
|
1859
1958
|
}
|
|
1959
|
+
/** A response's answers as they go: those shown, and what the parts that join the form add (a link's hidden fields). */
|
|
1960
|
+
function answersSent() {
|
|
1961
|
+
const values = shownValues();
|
|
1962
|
+
for (const join of joins)
|
|
1963
|
+
join.values?.(values);
|
|
1964
|
+
return values;
|
|
1965
|
+
}
|
|
1860
1966
|
async function save(chain = [], yes) {
|
|
1861
1967
|
return (await saving(chain, yes)) === null;
|
|
1862
1968
|
}
|
|
1969
|
+
/** A send — a response's, a save's, an upload's — through the parts that join the form, each around the next. Null: kept, not sent. */
|
|
1970
|
+
function through(kind, sent, send) {
|
|
1971
|
+
return joins.reduce((go, join) => (join.around ? (one) => join.around(kind, one, go) : go), send)(sent);
|
|
1972
|
+
}
|
|
1973
|
+
/** Started again, or another record opened: the parts that join the form let go of what the one before had. */
|
|
1974
|
+
const joinsReset = () => joins.forEach((join) => join.reset?.());
|
|
1975
|
+
/** The record loaded, or saved: what the parts that join the form ask again (a record's approvals). */
|
|
1976
|
+
const joinsLoaded = () => joins.forEach((join) => join.loaded?.());
|
|
1863
1977
|
/** Every value, for a source that saves whole records: those of fields hidden from the person, which they never had, left out. */
|
|
1864
1978
|
function sentValues() {
|
|
1865
1979
|
const values = structuredCopy(state.values);
|
|
@@ -1967,8 +2081,22 @@ function innerForm(options, within, place, outer) {
|
|
|
1967
2081
|
const refused = await stillTaking(asked);
|
|
1968
2082
|
if (refused)
|
|
1969
2083
|
return refused;
|
|
1970
|
-
//
|
|
2084
|
+
// The parts that join the form (`formJoins`), waited for where they have not come.
|
|
2085
|
+
if (joining) {
|
|
2086
|
+
await joining;
|
|
2087
|
+
if (asked !== generation)
|
|
2088
|
+
return elsewhere();
|
|
2089
|
+
}
|
|
2090
|
+
// A file that did not reach the store stops it, under its field, with the form's own checks — and what the parts that
|
|
2091
|
+
// join the form find: an email or a phone not proved by a code; a payment not taken sends nothing.
|
|
1971
2092
|
const errors = { ...collectErrors(), ...fileProblems() };
|
|
2093
|
+
for (const join of joins) {
|
|
2094
|
+
// A part that never came (a hole) has nothing to say.
|
|
2095
|
+
if ((await join?.before?.(errors)) === false)
|
|
2096
|
+
return { reason: 'save' };
|
|
2097
|
+
if (asked !== generation)
|
|
2098
|
+
return elsewhere();
|
|
2099
|
+
}
|
|
1972
2100
|
if (Object.keys(errors).length) {
|
|
1973
2101
|
set({ errors, ...unstored(errors) });
|
|
1974
2102
|
return { reason: 'save', message: Object.values(errors)[0] };
|
|
@@ -1998,13 +2126,18 @@ function innerForm(options, within, place, outer) {
|
|
|
1998
2126
|
if (page.data.kind === 'responses') {
|
|
1999
2127
|
if (!source?.submit)
|
|
2000
2128
|
throw new Error('This page has no data source to send its answers to');
|
|
2001
|
-
const sent =
|
|
2129
|
+
const sent = answersSent();
|
|
2002
2130
|
const before = state.values;
|
|
2003
2131
|
at = generation;
|
|
2004
2132
|
const resetAt = resets;
|
|
2005
2133
|
const seen = botReport();
|
|
2134
|
+
// A quiz's marking, in before the answers go: what the person sees of their results.
|
|
2006
2135
|
// What the source answered: the id it gave the response, and the values it added — what an ending reads.
|
|
2007
|
-
const
|
|
2136
|
+
const request = { pageId: page.id, values: sent, ...(seen ? { bot: seen } : {}), ...draft };
|
|
2137
|
+
// Through the parts that join the form: kept on the device where an outbox finds no signal (null), the form going
|
|
2138
|
+
// on as though it went.
|
|
2139
|
+
const got = await through('submit', request, (one) => source.submit(one));
|
|
2140
|
+
const answer = got ?? {};
|
|
2008
2141
|
// The captcha's token is spent once sent: the next send waits for a new one.
|
|
2009
2142
|
if (seen?.captcha)
|
|
2010
2143
|
bot.captcha = null;
|
|
@@ -2016,8 +2149,13 @@ function innerForm(options, within, place, outer) {
|
|
|
2016
2149
|
sentAs = before;
|
|
2017
2150
|
const added = answer.values && typeof answer.values === 'object' ? structuredCopy(answer.values) : {};
|
|
2018
2151
|
// Started again meanwhile (`reset`): a new response is on screen, and no ending is the old one's.
|
|
2019
|
-
|
|
2152
|
+
// A quiz's results: the source's marking, else the page's, on the answers as sent.
|
|
2153
|
+
const quiz = answer.quiz ?? joined('surveyflow')?.marked(sent);
|
|
2154
|
+
set({ status: 'saved', unsent: unsentNow(), ...(resets === resetAt ? { sent: { id: answer.id ?? null, values: { ...structuredCopy(sent), ...added }, ...(quiz ? { quiz } : {}) } } : {}) });
|
|
2020
2155
|
emit('send', { values: sent, ...(seen ? { bot: seen } : {}) });
|
|
2156
|
+
// Kept, not sent: its counts and its after-save steps wait for a send that went.
|
|
2157
|
+
if (got === null)
|
|
2158
|
+
return null;
|
|
2021
2159
|
// Counted again: one response fewer to take, this person's sent.
|
|
2022
2160
|
if (asksLimits())
|
|
2023
2161
|
void track(askLimits());
|
|
@@ -2030,7 +2168,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2030
2168
|
const sent = state.values;
|
|
2031
2169
|
at = generation;
|
|
2032
2170
|
const resetAt = resets;
|
|
2033
|
-
const
|
|
2171
|
+
const request = {
|
|
2034
2172
|
model: page.data.model,
|
|
2035
2173
|
id: state.recordId,
|
|
2036
2174
|
fields,
|
|
@@ -2039,7 +2177,10 @@ function innerForm(options, within, place, outer) {
|
|
|
2039
2177
|
// The version it was opened at, for a source that refuses a save over someone else's.
|
|
2040
2178
|
...(state.recordId != null && state.version ? { version: state.version } : {}),
|
|
2041
2179
|
...draft,
|
|
2042
|
-
}
|
|
2180
|
+
};
|
|
2181
|
+
const got = await through('save', request, (one) => source.save(one));
|
|
2182
|
+
// Kept on the device (null): the record as it was, its version too.
|
|
2183
|
+
const result = got ?? { id: state.recordId, version: state.version ?? undefined };
|
|
2043
2184
|
// Answered for a record no longer shown — another opened, or this one loaded again: the source has it, and the
|
|
2044
2185
|
// form lays nothing of it over the record it shows now.
|
|
2045
2186
|
if (at !== generation)
|
|
@@ -2051,6 +2192,11 @@ function innerForm(options, within, place, outer) {
|
|
|
2051
2192
|
sentAs = baseline;
|
|
2052
2193
|
// Its version now, for the next save to carry; and what the person may do now, when the save changed that.
|
|
2053
2194
|
// The person's changes it did not send are at risk (`unsent`): every way out asks before it leaves them.
|
|
2195
|
+
// A save starts the person's changes afresh, as one saved is no change to take back (the viewer's Ctrl+Z says
|
|
2196
|
+
// nothing changed since). Not on a page that saves itself as it is changed (`autosave`), whose changes are taken
|
|
2197
|
+
// back across its saves; nor where a change made while it saved goes on past it.
|
|
2198
|
+
if (!options.autosave && !dirty.length && got)
|
|
2199
|
+
noted = [];
|
|
2054
2200
|
set({ status: 'saved', recordId: result.id, values, dirty, warnings: withValues(values, collectWarnings), version: result.version ?? null, ...(result.can ? { can: result.can } : {}) });
|
|
2055
2201
|
// Changed while it saved: kept in a draft, and saved as any change is.
|
|
2056
2202
|
if (dirty.length) {
|
|
@@ -2058,11 +2204,11 @@ function innerForm(options, within, place, outer) {
|
|
|
2058
2204
|
scheduleAutosave();
|
|
2059
2205
|
}
|
|
2060
2206
|
// A save may have sent it for approval, or moved it on: its approvals asked again.
|
|
2061
|
-
|
|
2062
|
-
void track(askApprovals());
|
|
2207
|
+
joinsLoaded();
|
|
2063
2208
|
// The record as this save left it: a change made while it was on its way is no value of it.
|
|
2064
2209
|
emit('save', { recordId: state.recordId, values: baseline });
|
|
2065
|
-
|
|
2210
|
+
if (got)
|
|
2211
|
+
void track(moment('on.afterSave', page.on?.afterSave, chain, false));
|
|
2066
2212
|
return null;
|
|
2067
2213
|
}
|
|
2068
2214
|
catch (error) {
|
|
@@ -2168,7 +2314,8 @@ function innerForm(options, within, place, outer) {
|
|
|
2168
2314
|
}
|
|
2169
2315
|
function addLine(field, values, by) {
|
|
2170
2316
|
const def = lineField(field);
|
|
2171
|
-
const
|
|
2317
|
+
const before = state.values[field];
|
|
2318
|
+
const current = before ?? [];
|
|
2172
2319
|
// A key no line holds: lines given (a line's own lines, back from its form) may hold new-… keys already.
|
|
2173
2320
|
let key = `new-${++lineKeys}`;
|
|
2174
2321
|
while (current.some((line) => line.key === key))
|
|
@@ -2181,6 +2328,8 @@ function innerForm(options, within, place, outer) {
|
|
|
2181
2328
|
}
|
|
2182
2329
|
const lines = [...current, line];
|
|
2183
2330
|
writeValues({ ...state.values, [field]: lines });
|
|
2331
|
+
if (by === 'person')
|
|
2332
|
+
noteChange(field, key, before, false);
|
|
2184
2333
|
afterEdit(field);
|
|
2185
2334
|
changed(field, by, by === 'person' ? { line: key } : { keep: true });
|
|
2186
2335
|
return key;
|
|
@@ -2325,8 +2474,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2325
2474
|
if (asksLimits())
|
|
2326
2475
|
void track(askLimits());
|
|
2327
2476
|
// Its approvals: where it stands, what was decided, what this person may decide — its fields locked while it waits.
|
|
2328
|
-
|
|
2329
|
-
void track(askApprovals());
|
|
2477
|
+
joinsLoaded();
|
|
2330
2478
|
if (again)
|
|
2331
2479
|
return;
|
|
2332
2480
|
offerDraft();
|
|
@@ -2362,6 +2510,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2362
2510
|
const fresh = { dirty: [], errors: {}, warning: null, warningField: null, error: null, draft: null, step: steps[0] ?? null, skipped: [], saveProblem: null, version: null, can: null, conflict: null, setAside: null, unsent: [], unread: {}, takenOut: {}, resume: null, approval: null };
|
|
2363
2511
|
if (id != null) {
|
|
2364
2512
|
set({ ...fresh, recordId: id, status: 'idle' });
|
|
2513
|
+
joinsReset();
|
|
2365
2514
|
await load();
|
|
2366
2515
|
return true;
|
|
2367
2516
|
}
|
|
@@ -2369,6 +2518,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2369
2518
|
baseline = startFrom(startingValues(true, false));
|
|
2370
2519
|
const values = syncParts(structuredCopy(baseline), 'start');
|
|
2371
2520
|
set({ ...fresh, recordId: null, status: 'ready', values, warnings: withValues(values, collectWarnings) });
|
|
2521
|
+
joinsReset();
|
|
2372
2522
|
if (asksLimits())
|
|
2373
2523
|
void track(askLimits());
|
|
2374
2524
|
offerDraft();
|
|
@@ -2693,8 +2843,12 @@ function innerForm(options, within, place, outer) {
|
|
|
2693
2843
|
const builtin = 'builtin' in source && !list.length ? source.builtin : undefined;
|
|
2694
2844
|
if (builtin)
|
|
2695
2845
|
list.push({ do: builtin });
|
|
2696
|
-
|
|
2846
|
+
// Undo after it, in place of asking first: a built-in with `undo` asks nothing unless it says so.
|
|
2847
|
+
const offers = 'undo' in source ? source.undo : undefined;
|
|
2848
|
+
const asked = offers !== undefined ? undefined : builtin === 'archive' ? named(messages.archiveConfirmNoun, messages.archiveConfirm) : builtin === 'delete' ? named(messages.deleteConfirmNoun, messages.deleteConfirm) : undefined;
|
|
2697
2849
|
const confirm = 'confirm' in source && source.confirm !== undefined ? source.confirm : asked;
|
|
2850
|
+
if (offers !== undefined)
|
|
2851
|
+
scope.taken = takenNow(state.values);
|
|
2698
2852
|
// Its question's Yes, and its ask steps', say what it does: its own words, else Archive or Delete.
|
|
2699
2853
|
scope.yes ??= source.label || (builtin === 'archive' ? messages.archiveYes : builtin === 'delete' ? messages.deleteYes : undefined);
|
|
2700
2854
|
// On the record it was pressed on: another shown while its question is asked, it does nothing to that one.
|
|
@@ -2728,9 +2882,21 @@ function innerForm(options, within, place, outer) {
|
|
|
2728
2882
|
return result;
|
|
2729
2883
|
}
|
|
2730
2884
|
}
|
|
2731
|
-
|
|
2885
|
+
const result = await runTop(list, scope);
|
|
2886
|
+
if (scope.taken)
|
|
2887
|
+
offerUndo(source, scope, result, offers);
|
|
2888
|
+
return result;
|
|
2732
2889
|
})());
|
|
2733
2890
|
}
|
|
2891
|
+
/**
|
|
2892
|
+
* A press that offers Undo has ended: its words said — the last with Undo, where what it did can be taken back — by
|
|
2893
|
+
* the undo part (press-undo.ts), which a page whose buttons offer Undo asks for first; plainly where it cannot come.
|
|
2894
|
+
*/
|
|
2895
|
+
function offerUndo(source, scope, result, offers) {
|
|
2896
|
+
const taken = scope.taken;
|
|
2897
|
+
scope.taken = undefined;
|
|
2898
|
+
void track(pressUndoPart.load().then((part) => part.offerUndo(inner, { kit: runner.kit, source, scope, taken, result, offers }), () => taken.said.forEach((words) => options.host?.say(words.message, words.tone))));
|
|
2899
|
+
}
|
|
2734
2900
|
const form = {
|
|
2735
2901
|
page,
|
|
2736
2902
|
...(table ? { functions: table } : {}),
|
|
@@ -2755,7 +2921,10 @@ function innerForm(options, within, place, outer) {
|
|
|
2755
2921
|
// A choice whose places are all taken is shown, and refused when the person picks it: said under its field.
|
|
2756
2922
|
if ((how?.by ?? 'person') === 'person' && refusedFull(name, value))
|
|
2757
2923
|
return;
|
|
2924
|
+
const before = state.values[name];
|
|
2758
2925
|
writeValues({ ...state.values, [name]: structuredCopy(value) });
|
|
2926
|
+
if ((how?.by ?? 'person') === 'person')
|
|
2927
|
+
noteChange(name, undefined, before, true);
|
|
2759
2928
|
afterEdit(name);
|
|
2760
2929
|
changed(name, how?.by ?? 'person');
|
|
2761
2930
|
},
|
|
@@ -2780,7 +2949,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2780
2949
|
// Nothing of the record changed; what shows of it may have.
|
|
2781
2950
|
set({});
|
|
2782
2951
|
},
|
|
2783
|
-
fieldReadonly: (name) => lockedField(fieldDef(name)) || state.can?.edit === false || state.can?.readonlyFields?.includes(name) === true || signedNow().has(name) ||
|
|
2952
|
+
fieldReadonly: (name) => lockedField(fieldDef(name)) || state.can?.edit === false || state.can?.readonlyFields?.includes(name) === true || signedNow().has(name) || anyJoin((join) => join.locksField?.(name)),
|
|
2784
2953
|
loadChoices: (field) => loadChoices(field),
|
|
2785
2954
|
loadDefinitions,
|
|
2786
2955
|
addDefinition,
|
|
@@ -2821,8 +2990,9 @@ function innerForm(options, within, place, outer) {
|
|
|
2821
2990
|
return null;
|
|
2822
2991
|
if (state.unread[name])
|
|
2823
2992
|
return state.unread[name].message;
|
|
2824
|
-
// A property's text left unread answers for its field
|
|
2825
|
-
|
|
2993
|
+
// A property's text left unread answers for its field, and so does a box of a value drawn in parts (a repeat
|
|
2994
|
+
// rule's number, an opening hour's time: `field.part`).
|
|
2995
|
+
if (['properties', 'json', 'char'].includes(page.fields[name].type)) {
|
|
2826
2996
|
const row = Object.keys(state.unread).find((path) => path.startsWith(`${name}.`));
|
|
2827
2997
|
if (row)
|
|
2828
2998
|
return state.unread[row].message;
|
|
@@ -2836,7 +3006,9 @@ function innerForm(options, within, place, outer) {
|
|
|
2836
3006
|
const field = path.split('.')[0];
|
|
2837
3007
|
// A line's cell (`field.lineKey.subfield`), or a property (`field.property`).
|
|
2838
3008
|
const def = fieldDef(field);
|
|
2839
|
-
|
|
3009
|
+
// A line's cell, a property, or one of the boxes a value is drawn in (a repeat rule's number, an opening hour's
|
|
3010
|
+
// time: `field.part`, of a char or a json field).
|
|
3011
|
+
if (path.includes('.') && def.type !== 'properties' && !((def.type === 'json' || def.type === 'char') && path.split('.').length === 2))
|
|
2840
3012
|
lineField(field);
|
|
2841
3013
|
const had = state.unread[path];
|
|
2842
3014
|
if (!unread && !had)
|
|
@@ -2859,6 +3031,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2859
3031
|
set({ unread: next, errors });
|
|
2860
3032
|
},
|
|
2861
3033
|
boxLeft(path) {
|
|
3034
|
+
noted.push([path.split('.')[0]]);
|
|
2862
3035
|
const problem = leftProblem(path);
|
|
2863
3036
|
if (!problem || state.errors[path] === problem)
|
|
2864
3037
|
return;
|
|
@@ -2881,10 +3054,11 @@ function innerForm(options, within, place, outer) {
|
|
|
2881
3054
|
if (seen.captcha !== undefined)
|
|
2882
3055
|
bot.captcha = seen.captcha;
|
|
2883
3056
|
},
|
|
3057
|
+
joined: (name) => (joins[joinParts.findIndex((part) => part.name === name)] ?? null),
|
|
2884
3058
|
reading: () => readingNow > 0 || [...attached.values()].some((part) => part.inner.form.reading()),
|
|
2885
|
-
validate() {
|
|
3059
|
+
validate(within) {
|
|
2886
3060
|
readTyped();
|
|
2887
|
-
const errors = collectErrors();
|
|
3061
|
+
const errors = collectErrors(within);
|
|
2888
3062
|
set({ errors });
|
|
2889
3063
|
return Object.keys(errors).length === 0;
|
|
2890
3064
|
},
|
|
@@ -2977,6 +3151,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2977
3151
|
holding = setWhen.holding(baseline);
|
|
2978
3152
|
// The person's own Discard: nothing of theirs is left at risk, and a save on its way lands as the record it stored.
|
|
2979
3153
|
resets++;
|
|
3154
|
+
noted = [];
|
|
2980
3155
|
personal.clear();
|
|
2981
3156
|
personalLines.clear();
|
|
2982
3157
|
filed.clear();
|
|
@@ -2985,6 +3160,9 @@ function innerForm(options, within, place, outer) {
|
|
|
2985
3160
|
bot = { ...bot, honeypot: false, since: clock() };
|
|
2986
3161
|
const values = syncParts(structuredCopy(baseline), 'start');
|
|
2987
3162
|
set({ values, dirty: [], errors: {}, warnings: withValues(values, collectWarnings), warning: null, warningField: null, draft: null, step: steps[0] ?? null, skipped: [], saveProblem: null, conflict: null, unsent: [], unread: {}, takenOut: {}, sent: null });
|
|
3163
|
+
// Started again: a new response proves and pays its own; a new respondent is drawn anew (unless the app or the
|
|
3164
|
+
// person's id is the seed), on the first step drawn, no clock running.
|
|
3165
|
+
joinsReset();
|
|
2988
3166
|
// Started again: whether the page takes another response, and each choice's places, asked again.
|
|
2989
3167
|
if (asksLimits())
|
|
2990
3168
|
void track(askLimits());
|
|
@@ -3004,8 +3182,10 @@ function innerForm(options, within, place, outer) {
|
|
|
3004
3182
|
const def = lineField(field);
|
|
3005
3183
|
if (!def.fields[name])
|
|
3006
3184
|
throw new Error(`The lines of "${field}" have no field "${name}"`);
|
|
3007
|
-
const
|
|
3185
|
+
const before = state.values[field];
|
|
3186
|
+
const lines = (before ?? []).map((line) => line.key === key ? { ...line, values: { ...line.values, [name]: structuredCopy(value) } } : line);
|
|
3008
3187
|
writeValues({ ...state.values, [field]: lines });
|
|
3188
|
+
noteChange(field, key, before, true);
|
|
3009
3189
|
afterEdit(field);
|
|
3010
3190
|
changed(field, 'person', { line: key });
|
|
3011
3191
|
},
|
|
@@ -3035,7 +3215,9 @@ function innerForm(options, within, place, outer) {
|
|
|
3035
3215
|
const value = trusted ? (i >= low && i <= high ? pool[i - low] : line.values[sequence]) : i + 1;
|
|
3036
3216
|
return value === line.values[sequence] ? line : { ...line, values: { ...line.values, [sequence]: value } };
|
|
3037
3217
|
});
|
|
3218
|
+
const was = state.values[field];
|
|
3038
3219
|
writeValues({ ...state.values, [field]: renumbered });
|
|
3220
|
+
noteChange(field, undefined, was, false);
|
|
3039
3221
|
afterEdit(field);
|
|
3040
3222
|
changed(field, 'person');
|
|
3041
3223
|
},
|
|
@@ -3049,8 +3231,10 @@ function innerForm(options, within, place, outer) {
|
|
|
3049
3231
|
throw new Error(`The line "${key}" of "${field}" cannot be deleted now: its table's lineDelete does not hold on it`);
|
|
3050
3232
|
}
|
|
3051
3233
|
}
|
|
3052
|
-
const
|
|
3234
|
+
const before = state.values[field];
|
|
3235
|
+
const lines = (before ?? []).filter((line) => line.key !== key);
|
|
3053
3236
|
writeValues({ ...state.values, [field]: lines });
|
|
3237
|
+
noteChange(field, key, before, false);
|
|
3054
3238
|
afterEdit(field);
|
|
3055
3239
|
changed(field, 'person', { line: key });
|
|
3056
3240
|
},
|
|
@@ -3117,6 +3301,8 @@ function innerForm(options, within, place, outer) {
|
|
|
3117
3301
|
set({ step: previous, errors: {} });
|
|
3118
3302
|
return true;
|
|
3119
3303
|
},
|
|
3304
|
+
startTimer: () => joined('surveyflow')?.start(),
|
|
3305
|
+
countdown: () => joined('surveyflow')?.countdown() ?? null,
|
|
3120
3306
|
skip() {
|
|
3121
3307
|
const current = state.step;
|
|
3122
3308
|
if (!current || !optionalStep(current))
|
|
@@ -3257,7 +3443,7 @@ function innerForm(options, within, place, outer) {
|
|
|
3257
3443
|
return () => void heard.delete(listener);
|
|
3258
3444
|
},
|
|
3259
3445
|
restoreDraft() {
|
|
3260
|
-
const kept = found ?? draftRead(
|
|
3446
|
+
const kept = found ?? draftRead(draftStore?.getItem(storeKey()) ?? null);
|
|
3261
3447
|
if (kept && 'values' in kept)
|
|
3262
3448
|
applyDraft(kept);
|
|
3263
3449
|
},
|
|
@@ -3278,14 +3464,18 @@ function innerForm(options, within, place, outer) {
|
|
|
3278
3464
|
return saved;
|
|
3279
3465
|
},
|
|
3280
3466
|
refreshLimits: () => askLimits(),
|
|
3281
|
-
refreshApprovals
|
|
3467
|
+
async refreshApprovals() {
|
|
3468
|
+
await joining;
|
|
3469
|
+
await joined('approvals')?.ask();
|
|
3470
|
+
},
|
|
3282
3471
|
async decide(level, decision, reason) {
|
|
3283
|
-
|
|
3472
|
+
await joining;
|
|
3473
|
+
const kit = joined('approvals');
|
|
3284
3474
|
if (!kit)
|
|
3285
3475
|
throw new Error('No approvals on this page');
|
|
3286
3476
|
return track(kit.decide(level, decision, reason));
|
|
3287
3477
|
},
|
|
3288
|
-
approvalLock: (name) =>
|
|
3478
|
+
approvalLock: (name) => joined('approvals')?.lockWords(name) ?? null,
|
|
3289
3479
|
async reloadTheirs() {
|
|
3290
3480
|
// A save on its way first: its answer is not dropped by the load.
|
|
3291
3481
|
const asked = shownNow();
|
|
@@ -3385,6 +3575,9 @@ function innerForm(options, within, place, outer) {
|
|
|
3385
3575
|
if (state.setAside)
|
|
3386
3576
|
set({ setAside: null });
|
|
3387
3577
|
},
|
|
3578
|
+
undo: () => replay(true),
|
|
3579
|
+
redo: () => replay(false),
|
|
3580
|
+
history: () => pressUndoPart.loaded?.nextSteps(noted, inner.same) ?? { undo: null, redo: null },
|
|
3388
3581
|
async settled() {
|
|
3389
3582
|
while (pending.size)
|
|
3390
3583
|
await Promise.allSettled([...pending]);
|
|
@@ -3451,6 +3644,8 @@ function innerForm(options, within, place, outer) {
|
|
|
3451
3644
|
dispose() {
|
|
3452
3645
|
// An answer that lands after is for no record shown.
|
|
3453
3646
|
disposed = true;
|
|
3647
|
+
for (const join of joins)
|
|
3648
|
+
join?.dispose?.();
|
|
3454
3649
|
newGeneration();
|
|
3455
3650
|
clearFiles();
|
|
3456
3651
|
if (draftTimer !== null)
|
|
@@ -3470,9 +3665,22 @@ function innerForm(options, within, place, outer) {
|
|
|
3470
3665
|
// choice's places as the source tells them: a record's own once it is loaded, its own choices not counted against it.
|
|
3471
3666
|
if (keepsLimits())
|
|
3472
3667
|
void track(limitsCode());
|
|
3473
|
-
//
|
|
3474
|
-
|
|
3475
|
-
|
|
3668
|
+
// The parts its page or its source asks to join it — a survey's draw made before a draft is laid, which lays its own
|
|
3669
|
+
// draw over it; a record's approvals ready by the time its record is loaded; the record made live — each made at once
|
|
3670
|
+
// where it has come, else as it does, a draft laid meanwhile laid on it.
|
|
3671
|
+
if (joinParts.length) {
|
|
3672
|
+
const made = (at) => (part) => {
|
|
3673
|
+
if (disposed || joins[at])
|
|
3674
|
+
return;
|
|
3675
|
+
const join = (joins[at] = part.join(inner));
|
|
3676
|
+
if (laid)
|
|
3677
|
+
join.lay?.(laid);
|
|
3678
|
+
// Its record loaded before it came: what it asks of a loaded record, asked now.
|
|
3679
|
+
if (loadedOnce)
|
|
3680
|
+
join.loaded?.();
|
|
3681
|
+
};
|
|
3682
|
+
joining = Promise.all(joinParts.map((part, at) => (part.loaded ? made(at)(part.loaded) : track(part.load().then(made(at), () => undefined)))));
|
|
3683
|
+
}
|
|
3476
3684
|
// A new form has its values now — a draft continued from its key once it is laid — and opens once whoever made it
|
|
3477
3685
|
// has had the chance to listen.
|
|
3478
3686
|
if (state.recordId == null) {
|