@fieldia/core 0.28.0 → 0.30.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 (251) hide show
  1. package/README.md +125 -1
  2. package/package.json +1 -1
  3. package/schema/page.schema.json +279 -6
  4. package/src/index.d.mts +8 -2
  5. package/src/index.d.ts +8 -2
  6. package/src/index.js +29 -2
  7. package/src/index.js.map +1 -1
  8. package/src/index.mjs +11 -2
  9. package/src/index.mjs.map +1 -1
  10. package/src/lib/format/approval-checks.d.mts +8 -0
  11. package/src/lib/format/approval-checks.d.ts +8 -0
  12. package/src/lib/format/approval-checks.js +29 -0
  13. package/src/lib/format/approval-checks.js.map +1 -0
  14. package/src/lib/format/approval-checks.mjs +26 -0
  15. package/src/lib/format/approval-checks.mjs.map +1 -0
  16. package/src/lib/format/approval.d.mts +20 -0
  17. package/src/lib/format/approval.d.ts +20 -0
  18. package/src/lib/format/approval.js +25 -0
  19. package/src/lib/format/approval.js.map +1 -0
  20. package/src/lib/format/approval.mjs +22 -0
  21. package/src/lib/format/approval.mjs.map +1 -0
  22. package/src/lib/format/assist-checks.d.mts +14 -0
  23. package/src/lib/format/assist-checks.d.ts +14 -0
  24. package/src/lib/format/assist-checks.js +68 -0
  25. package/src/lib/format/assist-checks.js.map +1 -0
  26. package/src/lib/format/assist-checks.mjs +64 -0
  27. package/src/lib/format/assist-checks.mjs.map +1 -0
  28. package/src/lib/format/assist.d.mts +41 -0
  29. package/src/lib/format/assist.d.ts +41 -0
  30. package/src/lib/format/assist.js +29 -0
  31. package/src/lib/format/assist.js.map +1 -0
  32. package/src/lib/format/assist.mjs +26 -0
  33. package/src/lib/format/assist.mjs.map +1 -0
  34. package/src/lib/format/editable-checks.d.mts +8 -0
  35. package/src/lib/format/editable-checks.d.ts +8 -0
  36. package/src/lib/format/editable-checks.js +38 -0
  37. package/src/lib/format/editable-checks.js.map +1 -0
  38. package/src/lib/format/editable-checks.mjs +35 -0
  39. package/src/lib/format/editable-checks.mjs.map +1 -0
  40. package/src/lib/format/field.d.mts +86 -0
  41. package/src/lib/format/field.d.ts +86 -0
  42. package/src/lib/format/field.js +56 -10
  43. package/src/lib/format/field.js.map +1 -1
  44. package/src/lib/format/field.mjs +55 -9
  45. package/src/lib/format/field.mjs.map +1 -1
  46. package/src/lib/format/head-checks.d.mts +10 -2
  47. package/src/lib/format/head-checks.d.ts +10 -2
  48. package/src/lib/format/head-checks.js +9 -2
  49. package/src/lib/format/head-checks.js.map +1 -1
  50. package/src/lib/format/head-checks.mjs +10 -4
  51. package/src/lib/format/head-checks.mjs.map +1 -1
  52. package/src/lib/format/languages-checks.d.mts +8 -0
  53. package/src/lib/format/languages-checks.d.ts +8 -0
  54. package/src/lib/format/languages-checks.js +40 -0
  55. package/src/lib/format/languages-checks.js.map +1 -0
  56. package/src/lib/format/languages-checks.mjs +37 -0
  57. package/src/lib/format/languages-checks.mjs.map +1 -0
  58. package/src/lib/format/later-checks.d.mts +4 -3
  59. package/src/lib/format/later-checks.d.ts +4 -3
  60. package/src/lib/format/later-checks.js +4 -3
  61. package/src/lib/format/later-checks.js.map +1 -1
  62. package/src/lib/format/later-checks.mjs +4 -3
  63. package/src/lib/format/later-checks.mjs.map +1 -1
  64. package/src/lib/format/layout.d.mts +129 -26
  65. package/src/lib/format/layout.d.ts +129 -26
  66. package/src/lib/format/layout.js +24 -3
  67. package/src/lib/format/layout.js.map +1 -1
  68. package/src/lib/format/layout.mjs +22 -1
  69. package/src/lib/format/layout.mjs.map +1 -1
  70. package/src/lib/format/layout.types.d.mts +2 -1
  71. package/src/lib/format/layout.types.d.ts +2 -1
  72. package/src/lib/format/list-editable.d.mts +20 -0
  73. package/src/lib/format/list-editable.d.ts +20 -0
  74. package/src/lib/format/list-editable.js +33 -0
  75. package/src/lib/format/list-editable.js.map +1 -0
  76. package/src/lib/format/list-editable.mjs +28 -0
  77. package/src/lib/format/list-editable.mjs.map +1 -0
  78. package/src/lib/format/page.d.mts +88 -9
  79. package/src/lib/format/page.d.ts +88 -9
  80. package/src/lib/format/page.js +4 -0
  81. package/src/lib/format/page.js.map +1 -1
  82. package/src/lib/format/page.mjs +4 -0
  83. package/src/lib/format/page.mjs.map +1 -1
  84. package/src/lib/format/photos-options.d.mts +29 -0
  85. package/src/lib/format/photos-options.d.ts +29 -0
  86. package/src/lib/format/photos-options.js +49 -0
  87. package/src/lib/format/photos-options.js.map +1 -0
  88. package/src/lib/format/photos-options.mjs +45 -0
  89. package/src/lib/format/photos-options.mjs.map +1 -0
  90. package/src/lib/format/references.d.mts +3 -1
  91. package/src/lib/format/references.d.ts +3 -1
  92. package/src/lib/format/references.js +41 -14
  93. package/src/lib/format/references.js.map +1 -1
  94. package/src/lib/format/references.mjs +42 -15
  95. package/src/lib/format/references.mjs.map +1 -1
  96. package/src/lib/format/scan-checks.d.mts +12 -0
  97. package/src/lib/format/scan-checks.d.ts +12 -0
  98. package/src/lib/format/scan-checks.js +89 -0
  99. package/src/lib/format/scan-checks.js.map +1 -0
  100. package/src/lib/format/scan-checks.mjs +86 -0
  101. package/src/lib/format/scan-checks.mjs.map +1 -0
  102. package/src/lib/format/scan-options.d.mts +84 -0
  103. package/src/lib/format/scan-options.d.ts +84 -0
  104. package/src/lib/format/scan-options.js +58 -0
  105. package/src/lib/format/scan-options.js.map +1 -0
  106. package/src/lib/format/scan-options.mjs +54 -0
  107. package/src/lib/format/scan-options.mjs.map +1 -0
  108. package/src/lib/format/sheet-head-checks.d.mts +10 -2
  109. package/src/lib/format/sheet-head-checks.d.ts +10 -2
  110. package/src/lib/format/sheet-head-checks.js +24 -0
  111. package/src/lib/format/sheet-head-checks.js.map +1 -1
  112. package/src/lib/format/sheet-head-checks.mjs +23 -0
  113. package/src/lib/format/sheet-head-checks.mjs.map +1 -1
  114. package/src/lib/format/upgrade.d.mts +6 -2
  115. package/src/lib/format/upgrade.d.ts +6 -2
  116. package/src/lib/format/upgrade.js +6 -2
  117. package/src/lib/format/upgrade.js.map +1 -1
  118. package/src/lib/format/upgrade.mjs +6 -2
  119. package/src/lib/format/upgrade.mjs.map +1 -1
  120. package/src/lib/format/version.d.mts +2 -2
  121. package/src/lib/format/version.d.ts +2 -2
  122. package/src/lib/format/version.js +2 -2
  123. package/src/lib/format/version.js.map +1 -1
  124. package/src/lib/format/version.mjs +2 -2
  125. package/src/lib/format/version.mjs.map +1 -1
  126. package/src/lib/record/approvals-part.d.mts +6 -0
  127. package/src/lib/record/approvals-part.d.ts +6 -0
  128. package/src/lib/record/approvals-part.js +14 -0
  129. package/src/lib/record/approvals-part.js.map +1 -0
  130. package/src/lib/record/approvals-part.mjs +7 -0
  131. package/src/lib/record/approvals-part.mjs.map +1 -0
  132. package/src/lib/record/approving.d.mts +45 -0
  133. package/src/lib/record/approving.d.ts +45 -0
  134. package/src/lib/record/approving.js +10 -0
  135. package/src/lib/record/approving.js.map +1 -0
  136. package/src/lib/record/approving.mjs +7 -0
  137. package/src/lib/record/approving.mjs.map +1 -0
  138. package/src/lib/record/check.js +4 -0
  139. package/src/lib/record/check.js.map +1 -1
  140. package/src/lib/record/check.mjs +4 -0
  141. package/src/lib/record/check.mjs.map +1 -1
  142. package/src/lib/record/data-source.d.mts +270 -0
  143. package/src/lib/record/data-source.d.ts +270 -0
  144. package/src/lib/record/data-source.js.map +1 -1
  145. package/src/lib/record/data-source.mjs.map +1 -1
  146. package/src/lib/record/files-part.d.mts +7 -0
  147. package/src/lib/record/files-part.d.ts +7 -0
  148. package/src/lib/record/files-part.js +167 -0
  149. package/src/lib/record/files-part.js.map +1 -0
  150. package/src/lib/record/files-part.mjs +163 -0
  151. package/src/lib/record/files-part.mjs.map +1 -0
  152. package/src/lib/record/filter.d.mts +5 -0
  153. package/src/lib/record/filter.d.ts +5 -0
  154. package/src/lib/record/filter.js +1 -0
  155. package/src/lib/record/filter.js.map +1 -1
  156. package/src/lib/record/filter.mjs +1 -1
  157. package/src/lib/record/filter.mjs.map +1 -1
  158. package/src/lib/record/form-approvals.d.mts +47 -0
  159. package/src/lib/record/form-approvals.d.ts +47 -0
  160. package/src/lib/record/form-approvals.js +186 -0
  161. package/src/lib/record/form-approvals.js.map +1 -0
  162. package/src/lib/record/form-approvals.mjs +180 -0
  163. package/src/lib/record/form-approvals.mjs.map +1 -0
  164. package/src/lib/record/form.d.mts +32 -1
  165. package/src/lib/record/form.d.ts +32 -1
  166. package/src/lib/record/form.js +60 -15
  167. package/src/lib/record/form.js.map +1 -1
  168. package/src/lib/record/form.mjs +60 -15
  169. package/src/lib/record/form.mjs.map +1 -1
  170. package/src/lib/record/languages-part.d.mts +32 -0
  171. package/src/lib/record/languages-part.d.ts +32 -0
  172. package/src/lib/record/languages-part.js +104 -0
  173. package/src/lib/record/languages-part.js.map +1 -0
  174. package/src/lib/record/languages-part.mjs +96 -0
  175. package/src/lib/record/languages-part.mjs.map +1 -0
  176. package/src/lib/record/languages.d.mts +26 -0
  177. package/src/lib/record/languages.d.ts +26 -0
  178. package/src/lib/record/languages.js +20 -0
  179. package/src/lib/record/languages.js.map +1 -0
  180. package/src/lib/record/languages.mjs +15 -0
  181. package/src/lib/record/languages.mjs.map +1 -0
  182. package/src/lib/record/locales/ar.js +5 -0
  183. package/src/lib/record/locales/ar.js.map +1 -1
  184. package/src/lib/record/locales/ar.mjs +5 -0
  185. package/src/lib/record/locales/ar.mjs.map +1 -1
  186. package/src/lib/record/locales/de.js +5 -0
  187. package/src/lib/record/locales/de.js.map +1 -1
  188. package/src/lib/record/locales/de.mjs +5 -0
  189. package/src/lib/record/locales/de.mjs.map +1 -1
  190. package/src/lib/record/locales/fr.js +5 -0
  191. package/src/lib/record/locales/fr.js.map +1 -1
  192. package/src/lib/record/locales/fr.mjs +5 -0
  193. package/src/lib/record/locales/fr.mjs.map +1 -1
  194. package/src/lib/record/memory-approvals.d.mts +47 -0
  195. package/src/lib/record/memory-approvals.d.ts +47 -0
  196. package/src/lib/record/memory-approvals.js +78 -0
  197. package/src/lib/record/memory-approvals.js.map +1 -0
  198. package/src/lib/record/memory-approvals.mjs +75 -0
  199. package/src/lib/record/memory-approvals.mjs.map +1 -0
  200. package/src/lib/record/memory-assist.d.mts +53 -0
  201. package/src/lib/record/memory-assist.d.ts +53 -0
  202. package/src/lib/record/memory-assist.js +519 -0
  203. package/src/lib/record/memory-assist.js.map +1 -0
  204. package/src/lib/record/memory-assist.mjs +513 -0
  205. package/src/lib/record/memory-assist.mjs.map +1 -0
  206. package/src/lib/record/memory-clusters.d.mts +18 -0
  207. package/src/lib/record/memory-clusters.d.ts +18 -0
  208. package/src/lib/record/memory-clusters.js +83 -0
  209. package/src/lib/record/memory-clusters.js.map +1 -0
  210. package/src/lib/record/memory-clusters.mjs +80 -0
  211. package/src/lib/record/memory-clusters.mjs.map +1 -0
  212. package/src/lib/record/memory-data-source.d.mts +43 -3
  213. package/src/lib/record/memory-data-source.d.ts +43 -3
  214. package/src/lib/record/memory-data-source.js +105 -19
  215. package/src/lib/record/memory-data-source.js.map +1 -1
  216. package/src/lib/record/memory-data-source.mjs +105 -19
  217. package/src/lib/record/memory-data-source.mjs.map +1 -1
  218. package/src/lib/record/memory-later.d.mts +1 -1
  219. package/src/lib/record/memory-later.d.ts +1 -1
  220. package/src/lib/record/memory-later.js +4 -1
  221. package/src/lib/record/memory-later.js.map +1 -1
  222. package/src/lib/record/memory-later.mjs +4 -1
  223. package/src/lib/record/memory-later.mjs.map +1 -1
  224. package/src/lib/record/messages.d.mts +14 -0
  225. package/src/lib/record/messages.d.ts +14 -0
  226. package/src/lib/record/messages.js +5 -0
  227. package/src/lib/record/messages.js.map +1 -1
  228. package/src/lib/record/messages.mjs +5 -0
  229. package/src/lib/record/messages.mjs.map +1 -1
  230. package/src/lib/record/run.d.mts +6 -1
  231. package/src/lib/record/run.d.ts +6 -1
  232. package/src/lib/record/run.js.map +1 -1
  233. package/src/lib/record/run.mjs.map +1 -1
  234. package/src/lib/record/uploads.d.mts +35 -9
  235. package/src/lib/record/uploads.d.ts +35 -9
  236. package/src/lib/record/uploads.js +49 -169
  237. package/src/lib/record/uploads.js.map +1 -1
  238. package/src/lib/record/uploads.mjs +42 -164
  239. package/src/lib/record/uploads.mjs.map +1 -1
  240. package/src/lib/record/values.d.mts +23 -0
  241. package/src/lib/record/values.d.ts +23 -0
  242. package/src/lib/record/values.js +4 -0
  243. package/src/lib/record/values.js.map +1 -1
  244. package/src/lib/record/values.mjs +4 -0
  245. package/src/lib/record/values.mjs.map +1 -1
  246. package/src/runtime.d.mts +12 -5
  247. package/src/runtime.d.ts +12 -5
  248. package/src/runtime.js +18 -3
  249. package/src/runtime.js.map +1 -1
  250. package/src/runtime.mjs +7 -1
  251. package/src/runtime.mjs.map +1 -1
