@conduction/nextcloud-vue 2.117.0 → 2.118.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +2 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +6 -6
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js +2 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +18 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +14 -8
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +11 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +9 -4
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnMailTab/CnMailTab.vue2.js +2 -1
- package/dist/esm/components/CnMailTab/CnMailTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +71 -0
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +25 -9
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +11 -2
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSearchPage/CnSearchPage.vue.js +1 -1
- package/dist/esm/components/CnSearchPage/CnSearchPage.vue2.js +161 -5
- package/dist/esm/components/CnSearchPage/CnSearchPage.vue2.js.map +1 -1
- package/dist/esm/components/CnSearchPage/CnSearchPage.vue3.js +9 -9
- package/dist/esm/components/CnSearchPage/CnSearchPage.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +35 -5
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue.js +9 -0
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue.js.map +1 -0
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue2.js +435 -0
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue2.js.map +1 -0
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue3.js +300 -0
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue3.js.map +1 -0
- package/dist/esm/composables/useEndpointSource.js +1 -36
- package/dist/esm/composables/useEndpointSource.js.map +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue.js +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue2.js +57 -336
- package/dist/esm/dialogs/CnTransitionInputDialog.vue2.js.map +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue3.js +18 -155
- package/dist/esm/dialogs/CnTransitionInputDialog.vue3.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +2 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +2 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +308 -229
- package/dist/esm/utils/actionsDispatcher.js +3 -2
- package/dist/esm/utils/actionsDispatcher.js.map +1 -1
- package/dist/esm/utils/interpolateUrlTokens.js +43 -0
- package/dist/esm/utils/interpolateUrlTokens.js.map +1 -0
- package/dist/esm/utils/labelsByField.js +72 -0
- package/dist/esm/utils/labelsByField.js.map +1 -0
- package/dist/esm/utils/listCreateAction.js +91 -0
- package/dist/esm/utils/listCreateAction.js.map +1 -0
- package/dist/esm/utils/transitionInputs.js +493 -0
- package/dist/esm/utils/transitionInputs.js.map +1 -0
- package/dist/esm/utils/visibleWhen.js +9 -2
- package/dist/esm/utils/visibleWhen.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +6690 -5404
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +308 -229
- package/l10n/en.json +2 -1
- package/l10n/nl.json +2 -1
- package/package.json +1 -1
- package/src/components/CnActionsBar/CnActionsBar.vue +6 -6
- package/src/components/CnDashboardPage/CnDashboardPage.vue +27 -5
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +11 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +9 -4
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +86 -0
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +11 -2
- package/src/components/CnSearchPage/CnSearchPage.vue +170 -14
- package/src/components/CnTabsWidget/CnTabsWidget.vue +35 -5
- package/src/components/CnTransitionInputFields/CnTransitionInputFields.vue +750 -0
- package/src/components/CnTransitionInputFields/index.js +11 -0
- package/src/components/index.js +1 -0
- package/src/composables/useEndpointSource.js +3 -36
- package/src/dialogs/CnTransitionInputDialog.vue +73 -465
- package/src/index.js +2 -0
- package/src/utils/interpolateUrlTokens.js +40 -0
- package/src/utils/labelsByField.js +69 -0
- package/src/utils/listCreateAction.js +87 -0
- package/src/utils/transitionInputs.js +489 -0
- package/src/utils/visibleWhen.js +9 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listCreateAction.js","sources":["../../../src/utils/listCreateAction.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * listCreateAction — what a list's Add does when the manifest declares a\n * `content.createAction`, and whether it shows.\n *\n * A list's Add opens its own create form by default. `createAction` replaces\n * that with one action in the manifest action shape (`open-modal`,\n * `handler`, `navigate`, …), for a create that has to go through the app\n * rather than straight to the object API. Its `visibleWhen` is evaluated\n * against the record of the page the list sits on; while it fails, the Add\n * is hidden and `hiddenNote` may say why.\n *\n * `hiddenNote` is a string, or a list of `{ when, text }` where the first\n * entry whose `when` holds against the record wins. The text is returned\n * untranslated; the caller runs it through the host translate.\n *\n * Both CnObjectListWidget (its own Add buttons) and CnDetailWidgetHost (the\n * catalog Add in the card or panel menu) read this, so the two never\n * disagree about whether Add is there.\n *\n * @module utils/listCreateAction\n */\n\nimport { evaluateVisibleWhenLocal } from './visibleWhen.js'\n\n/**\n * The declared create action, or null.\n *\n * @param {object|null} content The list's `content` block.\n * @return {object|null} The action.\n */\nexport function listCreateAction(content) {\n\tconst action = content && content.createAction\n\tif (!action || typeof action !== 'object' || Array.isArray(action) || typeof action.type !== 'string') {\n\t\treturn null\n\t}\n\treturn action\n}\n\n/**\n * The note for a hidden create action, untranslated, or ''.\n *\n * @param {string|Array<{when: object, text: string}>|undefined} note The declared `hiddenNote`.\n * @param {object} record The page record.\n * @return {string} The note.\n */\nfunction hiddenNoteFor(note, record) {\n\tif (typeof note === 'string') {\n\t\treturn note\n\t}\n\tif (!Array.isArray(note)) {\n\t\treturn ''\n\t}\n\tconst hit = note.find((entry) => entry && typeof entry.text === 'string' && evaluateVisibleWhenLocal(entry.when, record))\n\treturn hit ? hit.text : ''\n}\n\n/**\n * Whether the create action shows against this record, and the note to show\n * instead when it does not.\n *\n * Without a `visibleWhen` the action always shows. With one, it stays hidden\n * until the record is known, so a page still loading does not flash an Add\n * it is about to take away; no note is given then either.\n *\n * @param {object|null} content The list's `content` block.\n * @param {object|null} record The page record, or null while it loads.\n * @return {{action: (object|null), visible: boolean, note: string}} The state.\n */\nexport function createActionState(content, record) {\n\tconst action = listCreateAction(content)\n\tif (!action) {\n\t\treturn { action: null, visible: true, note: '' }\n\t}\n\tif (!action.visibleWhen) {\n\t\treturn { action, visible: true, note: '' }\n\t}\n\tif (!record || typeof record !== 'object') {\n\t\treturn { action, visible: false, note: '' }\n\t}\n\tif (evaluateVisibleWhenLocal(action.visibleWhen, record)) {\n\t\treturn { action, visible: true, note: '' }\n\t}\n\treturn { action, visible: false, note: hiddenNoteFor(action.hiddenNote, record) }\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,OAAO,EAAE;AAC1C,CAAC,MAAM,MAAM,GAAG,OAAO,IAAI,OAAO,CAAC,aAAY;AAC/C,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE;AACxG,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,OAAO,MAAM;AACd,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE;AACrC,CAAC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;AAC/B,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;AAC3B,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,wBAAwB,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,EAAC;AAC1H,CAAC,OAAO,GAAG,GAAG,GAAG,CAAC,IAAI,GAAG,EAAE;AAC3B,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE;AACnD,CAAC,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,EAAC;AACzC,CAAC,IAAI,CAAC,MAAM,EAAE;AACd,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;AAClD,EAAE;AACF,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE;AAC1B,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;AAC5C,EAAE;AACF,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;AAC5C,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE;AAC7C,EAAE;AACF,CAAC,IAAI,wBAAwB,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE;AAC3D,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;AAC5C,EAAE;AACF,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE;AAClF;;;;"}
|
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import { formatDateForDisplay } from './dateDisplay.js';
|
|
2
|
+
import { readPath } from './readPath.js';
|
|
3
|
+
import { fieldsFromSchema } from './schema.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
7
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
8
|
+
*
|
|
9
|
+
* The declared inputs of a transition or an act, resolved into the fields a
|
|
10
|
+
* form draws, the check that they are complete and the values they send.
|
|
11
|
+
*
|
|
12
|
+
* Shared by CnTransitionInputDialog (the inputs in their own dialog) and
|
|
13
|
+
* CnTransitionInputFields (the same inputs drawn wherever a host puts them,
|
|
14
|
+
* such as under the chosen card of a list of acts), so the two never decide
|
|
15
|
+
* differently whether a form may be confirmed.
|
|
16
|
+
*
|
|
17
|
+
* @module utils/transitionInputs
|
|
18
|
+
* @spec openspec/changes/dq-abstract-forms/specs/transition-inputs/spec.md
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The picker value of an input's "Other" entry. Never sent: see `submittedValue`.
|
|
24
|
+
*
|
|
25
|
+
* @type {string}
|
|
26
|
+
*/
|
|
27
|
+
const OTHER_OPTION_VALUE = '__other';
|
|
28
|
+
|
|
29
|
+
/** Widgets an input may name outright, whatever the schema says. */
|
|
30
|
+
const DECLARED_WIDGETS = ['text', 'textarea', 'number', 'date', 'select', 'template'];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* A non-empty trimmed string, or null.
|
|
34
|
+
*
|
|
35
|
+
* @param {unknown} value The value.
|
|
36
|
+
* @return {string|null}
|
|
37
|
+
*/
|
|
38
|
+
function text(value) {
|
|
39
|
+
return typeof value === 'string' && value.trim() !== '' ? value.trim() : null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The choices an input declares, as `[{ value, label }]`, or null when it
|
|
44
|
+
* declares none. An option without a value is dropped, because it cannot be
|
|
45
|
+
* sent; one without a label shows its value.
|
|
46
|
+
*
|
|
47
|
+
* @param {unknown} options The input's declared `options`.
|
|
48
|
+
* @return {Array<{value: string, label: string}>|null}
|
|
49
|
+
*/
|
|
50
|
+
function declaredOptions(options) {
|
|
51
|
+
if (!Array.isArray(options)) {
|
|
52
|
+
return null
|
|
53
|
+
}
|
|
54
|
+
const usable = options
|
|
55
|
+
.filter((option) => option && typeof option === 'object'
|
|
56
|
+
&& (typeof option.value === 'string' || typeof option.value === 'number')
|
|
57
|
+
&& String(option.value).trim() !== '')
|
|
58
|
+
.map((option) => ({
|
|
59
|
+
value: String(option.value),
|
|
60
|
+
label: text(option.label) || String(option.value),
|
|
61
|
+
}));
|
|
62
|
+
return usable.length > 0 ? usable : null
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The declared inputs, normalised. An input without a `field` is dropped.
|
|
67
|
+
*
|
|
68
|
+
* @param {unknown} declared The declared `inputs`.
|
|
69
|
+
* @return {Array<object>} The inputs.
|
|
70
|
+
*/
|
|
71
|
+
function normaliseInputs(declared) {
|
|
72
|
+
if (!Array.isArray(declared)) {
|
|
73
|
+
return []
|
|
74
|
+
}
|
|
75
|
+
return declared
|
|
76
|
+
.filter((input) => input && typeof input.field === 'string' && input.field !== '')
|
|
77
|
+
.map((input) => ({
|
|
78
|
+
...input,
|
|
79
|
+
field: input.field,
|
|
80
|
+
required: input.required === true,
|
|
81
|
+
picker: input.picker && typeof input.picker === 'object' ? input.picker : null,
|
|
82
|
+
fields: Array.isArray(input.fields) ? input.fields.filter((f) => typeof f === 'string') : null,
|
|
83
|
+
label: text(input.label),
|
|
84
|
+
options: declaredOptions(input.options),
|
|
85
|
+
widget: DECLARED_WIDGETS.includes(input.widget) ? input.widget : null,
|
|
86
|
+
optionsSource: text(input.optionsSource),
|
|
87
|
+
other: input.other && typeof input.other === 'object' && text(input.other.label) ? input.other : null,
|
|
88
|
+
}))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Clamp a `fieldsFromSchema()` widget to the minimal set the inputs draw.
|
|
93
|
+
* Anything richer (select, user picker, …) degrades to a plain text input
|
|
94
|
+
* rather than pulling a form engine in.
|
|
95
|
+
*
|
|
96
|
+
* @param {string} widget The resolved widget name.
|
|
97
|
+
* @return {'text'|'textarea'|'number'|'checkbox'}
|
|
98
|
+
*/
|
|
99
|
+
function clampWidget(widget) {
|
|
100
|
+
if (widget === 'checkbox' || widget === 'switch') {
|
|
101
|
+
return 'checkbox'
|
|
102
|
+
}
|
|
103
|
+
if (widget === 'textarea' || widget === 'number') {
|
|
104
|
+
return widget
|
|
105
|
+
}
|
|
106
|
+
return 'text'
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The choices an `optionsSource` input reads off the context: each row's
|
|
111
|
+
* `optionValue` (default `value`) and `optionLabel` (default `label`), with
|
|
112
|
+
* the rows kept so `helperFrom` can read another key of the picked one.
|
|
113
|
+
*
|
|
114
|
+
* @param {object} input The normalised input.
|
|
115
|
+
* @param {object} context What the source path is read from.
|
|
116
|
+
* @return {{options: Array<{value: string, label: string}>, rows: Array<object>}}
|
|
117
|
+
*/
|
|
118
|
+
function sourcedOptions(input, context) {
|
|
119
|
+
const list = readPath(context || {}, input.optionsSource);
|
|
120
|
+
const valueKey = text(input.optionValue) || 'value';
|
|
121
|
+
const labelKey = text(input.optionLabel) || 'label';
|
|
122
|
+
const options = [];
|
|
123
|
+
const rows = [];
|
|
124
|
+
for (const row of Array.isArray(list) ? list : []) {
|
|
125
|
+
const value = row && typeof row === 'object' ? row[valueKey] : row;
|
|
126
|
+
if ((typeof value !== 'string' && typeof value !== 'number') || String(value).trim() === '') {
|
|
127
|
+
continue
|
|
128
|
+
}
|
|
129
|
+
const label = row && typeof row === 'object' ? row[labelKey] : null;
|
|
130
|
+
options.push({ value: String(value), label: text(label) || String(value) });
|
|
131
|
+
rows.push(row);
|
|
132
|
+
}
|
|
133
|
+
return { options, rows }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The descriptor of an object input: its sub-properties (narrowed to the
|
|
138
|
+
* input's `fields` when it names them), each with its own widget. A
|
|
139
|
+
* sub-field is required when the input names it in `fields` and the
|
|
140
|
+
* sub-schema lists it as required, or when the whole input is required and
|
|
141
|
+
* it is the only one asked.
|
|
142
|
+
*
|
|
143
|
+
* @param {object} input The normalised input.
|
|
144
|
+
* @param {object} prop The object property's schema.
|
|
145
|
+
* @return {object} The field descriptor.
|
|
146
|
+
*/
|
|
147
|
+
function objectField(input, prop) {
|
|
148
|
+
const subs = fieldsFromSchema(prop, { includeReadOnly: true, ...(input.fields ? { include: input.fields } : {}) });
|
|
149
|
+
const required = Array.isArray(prop.required) ? prop.required : [];
|
|
150
|
+
const only = subs.length === 1;
|
|
151
|
+
return {
|
|
152
|
+
key: input.field,
|
|
153
|
+
label: prop.title || input.field,
|
|
154
|
+
description: prop.description || '',
|
|
155
|
+
widget: 'object',
|
|
156
|
+
required: input.required,
|
|
157
|
+
subFields: subs.map((sub) => ({
|
|
158
|
+
...sub,
|
|
159
|
+
widget: clampWidget(sub.widget),
|
|
160
|
+
required: (input.fields ? required.includes(sub.key) : sub.required === true) || (input.required && only),
|
|
161
|
+
})),
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The field one input resolves to before the context is applied.
|
|
167
|
+
*
|
|
168
|
+
* @param {object} input The normalised input.
|
|
169
|
+
* @param {object} resolved The schema's fields for the declared keys.
|
|
170
|
+
* @param {object} options What the field is resolved against.
|
|
171
|
+
* @param {object|null} options.schema The object's JSON Schema.
|
|
172
|
+
* @param {string} options.register Register the reference pickers fall back to.
|
|
173
|
+
* @param {object} options.context What `optionsSource` reads.
|
|
174
|
+
* @return {object} The field descriptor.
|
|
175
|
+
*/
|
|
176
|
+
function baseField(input, resolved, { schema, register, context }) {
|
|
177
|
+
const field = resolved.find((f) => f.key === input.field);
|
|
178
|
+
const label = input.label || (field && field.label) || input.field;
|
|
179
|
+
const description = text(input.description) || (field && field.description) || '';
|
|
180
|
+
|
|
181
|
+
if (input.widget === 'template') {
|
|
182
|
+
return { key: input.field, label, description, widget: 'template', required: false }
|
|
183
|
+
}
|
|
184
|
+
if (input.optionsSource) {
|
|
185
|
+
const { options, rows } = sourcedOptions(input, context);
|
|
186
|
+
return { key: input.field, label, description, widget: 'select', options, rows, required: input.required }
|
|
187
|
+
}
|
|
188
|
+
if (input.options) {
|
|
189
|
+
return { key: input.field, label, description, widget: 'select', options: input.options, rows: [], required: input.required }
|
|
190
|
+
}
|
|
191
|
+
if (input.widget && input.widget !== 'select') {
|
|
192
|
+
return { key: input.field, label, description, widget: input.widget, required: input.required }
|
|
193
|
+
}
|
|
194
|
+
const prop = (schema && schema.properties && schema.properties[input.field]) || null;
|
|
195
|
+
if (prop && prop.type === 'object' && prop.properties && typeof prop.properties === 'object') {
|
|
196
|
+
return objectField(input, prop)
|
|
197
|
+
}
|
|
198
|
+
if (!field) {
|
|
199
|
+
return { key: input.field, label, description, widget: 'text', required: input.required }
|
|
200
|
+
}
|
|
201
|
+
if (field.reference && field.reference.multiple !== true) {
|
|
202
|
+
const referenceRegister = field.reference.register || register;
|
|
203
|
+
if (referenceRegister !== '') {
|
|
204
|
+
return { ...field, label, widget: 'reference', referenceRegister, picker: input.picker, required: input.required }
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return { ...field, label, widget: clampWidget(field.widget), required: input.required }
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* A date `days` whole days after `now`, or null without a positive count.
|
|
212
|
+
*
|
|
213
|
+
* @param {unknown} days The count.
|
|
214
|
+
* @param {Date} now Today.
|
|
215
|
+
* @return {Date|null}
|
|
216
|
+
*/
|
|
217
|
+
function daysAfter(days, now) {
|
|
218
|
+
const count = Number.parseInt(String(days ?? ''), 10);
|
|
219
|
+
if (!Number.isFinite(count) || count < 1) {
|
|
220
|
+
return null
|
|
221
|
+
}
|
|
222
|
+
const date = new Date(now.getTime());
|
|
223
|
+
date.setDate(date.getDate() + count);
|
|
224
|
+
return date
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* A date as a reader sees it: day, short month and year.
|
|
229
|
+
*
|
|
230
|
+
* @param {Date|string} value The date.
|
|
231
|
+
* @return {string} The text, '' when not a date.
|
|
232
|
+
*/
|
|
233
|
+
function dateText(value) {
|
|
234
|
+
const shown = formatDateForDisplay(value, { dateOptions: { day: 'numeric', month: 'short', year: 'numeric' } });
|
|
235
|
+
return shown ? shown.text : ''
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Fill a preview template from the input values.
|
|
240
|
+
*
|
|
241
|
+
* Tokens are `{field}`, `{field|date}`, `{today|date}` and
|
|
242
|
+
* `{today+field|date}` (today plus that many days). A token that resolves
|
|
243
|
+
* to nothing empties the whole preview, so a half-filled sentence is never
|
|
244
|
+
* shown: `{today+days|date}` says nothing until `days` is a positive whole
|
|
245
|
+
* number.
|
|
246
|
+
*
|
|
247
|
+
* @param {string} template The template.
|
|
248
|
+
* @param {object} values The input values.
|
|
249
|
+
* @param {Date} [now] What counts as today.
|
|
250
|
+
* @return {string} The filled text, or ''.
|
|
251
|
+
*/
|
|
252
|
+
function fillInputTemplate(template, values, now = new Date()) {
|
|
253
|
+
if (typeof template !== 'string' || template === '') {
|
|
254
|
+
return ''
|
|
255
|
+
}
|
|
256
|
+
let empty = false;
|
|
257
|
+
const filled = template.replace(/\{([^{}|]+)(?:\|(date))?\}/g, (whole, expression, format) => {
|
|
258
|
+
const name = expression.trim();
|
|
259
|
+
let value;
|
|
260
|
+
if (name === 'today') {
|
|
261
|
+
value = new Date(now.getTime());
|
|
262
|
+
} else if (name.startsWith('today+')) {
|
|
263
|
+
value = daysAfter((values || {})[name.slice(6).trim()], now);
|
|
264
|
+
} else {
|
|
265
|
+
value = (values || {})[name];
|
|
266
|
+
}
|
|
267
|
+
if (value === null || value === undefined || String(value).trim() === '') {
|
|
268
|
+
empty = true;
|
|
269
|
+
return ''
|
|
270
|
+
}
|
|
271
|
+
const shown = format === 'date' ? dateText(value) : String(value);
|
|
272
|
+
if (shown === '') {
|
|
273
|
+
empty = true;
|
|
274
|
+
}
|
|
275
|
+
return shown
|
|
276
|
+
});
|
|
277
|
+
return empty ? '' : filled
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* The descriptors of the declared inputs, with the context applied.
|
|
282
|
+
*
|
|
283
|
+
* Next to what the schema says, an input may declare:
|
|
284
|
+
* - `widget`: `text`, `textarea`, `number`, `date`, `select` or `template`.
|
|
285
|
+
* - `optionsSource`: a dot path into `context` holding the rows to pick from,
|
|
286
|
+
* read through `optionValue` and `optionLabel`. With no rows the input is
|
|
287
|
+
* hidden and asks nothing.
|
|
288
|
+
* - `other: { label, input }`: adds an "Other" entry; the input it names is
|
|
289
|
+
* only shown (and only asked) when this picker is hidden or Other is picked.
|
|
290
|
+
* - `helperFrom: { input, key }`: the helper text is `key` of the row picked
|
|
291
|
+
* in that other input.
|
|
292
|
+
* - `preview: { label, template, note, testid }`: a line under the input,
|
|
293
|
+
* filled by `fillInputTemplate` and shown only once it resolves.
|
|
294
|
+
* - `default`: the starting value.
|
|
295
|
+
* - Presentation: `labelOutside`, `id`, `note`, `wide`, `testid`, `rows`,
|
|
296
|
+
* `placeholder`.
|
|
297
|
+
*
|
|
298
|
+
* Every descriptor carries `hidden`; a hidden one is neither drawn nor asked.
|
|
299
|
+
*
|
|
300
|
+
* @param {unknown} declared The declared inputs.
|
|
301
|
+
* @param {object} [options] Options.
|
|
302
|
+
* @param {object|null} [options.schema] The object's JSON Schema.
|
|
303
|
+
* @param {string} [options.register] Register the reference pickers fall back to.
|
|
304
|
+
* @param {object} [options.context] What `optionsSource` reads.
|
|
305
|
+
* @param {object} [options.values] The values so far.
|
|
306
|
+
* @param {Date} [options.now] What counts as today, for previews.
|
|
307
|
+
* @return {Array<object>} The descriptors.
|
|
308
|
+
*/
|
|
309
|
+
function resolveInputFields(declared, { schema = null, register = '', context = {}, values = {}, now } = {}) {
|
|
310
|
+
const inputs = normaliseInputs(declared);
|
|
311
|
+
const resolved = fieldsFromSchema(schema, { include: inputs.map((input) => input.field), includeReadOnly: true });
|
|
312
|
+
const fields = inputs.map((input) => ({
|
|
313
|
+
...baseField(input, resolved, { schema, register, context }),
|
|
314
|
+
input,
|
|
315
|
+
hidden: false,
|
|
316
|
+
}));
|
|
317
|
+
|
|
318
|
+
for (const field of fields) {
|
|
319
|
+
const other = field.input.other;
|
|
320
|
+
if (field.widget !== 'select') {
|
|
321
|
+
continue
|
|
322
|
+
}
|
|
323
|
+
if (field.input.optionsSource && field.options.length === 0) {
|
|
324
|
+
field.hidden = true;
|
|
325
|
+
}
|
|
326
|
+
if (other) {
|
|
327
|
+
field.options = [...field.options, { value: OTHER_OPTION_VALUE, label: other.label.trim() }];
|
|
328
|
+
const target = fields.find((candidate) => candidate.key === other.input);
|
|
329
|
+
if (target && !field.hidden && values[field.key] !== OTHER_OPTION_VALUE) {
|
|
330
|
+
target.hidden = true;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
for (const field of fields) {
|
|
336
|
+
const helperFrom = field.input.helperFrom;
|
|
337
|
+
if (helperFrom && typeof helperFrom === 'object') {
|
|
338
|
+
const source = fields.find((candidate) => candidate.key === helperFrom.input);
|
|
339
|
+
const picked = source && Array.isArray(source.rows) && !source.hidden
|
|
340
|
+
? source.rows[source.options.findIndex((option) => option.value === String(values[source.key] ?? ''))]
|
|
341
|
+
: null;
|
|
342
|
+
const helper = picked && typeof picked === 'object' ? picked[helperFrom.key] : null;
|
|
343
|
+
field.description = helper === null || helper === undefined ? field.description : String(helper);
|
|
344
|
+
}
|
|
345
|
+
const preview = field.input.preview;
|
|
346
|
+
field.previewText = preview && typeof preview === 'object'
|
|
347
|
+
? fillInputTemplate(preview.template, values, now)
|
|
348
|
+
: '';
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return fields
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Whether a field holds a value: a checked switch for booleans, a non-blank
|
|
356
|
+
* string otherwise.
|
|
357
|
+
*
|
|
358
|
+
* @param {object} field The descriptor.
|
|
359
|
+
* @param {object} values The values.
|
|
360
|
+
* @return {boolean}
|
|
361
|
+
*/
|
|
362
|
+
function isInputFilled(field, values) {
|
|
363
|
+
const value = (values || {})[field.key];
|
|
364
|
+
if (field.widget === 'checkbox') {
|
|
365
|
+
return value === true
|
|
366
|
+
}
|
|
367
|
+
return String(value ?? '').trim() !== ''
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Whether an object input's sub-field holds a value.
|
|
372
|
+
*
|
|
373
|
+
* @param {object} field The object descriptor.
|
|
374
|
+
* @param {object} sub The sub-field descriptor.
|
|
375
|
+
* @param {object} values The values.
|
|
376
|
+
* @return {boolean}
|
|
377
|
+
*/
|
|
378
|
+
function isSubInputFilled(field, sub, values) {
|
|
379
|
+
const group = (values || {})[field.key];
|
|
380
|
+
const value = group && typeof group === 'object' ? group[sub.key] : undefined;
|
|
381
|
+
return sub.widget === 'checkbox' ? value === true : String(value ?? '').trim() !== ''
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Whether every shown, required field is filled. "Other" counts as a pick.
|
|
386
|
+
*
|
|
387
|
+
* @param {Array<object>} fields The descriptors (`resolveInputFields`).
|
|
388
|
+
* @param {object} values The values.
|
|
389
|
+
* @return {boolean}
|
|
390
|
+
*/
|
|
391
|
+
function inputFieldsComplete(fields, values) {
|
|
392
|
+
return fields.every((field) => {
|
|
393
|
+
if (field.hidden || field.widget === 'template') {
|
|
394
|
+
return true
|
|
395
|
+
}
|
|
396
|
+
if (field.widget === 'object') {
|
|
397
|
+
return field.subFields.every((sub) => !sub.required || isSubInputFilled(field, sub, values))
|
|
398
|
+
}
|
|
399
|
+
return !field.required || isInputFilled(field, values)
|
|
400
|
+
})
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* A picked value as it is sent: the "Other" entry becomes ''.
|
|
405
|
+
*
|
|
406
|
+
* @param {unknown} value The picked value.
|
|
407
|
+
* @return {unknown}
|
|
408
|
+
*/
|
|
409
|
+
function submittedValue(value) {
|
|
410
|
+
return value === OTHER_OPTION_VALUE ? '' : value
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* The values to start from: each input's `default`, else its schema default,
|
|
415
|
+
* else false for a boolean and '' otherwise. An object input starts from the
|
|
416
|
+
* record's present value, so the sub-fields it asks for show what is there.
|
|
417
|
+
*
|
|
418
|
+
* @param {unknown} declared The declared inputs.
|
|
419
|
+
* @param {object|null} [schema] The object's JSON Schema.
|
|
420
|
+
* @param {object|null} [currentObject] The record.
|
|
421
|
+
* @return {object} The values.
|
|
422
|
+
*/
|
|
423
|
+
function initialInputValues(declared, schema = null, currentObject = null) {
|
|
424
|
+
const values = {};
|
|
425
|
+
const properties = (schema && schema.properties) || {};
|
|
426
|
+
for (const input of normaliseInputs(declared)) {
|
|
427
|
+
const prop = properties[input.field] || {};
|
|
428
|
+
if (prop.type === 'object' && prop.properties && typeof prop.properties === 'object') {
|
|
429
|
+
const present = currentObject ? currentObject[input.field] : null;
|
|
430
|
+
values[input.field] = present && typeof present === 'object' ? { ...present } : {};
|
|
431
|
+
continue
|
|
432
|
+
}
|
|
433
|
+
if (input.default !== undefined) {
|
|
434
|
+
values[input.field] = input.default;
|
|
435
|
+
continue
|
|
436
|
+
}
|
|
437
|
+
values[input.field] = prop.default !== undefined
|
|
438
|
+
? prop.default
|
|
439
|
+
: (prop.type === 'boolean' ? false : '');
|
|
440
|
+
}
|
|
441
|
+
return values
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* The text a chosen template puts into its `applyTo` input: the template's
|
|
446
|
+
* `body`, else `presets.body`. It never writes over what was already typed,
|
|
447
|
+
* because text lost to a template cannot be got back.
|
|
448
|
+
*
|
|
449
|
+
* @param {object} values The values.
|
|
450
|
+
* @param {string} target The input the template fills.
|
|
451
|
+
* @param {object|null} option The chosen template.
|
|
452
|
+
* @return {object} The new values, or the same object when nothing changes.
|
|
453
|
+
*/
|
|
454
|
+
function applyTemplateText(values, target, option) {
|
|
455
|
+
const body = option ? (option.body || (option.presets && option.presets.body) || '') : '';
|
|
456
|
+
if (!target || typeof body !== 'string' || body === '' || String((values || {})[target] ?? '').trim() !== '') {
|
|
457
|
+
return values
|
|
458
|
+
}
|
|
459
|
+
return { ...values, [target]: body }
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Read a host's values against the declared inputs: whether every shown,
|
|
464
|
+
* required input is filled, and the values to send for the inputs that are
|
|
465
|
+
* asked now. A hidden input and a template are left out, a picked "Other"
|
|
466
|
+
* is sent as '' and a number input as a number. An object input is sent as
|
|
467
|
+
* it stands (CnTransitionInputDialog merges it over the record).
|
|
468
|
+
*
|
|
469
|
+
* @param {unknown} declared The declared inputs.
|
|
470
|
+
* @param {object} values The values, keyed by field.
|
|
471
|
+
* @param {object} [options] `{ schema, register, context, now }`, as for `resolveInputFields`.
|
|
472
|
+
* @return {{complete: boolean, data: object}}
|
|
473
|
+
* @spec openspec/changes/dq-abstract-forms/specs/transition-inputs/spec.md
|
|
474
|
+
*/
|
|
475
|
+
function readTransitionInputs(declared, values, options = {}) {
|
|
476
|
+
const current = values || {};
|
|
477
|
+
const fields = resolveInputFields(declared, { ...options, values: current });
|
|
478
|
+
const data = {};
|
|
479
|
+
for (const field of fields) {
|
|
480
|
+
if (field.hidden || field.widget === 'template') {
|
|
481
|
+
continue
|
|
482
|
+
}
|
|
483
|
+
let value = submittedValue(current[field.key]);
|
|
484
|
+
if (field.widget === 'number' && String(value ?? '').trim() !== '' && !Number.isNaN(Number(value))) {
|
|
485
|
+
value = Number(value);
|
|
486
|
+
}
|
|
487
|
+
data[field.key] = value;
|
|
488
|
+
}
|
|
489
|
+
return { complete: inputFieldsComplete(fields, current), data }
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export { OTHER_OPTION_VALUE, applyTemplateText, clampWidget, declaredOptions, fillInputTemplate, initialInputValues, inputFieldsComplete, isInputFilled, isSubInputFilled, normaliseInputs, readTransitionInputs, resolveInputFields, submittedValue };
|
|
493
|
+
//# sourceMappingURL=transitionInputs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transitionInputs.js","sources":["../../../src/utils/transitionInputs.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * The declared inputs of a transition or an act, resolved into the fields a\n * form draws, the check that they are complete and the values they send.\n *\n * Shared by CnTransitionInputDialog (the inputs in their own dialog) and\n * CnTransitionInputFields (the same inputs drawn wherever a host puts them,\n * such as under the chosen card of a list of acts), so the two never decide\n * differently whether a form may be confirmed.\n *\n * @module utils/transitionInputs\n * @spec openspec/changes/dq-abstract-forms/specs/transition-inputs/spec.md\n */\n\nimport { formatDateForDisplay } from './dateDisplay.js'\nimport { readPath } from './readPath.js'\nimport { fieldsFromSchema } from './schema.js'\n\n/**\n * The picker value of an input's \"Other\" entry. Never sent: see `submittedValue`.\n *\n * @type {string}\n */\nexport const OTHER_OPTION_VALUE = '__other'\n\n/** Widgets an input may name outright, whatever the schema says. */\nconst DECLARED_WIDGETS = ['text', 'textarea', 'number', 'date', 'select', 'template']\n\n/**\n * A non-empty trimmed string, or null.\n *\n * @param {unknown} value The value.\n * @return {string|null}\n */\nfunction text(value) {\n\treturn typeof value === 'string' && value.trim() !== '' ? value.trim() : null\n}\n\n/**\n * The choices an input declares, as `[{ value, label }]`, or null when it\n * declares none. An option without a value is dropped, because it cannot be\n * sent; one without a label shows its value.\n *\n * @param {unknown} options The input's declared `options`.\n * @return {Array<{value: string, label: string}>|null}\n */\nexport function declaredOptions(options) {\n\tif (!Array.isArray(options)) {\n\t\treturn null\n\t}\n\tconst usable = options\n\t\t.filter((option) => option && typeof option === 'object'\n\t\t\t&& (typeof option.value === 'string' || typeof option.value === 'number')\n\t\t\t&& String(option.value).trim() !== '')\n\t\t.map((option) => ({\n\t\t\tvalue: String(option.value),\n\t\t\tlabel: text(option.label) || String(option.value),\n\t\t}))\n\treturn usable.length > 0 ? usable : null\n}\n\n/**\n * The declared inputs, normalised. An input without a `field` is dropped.\n *\n * @param {unknown} declared The declared `inputs`.\n * @return {Array<object>} The inputs.\n */\nexport function normaliseInputs(declared) {\n\tif (!Array.isArray(declared)) {\n\t\treturn []\n\t}\n\treturn declared\n\t\t.filter((input) => input && typeof input.field === 'string' && input.field !== '')\n\t\t.map((input) => ({\n\t\t\t...input,\n\t\t\tfield: input.field,\n\t\t\trequired: input.required === true,\n\t\t\tpicker: input.picker && typeof input.picker === 'object' ? input.picker : null,\n\t\t\tfields: Array.isArray(input.fields) ? input.fields.filter((f) => typeof f === 'string') : null,\n\t\t\tlabel: text(input.label),\n\t\t\toptions: declaredOptions(input.options),\n\t\t\twidget: DECLARED_WIDGETS.includes(input.widget) ? input.widget : null,\n\t\t\toptionsSource: text(input.optionsSource),\n\t\t\tother: input.other && typeof input.other === 'object' && text(input.other.label) ? input.other : null,\n\t\t}))\n}\n\n/**\n * Clamp a `fieldsFromSchema()` widget to the minimal set the inputs draw.\n * Anything richer (select, user picker, …) degrades to a plain text input\n * rather than pulling a form engine in.\n *\n * @param {string} widget The resolved widget name.\n * @return {'text'|'textarea'|'number'|'checkbox'}\n */\nexport function clampWidget(widget) {\n\tif (widget === 'checkbox' || widget === 'switch') {\n\t\treturn 'checkbox'\n\t}\n\tif (widget === 'textarea' || widget === 'number') {\n\t\treturn widget\n\t}\n\treturn 'text'\n}\n\n/**\n * The choices an `optionsSource` input reads off the context: each row's\n * `optionValue` (default `value`) and `optionLabel` (default `label`), with\n * the rows kept so `helperFrom` can read another key of the picked one.\n *\n * @param {object} input The normalised input.\n * @param {object} context What the source path is read from.\n * @return {{options: Array<{value: string, label: string}>, rows: Array<object>}}\n */\nfunction sourcedOptions(input, context) {\n\tconst list = readPath(context || {}, input.optionsSource)\n\tconst valueKey = text(input.optionValue) || 'value'\n\tconst labelKey = text(input.optionLabel) || 'label'\n\tconst options = []\n\tconst rows = []\n\tfor (const row of Array.isArray(list) ? list : []) {\n\t\tconst value = row && typeof row === 'object' ? row[valueKey] : row\n\t\tif ((typeof value !== 'string' && typeof value !== 'number') || String(value).trim() === '') {\n\t\t\tcontinue\n\t\t}\n\t\tconst label = row && typeof row === 'object' ? row[labelKey] : null\n\t\toptions.push({ value: String(value), label: text(label) || String(value) })\n\t\trows.push(row)\n\t}\n\treturn { options, rows }\n}\n\n/**\n * The descriptor of an object input: its sub-properties (narrowed to the\n * input's `fields` when it names them), each with its own widget. A\n * sub-field is required when the input names it in `fields` and the\n * sub-schema lists it as required, or when the whole input is required and\n * it is the only one asked.\n *\n * @param {object} input The normalised input.\n * @param {object} prop The object property's schema.\n * @return {object} The field descriptor.\n */\nfunction objectField(input, prop) {\n\tconst subs = fieldsFromSchema(prop, { includeReadOnly: true, ...(input.fields ? { include: input.fields } : {}) })\n\tconst required = Array.isArray(prop.required) ? prop.required : []\n\tconst only = subs.length === 1\n\treturn {\n\t\tkey: input.field,\n\t\tlabel: prop.title || input.field,\n\t\tdescription: prop.description || '',\n\t\twidget: 'object',\n\t\trequired: input.required,\n\t\tsubFields: subs.map((sub) => ({\n\t\t\t...sub,\n\t\t\twidget: clampWidget(sub.widget),\n\t\t\trequired: (input.fields ? required.includes(sub.key) : sub.required === true) || (input.required && only),\n\t\t})),\n\t}\n}\n\n/**\n * The field one input resolves to before the context is applied.\n *\n * @param {object} input The normalised input.\n * @param {object} resolved The schema's fields for the declared keys.\n * @param {object} options What the field is resolved against.\n * @param {object|null} options.schema The object's JSON Schema.\n * @param {string} options.register Register the reference pickers fall back to.\n * @param {object} options.context What `optionsSource` reads.\n * @return {object} The field descriptor.\n */\nfunction baseField(input, resolved, { schema, register, context }) {\n\tconst field = resolved.find((f) => f.key === input.field)\n\tconst label = input.label || (field && field.label) || input.field\n\tconst description = text(input.description) || (field && field.description) || ''\n\n\tif (input.widget === 'template') {\n\t\treturn { key: input.field, label, description, widget: 'template', required: false }\n\t}\n\tif (input.optionsSource) {\n\t\tconst { options, rows } = sourcedOptions(input, context)\n\t\treturn { key: input.field, label, description, widget: 'select', options, rows, required: input.required }\n\t}\n\tif (input.options) {\n\t\treturn { key: input.field, label, description, widget: 'select', options: input.options, rows: [], required: input.required }\n\t}\n\tif (input.widget && input.widget !== 'select') {\n\t\treturn { key: input.field, label, description, widget: input.widget, required: input.required }\n\t}\n\tconst prop = (schema && schema.properties && schema.properties[input.field]) || null\n\tif (prop && prop.type === 'object' && prop.properties && typeof prop.properties === 'object') {\n\t\treturn objectField(input, prop)\n\t}\n\tif (!field) {\n\t\treturn { key: input.field, label, description, widget: 'text', required: input.required }\n\t}\n\tif (field.reference && field.reference.multiple !== true) {\n\t\tconst referenceRegister = field.reference.register || register\n\t\tif (referenceRegister !== '') {\n\t\t\treturn { ...field, label, widget: 'reference', referenceRegister, picker: input.picker, required: input.required }\n\t\t}\n\t}\n\treturn { ...field, label, widget: clampWidget(field.widget), required: input.required }\n}\n\n/**\n * A date `days` whole days after `now`, or null without a positive count.\n *\n * @param {unknown} days The count.\n * @param {Date} now Today.\n * @return {Date|null}\n */\nfunction daysAfter(days, now) {\n\tconst count = Number.parseInt(String(days ?? ''), 10)\n\tif (!Number.isFinite(count) || count < 1) {\n\t\treturn null\n\t}\n\tconst date = new Date(now.getTime())\n\tdate.setDate(date.getDate() + count)\n\treturn date\n}\n\n/**\n * A date as a reader sees it: day, short month and year.\n *\n * @param {Date|string} value The date.\n * @return {string} The text, '' when not a date.\n */\nfunction dateText(value) {\n\tconst shown = formatDateForDisplay(value, { dateOptions: { day: 'numeric', month: 'short', year: 'numeric' } })\n\treturn shown ? shown.text : ''\n}\n\n/**\n * Fill a preview template from the input values.\n *\n * Tokens are `{field}`, `{field|date}`, `{today|date}` and\n * `{today+field|date}` (today plus that many days). A token that resolves\n * to nothing empties the whole preview, so a half-filled sentence is never\n * shown: `{today+days|date}` says nothing until `days` is a positive whole\n * number.\n *\n * @param {string} template The template.\n * @param {object} values The input values.\n * @param {Date} [now] What counts as today.\n * @return {string} The filled text, or ''.\n */\nexport function fillInputTemplate(template, values, now = new Date()) {\n\tif (typeof template !== 'string' || template === '') {\n\t\treturn ''\n\t}\n\tlet empty = false\n\tconst filled = template.replace(/\\{([^{}|]+)(?:\\|(date))?\\}/g, (whole, expression, format) => {\n\t\tconst name = expression.trim()\n\t\tlet value\n\t\tif (name === 'today') {\n\t\t\tvalue = new Date(now.getTime())\n\t\t} else if (name.startsWith('today+')) {\n\t\t\tvalue = daysAfter((values || {})[name.slice(6).trim()], now)\n\t\t} else {\n\t\t\tvalue = (values || {})[name]\n\t\t}\n\t\tif (value === null || value === undefined || String(value).trim() === '') {\n\t\t\tempty = true\n\t\t\treturn ''\n\t\t}\n\t\tconst shown = format === 'date' ? dateText(value) : String(value)\n\t\tif (shown === '') {\n\t\t\tempty = true\n\t\t}\n\t\treturn shown\n\t})\n\treturn empty ? '' : filled\n}\n\n/**\n * The descriptors of the declared inputs, with the context applied.\n *\n * Next to what the schema says, an input may declare:\n * - `widget`: `text`, `textarea`, `number`, `date`, `select` or `template`.\n * - `optionsSource`: a dot path into `context` holding the rows to pick from,\n * read through `optionValue` and `optionLabel`. With no rows the input is\n * hidden and asks nothing.\n * - `other: { label, input }`: adds an \"Other\" entry; the input it names is\n * only shown (and only asked) when this picker is hidden or Other is picked.\n * - `helperFrom: { input, key }`: the helper text is `key` of the row picked\n * in that other input.\n * - `preview: { label, template, note, testid }`: a line under the input,\n * filled by `fillInputTemplate` and shown only once it resolves.\n * - `default`: the starting value.\n * - Presentation: `labelOutside`, `id`, `note`, `wide`, `testid`, `rows`,\n * `placeholder`.\n *\n * Every descriptor carries `hidden`; a hidden one is neither drawn nor asked.\n *\n * @param {unknown} declared The declared inputs.\n * @param {object} [options] Options.\n * @param {object|null} [options.schema] The object's JSON Schema.\n * @param {string} [options.register] Register the reference pickers fall back to.\n * @param {object} [options.context] What `optionsSource` reads.\n * @param {object} [options.values] The values so far.\n * @param {Date} [options.now] What counts as today, for previews.\n * @return {Array<object>} The descriptors.\n */\nexport function resolveInputFields(declared, { schema = null, register = '', context = {}, values = {}, now } = {}) {\n\tconst inputs = normaliseInputs(declared)\n\tconst resolved = fieldsFromSchema(schema, { include: inputs.map((input) => input.field), includeReadOnly: true })\n\tconst fields = inputs.map((input) => ({\n\t\t...baseField(input, resolved, { schema, register, context }),\n\t\tinput,\n\t\thidden: false,\n\t}))\n\n\tfor (const field of fields) {\n\t\tconst other = field.input.other\n\t\tif (field.widget !== 'select') {\n\t\t\tcontinue\n\t\t}\n\t\tif (field.input.optionsSource && field.options.length === 0) {\n\t\t\tfield.hidden = true\n\t\t}\n\t\tif (other) {\n\t\t\tfield.options = [...field.options, { value: OTHER_OPTION_VALUE, label: other.label.trim() }]\n\t\t\tconst target = fields.find((candidate) => candidate.key === other.input)\n\t\t\tif (target && !field.hidden && values[field.key] !== OTHER_OPTION_VALUE) {\n\t\t\t\ttarget.hidden = true\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const field of fields) {\n\t\tconst helperFrom = field.input.helperFrom\n\t\tif (helperFrom && typeof helperFrom === 'object') {\n\t\t\tconst source = fields.find((candidate) => candidate.key === helperFrom.input)\n\t\t\tconst picked = source && Array.isArray(source.rows) && !source.hidden\n\t\t\t\t? source.rows[source.options.findIndex((option) => option.value === String(values[source.key] ?? ''))]\n\t\t\t\t: null\n\t\t\tconst helper = picked && typeof picked === 'object' ? picked[helperFrom.key] : null\n\t\t\tfield.description = helper === null || helper === undefined ? field.description : String(helper)\n\t\t}\n\t\tconst preview = field.input.preview\n\t\tfield.previewText = preview && typeof preview === 'object'\n\t\t\t? fillInputTemplate(preview.template, values, now)\n\t\t\t: ''\n\t}\n\n\treturn fields\n}\n\n/**\n * Whether a field holds a value: a checked switch for booleans, a non-blank\n * string otherwise.\n *\n * @param {object} field The descriptor.\n * @param {object} values The values.\n * @return {boolean}\n */\nexport function isInputFilled(field, values) {\n\tconst value = (values || {})[field.key]\n\tif (field.widget === 'checkbox') {\n\t\treturn value === true\n\t}\n\treturn String(value ?? '').trim() !== ''\n}\n\n/**\n * Whether an object input's sub-field holds a value.\n *\n * @param {object} field The object descriptor.\n * @param {object} sub The sub-field descriptor.\n * @param {object} values The values.\n * @return {boolean}\n */\nexport function isSubInputFilled(field, sub, values) {\n\tconst group = (values || {})[field.key]\n\tconst value = group && typeof group === 'object' ? group[sub.key] : undefined\n\treturn sub.widget === 'checkbox' ? value === true : String(value ?? '').trim() !== ''\n}\n\n/**\n * Whether every shown, required field is filled. \"Other\" counts as a pick.\n *\n * @param {Array<object>} fields The descriptors (`resolveInputFields`).\n * @param {object} values The values.\n * @return {boolean}\n */\nexport function inputFieldsComplete(fields, values) {\n\treturn fields.every((field) => {\n\t\tif (field.hidden || field.widget === 'template') {\n\t\t\treturn true\n\t\t}\n\t\tif (field.widget === 'object') {\n\t\t\treturn field.subFields.every((sub) => !sub.required || isSubInputFilled(field, sub, values))\n\t\t}\n\t\treturn !field.required || isInputFilled(field, values)\n\t})\n}\n\n/**\n * A picked value as it is sent: the \"Other\" entry becomes ''.\n *\n * @param {unknown} value The picked value.\n * @return {unknown}\n */\nexport function submittedValue(value) {\n\treturn value === OTHER_OPTION_VALUE ? '' : value\n}\n\n/**\n * The values to start from: each input's `default`, else its schema default,\n * else false for a boolean and '' otherwise. An object input starts from the\n * record's present value, so the sub-fields it asks for show what is there.\n *\n * @param {unknown} declared The declared inputs.\n * @param {object|null} [schema] The object's JSON Schema.\n * @param {object|null} [currentObject] The record.\n * @return {object} The values.\n */\nexport function initialInputValues(declared, schema = null, currentObject = null) {\n\tconst values = {}\n\tconst properties = (schema && schema.properties) || {}\n\tfor (const input of normaliseInputs(declared)) {\n\t\tconst prop = properties[input.field] || {}\n\t\tif (prop.type === 'object' && prop.properties && typeof prop.properties === 'object') {\n\t\t\tconst present = currentObject ? currentObject[input.field] : null\n\t\t\tvalues[input.field] = present && typeof present === 'object' ? { ...present } : {}\n\t\t\tcontinue\n\t\t}\n\t\tif (input.default !== undefined) {\n\t\t\tvalues[input.field] = input.default\n\t\t\tcontinue\n\t\t}\n\t\tvalues[input.field] = prop.default !== undefined\n\t\t\t? prop.default\n\t\t\t: (prop.type === 'boolean' ? false : '')\n\t}\n\treturn values\n}\n\n/**\n * The text a chosen template puts into its `applyTo` input: the template's\n * `body`, else `presets.body`. It never writes over what was already typed,\n * because text lost to a template cannot be got back.\n *\n * @param {object} values The values.\n * @param {string} target The input the template fills.\n * @param {object|null} option The chosen template.\n * @return {object} The new values, or the same object when nothing changes.\n */\nexport function applyTemplateText(values, target, option) {\n\tconst body = option ? (option.body || (option.presets && option.presets.body) || '') : ''\n\tif (!target || typeof body !== 'string' || body === '' || String((values || {})[target] ?? '').trim() !== '') {\n\t\treturn values\n\t}\n\treturn { ...values, [target]: body }\n}\n\n/**\n * Read a host's values against the declared inputs: whether every shown,\n * required input is filled, and the values to send for the inputs that are\n * asked now. A hidden input and a template are left out, a picked \"Other\"\n * is sent as '' and a number input as a number. An object input is sent as\n * it stands (CnTransitionInputDialog merges it over the record).\n *\n * @param {unknown} declared The declared inputs.\n * @param {object} values The values, keyed by field.\n * @param {object} [options] `{ schema, register, context, now }`, as for `resolveInputFields`.\n * @return {{complete: boolean, data: object}}\n * @spec openspec/changes/dq-abstract-forms/specs/transition-inputs/spec.md\n */\nexport function readTransitionInputs(declared, values, options = {}) {\n\tconst current = values || {}\n\tconst fields = resolveInputFields(declared, { ...options, values: current })\n\tconst data = {}\n\tfor (const field of fields) {\n\t\tif (field.hidden || field.widget === 'template') {\n\t\t\tcontinue\n\t\t}\n\t\tlet value = submittedValue(current[field.key])\n\t\tif (field.widget === 'number' && String(value ?? '').trim() !== '' && !Number.isNaN(Number(value))) {\n\t\t\tvalue = Number(value)\n\t\t}\n\t\tdata[field.key] = value\n\t}\n\treturn { complete: inputFieldsComplete(fields, current), data }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isAppInstalled } from './appInstalled.js';
|
|
2
2
|
import { prefixUrl, buildHeaders, buildQueryString } from './headers.js';
|
|
3
|
+
import { interpolateUrlTokens } from './interpolateUrlTokens.js';
|
|
3
4
|
import { resolveFilterValue, resolveFilterTokens } from './resolveFilterTokens.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -22,7 +23,8 @@ import { resolveFilterValue, resolveFilterTokens } from './resolveFilterTokens.j
|
|
|
22
23
|
* The value itself comes from one of THREE evaluation modes (first match wins):
|
|
23
24
|
*
|
|
24
25
|
* 1. `endpoint` — GET a same-origin JSON endpoint; `field` is a dot-path
|
|
25
|
-
* into the response body.
|
|
26
|
+
* into the response body. `@objectId` and `@object.<field>` in the
|
|
27
|
+
* endpoint resolve against the caller's record.
|
|
26
28
|
* 2. `source` — `{ register, schema, filter? }`, an OpenRegister object
|
|
27
29
|
* query (`filter` supports the shared @-token grammar). `field` is a
|
|
28
30
|
* dot-path into the FIRST result; omit it — or use `@total` — to
|
|
@@ -205,7 +207,12 @@ function evaluateWorkspaceCondition(cond, workspace) {
|
|
|
205
207
|
*/
|
|
206
208
|
async function readVisibleWhenValue(cond, ctx, sink) {
|
|
207
209
|
if (cond.endpoint) {
|
|
208
|
-
|
|
210
|
+
// `@objectId` / `@object.<field>` name the record the condition sits on;
|
|
211
|
+
// without that record there is nothing to ask about.
|
|
212
|
+
if (/@object(Id|\.)/.test(cond.endpoint) && (ctx?.objectId === undefined || ctx?.objectId === null || ctx?.objectId === '')) {
|
|
213
|
+
throw new Error('endpoint names the record, and there is none')
|
|
214
|
+
}
|
|
215
|
+
const response = await fetch(prefixUrl(interpolateUrlTokens(cond.endpoint, ctx)), { headers: buildHeaders() });
|
|
209
216
|
if (!response.ok) {
|
|
210
217
|
throw new Error(`endpoint returned ${response.status}`)
|
|
211
218
|
}
|