@fieldia/core 0.31.0 → 0.32.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.
Files changed (65) hide show
  1. package/README.md +16 -5
  2. package/package.json +1 -1
  3. package/schema/page.schema.json +9 -1
  4. package/src/lib/format/page.d.mts +6 -1
  5. package/src/lib/format/page.d.ts +6 -1
  6. package/src/lib/format/phone.d.mts +8 -0
  7. package/src/lib/format/phone.d.ts +8 -0
  8. package/src/lib/format/phone.js +7 -2
  9. package/src/lib/format/phone.js.map +1 -1
  10. package/src/lib/format/phone.mjs +7 -2
  11. package/src/lib/format/phone.mjs.map +1 -1
  12. package/src/lib/format/upgrade.d.mts +3 -1
  13. package/src/lib/format/upgrade.d.ts +3 -1
  14. package/src/lib/format/upgrade.js +3 -1
  15. package/src/lib/format/upgrade.js.map +1 -1
  16. package/src/lib/format/upgrade.mjs +3 -1
  17. package/src/lib/format/upgrade.mjs.map +1 -1
  18. package/src/lib/format/version.d.mts +2 -2
  19. package/src/lib/format/version.d.ts +2 -2
  20. package/src/lib/format/version.js +2 -2
  21. package/src/lib/format/version.js.map +1 -1
  22. package/src/lib/format/version.mjs +2 -2
  23. package/src/lib/format/version.mjs.map +1 -1
  24. package/src/lib/record/data-source.d.mts +17 -5
  25. package/src/lib/record/data-source.d.ts +17 -5
  26. package/src/lib/record/data-source.js.map +1 -1
  27. package/src/lib/record/data-source.mjs.map +1 -1
  28. package/src/lib/record/drafts.d.mts +9 -0
  29. package/src/lib/record/drafts.d.ts +9 -0
  30. package/src/lib/record/drafts.js +20 -0
  31. package/src/lib/record/drafts.js.map +1 -1
  32. package/src/lib/record/drafts.mjs +19 -0
  33. package/src/lib/record/drafts.mjs.map +1 -1
  34. package/src/lib/record/form-approvals.js +5 -1
  35. package/src/lib/record/form-approvals.js.map +1 -1
  36. package/src/lib/record/form-approvals.mjs +5 -1
  37. package/src/lib/record/form-approvals.mjs.map +1 -1
  38. package/src/lib/record/form.d.mts +4 -1
  39. package/src/lib/record/form.d.ts +4 -1
  40. package/src/lib/record/form.js +22 -11
  41. package/src/lib/record/form.js.map +1 -1
  42. package/src/lib/record/form.mjs +23 -12
  43. package/src/lib/record/form.mjs.map +1 -1
  44. package/src/lib/record/memory-approvals.d.mts +4 -1
  45. package/src/lib/record/memory-approvals.d.ts +4 -1
  46. package/src/lib/record/memory-approvals.js +29 -0
  47. package/src/lib/record/memory-approvals.js.map +1 -1
  48. package/src/lib/record/memory-approvals.mjs +29 -0
  49. package/src/lib/record/memory-approvals.mjs.map +1 -1
  50. package/src/lib/record/memory-data-source.js +14 -2
  51. package/src/lib/record/memory-data-source.js.map +1 -1
  52. package/src/lib/record/memory-data-source.mjs +14 -2
  53. package/src/lib/record/memory-data-source.mjs.map +1 -1
  54. package/src/lib/record/memory-views.js +9 -1
  55. package/src/lib/record/memory-views.js.map +1 -1
  56. package/src/lib/record/memory-views.mjs +9 -1
  57. package/src/lib/record/memory-views.mjs.map +1 -1
  58. package/src/lib/record/survey-kit.js +11 -0
  59. package/src/lib/record/survey-kit.js.map +1 -1
  60. package/src/lib/record/survey-kit.mjs +11 -0
  61. package/src/lib/record/survey-kit.mjs.map +1 -1
  62. package/src/runtime.d.mts +1 -1
  63. package/src/runtime.d.ts +1 -1
  64. package/src/runtime.js.map +1 -1
  65. package/src/runtime.mjs.map +1 -1
@@ -22,7 +22,7 @@ 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 { draftRead, resumeStep } from './drafts.mjs';
25
+ import { draftRead, resumeStep, timedStore } from './drafts.mjs';
26
26
  import { waitingOn, workedOutFrom } from './waits.mjs';
27
27
  import { createUploads, fileBytesIn, uploadGoing, withoutFileBytes } from './uploads.mjs';
28
28
  import { hostScheduler } from './scheduler.mjs';