@@ -21,6 +21,8 @@ import { recordSentence } from './record-words.mjs';
21
21
  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
+ import { formLanguagesPart, hasLanguages } from './languages.mjs';
25
+ import { formApprovalsPart } from './approving.mjs';
24
26
  import { draftRead, resumeStep } from './drafts.mjs';
25
27
  import { waitingOn, workedOutFrom } from './waits.mjs';
26
28
  import { createUploads, fileBytesIn, uploadGoing, withoutFileBytes } from './uploads.mjs';
@@ -361,6 +363,7 @@ function innerForm(options, within, place, outer) {
361
363
  // Worked out once the form has its state, as the limits part comes (`limitsCode`).
362
364
  limits: null,
363
365
  resume: null,
366
+ approval: null,
364
367
  };
365
368
  started = true;
366
369
  /**
@@ -957,6 +960,10 @@ function innerForm(options, within, place, outer) {
957
960
  }
958
961
  if (hasOptionRules(page))
959
962
  void track(optionsCode());
963
+ // A text in several languages is checked by the languages part: asked for at once, a save waiting for it.
964
+ const languagesCode = () => formLanguagesPart.load().then(() => undefined, () => undefined);
965
+ if (hasLanguages(page))
966
+ void track(languagesCode());
960
967
  /** A table line's choice's options as they are offered on that line: its list's once loaded, or its own, by their conditions once they came. */
