@conduction/nextcloud-vue 2.78.0 → 2.80.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/bundled-packages.json +0 -2
- package/dist/esm/bundled-packages.json +0 -2
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js.map +1 -1
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js +3 -1
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +32 -3
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +115 -104
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +12 -2
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +39 -3
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +158 -0
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +157 -96
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +139 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +140 -92
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +77 -0
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +5 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +62 -4
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +85 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +43 -4
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +11 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +736 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +238 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnFactsWidget/index.js +30 -0
- package/dist/esm/components/CnFactsWidget/index.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +10 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +551 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +183 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/index.js +29 -0
- package/dist/esm/components/CnFieldListWidget/index.js.map +1 -0
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +173 -5
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +181 -99
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js +29 -11
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/openRegisterSource.js +252 -0
- package/dist/esm/components/CnFilesBrowser/openRegisterSource.js.map +1 -0
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +66 -18
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +4 -4
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +18 -3
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +330 -74
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +34 -15
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +10 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js.map +1 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +99 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +48 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -0
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +100 -3
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +4 -2
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js +29 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +3 -2
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +10 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +387 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +169 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/index.js +31 -0
- package/dist/esm/components/CnRecordListWidget/index.js.map +1 -0
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js +10 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js +351 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js +71 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSectionsWidget/index.js +35 -0
- package/dist/esm/components/CnSectionsWidget/index.js.map +1 -0
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +81 -2
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +1 -0
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +152 -8
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +84 -23
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +4 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -0
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +106 -2
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +105 -53
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
- package/dist/esm/composables/useAppManifest.js +8 -3
- package/dist/esm/composables/useAppManifest.js.map +1 -1
- package/dist/esm/composables/useFormDraft.js +140 -9
- package/dist/esm/composables/useFormDraft.js.map +1 -1
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js +28 -20
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js +9 -0
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection.js +21 -0
- package/dist/esm/integrations/builtin/field-inspection.js.map +1 -1
- package/dist/esm/integrations/offline/checklistSubmission.js +141 -0
- package/dist/esm/integrations/offline/checklistSubmission.js.map +1 -0
- package/dist/esm/integrations/offline/fieldCollectionHelpers.js +70 -5
- package/dist/esm/integrations/offline/fieldCollectionHelpers.js.map +1 -1
- package/dist/esm/integrations/offline/index.js +2 -1
- package/dist/esm/integrations/offline/index.js.map +1 -1
- package/dist/esm/integrations/offline/offlineDb.js +6 -1
- package/dist/esm/integrations/offline/offlineDb.js.map +1 -1
- package/dist/esm/integrations/offline/syncReplayService.js +8 -2
- package/dist/esm/integrations/offline/syncReplayService.js.map +1 -1
- package/dist/esm/l10n/en.json.js +72 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +120 -2
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +1314 -74
- package/dist/esm/utils/boardDate.js +193 -0
- package/dist/esm/utils/boardDate.js.map +1 -0
- package/dist/esm/utils/detailActionModel.js +60 -3
- package/dist/esm/utils/detailActionModel.js.map +1 -1
- package/dist/esm/utils/dynamicProperties.js +5 -1
- package/dist/esm/utils/dynamicProperties.js.map +1 -1
- package/dist/esm/utils/fillTemplate.js +114 -0
- package/dist/esm/utils/fillTemplate.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +4 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/queryPresets.js +96 -0
- package/dist/esm/utils/queryPresets.js.map +1 -0
- package/dist/esm/utils/resolveFilterTokens.js +11 -0
- package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
- package/dist/esm/utils/schema.js +21 -0
- package/dist/esm/utils/schema.js.map +1 -1
- package/dist/esm/utils/sentinelTokens.js +3 -3
- package/dist/esm/utils/sentinelTokens.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +57 -6
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/widgetDispatch.js +7 -1
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +14278 -8169
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1314 -74
- package/l10n/en.json +72 -1
- package/l10n/nl.json +120 -2
- package/package.json +1 -1
- package/scripts/build-validators.js +26 -0
- package/src/components/CnActionButtons/CnActionButtons.vue +2 -0
- package/src/components/CnActionsBar/CnActionsBar.md +4 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +83 -42
- package/src/components/CnAppRoot/CnAppRoot.vue +12 -2
- package/src/components/CnBannerWidget/CnBannerWidget.vue +39 -3
- package/src/components/CnCellRenderer/CnCellRenderer.vue +306 -2
- package/src/components/CnDashboardPage/CnDashboardPage.vue +219 -4
- package/src/components/CnDataTable/CnDataTable.md +2 -0
- package/src/components/CnDataTable/CnDataTable.vue +82 -2
- package/src/components/CnDetailPage/CnDetailPage.md +1 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +63 -3
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +127 -1
- package/src/components/CnFactsWidget/CnFactsWidget.vue +1050 -0
- package/src/components/CnFactsWidget/index.js +29 -0
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +746 -0
- package/src/components/CnFieldListWidget/index.js +28 -0
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +232 -11
- package/src/components/CnFilesBrowser/filesBrowser.js +43 -2
- package/src/components/CnFilesBrowser/index.js +1 -0
- package/src/components/CnFilesBrowser/openRegisterSource.js +248 -0
- package/src/components/CnFormDialog/CnFormDialog.md +9 -3
- package/src/components/CnFormDialog/CnFormDialog.vue +70 -22
- package/src/components/CnFormPage/CnFormPage.vue +18 -3
- package/src/components/CnIndexPage/CnIndexPage.vue +281 -9
- package/src/components/CnIndexPage/CnIndexPageWidgets.vue +169 -0
- package/src/components/CnObjectSidebar/CnFilesTab.vue +103 -4
- package/src/components/CnPageRenderer/CnPageRenderer.vue +29 -1
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +3 -2
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +603 -0
- package/src/components/CnRecordListWidget/index.js +30 -0
- package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +1 -1
- package/src/components/CnSectionsWidget/CnSectionsWidget.vue +489 -0
- package/src/components/CnSectionsWidget/index.js +34 -0
- package/src/components/CnStagesWidget/CnStagesWidget.vue +81 -2
- package/src/components/CnStatWidget/CnStatWidget.vue +1 -1
- package/src/components/CnTabsWidget/CnTabsWidget.vue +214 -9
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +4 -0
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -0
- package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +188 -4
- package/src/components/index.js +5 -1
- package/src/composables/__tests__/useFormDraft.spec.js +69 -0
- package/src/composables/useAppManifest.js +8 -3
- package/src/composables/useFormDraft.js +139 -8
- package/src/css/form-field.css +14 -0
- package/src/css/index-page.css +71 -0
- package/src/css/kpi-card.css +32 -0
- package/src/css/look-board-detail.css +87 -0
- package/src/css/look-board-index.css +143 -14
- package/src/css/look-board.css +114 -2
- package/src/css/table.css +56 -0
- package/src/index.js +8 -0
- package/src/integrations/builtin/field-inspection/CnFieldInspectionCard.vue +35 -20
- package/src/integrations/builtin/field-inspection.js +21 -0
- package/src/integrations/offline/checklistSubmission.js +137 -0
- package/src/integrations/offline/fieldCollectionHelpers.js +69 -4
- package/src/integrations/offline/index.js +3 -0
- package/src/integrations/offline/offlineDb.js +6 -1
- package/src/integrations/offline/syncReplayService.js +8 -2
- package/src/schemas/app-manifest-v2.schema.json +149 -5
- package/src/utils/boardDate.js +189 -0
- package/src/utils/detailActionModel.js +59 -2
- package/src/utils/dynamicProperties.js +5 -1
- package/src/utils/fillTemplate.js +110 -0
- package/src/utils/libraryWidgetKeys.js +4 -0
- package/src/utils/queryPresets.js +93 -0
- package/src/utils/resolveFilterTokens.js +11 -0
- package/src/utils/schema.js +21 -0
- package/src/utils/sentinelTokens.js +3 -3
- package/src/utils/validateManifestV2.compiled.js +57 -2
- package/src/utils/widgetDispatch.js +7 -1
- package/dist/esm/_virtual/equal.js +0 -4
- package/dist/esm/_virtual/equal.js.map +0 -1
- package/dist/esm/_virtual/ucs2length.js +0 -4
- package/dist/esm/_virtual/ucs2length.js.map +0 -1
- package/dist/esm/node_modules/ajv/dist/runtime/equal.js +0 -9
- package/dist/esm/node_modules/ajv/dist/runtime/equal.js.map +0 -1
- package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js +0 -25
- package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js.map +0 -1
- package/dist/esm/node_modules/fast-deep-equal/index.js +0 -47
- package/dist/esm/node_modules/fast-deep-equal/index.js.map +0 -1
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { translatePlural, getLanguage, getCanonicalLocale } from '@nextcloud/l10n';
|
|
2
|
+
import { parseDateValue } from './dateVariant.js';
|
|
3
|
+
import { compareVisibleWhen } from './visibleWhen.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
7
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
8
|
+
*
|
|
9
|
+
* boardDate: the two short time readings the drawn boards put in a table cell.
|
|
10
|
+
*
|
|
11
|
+
* - A date ("5 okt", "30 okt", "14 feb 2024"): day and short month in the
|
|
12
|
+
* user's language, the year only when it is not the current year.
|
|
13
|
+
* - An age ("4 uur", "1 dag", "3 dagen"): how long something has been
|
|
14
|
+
* waiting, in hours under a day and in whole calendar days after that.
|
|
15
|
+
*
|
|
16
|
+
* Both read the Nextcloud user LANGUAGE for the month and unit names. A
|
|
17
|
+
* Nextcloud account keeps its locale (date order) apart from its language,
|
|
18
|
+
* and a Dutch reader whose locale was left at the en_US default saw
|
|
19
|
+
* "Feb 14, 2024": the language decides the words, so it decides here, and the
|
|
20
|
+
* locale is only used when it is a region of that same language.
|
|
21
|
+
*
|
|
22
|
+
* @module utils/boardDate
|
|
23
|
+
* @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
const MS_PER_HOUR = 3600000;
|
|
28
|
+
const MS_PER_DAY = 86400000;
|
|
29
|
+
|
|
30
|
+
/** Variants an age rule may name. `danger` is accepted as an alias of `error`. */
|
|
31
|
+
const AGE_VARIANTS = Object.freeze(['default', 'success', 'warning', 'error']);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Read a language or locale tag from an @nextcloud/l10n getter, which throws
|
|
35
|
+
* when Nextcloud has not set one (outside a Nextcloud page, in a test).
|
|
36
|
+
*
|
|
37
|
+
* @param {() => string} getter `getLanguage` or `getCanonicalLocale`.
|
|
38
|
+
* @return {string} The tag with `-` separators, or ''.
|
|
39
|
+
*/
|
|
40
|
+
function readTag(getter) {
|
|
41
|
+
try {
|
|
42
|
+
return String(getter() || '').replace(/_/g, '-')
|
|
43
|
+
} catch {
|
|
44
|
+
return ''
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The BCP 47 tag the board dates are written in: the user's locale when it is
|
|
50
|
+
* a region of the user's language (`nl-BE` for language `nl`), else the
|
|
51
|
+
* language itself.
|
|
52
|
+
*
|
|
53
|
+
* @return {string|undefined} The tag, or undefined to let Intl choose.
|
|
54
|
+
* @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-a-board-date-cell-reads-day-and-short-month-in-the-user-language
|
|
55
|
+
*/
|
|
56
|
+
function boardDateLocale() {
|
|
57
|
+
const language = readTag(getLanguage);
|
|
58
|
+
const locale = readTag(getCanonicalLocale);
|
|
59
|
+
const base = (tag) => tag.split('-')[0].toLowerCase();
|
|
60
|
+
if (language && locale && base(language) === base(locale)) {
|
|
61
|
+
return locale
|
|
62
|
+
}
|
|
63
|
+
return language || locale || undefined
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* A date in the board's short form: "5 okt", or "14 feb 2024" outside the
|
|
68
|
+
* current year.
|
|
69
|
+
*
|
|
70
|
+
* @param {unknown} value A Date, a timestamp or an ISO string.
|
|
71
|
+
* @param {Date} [now] The reference moment (defaults to the current time).
|
|
72
|
+
* @param {string} [locale] BCP 47 tag (defaults to `boardDateLocale()`).
|
|
73
|
+
* @return {string} The short date, or '' when the value is not a date.
|
|
74
|
+
* @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-a-board-date-cell-reads-day-and-short-month-in-the-user-language
|
|
75
|
+
*/
|
|
76
|
+
function formatBoardDate(value, now = new Date(), locale = boardDateLocale()) {
|
|
77
|
+
const date = parseDateValue(value);
|
|
78
|
+
if (date === null) {
|
|
79
|
+
return ''
|
|
80
|
+
}
|
|
81
|
+
const options = { day: 'numeric', month: 'short' };
|
|
82
|
+
if (date.getFullYear() !== now.getFullYear()) {
|
|
83
|
+
options.year = 'numeric';
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
return new Intl.DateTimeFormat(locale, options).format(date)
|
|
87
|
+
} catch {
|
|
88
|
+
return new Intl.DateTimeFormat(undefined, options).format(date)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Whole calendar days from the date until `now` (0 today, 1 yesterday), or
|
|
94
|
+
* null when the value is not a date. A future date gives a negative number.
|
|
95
|
+
*
|
|
96
|
+
* @param {unknown} value The date.
|
|
97
|
+
* @param {Date} [now] The reference moment.
|
|
98
|
+
* @return {number|null} The day count.
|
|
99
|
+
* @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-says-how-long-something-has-waited
|
|
100
|
+
*/
|
|
101
|
+
function ageInDays(value, now = new Date()) {
|
|
102
|
+
const date = parseDateValue(value);
|
|
103
|
+
if (date === null) {
|
|
104
|
+
return null
|
|
105
|
+
}
|
|
106
|
+
// UTC midnights of the two LOCAL days, so a daylight-saving hour never
|
|
107
|
+
// turns one day into zero or two.
|
|
108
|
+
const start = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate());
|
|
109
|
+
const today = Date.UTC(now.getFullYear(), now.getMonth(), now.getDate());
|
|
110
|
+
return Math.round((today - start) / MS_PER_DAY)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* How long something has waited: "4 uur" under 24 hours (at least 1), else
|
|
115
|
+
* "1 dag" / "3 dagen" in whole calendar days. Plural forms come from the
|
|
116
|
+
* library's own catalogue (`nextcloud-vue`). A date in the future reads as
|
|
117
|
+
* waiting no time: "0 hours".
|
|
118
|
+
*
|
|
119
|
+
* @param {unknown} value The moment the wait started.
|
|
120
|
+
* @param {Date} [now] The reference moment.
|
|
121
|
+
* @return {string} The age, or '' when the value is not a date.
|
|
122
|
+
* @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-says-how-long-something-has-waited
|
|
123
|
+
*/
|
|
124
|
+
function formatAge(value, now = new Date()) {
|
|
125
|
+
const date = parseDateValue(value);
|
|
126
|
+
if (date === null) {
|
|
127
|
+
return ''
|
|
128
|
+
}
|
|
129
|
+
const elapsed = now.getTime() - date.getTime();
|
|
130
|
+
if (elapsed < MS_PER_DAY) {
|
|
131
|
+
const hours = elapsed <= 0 ? 0 : Math.max(1, Math.floor(elapsed / MS_PER_HOUR));
|
|
132
|
+
return translatePlural('nextcloud-vue', '%n hour', '%n hours', hours)
|
|
133
|
+
}
|
|
134
|
+
const days = shownAgeDays(date, now);
|
|
135
|
+
return translatePlural('nextcloud-vue', '%n day', '%n days', days)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The number of days an age cell shows: 0 under 24 hours (the cell then reads
|
|
140
|
+
* hours), else the whole calendar days, at least 1. The threshold rules
|
|
141
|
+
* compare against this number, so the colour always agrees with the text.
|
|
142
|
+
*
|
|
143
|
+
* @param {unknown} value The moment the wait started.
|
|
144
|
+
* @param {Date} [now] The reference moment.
|
|
145
|
+
* @return {number|null} The day count, or null when the value is not a date.
|
|
146
|
+
* @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-turns-red-past-its-threshold
|
|
147
|
+
*/
|
|
148
|
+
function shownAgeDays(value, now = new Date()) {
|
|
149
|
+
const date = parseDateValue(value);
|
|
150
|
+
if (date === null) {
|
|
151
|
+
return null
|
|
152
|
+
}
|
|
153
|
+
if (now.getTime() - date.getTime() < MS_PER_DAY) {
|
|
154
|
+
return 0
|
|
155
|
+
}
|
|
156
|
+
return Math.max(1, ageInDays(date, now))
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The variant of the first `variantWhen` rule the age in days matches, or ''
|
|
161
|
+
* when none does. Same rule shape as the `date` widget (`{ op, value,
|
|
162
|
+
* variant }`, first match wins), compared against the number of days the cell
|
|
163
|
+
* shows (`shownAgeDays`): `[{ op: "gte", value: 3, variant: "error" }]` turns a wait of three
|
|
164
|
+
* days or more red.
|
|
165
|
+
*
|
|
166
|
+
* @param {unknown} value The moment the wait started.
|
|
167
|
+
* @param {Array<{op: string, value: number, variant: string}>} rules The rules.
|
|
168
|
+
* @param {Date} [now] The reference moment.
|
|
169
|
+
* @return {string} `success | warning | error | default`, or ''.
|
|
170
|
+
* @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-turns-red-past-its-threshold
|
|
171
|
+
*/
|
|
172
|
+
function resolveAgeVariant(value, rules, now = new Date()) {
|
|
173
|
+
if (!Array.isArray(rules) || rules.length === 0) {
|
|
174
|
+
return ''
|
|
175
|
+
}
|
|
176
|
+
const days = shownAgeDays(value, now);
|
|
177
|
+
if (days === null) {
|
|
178
|
+
return ''
|
|
179
|
+
}
|
|
180
|
+
for (const rule of rules) {
|
|
181
|
+
if (!rule || typeof rule !== 'object') {
|
|
182
|
+
continue
|
|
183
|
+
}
|
|
184
|
+
if (compareVisibleWhen(days, rule.op || 'eq', rule.value) === true) {
|
|
185
|
+
const variant = rule.variant === 'danger' ? 'error' : rule.variant;
|
|
186
|
+
return AGE_VARIANTS.includes(variant) ? variant : ''
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return ''
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export { AGE_VARIANTS, ageInDays, boardDateLocale, formatAge, formatBoardDate, resolveAgeVariant, shownAgeDays };
|
|
193
|
+
//# sourceMappingURL=boardDate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boardDate.js","sources":["../../../src/utils/boardDate.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * boardDate: the two short time readings the drawn boards put in a table cell.\n *\n * - A date (\"5 okt\", \"30 okt\", \"14 feb 2024\"): day and short month in the\n * user's language, the year only when it is not the current year.\n * - An age (\"4 uur\", \"1 dag\", \"3 dagen\"): how long something has been\n * waiting, in hours under a day and in whole calendar days after that.\n *\n * Both read the Nextcloud user LANGUAGE for the month and unit names. A\n * Nextcloud account keeps its locale (date order) apart from its language,\n * and a Dutch reader whose locale was left at the en_US default saw\n * \"Feb 14, 2024\": the language decides the words, so it decides here, and the\n * locale is only used when it is a region of that same language.\n *\n * @module utils/boardDate\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md\n */\n\nimport { getCanonicalLocale, getLanguage, translatePlural as n } from '@nextcloud/l10n'\nimport { parseDateValue } from './dateVariant.js'\nimport { compareVisibleWhen } from './visibleWhen.js'\n\nconst MS_PER_HOUR = 3600000\nconst MS_PER_DAY = 86400000\n\n/** Variants an age rule may name. `danger` is accepted as an alias of `error`. */\nexport const AGE_VARIANTS = Object.freeze(['default', 'success', 'warning', 'error'])\n\n/**\n * Read a language or locale tag from an @nextcloud/l10n getter, which throws\n * when Nextcloud has not set one (outside a Nextcloud page, in a test).\n *\n * @param {() => string} getter `getLanguage` or `getCanonicalLocale`.\n * @return {string} The tag with `-` separators, or ''.\n */\nfunction readTag(getter) {\n\ttry {\n\t\treturn String(getter() || '').replace(/_/g, '-')\n\t} catch {\n\t\treturn ''\n\t}\n}\n\n/**\n * The BCP 47 tag the board dates are written in: the user's locale when it is\n * a region of the user's language (`nl-BE` for language `nl`), else the\n * language itself.\n *\n * @return {string|undefined} The tag, or undefined to let Intl choose.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-a-board-date-cell-reads-day-and-short-month-in-the-user-language\n */\nexport function boardDateLocale() {\n\tconst language = readTag(getLanguage)\n\tconst locale = readTag(getCanonicalLocale)\n\tconst base = (tag) => tag.split('-')[0].toLowerCase()\n\tif (language && locale && base(language) === base(locale)) {\n\t\treturn locale\n\t}\n\treturn language || locale || undefined\n}\n\n/**\n * A date in the board's short form: \"5 okt\", or \"14 feb 2024\" outside the\n * current year.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {Date} [now] The reference moment (defaults to the current time).\n * @param {string} [locale] BCP 47 tag (defaults to `boardDateLocale()`).\n * @return {string} The short date, or '' when the value is not a date.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-a-board-date-cell-reads-day-and-short-month-in-the-user-language\n */\nexport function formatBoardDate(value, now = new Date(), locale = boardDateLocale()) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn ''\n\t}\n\tconst options = { day: 'numeric', month: 'short' }\n\tif (date.getFullYear() !== now.getFullYear()) {\n\t\toptions.year = 'numeric'\n\t}\n\ttry {\n\t\treturn new Intl.DateTimeFormat(locale, options).format(date)\n\t} catch {\n\t\treturn new Intl.DateTimeFormat(undefined, options).format(date)\n\t}\n}\n\n/**\n * Whole calendar days from the date until `now` (0 today, 1 yesterday), or\n * null when the value is not a date. A future date gives a negative number.\n *\n * @param {unknown} value The date.\n * @param {Date} [now] The reference moment.\n * @return {number|null} The day count.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-says-how-long-something-has-waited\n */\nexport function ageInDays(value, now = new Date()) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn null\n\t}\n\t// UTC midnights of the two LOCAL days, so a daylight-saving hour never\n\t// turns one day into zero or two.\n\tconst start = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())\n\tconst today = Date.UTC(now.getFullYear(), now.getMonth(), now.getDate())\n\treturn Math.round((today - start) / MS_PER_DAY)\n}\n\n/**\n * How long something has waited: \"4 uur\" under 24 hours (at least 1), else\n * \"1 dag\" / \"3 dagen\" in whole calendar days. Plural forms come from the\n * library's own catalogue (`nextcloud-vue`). A date in the future reads as\n * waiting no time: \"0 hours\".\n *\n * @param {unknown} value The moment the wait started.\n * @param {Date} [now] The reference moment.\n * @return {string} The age, or '' when the value is not a date.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-says-how-long-something-has-waited\n */\nexport function formatAge(value, now = new Date()) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn ''\n\t}\n\tconst elapsed = now.getTime() - date.getTime()\n\tif (elapsed < MS_PER_DAY) {\n\t\tconst hours = elapsed <= 0 ? 0 : Math.max(1, Math.floor(elapsed / MS_PER_HOUR))\n\t\treturn n('nextcloud-vue', '%n hour', '%n hours', hours)\n\t}\n\tconst days = shownAgeDays(date, now)\n\treturn n('nextcloud-vue', '%n day', '%n days', days)\n}\n\n/**\n * The number of days an age cell shows: 0 under 24 hours (the cell then reads\n * hours), else the whole calendar days, at least 1. The threshold rules\n * compare against this number, so the colour always agrees with the text.\n *\n * @param {unknown} value The moment the wait started.\n * @param {Date} [now] The reference moment.\n * @return {number|null} The day count, or null when the value is not a date.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-turns-red-past-its-threshold\n */\nexport function shownAgeDays(value, now = new Date()) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn null\n\t}\n\tif (now.getTime() - date.getTime() < MS_PER_DAY) {\n\t\treturn 0\n\t}\n\treturn Math.max(1, ageInDays(date, now))\n}\n\n/**\n * The variant of the first `variantWhen` rule the age in days matches, or ''\n * when none does. Same rule shape as the `date` widget (`{ op, value,\n * variant }`, first match wins), compared against the number of days the cell\n * shows (`shownAgeDays`): `[{ op: \"gte\", value: 3, variant: \"error\" }]` turns a wait of three\n * days or more red.\n *\n * @param {unknown} value The moment the wait started.\n * @param {Array<{op: string, value: number, variant: string}>} rules The rules.\n * @param {Date} [now] The reference moment.\n * @return {string} `success | warning | error | default`, or ''.\n * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-turns-red-past-its-threshold\n */\nexport function resolveAgeVariant(value, rules, now = new Date()) {\n\tif (!Array.isArray(rules) || rules.length === 0) {\n\t\treturn ''\n\t}\n\tconst days = shownAgeDays(value, now)\n\tif (days === null) {\n\t\treturn ''\n\t}\n\tfor (const rule of rules) {\n\t\tif (!rule || typeof rule !== 'object') {\n\t\t\tcontinue\n\t\t}\n\t\tif (compareVisibleWhen(days, rule.op || 'eq', rule.value) === true) {\n\t\t\tconst variant = rule.variant === 'danger' ? 'error' : rule.variant\n\t\t\treturn AGE_VARIANTS.includes(variant) ? variant : ''\n\t\t}\n\t}\n\treturn ''\n}\n"],"names":["n"],"mappings":"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"}
|
|
@@ -202,13 +202,29 @@ function resolveNextStep(config, object, stageField = 'status') {
|
|
|
202
202
|
/**
|
|
203
203
|
* Resolve a header pill from the record.
|
|
204
204
|
*
|
|
205
|
-
*
|
|
205
|
+
* A pill with `field` reads it off the record; one without `field` and with
|
|
206
|
+
* `text` is a constant. A count pill (`countFrom`) is resolved by
|
|
207
|
+
* {@link resolveCountPill} once its count is known.
|
|
208
|
+
*
|
|
209
|
+
* @param {{field?: string, text?: string, colorMap?: object, labels?: object, variant?: string}|null} config The pill config.
|
|
206
210
|
* @param {object|null} object The record.
|
|
207
211
|
* @return {{label: string, colorKey: string, colorMap: object|null, variant: string}|null} What CnStatusBadge needs, or null when the field is empty.
|
|
208
212
|
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-header-pills
|
|
209
213
|
*/
|
|
210
214
|
function resolvePill(config, object) {
|
|
211
|
-
if (!config || typeof config !== 'object'
|
|
215
|
+
if (!config || typeof config !== 'object') {
|
|
216
|
+
return null
|
|
217
|
+
}
|
|
218
|
+
// A constant pill (`text`): "Inwoner", "Klant" (screens-relation-pages).
|
|
219
|
+
if ((typeof config.field !== 'string' || config.field === '') && typeof config.text === 'string' && config.text !== '') {
|
|
220
|
+
return {
|
|
221
|
+
label: config.text,
|
|
222
|
+
colorKey: config.text,
|
|
223
|
+
colorMap: config.colorMap && typeof config.colorMap === 'object' ? config.colorMap : null,
|
|
224
|
+
variant: typeof config.variant === 'string' && config.variant !== '' ? config.variant : 'default',
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (typeof config.field !== 'string' || config.field === '') {
|
|
212
228
|
return null
|
|
213
229
|
}
|
|
214
230
|
const raw = stageOf(object, config.field);
|
|
@@ -262,5 +278,46 @@ function resolveTabCount(tab, object) {
|
|
|
262
278
|
return Number.isFinite(number) ? number : null
|
|
263
279
|
}
|
|
264
280
|
|
|
265
|
-
|
|
281
|
+
/**
|
|
282
|
+
* The list a count pill counts (`countFrom`), or null for any other pill.
|
|
283
|
+
*
|
|
284
|
+
* @param {object|null} config The pill config.
|
|
285
|
+
* @return {{register: string, schema: string, filter: object}|null} The list.
|
|
286
|
+
* @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-header-pill-can-be-a-constant-or-a-count
|
|
287
|
+
*/
|
|
288
|
+
function pillCountSource(config) {
|
|
289
|
+
const from = config && typeof config === 'object' && !config.field ? config.countFrom : null;
|
|
290
|
+
if (!from || typeof from !== 'object' || typeof from.register !== 'string' || from.register === '' || typeof from.schema !== 'string' || from.schema === '') {
|
|
291
|
+
return null
|
|
292
|
+
}
|
|
293
|
+
const filter = from.filter && typeof from.filter === 'object' && !Array.isArray(from.filter) ? from.filter : {};
|
|
294
|
+
return { register: from.register, schema: from.schema, filter }
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* A count pill once its count is known: "1 lopende zaak", "2 lopende zaken".
|
|
299
|
+
* Nothing for a count of 0, an unknown count or a failed one.
|
|
300
|
+
*
|
|
301
|
+
* @param {{countLabel?: {one?: string, other?: string}, colorMap?: object, variant?: string}|null} config The pill config.
|
|
302
|
+
* @param {number|null|undefined} count The count.
|
|
303
|
+
* @param {(text: string) => string} [translate] Applied to the label before `{count}` is filled.
|
|
304
|
+
* @return {{label: string, colorKey: string, colorMap: object|null, variant: string}|null} What CnStatusBadge needs, or null.
|
|
305
|
+
* @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-header-pill-can-be-a-constant-or-a-count
|
|
306
|
+
*/
|
|
307
|
+
function resolveCountPill(config, count, translate = (text) => text) {
|
|
308
|
+
if (!pillCountSource(config) || typeof count !== 'number' || !Number.isFinite(count) || count <= 0) {
|
|
309
|
+
return null
|
|
310
|
+
}
|
|
311
|
+
const labels = config.countLabel && typeof config.countLabel === 'object' ? config.countLabel : {};
|
|
312
|
+
const template = (count === 1 ? labels.one : labels.other) || labels.other || labels.one || '{count}';
|
|
313
|
+
const label = translate(template).replace(/\{count\}/g, String(count));
|
|
314
|
+
return {
|
|
315
|
+
label,
|
|
316
|
+
colorKey: '',
|
|
317
|
+
colorMap: config.colorMap && typeof config.colorMap === 'object' ? config.colorMap : null,
|
|
318
|
+
variant: typeof config.variant === 'string' && config.variant !== '' ? config.variant : 'default',
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export { MAX_QUICK_ACTIONS, groupMenuEntries, isChecklistItemDone, normalisePinnedAction, pillCountSource, resolveCountPill, resolveNextStep, resolvePill, resolveTabCount, stageEntry, stageOf };
|
|
266
323
|
//# sourceMappingURL=detailActionModel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detailActionModel.js","sources":["../../../src/utils/detailActionModel.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * detailActionModel: the rules behind a detail page's four action levels and\n * the case surfaces that read the record's stage.\n *\n * 1. the next step: one primary button, chosen by the record's stage,\n * 2. quick actions: at most three always visible buttons,\n * 3. the overflow menu: the rest, in named groups,\n * 4. admin actions: the last group, for admins only.\n *\n * Pure functions, no Vue and no fetch, so CnDetailPage, the widgets and the\n * tests all read one implementation.\n *\n * @module utils/detailActionModel\n */\n\nimport { readPath } from './readPath.js'\nimport { evaluateVisibleWhenLocal } from './visibleWhen.js'\n\n/** The most quick actions a page shows. Later entries are dropped. */\nexport const MAX_QUICK_ACTIONS = 3\n\n/**\n * The record's stage, as the string a stage map is keyed on.\n *\n * @param {object|null} object The record.\n * @param {string} [stageField] Dot-path to the stage. Defaults to `status`.\n * @return {string} The stage, or '' when the record has none.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage\n */\nexport function stageOf(object, stageField = 'status') {\n\tif (!object || typeof object !== 'object') {\n\t\treturn ''\n\t}\n\tconst value = readPath(object, stageField || 'status')\n\tif (value === null || value === undefined || typeof value === 'object') {\n\t\treturn ''\n\t}\n\treturn String(value)\n}\n\n/**\n * Look a stage up in a stage-keyed map. An exact key wins; otherwise the\n * match ignores case, because a status is stored as `in_behandeling` in one\n * register and `In_Behandeling` in the next.\n *\n * @param {object|null} map The stage-keyed map.\n * @param {string} stage The stage.\n * @return {unknown} The entry, or undefined.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage\n */\nexport function stageEntry(map, stage) {\n\tif (!map || typeof map !== 'object' || Array.isArray(map) || stage === '') {\n\t\treturn undefined\n\t}\n\tif (Object.hasOwn(map, stage)) {\n\t\treturn map[stage]\n\t}\n\tconst wanted = stage.toLowerCase()\n\tconst key = Object.keys(map).find((candidate) => candidate.toLowerCase() === wanted)\n\treturn key === undefined ? undefined : map[key]\n}\n\n/**\n * Turn a pinned action entry into a descriptor the page can dispatch.\n *\n * An entry is the id of a declared header action, or an action object of its\n * own. An object without an id gets `fallbackId`, since every dispatched\n * action is addressed by id.\n *\n * @param {string|object|null} entry The entry.\n * @param {string} fallbackId Id for an inline action that names none.\n * @return {{id: string, inline: object|null}|null} The id, plus the inline definition when the entry carried one.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-quick-actions\n */\nexport function normalisePinnedAction(entry, fallbackId) {\n\tif (typeof entry === 'string') {\n\t\treturn entry === '' ? null : { id: entry, inline: null }\n\t}\n\tif (!entry || typeof entry !== 'object' || Array.isArray(entry)) {\n\t\treturn null\n\t}\n\tif (typeof entry.label !== 'string' || entry.label === '') {\n\t\treturn null\n\t}\n\tconst id = typeof entry.id === 'string' && entry.id !== '' ? entry.id : fallbackId\n\treturn { id, inline: { ...entry, id } }\n}\n\n/**\n * Split menu entries into the groups the overflow menu draws.\n *\n * Ungrouped entries come first, under no caption. Named groups follow in the\n * order they first appear. Admin only entries are dropped for everyone else\n * and form the last group for admins, whatever `group` they also name.\n *\n * `adminOnly` hides a control. It is never an authorization decision: the\n * endpoint behind the action decides who may call it.\n *\n * @param {Array<object>} entries Menu-ready entries, each with an `id`.\n * @param {object} actionsById The declared actions by id, carrying `group` / `adminOnly`.\n * @param {{isAdmin?: boolean, adminLabel?: string}} [options] Who is looking, and what the admin group is called.\n * @return {Array<{key: string, label: string, entries: Array<object>}>} The non-empty groups, in render order.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-grouped-and-admin-only-menu-actions\n */\nexport function groupMenuEntries(entries, actionsById = {}, options = {}) {\n\tconst isAdmin = options.isAdmin === true\n\tconst plain = { key: '', label: '', entries: [] }\n\tconst named = []\n\tconst admin = { key: 'cn-admin', label: options.adminLabel || '', entries: [] }\n\tfor (const entry of Array.isArray(entries) ? entries : []) {\n\t\tconst declared = (entry && actionsById[entry.id]) || {}\n\t\tif (declared.adminOnly === true) {\n\t\t\tif (isAdmin) {\n\t\t\t\tadmin.entries.push(entry)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tconst label = typeof declared.group === 'string' ? declared.group.trim() : ''\n\t\tif (label === '') {\n\t\t\tplain.entries.push(entry)\n\t\t\tcontinue\n\t\t}\n\t\tlet group = named.find((candidate) => candidate.label === label)\n\t\tif (!group) {\n\t\t\tgroup = { key: `cn-group-${named.length}`, label, entries: [] }\n\t\t\tnamed.push(group)\n\t\t}\n\t\tgroup.entries.push(entry)\n\t}\n\treturn [plain, ...named, admin].filter((group) => group.entries.length > 0)\n}\n\n/**\n * Whether one checklist item is done for this record.\n *\n * `done` (a literal) wins, then `doneWhen` (a local visibleWhen condition),\n * then `doneField` (truthy, and for a list: not empty).\n *\n * @param {object} item The checklist item.\n * @param {object|null} object The record.\n * @return {boolean} True when the item is done.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-next-step-card\n */\nexport function isChecklistItemDone(item, object) {\n\tif (!item || typeof item !== 'object') {\n\t\treturn false\n\t}\n\tif (typeof item.done === 'boolean') {\n\t\treturn item.done\n\t}\n\tconst data = object && typeof object === 'object' ? object : {}\n\tif (item.doneWhen && typeof item.doneWhen === 'object') {\n\t\treturn evaluateVisibleWhenLocal(item.doneWhen, data)\n\t}\n\tif (typeof item.doneField === 'string' && item.doneField !== '') {\n\t\tconst value = readPath(data, item.doneField)\n\t\treturn Array.isArray(value) ? value.length > 0 : Boolean(value)\n\t}\n\treturn false\n}\n\n/**\n * Resolve the \"what now\" card for the record's stage.\n *\n * @param {{stages?: object, title?: string}|null} config The `nextStep` config.\n * @param {object|null} object The record.\n * @param {string} [stageField] Dot-path to the stage.\n * @return {{stage: string, title: string, items: Array<{label: string, done: boolean, hint: string}>, after: string}|null} The card, or null when this stage declares no checklist.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-next-step-card\n */\nexport function resolveNextStep(config, object, stageField = 'status') {\n\tif (!config || typeof config !== 'object') {\n\t\treturn null\n\t}\n\tconst stage = stageOf(object, config.field || stageField)\n\tconst declared = stageEntry(config.stages, stage)\n\tif (!declared || typeof declared !== 'object' || !Array.isArray(declared.checklist)) {\n\t\treturn null\n\t}\n\tconst items = declared.checklist\n\t\t.filter((item) => item && typeof item === 'object' && typeof item.label === 'string' && item.label !== '')\n\t\t.map((item) => ({\n\t\t\tlabel: item.label,\n\t\t\tdone: isChecklistItemDone(item, object),\n\t\t\thint: typeof item.hint === 'string' ? item.hint : '',\n\t\t}))\n\tif (items.length === 0) {\n\t\treturn null\n\t}\n\treturn {\n\t\tstage,\n\t\ttitle: typeof declared.title === 'string' ? declared.title : (typeof config.title === 'string' ? config.title : ''),\n\t\titems,\n\t\tafter: typeof declared.after === 'string' ? declared.after : '',\n\t}\n}\n\n/**\n * Resolve a header pill from the record.\n *\n * @param {{field?: string, colorMap?: object, labels?: object, variant?: string}|null} config The pill config.\n * @param {object|null} object The record.\n * @return {{label: string, colorKey: string, colorMap: object|null, variant: string}|null} What CnStatusBadge needs, or null when the field is empty.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-header-pills\n */\nexport function resolvePill(config, object) {\n\tif (!config || typeof config !== 'object' || typeof config.field !== 'string' || config.field === '') {\n\t\treturn null\n\t}\n\tconst raw = stageOf(object, config.field)\n\tif (raw === '') {\n\t\treturn null\n\t}\n\tconst mapped = stageEntry(config.labels, raw)\n\treturn {\n\t\tlabel: typeof mapped === 'string' && mapped !== '' ? mapped : raw,\n\t\tcolorKey: raw,\n\t\tcolorMap: config.colorMap && typeof config.colorMap === 'object' ? config.colorMap : null,\n\t\tvariant: typeof config.variant === 'string' && config.variant !== '' ? config.variant : 'default',\n\t}\n}\n\n/**\n * Resolve a tab's count.\n *\n * `count` (a number) wins. Otherwise `countField` is read off the record: a\n * list counts its items, a number is taken as is. A tab that declares neither\n * takes what is new on it from `@self.unreadCounts[<unreadKey>]` (the key\n * defaults to the tab's `id`, else its `widgetId`) when that is above 0;\n * absent or 0 is no count.\n *\n * @param {{count?: number, countField?: string, unreadKey?: string, id?: string, widgetId?: string}|null} tab The tab declaration.\n * @param {object|null} object The record.\n * @return {number|null} The count, or null when the tab declares none.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-tab-counts-and-overflow\n */\nexport function resolveTabCount(tab, object) {\n\tif (!tab || typeof tab !== 'object') {\n\t\treturn null\n\t}\n\tif (typeof tab.count === 'number' && Number.isFinite(tab.count)) {\n\t\treturn tab.count\n\t}\n\tif (typeof tab.countField !== 'string' || tab.countField === '') {\n\t\tconst counts = object && typeof object === 'object' && object['@self'] && object['@self'].unreadCounts\n\t\tconst key = tab.unreadKey || tab.id || tab.widgetId\n\t\tconst unread = counts && typeof counts === 'object' && typeof key === 'string' ? Number(counts[key]) : 0\n\t\treturn Number.isFinite(unread) && unread > 0 ? unread : null\n\t}\n\tconst value = readPath(object && typeof object === 'object' ? object : {}, tab.countField)\n\tif (Array.isArray(value)) {\n\t\treturn value.length\n\t}\n\tif (value === null || value === undefined || value === '') {\n\t\treturn 0\n\t}\n\tconst number = Number(value)\n\treturn Number.isFinite(number) ? number : null\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"detailActionModel.js","sources":["../../../src/utils/detailActionModel.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * detailActionModel: the rules behind a detail page's four action levels and\n * the case surfaces that read the record's stage.\n *\n * 1. the next step: one primary button, chosen by the record's stage,\n * 2. quick actions: at most three always visible buttons,\n * 3. the overflow menu: the rest, in named groups,\n * 4. admin actions: the last group, for admins only.\n *\n * Pure functions, no Vue and no fetch, so CnDetailPage, the widgets and the\n * tests all read one implementation.\n *\n * @module utils/detailActionModel\n */\n\nimport { readPath } from './readPath.js'\nimport { evaluateVisibleWhenLocal } from './visibleWhen.js'\n\n/** The most quick actions a page shows. Later entries are dropped. */\nexport const MAX_QUICK_ACTIONS = 3\n\n/**\n * The record's stage, as the string a stage map is keyed on.\n *\n * @param {object|null} object The record.\n * @param {string} [stageField] Dot-path to the stage. Defaults to `status`.\n * @return {string} The stage, or '' when the record has none.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage\n */\nexport function stageOf(object, stageField = 'status') {\n\tif (!object || typeof object !== 'object') {\n\t\treturn ''\n\t}\n\tconst value = readPath(object, stageField || 'status')\n\tif (value === null || value === undefined || typeof value === 'object') {\n\t\treturn ''\n\t}\n\treturn String(value)\n}\n\n/**\n * Look a stage up in a stage-keyed map. An exact key wins; otherwise the\n * match ignores case, because a status is stored as `in_behandeling` in one\n * register and `In_Behandeling` in the next.\n *\n * @param {object|null} map The stage-keyed map.\n * @param {string} stage The stage.\n * @return {unknown} The entry, or undefined.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage\n */\nexport function stageEntry(map, stage) {\n\tif (!map || typeof map !== 'object' || Array.isArray(map) || stage === '') {\n\t\treturn undefined\n\t}\n\tif (Object.hasOwn(map, stage)) {\n\t\treturn map[stage]\n\t}\n\tconst wanted = stage.toLowerCase()\n\tconst key = Object.keys(map).find((candidate) => candidate.toLowerCase() === wanted)\n\treturn key === undefined ? undefined : map[key]\n}\n\n/**\n * Turn a pinned action entry into a descriptor the page can dispatch.\n *\n * An entry is the id of a declared header action, or an action object of its\n * own. An object without an id gets `fallbackId`, since every dispatched\n * action is addressed by id.\n *\n * @param {string|object|null} entry The entry.\n * @param {string} fallbackId Id for an inline action that names none.\n * @return {{id: string, inline: object|null}|null} The id, plus the inline definition when the entry carried one.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-quick-actions\n */\nexport function normalisePinnedAction(entry, fallbackId) {\n\tif (typeof entry === 'string') {\n\t\treturn entry === '' ? null : { id: entry, inline: null }\n\t}\n\tif (!entry || typeof entry !== 'object' || Array.isArray(entry)) {\n\t\treturn null\n\t}\n\tif (typeof entry.label !== 'string' || entry.label === '') {\n\t\treturn null\n\t}\n\tconst id = typeof entry.id === 'string' && entry.id !== '' ? entry.id : fallbackId\n\treturn { id, inline: { ...entry, id } }\n}\n\n/**\n * Split menu entries into the groups the overflow menu draws.\n *\n * Ungrouped entries come first, under no caption. Named groups follow in the\n * order they first appear. Admin only entries are dropped for everyone else\n * and form the last group for admins, whatever `group` they also name.\n *\n * `adminOnly` hides a control. It is never an authorization decision: the\n * endpoint behind the action decides who may call it.\n *\n * @param {Array<object>} entries Menu-ready entries, each with an `id`.\n * @param {object} actionsById The declared actions by id, carrying `group` / `adminOnly`.\n * @param {{isAdmin?: boolean, adminLabel?: string}} [options] Who is looking, and what the admin group is called.\n * @return {Array<{key: string, label: string, entries: Array<object>}>} The non-empty groups, in render order.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-grouped-and-admin-only-menu-actions\n */\nexport function groupMenuEntries(entries, actionsById = {}, options = {}) {\n\tconst isAdmin = options.isAdmin === true\n\tconst plain = { key: '', label: '', entries: [] }\n\tconst named = []\n\tconst admin = { key: 'cn-admin', label: options.adminLabel || '', entries: [] }\n\tfor (const entry of Array.isArray(entries) ? entries : []) {\n\t\tconst declared = (entry && actionsById[entry.id]) || {}\n\t\tif (declared.adminOnly === true) {\n\t\t\tif (isAdmin) {\n\t\t\t\tadmin.entries.push(entry)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tconst label = typeof declared.group === 'string' ? declared.group.trim() : ''\n\t\tif (label === '') {\n\t\t\tplain.entries.push(entry)\n\t\t\tcontinue\n\t\t}\n\t\tlet group = named.find((candidate) => candidate.label === label)\n\t\tif (!group) {\n\t\t\tgroup = { key: `cn-group-${named.length}`, label, entries: [] }\n\t\t\tnamed.push(group)\n\t\t}\n\t\tgroup.entries.push(entry)\n\t}\n\treturn [plain, ...named, admin].filter((group) => group.entries.length > 0)\n}\n\n/**\n * Whether one checklist item is done for this record.\n *\n * `done` (a literal) wins, then `doneWhen` (a local visibleWhen condition),\n * then `doneField` (truthy, and for a list: not empty).\n *\n * @param {object} item The checklist item.\n * @param {object|null} object The record.\n * @return {boolean} True when the item is done.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-next-step-card\n */\nexport function isChecklistItemDone(item, object) {\n\tif (!item || typeof item !== 'object') {\n\t\treturn false\n\t}\n\tif (typeof item.done === 'boolean') {\n\t\treturn item.done\n\t}\n\tconst data = object && typeof object === 'object' ? object : {}\n\tif (item.doneWhen && typeof item.doneWhen === 'object') {\n\t\treturn evaluateVisibleWhenLocal(item.doneWhen, data)\n\t}\n\tif (typeof item.doneField === 'string' && item.doneField !== '') {\n\t\tconst value = readPath(data, item.doneField)\n\t\treturn Array.isArray(value) ? value.length > 0 : Boolean(value)\n\t}\n\treturn false\n}\n\n/**\n * Resolve the \"what now\" card for the record's stage.\n *\n * @param {{stages?: object, title?: string}|null} config The `nextStep` config.\n * @param {object|null} object The record.\n * @param {string} [stageField] Dot-path to the stage.\n * @return {{stage: string, title: string, items: Array<{label: string, done: boolean, hint: string}>, after: string}|null} The card, or null when this stage declares no checklist.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-next-step-card\n */\nexport function resolveNextStep(config, object, stageField = 'status') {\n\tif (!config || typeof config !== 'object') {\n\t\treturn null\n\t}\n\tconst stage = stageOf(object, config.field || stageField)\n\tconst declared = stageEntry(config.stages, stage)\n\tif (!declared || typeof declared !== 'object' || !Array.isArray(declared.checklist)) {\n\t\treturn null\n\t}\n\tconst items = declared.checklist\n\t\t.filter((item) => item && typeof item === 'object' && typeof item.label === 'string' && item.label !== '')\n\t\t.map((item) => ({\n\t\t\tlabel: item.label,\n\t\t\tdone: isChecklistItemDone(item, object),\n\t\t\thint: typeof item.hint === 'string' ? item.hint : '',\n\t\t}))\n\tif (items.length === 0) {\n\t\treturn null\n\t}\n\treturn {\n\t\tstage,\n\t\ttitle: typeof declared.title === 'string' ? declared.title : (typeof config.title === 'string' ? config.title : ''),\n\t\titems,\n\t\tafter: typeof declared.after === 'string' ? declared.after : '',\n\t}\n}\n\n/**\n * Resolve a header pill from the record.\n *\n * A pill with `field` reads it off the record; one without `field` and with\n * `text` is a constant. A count pill (`countFrom`) is resolved by\n * {@link resolveCountPill} once its count is known.\n *\n * @param {{field?: string, text?: string, colorMap?: object, labels?: object, variant?: string}|null} config The pill config.\n * @param {object|null} object The record.\n * @return {{label: string, colorKey: string, colorMap: object|null, variant: string}|null} What CnStatusBadge needs, or null when the field is empty.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-header-pills\n */\nexport function resolvePill(config, object) {\n\tif (!config || typeof config !== 'object') {\n\t\treturn null\n\t}\n\t// A constant pill (`text`): \"Inwoner\", \"Klant\" (screens-relation-pages).\n\tif ((typeof config.field !== 'string' || config.field === '') && typeof config.text === 'string' && config.text !== '') {\n\t\treturn {\n\t\t\tlabel: config.text,\n\t\t\tcolorKey: config.text,\n\t\t\tcolorMap: config.colorMap && typeof config.colorMap === 'object' ? config.colorMap : null,\n\t\t\tvariant: typeof config.variant === 'string' && config.variant !== '' ? config.variant : 'default',\n\t\t}\n\t}\n\tif (typeof config.field !== 'string' || config.field === '') {\n\t\treturn null\n\t}\n\tconst raw = stageOf(object, config.field)\n\tif (raw === '') {\n\t\treturn null\n\t}\n\tconst mapped = stageEntry(config.labels, raw)\n\treturn {\n\t\tlabel: typeof mapped === 'string' && mapped !== '' ? mapped : raw,\n\t\tcolorKey: raw,\n\t\tcolorMap: config.colorMap && typeof config.colorMap === 'object' ? config.colorMap : null,\n\t\tvariant: typeof config.variant === 'string' && config.variant !== '' ? config.variant : 'default',\n\t}\n}\n\n/**\n * Resolve a tab's count.\n *\n * `count` (a number) wins. Otherwise `countField` is read off the record: a\n * list counts its items, a number is taken as is. A tab that declares neither\n * takes what is new on it from `@self.unreadCounts[<unreadKey>]` (the key\n * defaults to the tab's `id`, else its `widgetId`) when that is above 0;\n * absent or 0 is no count.\n *\n * @param {{count?: number, countField?: string, unreadKey?: string, id?: string, widgetId?: string}|null} tab The tab declaration.\n * @param {object|null} object The record.\n * @return {number|null} The count, or null when the tab declares none.\n * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-tab-counts-and-overflow\n */\nexport function resolveTabCount(tab, object) {\n\tif (!tab || typeof tab !== 'object') {\n\t\treturn null\n\t}\n\tif (typeof tab.count === 'number' && Number.isFinite(tab.count)) {\n\t\treturn tab.count\n\t}\n\tif (typeof tab.countField !== 'string' || tab.countField === '') {\n\t\tconst counts = object && typeof object === 'object' && object['@self'] && object['@self'].unreadCounts\n\t\tconst key = tab.unreadKey || tab.id || tab.widgetId\n\t\tconst unread = counts && typeof counts === 'object' && typeof key === 'string' ? Number(counts[key]) : 0\n\t\treturn Number.isFinite(unread) && unread > 0 ? unread : null\n\t}\n\tconst value = readPath(object && typeof object === 'object' ? object : {}, tab.countField)\n\tif (Array.isArray(value)) {\n\t\treturn value.length\n\t}\n\tif (value === null || value === undefined || value === '') {\n\t\treturn 0\n\t}\n\tconst number = Number(value)\n\treturn Number.isFinite(number) ? number : null\n}\n\n/**\n * The list a count pill counts (`countFrom`), or null for any other pill.\n *\n * @param {object|null} config The pill config.\n * @return {{register: string, schema: string, filter: object}|null} The list.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-header-pill-can-be-a-constant-or-a-count\n */\nexport function pillCountSource(config) {\n\tconst from = config && typeof config === 'object' && !config.field ? config.countFrom : null\n\tif (!from || typeof from !== 'object' || typeof from.register !== 'string' || from.register === '' || typeof from.schema !== 'string' || from.schema === '') {\n\t\treturn null\n\t}\n\tconst filter = from.filter && typeof from.filter === 'object' && !Array.isArray(from.filter) ? from.filter : {}\n\treturn { register: from.register, schema: from.schema, filter }\n}\n\n/**\n * A count pill once its count is known: \"1 lopende zaak\", \"2 lopende zaken\".\n * Nothing for a count of 0, an unknown count or a failed one.\n *\n * @param {{countLabel?: {one?: string, other?: string}, colorMap?: object, variant?: string}|null} config The pill config.\n * @param {number|null|undefined} count The count.\n * @param {(text: string) => string} [translate] Applied to the label before `{count}` is filled.\n * @return {{label: string, colorKey: string, colorMap: object|null, variant: string}|null} What CnStatusBadge needs, or null.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-header-pill-can-be-a-constant-or-a-count\n */\nexport function resolveCountPill(config, count, translate = (text) => text) {\n\tif (!pillCountSource(config) || typeof count !== 'number' || !Number.isFinite(count) || count <= 0) {\n\t\treturn null\n\t}\n\tconst labels = config.countLabel && typeof config.countLabel === 'object' ? config.countLabel : {}\n\tconst template = (count === 1 ? labels.one : labels.other) || labels.other || labels.one || '{count}'\n\tconst label = translate(template).replace(/\\{count\\}/g, String(count))\n\treturn {\n\t\tlabel,\n\t\tcolorKey: '',\n\t\tcolorMap: config.colorMap && typeof config.colorMap === 'object' ? config.colorMap : null,\n\t\tvariant: typeof config.variant === 'string' && config.variant !== '' ? config.variant : 'default',\n\t}\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -76,6 +76,7 @@ const DEFAULT_MAP = {
|
|
|
76
76
|
enum: 'enumValues',
|
|
77
77
|
required: 'isRequired',
|
|
78
78
|
default: 'defaultValue',
|
|
79
|
+
hideOnCreate: 'hideOnCreate',
|
|
79
80
|
};
|
|
80
81
|
|
|
81
82
|
/**
|
|
@@ -287,6 +288,8 @@ function definitionQueryParams(config, value, formData = {}) {
|
|
|
287
288
|
* @param {object} [config] The `x-openregister-extends-form` block (`map`, `typeMap`).
|
|
288
289
|
* @param {object} [options] Extra options.
|
|
289
290
|
* @param {number} [options.orderFrom] Order assigned to the first field; each subsequent field takes the next integer. Dynamic fields sort after the schema's own, so pass a number above every declared `order`.
|
|
291
|
+
* @param {boolean} [options.create] The form creates a new object: a definition whose `hideOnCreate` (or the field `config.map.hideOnCreate` names) is `true` gets no field, because the app fills that answer itself at creation. Edit forms still show it.
|
|
292
|
+
* @spec openspec/changes/dynamic-question-hide-on-create/specs/dynamic-question-hide-on-create/spec.md#requirement-a-question-can-stay-off-the-create-form
|
|
290
293
|
* @return {{properties: object, required: string[]}} Properties keyed by prefixed id, plus the required key list.
|
|
291
294
|
*/
|
|
292
295
|
function propertiesFromDefinitions(definitions, config = {}, options = {}) {
|
|
@@ -296,7 +299,8 @@ function propertiesFromDefinitions(definitions, config = {}, options = {}) {
|
|
|
296
299
|
|
|
297
300
|
const properties = {};
|
|
298
301
|
const required = [];
|
|
299
|
-
const list = Array.isArray(definitions) ? definitions : []
|
|
302
|
+
const list = (Array.isArray(definitions) ? definitions : [])
|
|
303
|
+
.filter((record) => !(options.create === true && record && mapped(record, map, 'hideOnCreate') === true));
|
|
300
304
|
|
|
301
305
|
list.forEach((record, index) => {
|
|
302
306
|
if (!record || !record.id) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamicProperties.js","sources":["../../../src/utils/dynamicProperties.js"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 Conduction B.V.\n// SPDX-License-Identifier: EUPL-1.2\n\n/**\n * Form fields that a record's own data decides, rather than its schema.\n *\n * Some domains keep half of an object's shape in the database instead of the\n * schema. A municipal case type declares which extra questions its cases must\n * answer; a product line declares which attributes its orders carry. The\n * schema cannot enumerate those, because a functional admin adds them at\n * runtime without a release.\n *\n * The pattern is always the same three parts, so this module names them once:\n *\n * - a **driving property** on the schema — a reference the user picks first\n * (`case.caseType`), carrying an `x-openregister-extends-form` block;\n * - a **definitions schema** whose records describe the extra fields\n * (`propertyDefinition`), filtered by the driving property's value;\n * - a **values schema** that stores one answer per definition per object\n * (`caseProperty`), written after the parent object exists.\n *\n * Definitions are mapped into ordinary JSON Schema properties and handed to\n * `fieldsFromSchema`, so widget resolution, enum labels, validation and\n * description splitting all come from the one engine. A dynamic field is not\n * a second kind of field; it is a normal field whose declaration arrived late.\n *\n * @module utils/dynamicProperties\n */\n\nimport { resolveFilterTokens } from './resolveFilterTokens.js'\n\n/**\n * The vendor extension a driving property carries.\n *\n * @type {string}\n */\nexport const EXTENDS_FORM_KEY = 'x-openregister-extends-form'\n\n/**\n * The vendor extension a driving property carries to prefill its siblings.\n *\n * Where `x-openregister-extends-form` adds NEW fields, this one fills fields\n * the schema already declares. A case type knows the status a case of its\n * kind starts in and who normally handles it, so a person should not have to\n * retype what the chosen type already answers.\n *\n * @type {string}\n */\nexport const PREFILL_KEY = 'x-openregister-prefill'\n\n/**\n * Prefix that namespaces a dynamic field's key inside form data.\n *\n * Definition records are named by a functional admin, so a definition called\n * `title` or `status` is not merely possible but likely. Without a namespace\n * it would overwrite the real schema property of that name — silently, since\n * a form has no way to tell the two apart once they share a key.\n *\n * @type {string}\n */\nexport const DYNAMIC_KEY_PREFIX = 'x-prop:'\n\n/**\n * How a definition record's declared type becomes a JSON Schema property.\n *\n * Covers the JSON Schema type names themselves plus the shorthands a\n * definitions schema tends to use, because an admin picking a type from a\n * dropdown picks `date`, not `string`/`format: date`.\n *\n * @type {Record<string, object>}\n */\nconst TYPE_MAP = {\n\tstring: { type: 'string' },\n\ttext: { type: 'string' },\n\ttextarea: { type: 'string', format: 'textarea' },\n\tmarkdown: { type: 'string', format: 'markdown' },\n\tnumber: { type: 'number' },\n\tinteger: { type: 'integer' },\n\tboolean: { type: 'boolean' },\n\tdate: { type: 'string', format: 'date' },\n\tdatetime: { type: 'string', format: 'date-time' },\n\t'date-time': { type: 'string', format: 'date-time' },\n\turl: { type: 'string', format: 'uri' },\n\turi: { type: 'string', format: 'uri' },\n\temail: { type: 'string', format: 'email' },\n\tenum: { type: 'string' },\n\tjson: { type: 'object', widget: 'json' },\n\tarray: { type: 'array' },\n\tobject: { type: 'object', widget: 'json' },\n}\n\n/**\n * Field names read off a definition record when the declaration omits `map`.\n *\n * @type {Record<string, string>}\n */\nconst DEFAULT_MAP = {\n\ttitle: 'name',\n\tdescription: 'description',\n\ttype: 'propertyType',\n\tenum: 'enumValues',\n\trequired: 'isRequired',\n\tdefault: 'defaultValue',\n}\n\n/**\n * Read a value off a record by mapped field name.\n *\n * @param {object} record The definition record.\n * @param {object} map The resolved field-name map.\n * @param {string} role The map role (`title`, `type`, …).\n * @return {unknown} The value, or undefined when the role is unmapped or absent.\n */\nfunction mapped(record, map, role) {\n\tconst field = map[role]\n\tif (!field) {\n\t\treturn undefined\n\t}\n\treturn record[field]\n}\n\n/**\n * The `x-openregister-extends-form` declarations on a schema, one per driving\n * property. Pure: reads the schema and nothing else.\n *\n * A schema may carry more than one — an order extended by both its product\n * line and its customer segment — so this always returns a list.\n *\n * @param {object} schema The schema object with a `properties` field.\n * @return {Array<{key: string, config: object}>} The declarations, in property order.\n */\nexport function extendsFormDeclarations(schema) {\n\tif (!schema || !schema.properties) {\n\t\treturn []\n\t}\n\tconst out = []\n\tfor (const [key, prop] of Object.entries(schema.properties)) {\n\t\tconst config = prop && prop[EXTENDS_FORM_KEY]\n\t\tif (config && typeof config === 'object' && config.definitions) {\n\t\t\tout.push({ key, config })\n\t\t}\n\t}\n\treturn out\n}\n\n/**\n * A definition's name as a person should read it.\n *\n * Definition names are typed by a functional admin, and in practice a good\n * many arrive as identifiers rather than labels: real seeded data in dossiq\n * carries `auditorsStatementThreshold` and `interimReportTermWeeks`, which\n * render verbatim on a form a case handler is supposed to find friendly.\n *\n * Only an identifier-shaped name is touched. Anything containing a space is\n * already a label someone wrote deliberately, and reformatting it would be\n * this function deciding it knows better than the person who typed it.\n *\n * @param {string} name The definition's name.\n * @return {string} The name, made readable when it was an identifier.\n */\nfunction humaniseName(name) {\n\tconst value = String(name || '')\n\tif (!value || value.includes(' ')) {\n\t\treturn value\n\t}\n\n\tconst words = value\n\t\t// `camelCase` and `snake_case` / `kebab-case` both become words. The\n\t\t// lookahead pair keeps an acronym whole: `defaultBSNPolicy` splits to\n\t\t// `default BSN Policy`, not `default B S N Policy`.\n\t\t.replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n\t\t.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n\t\t.replace(/[_-]+/g, ' ')\n\t\t.trim()\n\t\t.split(/\\s+/)\n\t\t.filter(Boolean)\n\tif (words.length === 0) {\n\t\treturn value\n\t}\n\n\t// Sentence case, not Title Case: splitting `auditorsStatementThreshold`\n\t// leaves each word capitalised, and shipping that would put a Title Case\n\t// label on every form. An all-caps word is an acronym and keeps its shape.\n\tconst cased = words.map((word, index) => {\n\t\tif (word.length > 1 && word === word.toUpperCase()) {\n\t\t\treturn word\n\t\t}\n\t\tconst lower = word.toLowerCase()\n\t\treturn index === 0 ? lower.charAt(0).toUpperCase() + lower.slice(1) : lower\n\t})\n\n\treturn cased.join(' ')\n}\n\n/**\n * The schema's `x-openregister-prefill` declarations, in property order.\n *\n * A declaration needs a `fields` map to be worth acting on: without it there\n * is nothing to copy, and returning it anyway would cost a fetch per change\n * for no effect.\n *\n * @param {object} schema The schema object with a `properties` field.\n * @return {Array<{key: string, config: object}>} The declarations, in property order.\n */\nexport function prefillDeclarations(schema) {\n\tif (!schema || !schema.properties) {\n\t\treturn []\n\t}\n\tconst out = []\n\tfor (const [key, prop] of Object.entries(schema.properties)) {\n\t\tconst config = prop && prop[PREFILL_KEY]\n\t\tif (config && typeof config === 'object' && config.fields\n\t\t\t&& Object.keys(config.fields).length > 0) {\n\t\t\tout.push({ key, config })\n\t\t}\n\t}\n\treturn out\n}\n\n/**\n * The values one chosen record offers its siblings.\n *\n * Reads `config.fields` as `{ targetProperty: sourceProperty }` and returns\n * only the pairs the record can actually answer. A source field the record\n * leaves empty yields nothing, so a case type with no default assignee\n * prefills the status it does know and leaves the assignee alone, rather\n * than blanking a value.\n *\n * @param {object} record The chosen record, as the API returned it.\n * @param {object} config The `x-openregister-prefill` block (needs `fields`).\n * @return {object} Target property keys mapped to the values to apply.\n */\nexport function prefillValues(record, config) {\n\tconst fields = (config && config.fields) || null\n\tif (!record || !fields) {\n\t\treturn {}\n\t}\n\n\tconst out = {}\n\tfor (const [target, source] of Object.entries(fields)) {\n\t\tif (typeof source !== 'string' || !source) {\n\t\t\tcontinue\n\t\t}\n\t\tconst value = record[source]\n\t\tif (value === undefined || value === null || value === '') {\n\t\t\tcontinue\n\t\t}\n\t\tif (Array.isArray(value) && value.length === 0) {\n\t\t\tcontinue\n\t\t}\n\t\tout[target] = value\n\t}\n\treturn out\n}\n\n/**\n * The query parameters that fetch the definitions for one driving value.\n *\n * `config.definitions.filter` is token-resolved the way `x-relation-filter`\n * is, with the driving property's value available as `$value` and the whole\n * form as `@object.<field>`. A filter entry whose token stays unresolved is\n * dropped rather than sent, because a literal `@object.foo` reaching the API\n * filters on nonsense and returns zero rows — which reads exactly like a\n * case type that has no extra properties.\n *\n * @param {object} config The `x-openregister-extends-form` block.\n * @param {unknown} value The driving property's current value.\n * @param {object} [formData] The full form data, for `@object.<field>` tokens.\n * @return {object} Query parameters for `fetchCollection`.\n */\nexport function definitionQueryParams(config, value, formData = {}) {\n\tconst params = { _limit: 100 }\n\tconst raw = (config && config.definitions && config.definitions.filter) || null\n\tif (!raw || typeof raw !== 'object') {\n\t\treturn params\n\t}\n\n\t// `$value` is the driving property itself; substitute it before token\n\t// resolution so the generic resolver never sees a token it cannot answer.\n\tconst seeded = {}\n\tfor (const [key, entry] of Object.entries(raw)) {\n\t\tseeded[key] = entry === '$value' ? value : entry\n\t}\n\tconst resolved = resolveFilterTokens(seeded, { object: { ...formData }, objectId: formData.id })\n\tfor (const [key, entry] of Object.entries(resolved)) {\n\t\tif (typeof entry === 'string' && entry.charAt(0) === '@') {\n\t\t\tcontinue\n\t\t}\n\t\tif (entry && typeof entry === 'object' && !Array.isArray(entry)) {\n\t\t\tfor (const [op, operand] of Object.entries(entry)) {\n\t\t\t\tparams[`${key}[${op}]`] = operand\n\t\t\t}\n\t\t} else if (entry !== '' && entry !== null && entry !== undefined) {\n\t\t\tparams[key] = entry\n\t\t}\n\t}\n\treturn params\n}\n\n/**\n * Turn definition records into JSON Schema properties.\n *\n * The result is an ordinary `properties` map — the same shape a schema\n * carries — so the caller can hand it straight to `fieldsFromSchema` and get\n * widgets, enum labels, validation and required-marking for free.\n *\n * Each key is `DYNAMIC_KEY_PREFIX` + the record's id, never the admin-authored\n * name, so a definition can be renamed without orphaning the answers already\n * stored against it.\n *\n * @param {Array<object>} definitions The definition records.\n * @param {object} [config] The `x-openregister-extends-form` block (`map`, `typeMap`).\n * @param {object} [options] Extra options.\n * @param {number} [options.orderFrom] Order assigned to the first field; each subsequent field takes the next integer. Dynamic fields sort after the schema's own, so pass a number above every declared `order`.\n * @return {{properties: object, required: string[]}} Properties keyed by prefixed id, plus the required key list.\n */\nexport function propertiesFromDefinitions(definitions, config = {}, options = {}) {\n\tconst map = { ...DEFAULT_MAP, ...(config.map || {}) }\n\tconst typeMap = { ...TYPE_MAP, ...(config.typeMap || {}) }\n\tconst orderFrom = typeof options.orderFrom === 'number' ? options.orderFrom : 1000\n\n\tconst properties = {}\n\tconst required = []\n\tconst list = Array.isArray(definitions) ? definitions : []\n\n\tlist.forEach((record, index) => {\n\t\tif (!record || !record.id) {\n\t\t\treturn\n\t\t}\n\t\tconst key = DYNAMIC_KEY_PREFIX + record.id\n\t\tconst declaredType = mapped(record, map, 'type')\n\t\tconst base = typeMap[declaredType] || typeMap.string\n\n\t\tconst title = mapped(record, map, 'title')\n\t\tconst description = mapped(record, map, 'description')\n\t\tconst enumValues = mapped(record, map, 'enum')\n\t\tconst defaultValue = mapped(record, map, 'default')\n\t\tconst maxLength = mapped(record, map, 'maxLength')\n\t\tconst format = mapped(record, map, 'format')\n\n\t\tconst prop = {\n\t\t\t...base,\n\t\t\ttitle: humaniseName(title) || key,\n\t\t\t// A definition record separates its short `definition` from its\n\t\t\t// longer `description`; either may be absent, and an empty string\n\t\t\t// must not become the helper text \"undefined\".\n\t\t\tdescription: description || mapped(record, map, 'definition') || '',\n\t\t}\n\t\t// An explicitly mapped format wins over the one the type shorthand\n\t\t// implied, so a definition can say `string` + `date` and still get a\n\t\t// date picker.\n\t\tif (format) {\n\t\t\tprop.format = format\n\t\t}\n\t\tif (typeof maxLength === 'number' && maxLength > 0) {\n\t\t\tprop.maxLength = maxLength\n\t\t}\n\t\tif (Array.isArray(enumValues) && enumValues.length > 0) {\n\t\t\tprop.enum = [...enumValues]\n\t\t}\n\t\tif (defaultValue !== undefined && defaultValue !== null && defaultValue !== '') {\n\t\t\tprop.default = defaultValue\n\t\t}\n\t\tprop.order = orderFrom + index\n\n\t\tproperties[key] = prop\n\t\tif (mapped(record, map, 'required') === true) {\n\t\t\trequired.push(key)\n\t\t}\n\t})\n\n\treturn { properties, required }\n}\n\n/**\n * Whether a form-data key holds a dynamic field's answer.\n *\n * @param {string} key The form-data key.\n * @return {boolean} True for a dynamic key.\n */\nexport function isDynamicKey(key) {\n\treturn typeof key === 'string' && key.startsWith(DYNAMIC_KEY_PREFIX)\n}\n\n/**\n * The definition id a dynamic key refers to.\n *\n * @param {string} key The dynamic form-data key.\n * @return {string} The definition id, or '' when the key is not dynamic.\n */\nexport function definitionIdFromKey(key) {\n\treturn isDynamicKey(key) ? key.slice(DYNAMIC_KEY_PREFIX.length) : ''\n}\n\n/**\n * Split a submitted form payload into the object's own fields and the answers\n * that belong in the values schema.\n *\n * The parent object must be saved first — a value row references it — so the\n * two halves cannot go out in one call, and mixing them would post keys the\n * parent schema does not declare. OpenRegister drops undeclared properties\n * silently, so an unsplit payload loses every answer with a 200 and no error.\n *\n * @param {object} formData The dialog's confirmed payload.\n * @return {{base: object, answers: Array<{definitionId: string, value: unknown}>}} The parent's fields, and one answer per dynamic field.\n */\nexport function splitDynamicFormData(formData) {\n\tconst base = {}\n\tconst answers = []\n\tfor (const [key, value] of Object.entries(formData || {})) {\n\t\tif (isDynamicKey(key)) {\n\t\t\tanswers.push({ definitionId: definitionIdFromKey(key), value })\n\t\t} else {\n\t\t\tbase[key] = value\n\t\t}\n\t}\n\treturn { base, answers }\n}\n\n/**\n * Whether a declaration stores its answers as an array on the parent.\n *\n * Two shapes are supported. The default writes one child record per answer\n * (`values.schema` + `objectRef` + `definitionRef`), which is normalised and\n * queryable. Setting `values.mode: 'array'` instead folds the answers into an\n * array property on the parent object itself, which reads in one request and\n * saves in one write, at the cost of not being queryable per answer.\n *\n * @param {object} config One `x-openregister-extends-form` declaration.\n * @return {boolean} True when the declaration stores an array on the parent.\n */\nexport function usesArrayValues(config) {\n\tconst values = (config && config.values) || null\n\treturn !!(values && values.mode === 'array' && values.arrayKey)\n}\n\n/**\n * Build the array a declaration stores on its parent object.\n *\n * Each entry carries the definition it answers as well as its name, so a\n * reader can render the answers without resolving every definition first.\n * That denormalisation is the point of the array shape: one read, no joins.\n *\n * @param {Array<object>} answers `{ definitionId, value }` entries.\n * @param {object} config One `x-openregister-extends-form` declaration.\n * @param {Array<object>} definitions The definition records, for their names.\n * @return {Array<object>} Entries for the parent's array property.\n */\nexport function valueArrayFor(answers, config, definitions = []) {\n\tif (!usesArrayValues(config)) {\n\t\treturn []\n\t}\n\tconst values = config.values\n\tconst definitionRef = values.definitionRef || 'definition'\n\tconst valueKey = values.valueKey || 'value'\n\tconst nameKey = values.nameKey || 'name'\n\tconst nameFrom = (config.map && config.map.title) || 'name'\n\tconst byId = new Map((definitions || []).map((d) => [String(d.id ?? d.uuid ?? ''), d]))\n\n\treturn (answers || [])\n\t\t.filter(({ value }) => value !== undefined && value !== null && value !== '')\n\t\t.map(({ definitionId, value }) => {\n\t\t\tconst def = byId.get(String(definitionId))\n\t\t\treturn {\n\t\t\t\t[definitionRef]: definitionId,\n\t\t\t\t[nameKey]: def ? String(def[nameFrom] ?? '') : '',\n\t\t\t\t// Same rule as the record shape: a non-scalar answer is\n\t\t\t\t// serialised rather than dropped.\n\t\t\t\t[valueKey]: (typeof value === 'object') ? JSON.stringify(value) : String(value),\n\t\t\t}\n\t\t})\n}\n\n/**\n * The value-schema records to write for one saved parent object.\n *\n * An answer left empty writes no row: an absent row and a row holding `''`\n * mean the same thing to every reader, and not writing it keeps the value\n * schema free of rows that only record that someone opened the form.\n *\n * @param {Array<{definitionId: string, value: unknown}>} answers The answers from `splitDynamicFormData`.\n * @param {object} config The `x-openregister-extends-form` block (needs `values`).\n * @param {string} objectId The saved parent object's id.\n * @return {Array<object>} One payload per row to create; empty when nothing is to be written.\n */\nexport function valueRecordsFor(answers, config, objectId) {\n\tconst values = (config && config.values) || null\n\tif (!values || !values.schema || !objectId) {\n\t\treturn []\n\t}\n\tconst objectRef = values.objectRef || 'object'\n\tconst definitionRef = values.definitionRef || 'definition'\n\tconst valueKey = values.valueKey || 'value'\n\n\treturn (answers || [])\n\t\t.filter(({ value }) => value !== undefined && value !== null && value !== '')\n\t\t.map(({ definitionId, value }) => ({\n\t\t\t[objectRef]: objectId,\n\t\t\t[definitionRef]: definitionId,\n\t\t\t// A value schema stores one column for every type it carries, so a\n\t\t\t// non-scalar answer is serialised rather than dropped.\n\t\t\t[valueKey]: (typeof value === 'object') ? JSON.stringify(value) : String(value),\n\t\t}))\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dynamicProperties.js","sources":["../../../src/utils/dynamicProperties.js"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 Conduction B.V.\n// SPDX-License-Identifier: EUPL-1.2\n\n/**\n * Form fields that a record's own data decides, rather than its schema.\n *\n * Some domains keep half of an object's shape in the database instead of the\n * schema. A municipal case type declares which extra questions its cases must\n * answer; a product line declares which attributes its orders carry. The\n * schema cannot enumerate those, because a functional admin adds them at\n * runtime without a release.\n *\n * The pattern is always the same three parts, so this module names them once:\n *\n * - a **driving property** on the schema — a reference the user picks first\n * (`case.caseType`), carrying an `x-openregister-extends-form` block;\n * - a **definitions schema** whose records describe the extra fields\n * (`propertyDefinition`), filtered by the driving property's value;\n * - a **values schema** that stores one answer per definition per object\n * (`caseProperty`), written after the parent object exists.\n *\n * Definitions are mapped into ordinary JSON Schema properties and handed to\n * `fieldsFromSchema`, so widget resolution, enum labels, validation and\n * description splitting all come from the one engine. A dynamic field is not\n * a second kind of field; it is a normal field whose declaration arrived late.\n *\n * @module utils/dynamicProperties\n */\n\nimport { resolveFilterTokens } from './resolveFilterTokens.js'\n\n/**\n * The vendor extension a driving property carries.\n *\n * @type {string}\n */\nexport const EXTENDS_FORM_KEY = 'x-openregister-extends-form'\n\n/**\n * The vendor extension a driving property carries to prefill its siblings.\n *\n * Where `x-openregister-extends-form` adds NEW fields, this one fills fields\n * the schema already declares. A case type knows the status a case of its\n * kind starts in and who normally handles it, so a person should not have to\n * retype what the chosen type already answers.\n *\n * @type {string}\n */\nexport const PREFILL_KEY = 'x-openregister-prefill'\n\n/**\n * Prefix that namespaces a dynamic field's key inside form data.\n *\n * Definition records are named by a functional admin, so a definition called\n * `title` or `status` is not merely possible but likely. Without a namespace\n * it would overwrite the real schema property of that name — silently, since\n * a form has no way to tell the two apart once they share a key.\n *\n * @type {string}\n */\nexport const DYNAMIC_KEY_PREFIX = 'x-prop:'\n\n/**\n * How a definition record's declared type becomes a JSON Schema property.\n *\n * Covers the JSON Schema type names themselves plus the shorthands a\n * definitions schema tends to use, because an admin picking a type from a\n * dropdown picks `date`, not `string`/`format: date`.\n *\n * @type {Record<string, object>}\n */\nconst TYPE_MAP = {\n\tstring: { type: 'string' },\n\ttext: { type: 'string' },\n\ttextarea: { type: 'string', format: 'textarea' },\n\tmarkdown: { type: 'string', format: 'markdown' },\n\tnumber: { type: 'number' },\n\tinteger: { type: 'integer' },\n\tboolean: { type: 'boolean' },\n\tdate: { type: 'string', format: 'date' },\n\tdatetime: { type: 'string', format: 'date-time' },\n\t'date-time': { type: 'string', format: 'date-time' },\n\turl: { type: 'string', format: 'uri' },\n\turi: { type: 'string', format: 'uri' },\n\temail: { type: 'string', format: 'email' },\n\tenum: { type: 'string' },\n\tjson: { type: 'object', widget: 'json' },\n\tarray: { type: 'array' },\n\tobject: { type: 'object', widget: 'json' },\n}\n\n/**\n * Field names read off a definition record when the declaration omits `map`.\n *\n * @type {Record<string, string>}\n */\nconst DEFAULT_MAP = {\n\ttitle: 'name',\n\tdescription: 'description',\n\ttype: 'propertyType',\n\tenum: 'enumValues',\n\trequired: 'isRequired',\n\tdefault: 'defaultValue',\n\thideOnCreate: 'hideOnCreate',\n}\n\n/**\n * Read a value off a record by mapped field name.\n *\n * @param {object} record The definition record.\n * @param {object} map The resolved field-name map.\n * @param {string} role The map role (`title`, `type`, …).\n * @return {unknown} The value, or undefined when the role is unmapped or absent.\n */\nfunction mapped(record, map, role) {\n\tconst field = map[role]\n\tif (!field) {\n\t\treturn undefined\n\t}\n\treturn record[field]\n}\n\n/**\n * The `x-openregister-extends-form` declarations on a schema, one per driving\n * property. Pure: reads the schema and nothing else.\n *\n * A schema may carry more than one — an order extended by both its product\n * line and its customer segment — so this always returns a list.\n *\n * @param {object} schema The schema object with a `properties` field.\n * @return {Array<{key: string, config: object}>} The declarations, in property order.\n */\nexport function extendsFormDeclarations(schema) {\n\tif (!schema || !schema.properties) {\n\t\treturn []\n\t}\n\tconst out = []\n\tfor (const [key, prop] of Object.entries(schema.properties)) {\n\t\tconst config = prop && prop[EXTENDS_FORM_KEY]\n\t\tif (config && typeof config === 'object' && config.definitions) {\n\t\t\tout.push({ key, config })\n\t\t}\n\t}\n\treturn out\n}\n\n/**\n * A definition's name as a person should read it.\n *\n * Definition names are typed by a functional admin, and in practice a good\n * many arrive as identifiers rather than labels: real seeded data in dossiq\n * carries `auditorsStatementThreshold` and `interimReportTermWeeks`, which\n * render verbatim on a form a case handler is supposed to find friendly.\n *\n * Only an identifier-shaped name is touched. Anything containing a space is\n * already a label someone wrote deliberately, and reformatting it would be\n * this function deciding it knows better than the person who typed it.\n *\n * @param {string} name The definition's name.\n * @return {string} The name, made readable when it was an identifier.\n */\nfunction humaniseName(name) {\n\tconst value = String(name || '')\n\tif (!value || value.includes(' ')) {\n\t\treturn value\n\t}\n\n\tconst words = value\n\t\t// `camelCase` and `snake_case` / `kebab-case` both become words. The\n\t\t// lookahead pair keeps an acronym whole: `defaultBSNPolicy` splits to\n\t\t// `default BSN Policy`, not `default B S N Policy`.\n\t\t.replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n\t\t.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n\t\t.replace(/[_-]+/g, ' ')\n\t\t.trim()\n\t\t.split(/\\s+/)\n\t\t.filter(Boolean)\n\tif (words.length === 0) {\n\t\treturn value\n\t}\n\n\t// Sentence case, not Title Case: splitting `auditorsStatementThreshold`\n\t// leaves each word capitalised, and shipping that would put a Title Case\n\t// label on every form. An all-caps word is an acronym and keeps its shape.\n\tconst cased = words.map((word, index) => {\n\t\tif (word.length > 1 && word === word.toUpperCase()) {\n\t\t\treturn word\n\t\t}\n\t\tconst lower = word.toLowerCase()\n\t\treturn index === 0 ? lower.charAt(0).toUpperCase() + lower.slice(1) : lower\n\t})\n\n\treturn cased.join(' ')\n}\n\n/**\n * The schema's `x-openregister-prefill` declarations, in property order.\n *\n * A declaration needs a `fields` map to be worth acting on: without it there\n * is nothing to copy, and returning it anyway would cost a fetch per change\n * for no effect.\n *\n * @param {object} schema The schema object with a `properties` field.\n * @return {Array<{key: string, config: object}>} The declarations, in property order.\n */\nexport function prefillDeclarations(schema) {\n\tif (!schema || !schema.properties) {\n\t\treturn []\n\t}\n\tconst out = []\n\tfor (const [key, prop] of Object.entries(schema.properties)) {\n\t\tconst config = prop && prop[PREFILL_KEY]\n\t\tif (config && typeof config === 'object' && config.fields\n\t\t\t&& Object.keys(config.fields).length > 0) {\n\t\t\tout.push({ key, config })\n\t\t}\n\t}\n\treturn out\n}\n\n/**\n * The values one chosen record offers its siblings.\n *\n * Reads `config.fields` as `{ targetProperty: sourceProperty }` and returns\n * only the pairs the record can actually answer. A source field the record\n * leaves empty yields nothing, so a case type with no default assignee\n * prefills the status it does know and leaves the assignee alone, rather\n * than blanking a value.\n *\n * @param {object} record The chosen record, as the API returned it.\n * @param {object} config The `x-openregister-prefill` block (needs `fields`).\n * @return {object} Target property keys mapped to the values to apply.\n */\nexport function prefillValues(record, config) {\n\tconst fields = (config && config.fields) || null\n\tif (!record || !fields) {\n\t\treturn {}\n\t}\n\n\tconst out = {}\n\tfor (const [target, source] of Object.entries(fields)) {\n\t\tif (typeof source !== 'string' || !source) {\n\t\t\tcontinue\n\t\t}\n\t\tconst value = record[source]\n\t\tif (value === undefined || value === null || value === '') {\n\t\t\tcontinue\n\t\t}\n\t\tif (Array.isArray(value) && value.length === 0) {\n\t\t\tcontinue\n\t\t}\n\t\tout[target] = value\n\t}\n\treturn out\n}\n\n/**\n * The query parameters that fetch the definitions for one driving value.\n *\n * `config.definitions.filter` is token-resolved the way `x-relation-filter`\n * is, with the driving property's value available as `$value` and the whole\n * form as `@object.<field>`. A filter entry whose token stays unresolved is\n * dropped rather than sent, because a literal `@object.foo` reaching the API\n * filters on nonsense and returns zero rows — which reads exactly like a\n * case type that has no extra properties.\n *\n * @param {object} config The `x-openregister-extends-form` block.\n * @param {unknown} value The driving property's current value.\n * @param {object} [formData] The full form data, for `@object.<field>` tokens.\n * @return {object} Query parameters for `fetchCollection`.\n */\nexport function definitionQueryParams(config, value, formData = {}) {\n\tconst params = { _limit: 100 }\n\tconst raw = (config && config.definitions && config.definitions.filter) || null\n\tif (!raw || typeof raw !== 'object') {\n\t\treturn params\n\t}\n\n\t// `$value` is the driving property itself; substitute it before token\n\t// resolution so the generic resolver never sees a token it cannot answer.\n\tconst seeded = {}\n\tfor (const [key, entry] of Object.entries(raw)) {\n\t\tseeded[key] = entry === '$value' ? value : entry\n\t}\n\tconst resolved = resolveFilterTokens(seeded, { object: { ...formData }, objectId: formData.id })\n\tfor (const [key, entry] of Object.entries(resolved)) {\n\t\tif (typeof entry === 'string' && entry.charAt(0) === '@') {\n\t\t\tcontinue\n\t\t}\n\t\tif (entry && typeof entry === 'object' && !Array.isArray(entry)) {\n\t\t\tfor (const [op, operand] of Object.entries(entry)) {\n\t\t\t\tparams[`${key}[${op}]`] = operand\n\t\t\t}\n\t\t} else if (entry !== '' && entry !== null && entry !== undefined) {\n\t\t\tparams[key] = entry\n\t\t}\n\t}\n\treturn params\n}\n\n/**\n * Turn definition records into JSON Schema properties.\n *\n * The result is an ordinary `properties` map — the same shape a schema\n * carries — so the caller can hand it straight to `fieldsFromSchema` and get\n * widgets, enum labels, validation and required-marking for free.\n *\n * Each key is `DYNAMIC_KEY_PREFIX` + the record's id, never the admin-authored\n * name, so a definition can be renamed without orphaning the answers already\n * stored against it.\n *\n * @param {Array<object>} definitions The definition records.\n * @param {object} [config] The `x-openregister-extends-form` block (`map`, `typeMap`).\n * @param {object} [options] Extra options.\n * @param {number} [options.orderFrom] Order assigned to the first field; each subsequent field takes the next integer. Dynamic fields sort after the schema's own, so pass a number above every declared `order`.\n * @param {boolean} [options.create] The form creates a new object: a definition whose `hideOnCreate` (or the field `config.map.hideOnCreate` names) is `true` gets no field, because the app fills that answer itself at creation. Edit forms still show it.\n * @spec openspec/changes/dynamic-question-hide-on-create/specs/dynamic-question-hide-on-create/spec.md#requirement-a-question-can-stay-off-the-create-form\n * @return {{properties: object, required: string[]}} Properties keyed by prefixed id, plus the required key list.\n */\nexport function propertiesFromDefinitions(definitions, config = {}, options = {}) {\n\tconst map = { ...DEFAULT_MAP, ...(config.map || {}) }\n\tconst typeMap = { ...TYPE_MAP, ...(config.typeMap || {}) }\n\tconst orderFrom = typeof options.orderFrom === 'number' ? options.orderFrom : 1000\n\n\tconst properties = {}\n\tconst required = []\n\tconst list = (Array.isArray(definitions) ? definitions : [])\n\t\t.filter((record) => !(options.create === true && record && mapped(record, map, 'hideOnCreate') === true))\n\n\tlist.forEach((record, index) => {\n\t\tif (!record || !record.id) {\n\t\t\treturn\n\t\t}\n\t\tconst key = DYNAMIC_KEY_PREFIX + record.id\n\t\tconst declaredType = mapped(record, map, 'type')\n\t\tconst base = typeMap[declaredType] || typeMap.string\n\n\t\tconst title = mapped(record, map, 'title')\n\t\tconst description = mapped(record, map, 'description')\n\t\tconst enumValues = mapped(record, map, 'enum')\n\t\tconst defaultValue = mapped(record, map, 'default')\n\t\tconst maxLength = mapped(record, map, 'maxLength')\n\t\tconst format = mapped(record, map, 'format')\n\n\t\tconst prop = {\n\t\t\t...base,\n\t\t\ttitle: humaniseName(title) || key,\n\t\t\t// A definition record separates its short `definition` from its\n\t\t\t// longer `description`; either may be absent, and an empty string\n\t\t\t// must not become the helper text \"undefined\".\n\t\t\tdescription: description || mapped(record, map, 'definition') || '',\n\t\t}\n\t\t// An explicitly mapped format wins over the one the type shorthand\n\t\t// implied, so a definition can say `string` + `date` and still get a\n\t\t// date picker.\n\t\tif (format) {\n\t\t\tprop.format = format\n\t\t}\n\t\tif (typeof maxLength === 'number' && maxLength > 0) {\n\t\t\tprop.maxLength = maxLength\n\t\t}\n\t\tif (Array.isArray(enumValues) && enumValues.length > 0) {\n\t\t\tprop.enum = [...enumValues]\n\t\t}\n\t\tif (defaultValue !== undefined && defaultValue !== null && defaultValue !== '') {\n\t\t\tprop.default = defaultValue\n\t\t}\n\t\tprop.order = orderFrom + index\n\n\t\tproperties[key] = prop\n\t\tif (mapped(record, map, 'required') === true) {\n\t\t\trequired.push(key)\n\t\t}\n\t})\n\n\treturn { properties, required }\n}\n\n/**\n * Whether a form-data key holds a dynamic field's answer.\n *\n * @param {string} key The form-data key.\n * @return {boolean} True for a dynamic key.\n */\nexport function isDynamicKey(key) {\n\treturn typeof key === 'string' && key.startsWith(DYNAMIC_KEY_PREFIX)\n}\n\n/**\n * The definition id a dynamic key refers to.\n *\n * @param {string} key The dynamic form-data key.\n * @return {string} The definition id, or '' when the key is not dynamic.\n */\nexport function definitionIdFromKey(key) {\n\treturn isDynamicKey(key) ? key.slice(DYNAMIC_KEY_PREFIX.length) : ''\n}\n\n/**\n * Split a submitted form payload into the object's own fields and the answers\n * that belong in the values schema.\n *\n * The parent object must be saved first — a value row references it — so the\n * two halves cannot go out in one call, and mixing them would post keys the\n * parent schema does not declare. OpenRegister drops undeclared properties\n * silently, so an unsplit payload loses every answer with a 200 and no error.\n *\n * @param {object} formData The dialog's confirmed payload.\n * @return {{base: object, answers: Array<{definitionId: string, value: unknown}>}} The parent's fields, and one answer per dynamic field.\n */\nexport function splitDynamicFormData(formData) {\n\tconst base = {}\n\tconst answers = []\n\tfor (const [key, value] of Object.entries(formData || {})) {\n\t\tif (isDynamicKey(key)) {\n\t\t\tanswers.push({ definitionId: definitionIdFromKey(key), value })\n\t\t} else {\n\t\t\tbase[key] = value\n\t\t}\n\t}\n\treturn { base, answers }\n}\n\n/**\n * Whether a declaration stores its answers as an array on the parent.\n *\n * Two shapes are supported. The default writes one child record per answer\n * (`values.schema` + `objectRef` + `definitionRef`), which is normalised and\n * queryable. Setting `values.mode: 'array'` instead folds the answers into an\n * array property on the parent object itself, which reads in one request and\n * saves in one write, at the cost of not being queryable per answer.\n *\n * @param {object} config One `x-openregister-extends-form` declaration.\n * @return {boolean} True when the declaration stores an array on the parent.\n */\nexport function usesArrayValues(config) {\n\tconst values = (config && config.values) || null\n\treturn !!(values && values.mode === 'array' && values.arrayKey)\n}\n\n/**\n * Build the array a declaration stores on its parent object.\n *\n * Each entry carries the definition it answers as well as its name, so a\n * reader can render the answers without resolving every definition first.\n * That denormalisation is the point of the array shape: one read, no joins.\n *\n * @param {Array<object>} answers `{ definitionId, value }` entries.\n * @param {object} config One `x-openregister-extends-form` declaration.\n * @param {Array<object>} definitions The definition records, for their names.\n * @return {Array<object>} Entries for the parent's array property.\n */\nexport function valueArrayFor(answers, config, definitions = []) {\n\tif (!usesArrayValues(config)) {\n\t\treturn []\n\t}\n\tconst values = config.values\n\tconst definitionRef = values.definitionRef || 'definition'\n\tconst valueKey = values.valueKey || 'value'\n\tconst nameKey = values.nameKey || 'name'\n\tconst nameFrom = (config.map && config.map.title) || 'name'\n\tconst byId = new Map((definitions || []).map((d) => [String(d.id ?? d.uuid ?? ''), d]))\n\n\treturn (answers || [])\n\t\t.filter(({ value }) => value !== undefined && value !== null && value !== '')\n\t\t.map(({ definitionId, value }) => {\n\t\t\tconst def = byId.get(String(definitionId))\n\t\t\treturn {\n\t\t\t\t[definitionRef]: definitionId,\n\t\t\t\t[nameKey]: def ? String(def[nameFrom] ?? '') : '',\n\t\t\t\t// Same rule as the record shape: a non-scalar answer is\n\t\t\t\t// serialised rather than dropped.\n\t\t\t\t[valueKey]: (typeof value === 'object') ? JSON.stringify(value) : String(value),\n\t\t\t}\n\t\t})\n}\n\n/**\n * The value-schema records to write for one saved parent object.\n *\n * An answer left empty writes no row: an absent row and a row holding `''`\n * mean the same thing to every reader, and not writing it keeps the value\n * schema free of rows that only record that someone opened the form.\n *\n * @param {Array<{definitionId: string, value: unknown}>} answers The answers from `splitDynamicFormData`.\n * @param {object} config The `x-openregister-extends-form` block (needs `values`).\n * @param {string} objectId The saved parent object's id.\n * @return {Array<object>} One payload per row to create; empty when nothing is to be written.\n */\nexport function valueRecordsFor(answers, config, objectId) {\n\tconst values = (config && config.values) || null\n\tif (!values || !values.schema || !objectId) {\n\t\treturn []\n\t}\n\tconst objectRef = values.objectRef || 'object'\n\tconst definitionRef = values.definitionRef || 'definition'\n\tconst valueKey = values.valueKey || 'value'\n\n\treturn (answers || [])\n\t\t.filter(({ value }) => value !== undefined && value !== null && value !== '')\n\t\t.map(({ definitionId, value }) => ({\n\t\t\t[objectRef]: objectId,\n\t\t\t[definitionRef]: definitionId,\n\t\t\t// A value schema stores one column for every type it carries, so a\n\t\t\t// non-scalar answer is serialised rather than dropped.\n\t\t\t[valueKey]: (typeof value === 'object') ? JSON.stringify(value) : String(value),\n\t\t}))\n}\n"],"names":[],"mappings":"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"}
|