@@ -608,7 +608,8 @@ function innerForm(options, within, place, outer) {
608
608
  started: () => started,
609
609
  listened: () => listeners.size > 0,
610
610
  disposed: () => disposed,
611
- keep: () => void ((options.drafts || onServer()) && scheduleDraft()),
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())),
612
613
  order: (list) => void (steps = list ?? layoutSteps),
613
614
  refuse(name, message) {
614
615
  // Said until the field changes from what it holds, as a refused save's words are.
@@ -1666,8 +1667,10 @@ function innerForm(options, within, place, outer) {
1666
1667
  * form continues one by its key). A page without either keeps its drafts where it always did.
1667
1668
  */
1668
1669
  const onServer = () => !!options.dataSource?.saveDraft && !!options.dataSource.loadDraft && (page.continueLater === true || !!options.resume);
1669
- /** Whether drafts are kept: the app gave a store, or they are kept on the server. */
1670
- const draftsOn = () => !!options.drafts || onServer();
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();
1671
1674
  /** The key of the draft kept on the server for the record shown; null until the source gave one. */
1672
1675
  let serverKey = null;
1673
1676
  /** Counted each time the draft is let go (sent, discarded, another record shown): a keeping answered after is dropped. */
@@ -1707,7 +1710,7 @@ function innerForm(options, within, place, outer) {
1707
1710
  /** A draft in the browser's store, where the app gave one: a store that is full keeps none. */
1708
1711
  function keepHere(kept) {
1709
1712
  try {
1710
- options.drafts?.store.setItem(storeKey(), JSON.stringify(kept));
1713
+ draftStore?.setItem(storeKey(), JSON.stringify(kept));
1711
1714
  }
1712
1715
  catch {
1713
1716
  // The draft is a convenience: the form goes on without it.
@@ -1780,12 +1783,15 @@ function innerForm(options, within, place, outer) {
1780
1783
  serverKey = null;
1781
1784
  if (state.resume)
1782
1785
  set({ resume: null });
1783
- options.drafts?.store.removeItem(storeKey());
1786
+ draftStore?.removeItem(storeKey());
1784
1787
  }
1785
- /** A draft found: laid at once (`restore: "auto"`, or a draft continued from its key over nothing typed), else offered. */
1786
- function offer(draft, auto = options.drafts?.restore === 'auto') {
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') {
1787
1793
  // What the person typed while it was asked for is theirs: the draft is offered, never laid over it.
1788
- if (auto && !state.dirty.length && !Object.keys(state.unread).length)
1794
+ if ((auto || draft.timers) && !state.dirty.length && !Object.keys(state.unread).length)
1789
1795
  return applyDraft(draft);
1790
1796
  found = draft;
1791
1797
  set({ draft: { savedAt: draft.savedAt } });
@@ -1799,7 +1805,7 @@ function innerForm(options, within, place, outer) {
1799
1805
  const source = options.dataSource;
1800
1806
  const given = resumeKey;
1801
1807
  resumeKey = undefined;
1802
- const kept = given ? null : draftRead(options.drafts?.store.getItem(storeKey()) ?? null);
1808
+ const kept = given ? null : draftRead(draftStore?.getItem(storeKey()) ?? null);
1803
1809
  if (!given && !kept)
1804
1810
  return;
1805
1811
  if (kept && !('key' in kept))
@@ -1817,7 +1823,7 @@ function innerForm(options, within, place, outer) {
1817
1823
  set({ status: 'ready' });
1818
1824
  if (!draft) {
1819
1825
  // Sent, or gone: the form starts empty, and says so where it was asked to continue it.
1820
- options.drafts?.store.removeItem(storeKey());
1826
+ draftStore?.removeItem(storeKey());
1821
1827
  if (given)
1822
1828
  set({ resume: { key, missing: true } });
1823
1829
  return;
@@ -2186,6 +2192,11 @@ function innerForm(options, within, place, outer) {
2186
2192
  sentAs = baseline;
2187
2193
  // Its version now, for the next save to carry; and what the person may do now, when the save changed that.
2188
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 = [];
2189
2200
  set({ status: 'saved', recordId: result.id, values, dirty, warnings: withValues(values, collectWarnings), version: result.version ?? null, ...(result.can ? { can: result.can } : {}) });
2190
2201
  // Changed while it saved: kept in a draft, and saved as any change is.
2191
2202
  if (dirty.length) {
@@ -3432,7 +3443,7 @@ function innerForm(options, within, place, outer) {
3432
3443
  return () => void heard.delete(listener);
3433
3444
  },
3434
3445
  restoreDraft() {
3435
- const kept = found ?? draftRead(options.drafts?.store.getItem(storeKey()) ?? null);
3446
+ const kept = found ?? draftRead(draftStore?.getItem(storeKey()) ?? null);
3436
3447
  if (kept && 'values' in kept)
3437
3448
  applyDraft(kept);
3438
3449
  },