961
968
  function lineOffered(lines, line, column, record) {
962
969
  if (optionKit)
@@ -1124,6 +1131,9 @@ function innerForm(options, within, place, outer) {
1124
1131
  const answer = answerOf(node);
1125
1132
  if (answer !== undefined && signedNow().has(answer))
1126
1133
  readonly = true;
1134
+ // A record waiting for approval: locked but for the fields the page leaves open (the approvals part).
1135
+ if (answer !== undefined && approvalKit?.locks(answer))
1136
+ readonly = true;
1127
1137
  return {
1128
1138
  invisible,
1129
1139
  readonly: readonly || (def !== undefined && lockedField(def)),
@@ -1762,6 +1772,15 @@ function innerForm(options, within, place, outer) {
1762
1772
  }
1763
1773
  /** A choice of a full option, picked by the person, refused under its field: the field keeps what it held. */
1764
1774
  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());
1765
1784
  function scheduleAutosave() {
1766
1785
  const autosave = options.autosave;
1767
1786
  // A record the person may not change is never saved unasked: it would only be refused.
@@ -1908,6 +1927,12 @@ function innerForm(options, within, place, outer) {
1908
1927
  if (asked !== generation)
1909
1928
  return elsewhere();
1910
1929
  }
1930
+ // A text in several languages, checked in each once its part has come.
1931
+ if (!formLanguagesPart.loaded && hasLanguages(page)) {
1932
+ await languagesCode();
1933
+ if (asked !== generation)
1934
+ return elsewhere();
1935
+ }
1911
1936
  // Values the app's functions are asked for are waited for — those that failed asked again — so what is saved is worked out.
1912
1937
  if (asking.size || Object.values(state.asked).some((found) => found.failed !== undefined)) {
1913
1938
  set({ status: 'saving', error: null, saveProblem: null });
@@ -2031,6 +2056,9 @@ function innerForm(options, within, place, outer) {
2031
2056
  scheduleDraft();
2032
2057
  scheduleAutosave();
2033
2058
  }
2059
+ // A save may have sent it for approval, or moved it on: its approvals asked again.
2060
+ if (keepsApprovals)
2061
+ void track(askApprovals());
2034
2062
  // The record as this save left it: a change made while it was on its way is no value of it.
2035
2063
  emit('save', { recordId: state.recordId, values: baseline });
2036
2064
  void track(moment('on.afterSave', page.on?.afterSave, chain, false));
@@ -2237,7 +2265,7 @@ function innerForm(options, within, place, outer) {
2237
2265
  throw new Error(`"${name}" cannot make records: its data source has no create, or it is not a link`);
2238
2266
  return track(source.create({ model, name: typed.trim(), ...(Object.keys(values).length ? { values } : {}) }));
2239
2267
  }
2240
- async function runSearch(def, name, ctx, query, limit, { model, ids, exact } = {}) {
2268
+ async function runSearch(def, name, ctx, query, limit, { model, ids, exact, field } = {}) {
2241
2269
  // Formatted text names people and records in place (@mention, #4127): searched by the model they are of.
2242
2270
  const byModel = def.type === 'reference' || def.type === 'json' || def.type === 'html';
2243
2271
  if (def.type !== 'many2one' && def.type !== 'many2many' && !byModel) {
@@ -2250,9 +2278,11 @@ function innerForm(options, within, place, outer) {
2250
2278
  return [];
2251
2279
  // Every valueFrom, in groups too, becomes the value it names before the search goes out.
2252
2280
  const own = byModel || !def.filter ? undefined : resolveFilter(def.filter, ctx);
2253
- const filter = ids ? [...(own ?? []), { field: 'id', op: 'in', value: ids }] : own;
2281
+ const kept = ids ? [...(own ?? []), { field: 'id', op: 'in', value: ids }] : own;
2282
+ // A code read by a scanner: the records whose field holds it, whatever their names.
2283
+ const filter = field ? [...(kept ?? []), { field, op: '=', value: query }] : kept;
2254
2284
  const relation = byModel ? model : def.relation;
2255
- return track(source.search({ model: relation, query, ...(filter ? { filter } : {}), limit, ...(exact ? { exact: true } : {}) }));
2285
+ return track(source.search({ model: relation, query: field ? '' : query, ...(filter ? { filter } : {}), limit, ...(exact ? { exact: true } : {}) }));
2256
2286
  }
2257
2287
  function lineField(field) {
2258
2288
  const def = fieldDef(field);
@@ -2293,6 +2323,9 @@ function innerForm(options, within, place, outer) {
2293
2323
  loadedOnce = true;
2294
2324
  if (asksLimits())
2295
2325
  void track(askLimits());
2326
+ // Its approvals: where it stands, what was decided, what this person may decide — its fields locked while it waits.
2327
+ if (keepsApprovals)
2328
+ void track(askApprovals());
2296
2329
  if (again)
2297
2330
  return;
2298
2331
  offerDraft();
@@ -2325,7 +2358,7 @@ function innerForm(options, within, place, outer) {
2325
2358
  found = null;
2326
2359
  serverKey = null;
2327
2360
  draftEpoch++;
2328
- 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 };
2361
+ 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 };
2329
2362
  if (id != null) {
2330
2363
  set({ ...fresh, recordId: id, status: 'idle' });
2331
2364
  await load();
@@ -2744,7 +2777,7 @@ function innerForm(options, within, place, outer) {
2744
2777
  // Nothing of the record changed; what shows of it may have.
2745
2778
  set({});
2746
2779
  },
2747
- fieldReadonly: (name) => lockedField(fieldDef(name)) || state.can?.edit === false || state.can?.readonlyFields?.includes(name) === true || signedNow().has(name),
2780
+ fieldReadonly: (name) => lockedField(fieldDef(name)) || state.can?.edit === false || state.can?.readonlyFields?.includes(name) === true || signedNow().has(name) || !!approvalKit?.locks(name),
2748
2781
  loadChoices: (field) => loadChoices(field),
2749
2782
  loadDefinitions,
2750
2783
  addDefinition,
@@ -3242,6 +3275,14 @@ function innerForm(options, within, place, outer) {
3242
3275
  return saved;
3243
3276
  },
3244
3277
  refreshLimits: () => askLimits(),
3278
+ refreshApprovals: askApprovals,
3279
+ async decide(level, decision, reason) {
3280
+ const kit = await approvalsCode();
3281
+ if (!kit)
3282
+ throw new Error('No approvals on this page');
3283
+ return track(kit.decide(level, decision, reason));
3284
+ },
3285
+ approvalLock: (name) => approvalKit?.lockWords(name) ?? null,
3245
3286
  async reloadTheirs() {
3246
3287
  // A save on its way first: its answer is not dropped by the load.
3247
3288
  const asked = shownNow();
@@ -3426,6 +3467,9 @@ function innerForm(options, within, place, outer) {
3426
3467
  // choice's places as the source tells them: a record's own once it is loaded, its own choices not counted against it.
3427
3468
  if (keepsLimits())
3428
3469
  void track(limitsCode());
3470
+ // A record's approvals: their part asked for as the form is made, ready by the time its record is loaded.
3471
+ if (keepsApprovals)
3472
+ void track(approvalsCode());
3429
3473
  // A new form has its values now — a draft continued from its key once it is laid — and opens once whoever made it
3430
3474
  // has had the chance to listen.
3431
3475
  if (state.recordId == null) {
@@ -3596,7 +3640,14 @@ function indexLayout(root, functions) {
3596
3640
  ...(does.length ? { does } : {}),
3597
3641
  source,
3598
3642
  });
3599
- // A button's menu: each item a button of the record, hidden with it.
3643
+ // A menu's built-in Archive shows while the record is active, Unarchive while it is not.
3644
+ const builtin = source.builtin;
3645
+ if (builtin === 'archive' || builtin === 'unarchive') {
3646
+ const own = index.get(source.id);
3647
+ const mine = own.invisible;
3648
+ own.invisible = { ...mine, evaluate: (context, env) => (context['active'] === false) !== (builtin === 'unarchive') || mine.evaluate(context, env) };
3649
+ }
3650
+ // A button's menu, and a menu inside a menu: each item a button of the record, hidden with it.
3600
3651
  if (kind === 'button')
3601
3652
  for (const item of source.menu ?? [])
3602
3653
  add(item, source.id, 'button', { invisible: item.invisible });
@@ -3705,16 +3756,10 @@ function indexLayout(root, functions) {
3705
3756
  walk(head?.band?.children ?? [], root.id);
3706
3757
  if (root.sidePanel)
3707
3758
  add(root.sidePanel, root.id, 'other', { invisible: root.sidePanel.invisible });
3708
- // The gear menu's items are buttons of the record; Archive shows while it is active, Unarchive while it is not.
3709
- for (const item of root.toolbar?.menu ?? []) {
3759
+ // The gear menu's items are buttons of the record — those of a menu inside it too, hidden with the item that opens
3760
+ // it (add's own); Archive shows while it is active, Unarchive while it is not.
3761
+ for (const item of root.toolbar?.menu ?? [])
3710
3762
  add(item, root.id, 'button', { invisible: item.invisible });
3711
- if (item.builtin === 'archive' || item.builtin === 'unarchive') {
3712
- const own = index.get(item.id);
3713
- const mine = own.invisible;
3714
- const archived = item.builtin === 'unarchive';
3715
- own.invisible = { ...mine, evaluate: (context, env) => (context['active'] === false) !== archived || mine.evaluate(context, env) };
3716
- }
3717
- }
3718
3763
  if (root.attachmentPreview)
3719
3764
  add({ id: '#preview', ...root.attachmentPreview }, root.id, 'other', { invisible: root.attachmentPreview.invisible });
3720
3765
  }