@conduction/nextcloud-vue 2.79.0 → 2.81.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/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 +13 -11
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +80 -4
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +95 -91
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +179 -13
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +52 -16
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +96 -4
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +158 -110
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js +10 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js +252 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js +140 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/index.js +26 -0
- package/dist/esm/components/CnChipsFieldWidget/index.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js +10 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js +354 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js +172 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/index.js +27 -0
- package/dist/esm/components/CnChoicePickerWidget/index.js.map +1 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +2 -2
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +65 -5
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +19 -11
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +4 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +134 -4
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +21 -4
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +24 -0
- 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/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +1 -1
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js +7 -7
- package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.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 +867 -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 +626 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +396 -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 +302 -4
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +417 -244
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js +101 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +141 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +65 -17
- 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 +78 -3
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue3.js +74 -49
- package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +333 -73
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +79 -17
- 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/CnIndexSidebar/CnIndexSidebar.vue2.js +7 -7
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +1 -1
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +65 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +63 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +8 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +14 -0
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js +5 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.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/CnRadioCardsWidget/CnRadioCardsWidget.vue.js +10 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js +174 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js +75 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/index.js +28 -0
- package/dist/esm/components/CnRadioCardsWidget/index.js.map +1 -0
- 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 +411 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +178 -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/CnSavedViewsWidget/CnSavedViewsWidget.vue.js +10 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js +222 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js +64 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/index.js +30 -0
- package/dist/esm/components/CnSavedViewsWidget/index.js.map +1 -0
- 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/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -1
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js +10 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js +347 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js +184 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/index.js +27 -0
- package/dist/esm/components/CnSettingsFormWidget/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 +87 -6
- 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/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +84 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +85 -41
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js +10 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js +159 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js +60 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/index.js +26 -0
- package/dist/esm/components/CnSwitchRowsWidget/index.js.map +1 -0
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +168 -7
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +86 -23
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +80 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +75 -32
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +10 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +269 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js +106 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/index.js +27 -0
- package/dist/esm/components/CnToggleTableWidget/index.js.map +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +11 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +7 -2
- 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/CnWidgetObjectTable/dashboardRegistration.js +5 -0
- package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +301 -6
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +153 -55
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
- package/dist/esm/composables/useFormDraft.js +140 -9
- package/dist/esm/composables/useFormDraft.js.map +1 -1
- package/dist/esm/composables/useListView.js +4 -4
- package/dist/esm/composables/useListView.js.map +1 -1
- package/dist/esm/index.js +21 -1
- 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 +20 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +70 -2
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +2192 -222
- package/dist/esm/utils/contentHeaderLink.js +51 -0
- package/dist/esm/utils/contentHeaderLink.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/facets.js +75 -3
- package/dist/esm/utils/facets.js.map +1 -1
- package/dist/esm/utils/fetchFilterCounts.js +40 -5
- package/dist/esm/utils/fetchFilterCounts.js.map +1 -1
- package/dist/esm/utils/fillTemplate.js +120 -0
- package/dist/esm/utils/fillTemplate.js.map +1 -0
- package/dist/esm/utils/initials.js +32 -0
- package/dist/esm/utils/initials.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +11 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/objectWrite.js +59 -0
- package/dist/esm/utils/objectWrite.js.map +1 -0
- package/dist/esm/utils/pageControls.js +81 -0
- package/dist/esm/utils/pageControls.js.map +1 -0
- package/dist/esm/utils/personalLenses.js +40 -3
- package/dist/esm/utils/personalLenses.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 +12 -1
- package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
- package/dist/esm/utils/resolveManifestTokens.js +2 -0
- package/dist/esm/utils/resolveManifestTokens.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 +22 -4
- package/dist/esm/utils/sentinelTokens.js.map +1 -1
- package/dist/esm/utils/userGroupsPeek.js +91 -0
- package/dist/esm/utils/userGroupsPeek.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/visibleWhen.js +57 -5
- package/dist/esm/utils/visibleWhen.js.map +1 -1
- package/dist/esm/utils/widgetDispatch.js +7 -1
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/esm/utils/widgetVisibility.js +4 -57
- package/dist/esm/utils/widgetVisibility.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +19708 -9301
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +2192 -222
- package/l10n/en.json +20 -1
- package/l10n/nl.json +70 -2
- package/package.json +2 -1
- package/scripts/check-docs-mdx.mjs +108 -0
- package/src/components/CnActionButtons/CnActionButtons.vue +2 -0
- package/src/components/CnActionsBar/CnActionsBar.md +4 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +48 -11
- package/src/components/CnAppRoot/CnAppRoot.md +4 -2
- package/src/components/CnAppRoot/CnAppRoot.vue +151 -59
- package/src/components/CnBannerWidget/CnBannerWidget.vue +244 -23
- package/src/components/CnCellRenderer/CnCellRenderer.md +2 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +235 -5
- package/src/components/CnChipsFieldWidget/CnChipsFieldWidget.vue +433 -0
- package/src/components/CnChipsFieldWidget/index.js +25 -0
- package/src/components/CnChoicePickerWidget/CnChoicePickerWidget.vue +556 -0
- package/src/components/CnChoicePickerWidget/index.js +26 -0
- package/src/components/CnConversationThread/CnConversationWidget.vue +1 -1
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +2 -2
- package/src/components/CnDashboardPage/CnDashboardPage.vue +78 -5
- package/src/components/CnDataTable/CnDataTable.vue +22 -12
- package/src/components/CnDetailPage/CnDetailPage.md +1 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +152 -6
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +66 -0
- package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +1 -1
- package/src/components/CnFacetSidebar/CnFacetSidebar.vue +7 -7
- package/src/components/CnFactsWidget/CnFactsWidget.vue +1190 -0
- package/src/components/CnFactsWidget/index.js +29 -0
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +1008 -0
- package/src/components/CnFieldListWidget/index.js +28 -0
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +588 -117
- package/src/components/CnFilesBrowser/filesBrowser.js +100 -0
- package/src/components/CnFilesBrowser/filesBrowserColumns.js +140 -0
- package/src/components/CnFormDialog/CnFormDialog.md +9 -3
- package/src/components/CnFormDialog/CnFormDialog.vue +69 -21
- package/src/components/CnFormPage/CnFormPage.vue +99 -3
- package/src/components/CnIndexPage/CnIndexPage.vue +309 -9
- package/src/components/CnIndexPage/CnIndexPageWidgets.vue +169 -0
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +7 -7
- package/src/components/CnNextStepCard/CnNextStepWidget.vue +1 -1
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +69 -2
- package/src/components/CnObjectSidebar/CnFilesTab.vue +70 -1
- package/src/components/CnObjectSidebar/CnNotesTab.vue +16 -0
- package/src/components/CnPageRenderer/CnPageRenderer.vue +29 -1
- package/src/components/CnRadioCardsWidget/CnRadioCardsWidget.vue +313 -0
- package/src/components/CnRadioCardsWidget/index.js +27 -0
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +652 -0
- package/src/components/CnRecordListWidget/index.js +30 -0
- package/src/components/CnSavedViewsWidget/CnSavedViewsWidget.vue +296 -0
- package/src/components/CnSavedViewsWidget/index.js +29 -0
- package/src/components/CnSectionsWidget/CnSectionsWidget.vue +489 -0
- package/src/components/CnSectionsWidget/index.js +34 -0
- package/src/components/CnSegmentedControl/CnSegmentedControl.vue +13 -0
- package/src/components/CnSettingsFormWidget/CnSettingsFormWidget.vue +555 -0
- package/src/components/CnSettingsFormWidget/index.js +26 -0
- package/src/components/CnStagesWidget/CnStagesWidget.vue +81 -2
- package/src/components/CnStatWidget/CnStatWidget.vue +88 -7
- package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +180 -3
- package/src/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue +226 -0
- package/src/components/CnSwitchRowsWidget/index.js +25 -0
- package/src/components/CnTabsWidget/CnTabsWidget.vue +263 -8
- package/src/components/CnTasksWidget/CnTasksWidget.vue +175 -15
- package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +386 -0
- package/src/components/CnToggleTableWidget/index.js +26 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +11 -0
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +7 -2
- package/src/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
- package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +493 -11
- package/src/components/index.js +19 -0
- package/src/composables/__tests__/useFormDraft.spec.js +69 -0
- package/src/composables/useFormDraft.js +139 -8
- package/src/composables/useListView.js +4 -4
- 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 +113 -2
- package/src/css/look-board-index.css +90 -8
- package/src/css/look-board.css +109 -2
- package/src/css/table.css +19 -0
- package/src/index.js +19 -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 +196 -13
- package/src/utils/contentHeaderLink.js +48 -0
- package/src/utils/detailActionModel.js +59 -2
- package/src/utils/facets.js +74 -2
- package/src/utils/fetchFilterCounts.js +39 -4
- package/src/utils/fillTemplate.js +116 -0
- package/src/utils/initials.js +29 -0
- package/src/utils/libraryWidgetKeys.js +11 -0
- package/src/utils/objectWrite.js +55 -0
- package/src/utils/pageControls.js +78 -0
- package/src/utils/personalLenses.js +39 -2
- package/src/utils/queryPresets.js +93 -0
- package/src/utils/resolveFilterTokens.js +14 -1
- package/src/utils/resolveManifestTokens.js +2 -0
- package/src/utils/schema.js +21 -0
- package/src/utils/sentinelTokens.js +22 -4
- package/src/utils/userGroupsPeek.js +87 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/visibleWhen.js +56 -4
- package/src/utils/widgetDispatch.js +7 -1
- package/src/utils/widgetVisibility.js +7 -56
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolveComponent, openBlock, createElementBlock, normalizeClass, createElementVNode, toDisplayString, createCommentVNode, Fragment, renderList, createBlock, resolveDynamicComponent, withCtx, createTextVNode, createVNode } from 'vue';
|
|
1
|
+
import { resolveComponent, openBlock, createElementBlock, normalizeClass, createElementVNode, toDisplayString, createCommentVNode, Fragment, renderList, createBlock, resolveDynamicComponent, withCtx, createTextVNode, createVNode, createSlots } from 'vue';
|
|
2
2
|
|
|
3
3
|
const _hoisted_1 = ["aria-labelledby"];
|
|
4
4
|
const _hoisted_2 = { class: "cn-banner-widget__body" };
|
|
@@ -22,14 +22,22 @@ const _hoisted_10 = {
|
|
|
22
22
|
key: 2,
|
|
23
23
|
class: "cn-banner-widget"
|
|
24
24
|
};
|
|
25
|
+
const _hoisted_11 = { class: "cn-banner-widget__line" };
|
|
26
|
+
const _hoisted_12 = {
|
|
27
|
+
key: 0,
|
|
28
|
+
class: "cn-banner-widget__lead",
|
|
29
|
+
"data-testid": "cn-banner-widget-lead"
|
|
30
|
+
};
|
|
31
|
+
const _hoisted_13 = ["href", "onClick"];
|
|
25
32
|
|
|
26
33
|
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
34
|
+
const _component_CnIcon = resolveComponent("CnIcon");
|
|
27
35
|
const _component_NcNoteCard = resolveComponent("NcNoteCard");
|
|
28
36
|
|
|
29
37
|
return ($options.visible && $options.isAttention)
|
|
30
38
|
? (openBlock(), createElementBlock("section", {
|
|
31
39
|
key: 0,
|
|
32
|
-
class: normalizeClass(["cn-banner-widget cn-banner-widget--attention", 'cn-banner-widget--' + $options.resolvedVariant]),
|
|
40
|
+
class: normalizeClass(["cn-banner-widget cn-banner-widget--attention", ['cn-banner-widget--' + $options.resolvedVariant, { 'cn-banner-widget--stacked': $options.isStacked }]]),
|
|
33
41
|
"aria-labelledby": $data.headingId,
|
|
34
42
|
"data-testid": "cn-banner-widget-attention"
|
|
35
43
|
}, [
|
|
@@ -80,22 +88,50 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
80
88
|
createVNode(_component_NcNoteCard, {
|
|
81
89
|
type: $options.resolvedVariant,
|
|
82
90
|
class: "cn-banner-widget__card"
|
|
83
|
-
}, {
|
|
91
|
+
}, createSlots({
|
|
84
92
|
default: withCtx(() => [
|
|
85
|
-
(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
createElementVNode("span", _hoisted_11, [
|
|
94
|
+
(openBlock(), createBlock(resolveDynamicComponent($options.clickable ? 'a' : 'span'), {
|
|
95
|
+
class: normalizeClass(["cn-banner-widget__text", { 'cn-banner-widget__text--clickable': $options.clickable }]),
|
|
96
|
+
href: $options.clickable ? $options.routeHref : null,
|
|
97
|
+
"data-testid": "cn-banner-widget-text",
|
|
98
|
+
onClick: $options.onClick
|
|
99
|
+
}, {
|
|
100
|
+
default: withCtx(() => [
|
|
101
|
+
($options.displayLead)
|
|
102
|
+
? (openBlock(), createElementBlock("strong", _hoisted_12, toDisplayString($options.displayLead), 1 /* TEXT */))
|
|
103
|
+
: createCommentVNode("v-if", true),
|
|
104
|
+
createTextVNode(toDisplayString($options.displayLead && $options.displayText ? ' ' : '') + toDisplayString($options.displayText), 1 /* TEXT */)
|
|
105
|
+
]),
|
|
106
|
+
_: 1 /* STABLE */
|
|
107
|
+
}, 8 /* PROPS */, ["class", "href", "onClick"])),
|
|
108
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList($options.noteActions, (action) => {
|
|
109
|
+
return (openBlock(), createElementBlock("a", {
|
|
110
|
+
key: action.key,
|
|
111
|
+
class: "cn-banner-widget__link",
|
|
112
|
+
href: action.href || null,
|
|
113
|
+
"data-testid": "cn-banner-widget-link",
|
|
114
|
+
onClick: $event => ($options.onActionClick($event, action))
|
|
115
|
+
}, toDisplayString(action.label), 9 /* TEXT, PROPS */, _hoisted_13))
|
|
116
|
+
}), 128 /* KEYED_FRAGMENT */))
|
|
117
|
+
])
|
|
96
118
|
]),
|
|
97
|
-
_:
|
|
98
|
-
},
|
|
119
|
+
_: 2 /* DYNAMIC */
|
|
120
|
+
}, [
|
|
121
|
+
($options.resolvedIcon)
|
|
122
|
+
? {
|
|
123
|
+
name: "icon",
|
|
124
|
+
fn: withCtx(() => [
|
|
125
|
+
createVNode(_component_CnIcon, {
|
|
126
|
+
name: $options.resolvedIcon,
|
|
127
|
+
size: 22,
|
|
128
|
+
"data-testid": "cn-banner-widget-icon"
|
|
129
|
+
}, null, 8 /* PROPS */, ["name"])
|
|
130
|
+
]),
|
|
131
|
+
key: "0"
|
|
132
|
+
}
|
|
133
|
+
: undefined
|
|
134
|
+
]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["type"])
|
|
99
135
|
]))
|
|
100
136
|
: createCommentVNode("v-if", true)
|
|
101
137
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnBannerWidget.vue3.js","sources":["../../../../src/components/CnBannerWidget/CnBannerWidget.vue?vue&type=template&id=3531692f&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<section\n\t\tv-if=\"visible && isAttention\"\n\t\tclass=\"cn-banner-widget cn-banner-widget--attention\"\n\t\t:class=\"'cn-banner-widget--' + resolvedVariant\"\n\t\t:aria-labelledby=\"headingId\"\n\t\tdata-testid=\"cn-banner-widget-attention\">\n\t\t<div class=\"cn-banner-widget__body\">\n\t\t\t<p v-if=\"resolvedKicker\" class=\"cn-banner-widget__kicker\">\n\t\t\t\t{{ resolvedKicker }}\n\t\t\t</p>\n\t\t\t<h3 :id=\"headingId\" class=\"cn-banner-widget__title\">\n\t\t\t\t{{ displayTitle }}\n\t\t\t</h3>\n\t\t\t<p v-if=\"displayReason\" class=\"cn-banner-widget__reason\">\n\t\t\t\t{{ displayReason }}\n\t\t\t</p>\n\t\t</div>\n\t\t<div v-if=\"resolvedActions.length > 0\" class=\"cn-banner-widget__actions\">\n\t\t\t<component\n\t\t\t\t:is=\"action.href ? 'a' : 'button'\"\n\t\t\t\tv-for=\"action in resolvedActions\"\n\t\t\t\t:key=\"action.key\"\n\t\t\t\tclass=\"cn-banner-widget__action\"\n\t\t\t\t:class=\"action.primary ? 'cn-banner-widget__action--primary' : 'cn-banner-widget__action--secondary'\"\n\t\t\t\t:href=\"action.href || null\"\n\t\t\t\t:type=\"action.href ? null : 'button'\"\n\t\t\t\tdata-testid=\"cn-banner-widget-action\"\n\t\t\t\t@click=\"onActionClick($event, action)\">\n\t\t\t\t{{ action.label }}\n\t\t\t</component>\n\t\t</div>\n\t</section>\n\t<!-- The count could not be read. One quiet line, not the card and not an\n\t alarm: hidden would read as \"nothing to report\", which is not known. -->\n\t<p\n\t\tv-else-if=\"checkFailed\"\n\t\tclass=\"cn-banner-widget cn-banner-widget--unchecked\"\n\t\t:title=\"failureTooltip\"\n\t\tdata-testid=\"cn-banner-widget-unchecked\">\n\t\t<span class=\"cn-banner-widget__unchecked-text\">{{ uncheckedText }}</span>\n\t\t<span class=\"cn-banner-widget__unchecked-reason\">{{ failureTooltip }}</span>\n\t</p>\n\t<div v-else-if=\"visible\" class=\"cn-banner-widget\">\n\t\t<NcNoteCard :type=\"resolvedVariant\" class=\"cn-banner-widget__card\">\n\t\t\t<component\n\t\t\t\t:is=\"clickable ? 'a' : 'span'\"\n\t\t\t\tclass=\"cn-banner-widget__text\"\n\t\t\t\t:class=\"{ 'cn-banner-widget__text--clickable': clickable }\"\n\t\t\t\t:href=\"clickable ? routeHref : null\"\n\t\t\t\tdata-testid=\"cn-banner-widget-text\"\n\t\t\t\t@click=\"onClick\">\n\t\t\t\t{{ displayText }}\n\t\t\t</component>\n\t\t</NcNoteCard>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcNoteCard } from '@nextcloud/vue'\nimport { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'\nimport { safeHref } from '../../utils/safeHref.js'\nimport { nextUid } from '../../utils/uid.js'\nimport { compareVisibleWhen, readVisibleWhenValue } from '../../utils/visibleWhen.js'\n\n/** Variants understood by NcNoteCard. */\nconst VARIANTS = ['info', 'warning', 'error', 'success']\n\n/** Layouts: the note card (default) or the attention card. */\nconst LAYOUTS = ['banner', 'attention']\n\n/** An attention card offers a primary and a secondary action, no more. */\nconst MAX_ACTIONS = 2\n\n/**\n * CnBannerWidget — declarative notice banner for dashboards and v2 pages\n * (`banner` widget type, Wave 1 of nextcloud-vue#91).\n *\n * Renders a themed NcNoteCard with a variant (`info | warning | error`),\n * a text, an optional click-through route, and an optional `visibleWhen`\n * condition: a simple `{ field, op, value }` predicate evaluated against\n * an endpoint response or an OpenRegister source (the doriath\n * migration-banner case — \"show while `pending > 0`\").\n *\n * Config arrives either as flat props (v2 grid spreads `props`) or as a\n * stored `content` blob (CnDashboardPage's registry branch) — explicit\n * flat props win on collision.\n *\n * ```json\n * {\n * \"widgetKey\": \"banner\",\n * \"props\": {\n * \"variant\": \"warning\",\n * \"text\": \"Migrations pending — open the migration overview.\",\n * \"visibleWhen\": { \"endpoint\": \"/apps/doriath/api/migrations/status\", \"field\": \"pending\", \"op\": \"gt\", \"value\": 0 },\n * \"route\": \"migrations\"\n * }\n * }\n * ```\n *\n * Fail-safe: with a `visibleWhen`, the banner stays HIDDEN until the\n * condition evaluates true — a failed fetch never breaks (or spams) a\n * dashboard.\n *\n * An attention card (`layout: \"attention\"`) whose `visibleWhen` request\n * FAILS renders one quiet line instead, \"Could not check\", with the reason as\n * a tooltip. A hidden card reads as \"nothing needs attention\", and a failed\n * count does not know that. A request that succeeds with a value that does\n * not meet the condition still renders nothing.\n *\n * `layout: \"attention\"` turns the banner into an attention card: a card with\n * a coloured edge by severity, a `kicker` label, a `title`, a `reason` line\n * and up to two `actions` (the first is the primary one unless an action says\n * `primary` itself). `visibleWhen` works the same way. Without `layout` the\n * note card above renders, as it always did.\n *\n * ```json\n * {\n * \"widgetKey\": \"banner\",\n * \"props\": {\n * \"layout\": \"attention\",\n * \"variant\": \"error\",\n * \"kicker\": \"First today\",\n * \"title\": \"Parking permits city centre\",\n * \"reason\": \"The deadline ends today.\",\n * \"actions\": [\n * { \"label\": \"Open case\", \"route\": { \"name\": \"CaseDetail\", \"params\": { \"id\": \"2026-0061\" } } },\n * { \"label\": \"Suspend deadline\", \"route\": \"CaseSuspend\" }\n * ]\n * }\n * }\n * ```\n */\nexport default {\n\tname: 'CnBannerWidget',\n\n\tcomponents: { NcNoteCard },\n\n\tinject: {\n\t\t/**\n\t\t * The host's label lookup (CnAppRoot provides it); identity without one, so a\n\t\t * label that is not a key renders as written.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Banner severity variant: `info | warning | error` (plus `success`\n\t\t * for completeness — NcNoteCard's set). Empty falls back to the\n\t\t * `content` blob, then `info`.\n\t\t */\n\t\tvariant: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (v) => v === '' || VARIANTS.includes(v),\n\t\t},\n\n\t\t/** Pre-translated banner text. Empty falls back to the `content` blob. */\n\t\ttext: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional visibility condition. Shape:\n\t\t * `{ endpoint?, source?, field?, op?, value }` — exactly one of\n\t\t * `endpoint` (a same-origin URL returning JSON) or `source`\n\t\t * (`{ register, schema, filter? }`, an OpenRegister object query whose\n\t\t * `filter` supports the shared @-token grammar). `field` is a\n\t\t * dot-path into the response (for a `source`, into the first result;\n\t\t * omit it — or use `@total` — to compare the collection total).\n\t\t * `op` is `eq | neq | gt | gte | lt | lte` (default `eq`); `value` is\n\t\t * the literal right-hand side. `null` (the default) shows the banner\n\t\t * unconditionally.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tvisibleWhen: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Optional click-through route: a vue-router route NAME (string) or\n\t\t * a full location object. When set, the banner text is a link to that\n\t\t * route. `null` renders static text.\n\t\t *\n\t\t * @type {string|object|null}\n\t\t */\n\t\troute: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * How the banner renders: `banner` (the note card, default) or\n\t\t * `attention` (a card with a severity edge, kicker, title, reason and\n\t\t * up to two actions). Empty falls back to the `content` blob, then\n\t\t * `banner`.\n\t\t *\n\t\t * @type {''|'banner'|'attention'}\n\t\t */\n\t\tlayout: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (v) => v === '' || LAYOUTS.includes(v),\n\t\t},\n\n\t\t/** Attention card: the small label above the title (\"First today\"). */\n\t\tkicker: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Attention card: the title. Falls back to `text` when empty. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Attention card: the line under the title that says why this needs attention. `{value}` is replaced like in `text`. */\n\t\treason: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Attention card: at most two actions. Each is `{ label, route?,\n\t\t * href?, primary?, id? }`: `route` is a route name or location,\n\t\t * `href` an URL. An action with neither renders a button that emits\n\t\t * `action`. The first action is the primary one unless one sets\n\t\t * `primary: true`.\n\t\t *\n\t\t * @type {Array<{label: string, route?: (string|object), href?: string, primary?: boolean, id?: string}>|null}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Stored content blob (CnDashboardPage registry branch) carrying the\n\t\t * same keys as the flat props: `{ variant, text, visibleWhen, route,\n\t\t * layout, kicker, title, reason, actions }`. Explicit flat props win\n\t\t * on collision.\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Pre-evaluated `visibleWhen` outcome `{ met, value }`, injected by a\n\t\t * host that already ran the predicate (CnDashboardPage evaluates it\n\t\t * to know whether to collapse the banner's grid cell). When present,\n\t\t * the banner renders from this verdict instead of fetching again —\n\t\t * no duplicate request and no hidden-until-self-evaluated flash —\n\t\t * and `value` feeds the `{value}` text placeholder. `null` (the\n\t\t * default) keeps the banner self-evaluating. A host whose request\n\t\t * failed passes `{ met: false, value: null, failed: true, reason }`,\n\t\t * and an attention card then says it could not check.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tconditionOutcome: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted when an attention-card action without a `route` or `href`\n\t\t * is clicked, so the host can run it.\n\t\t *\n\t\t * @event action\n\t\t * @type {{id: (string|undefined), label: string, index: number}}\n\t\t */\n\t\t'action',\n\t],\n\n\t/**\n\t * The banner's own state: the evaluated condition, the value it read,\n\t * and whether the request for it failed.\n\t *\n\t * @return {object} The state.\n\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t */\n\tdata() {\n\t\treturn {\n\t\t\t/** Id that ties the attention card's region to its title. */\n\t\t\theadingId: `cn-banner-title-${nextUid()}`,\n\t\t\t/** Evaluated visibleWhen outcome (null = not yet evaluated). */\n\t\t\tconditionMet: null,\n\t\t\t/**\n\t\t\t * The raw field value the predicate read (from the injected\n\t\t\t * outcome or the banner's own evaluation) — interpolated into\n\t\t\t * the text wherever it says `{value}`.\n\t\t\t */\n\t\t\tconditionValue: null,\n\t\t\t/** Whether the visibleWhen request itself failed (not: answered \"no\"). */\n\t\t\tconditionFailed: false,\n\t\t\t/** Why it failed, as the request reported it. */\n\t\t\tconditionFailureReason: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** The effective variant (prop → content → 'info'). */\n\t\tresolvedVariant() {\n\t\t\tconst v = this.variant || (this.content && this.content.variant) || 'info'\n\t\t\treturn VARIANTS.includes(v) ? v : 'info'\n\t\t},\n\n\t\t/** The effective banner text (prop → content → ''). */\n\t\tresolvedText() {\n\t\t\treturn this.text || (this.content && this.content.text) || ''\n\t\t},\n\n\t\t/** The effective visibleWhen condition (prop → content → null). */\n\t\tresolvedVisibleWhen() {\n\t\t\treturn this.visibleWhen || (this.content && this.content.visibleWhen) || null\n\t\t},\n\n\t\t/** The effective injected outcome (prop → content → null). */\n\t\tresolvedConditionOutcome() {\n\t\t\treturn this.conditionOutcome || (this.content && this.content.conditionOutcome) || null\n\t\t},\n\n\t\t/**\n\t\t * The rendered text: `resolvedText` with `{value}` replaced by the\n\t\t * predicate's field value once one is known. Without a known value\n\t\t * the text renders as written — a `{value}` placeholder only makes\n\t\t * sense on a conditional banner, which stays hidden until its\n\t\t * predicate (and therefore its value) resolves.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayText() {\n\t\t\treturn this.fillValue(this.resolvedText)\n\t\t},\n\n\t\t/** The effective click-through route (prop → content → null). */\n\t\tresolvedRoute() {\n\t\t\treturn this.route || (this.content && this.content.route) || null\n\t\t},\n\n\t\t/**\n\t\t * Whether the attention card renders instead of the note card.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tisAttention() {\n\t\t\treturn (this.layout || (this.content && this.content.layout) || 'banner') === 'attention'\n\t\t},\n\n\t\t/**\n\t\t * The attention card's kicker (prop, else content).\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tresolvedKicker() {\n\t\t\tconst kicker = this.kicker || (this.content && this.content.kicker) || ''\n\t\t\treturn kicker ? this.tl(kicker) : ''\n\t\t},\n\n\t\t/**\n\t\t * The attention card's title: `title`, else `text`, with `{value}` filled in.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayTitle() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\treturn title ? this.fillValue(this.tl(title)) : this.displayText\n\t\t},\n\n\t\t/**\n\t\t * The attention card's reason line. When the title already used\n\t\t * `text`, the text is not repeated here.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayReason() {\n\t\t\tconst reason = this.reason || (this.content && this.content.reason) || ''\n\t\t\tif (reason) {\n\t\t\t\treturn this.fillValue(this.tl(reason))\n\t\t\t}\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\t// A card that sets `text` to the same words as its `title` says\n\t\t\t// them once.\n\t\t\treturn title && this.displayText !== this.displayTitle ? this.displayText : ''\n\t\t},\n\n\t\t/**\n\t\t * The attention card's actions: the first two that have a label, each\n\t\t * with its href and whether it is the primary one.\n\t\t *\n\t\t * @return {Array<{key: string, id: (string|undefined), label: string, href: string, target: (object|null), primary: boolean, index: number}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tresolvedActions() {\n\t\t\tconst raw = this.actions || (this.content && this.content.actions) || []\n\t\t\tif (!Array.isArray(raw)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst usable = raw\n\t\t\t\t.filter((action) => action && typeof action === 'object' && typeof action.label === 'string' && action.label !== '')\n\t\t\t\t.slice(0, MAX_ACTIONS)\n\t\t\tconst declaredPrimary = usable.findIndex((action) => action.primary === true)\n\t\t\tconst primaryIndex = declaredPrimary === -1 ? 0 : declaredPrimary\n\t\t\treturn usable.map((action, index) => {\n\t\t\t\tlet target = null\n\t\t\t\tlet href = ''\n\t\t\t\tif (action.route) {\n\t\t\t\t\ttarget = typeof action.route === 'string' ? { name: action.route } : action.route\n\t\t\t\t\thref = resolveHref(target, this.$router)\n\t\t\t\t} else if (typeof action.href === 'string' && action.href !== '') {\n\t\t\t\t\tconst safe = safeHref(action.href)\n\t\t\t\t\thref = safe === '#' ? '' : safe\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tkey: `${index}-${action.id || action.label}`,\n\t\t\t\t\tid: action.id,\n\t\t\t\t\tlabel: this.tl(action.label),\n\t\t\t\t\thref,\n\t\t\t\t\ttarget,\n\t\t\t\t\tprimary: index === primaryIndex,\n\t\t\t\t\tindex,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Whether the banner renders: no condition = always; else the evaluated outcome.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tvisible() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\tif (this.resolvedText === '' && !(this.isAttention && title !== '')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (!this.resolvedVisibleWhen) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn this.conditionMet === true\n\t\t},\n\n\t\t/**\n\t\t * Whether to say the check failed: an attention card, with words to\n\t\t * show, whose `visibleWhen` request failed. A plain banner stays\n\t\t * hidden on failure, as it always did.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tcheckFailed() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\treturn this.isAttention\n\t\t\t\t&& !!this.resolvedVisibleWhen\n\t\t\t\t&& this.conditionFailed\n\t\t\t\t&& (title !== '' || this.resolvedText !== '')\n\t\t},\n\n\t\t/**\n\t\t * The one line shown when the check failed. It names the card by its\n\t\t * title, unless the title needs the value that could not be read.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tuncheckedText() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || this.resolvedText\n\t\t\tif (!title || title.includes('{value}')) {\n\t\t\t\treturn t('nextcloud-vue', 'Could not check')\n\t\t\t}\n\t\t\t// `escape: false`: the result is rendered through `{{ }}`, which escapes.\n\t\t\treturn t('nextcloud-vue', 'Could not check: {subject}', { subject: title }, undefined, { escape: false })\n\t\t},\n\n\t\t/**\n\t\t * The reason the check failed, for the tooltip and for assistive\n\t\t * technology.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tfailureTooltip() {\n\t\t\tif (!this.conditionFailureReason) {\n\t\t\t\treturn t('nextcloud-vue', 'The check failed.')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'The check failed: {reason}', { reason: this.conditionFailureReason }, undefined, { escape: false })\n\t\t},\n\n\t\t/** Whether the banner navigates on click (route set + router present). */\n\t\tclickable() {\n\t\t\treturn !!this.resolvedRoute && !!this.$router\n\t\t},\n\n\t\t/** The click-through route as a router location (a string is a route name). */\n\t\trouteLocation() {\n\t\t\tconst route = this.resolvedRoute\n\t\t\treturn typeof route === 'string' ? { name: route } : route\n\t\t},\n\n\t\t/** The href of the click-through link. */\n\t\trouteHref() {\n\t\t\treturn resolveHref(this.routeLocation, this.$router)\n\t\t},\n\t},\n\n\twatch: {\n\t\tresolvedVisibleWhen: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.evaluateCondition()\n\t\t\t},\n\t\t},\n\n\t\tresolvedConditionOutcome() {\n\t\t\tthis.evaluateCondition()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * A manifest string through the host's label lookup (`cnTranslate`), so a key\n\t\t * such as \"Mine\" reads in the user's language.\n\t\t *\n\t\t * @param {string} text The text as written in the manifest.\n\t\t * @return {string} The translated text.\n\t\t */\n\t\ttl(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * Replace `{value}` in a text by the value the predicate read, once\n\t\t * one is known.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The text with the value filled in.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tfillValue(text) {\n\t\t\tif (this.conditionValue === null || !text.includes('{value}')) {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\treturn text.replaceAll('{value}', String(this.conditionValue))\n\t\t},\n\n\t\t/**\n\t\t * Run an attention-card action: route an in-app link, leave an\n\t\t * external link to the browser, and emit `action` for a button.\n\t\t *\n\t\t * @param {MouseEvent} event The click.\n\t\t * @param {object} action The resolved action.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tonActionClick(event, action) {\n\t\t\tif (action.target) {\n\t\t\t\tfollowLinkClick(event, action.target, this.$router)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!action.href) {\n\t\t\t\tthis.$emit('action', { id: action.id, label: action.label, index: action.index })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve the `visibleWhen` verdict. A host-injected\n\t\t * `conditionOutcome` wins outright — the host already ran the\n\t\t * predicate, so re-fetching would double the request and flash the\n\t\t * banner hidden until its own copy resolved. Otherwise evaluate\n\t\t * through the shared util primitives (endpoint / OpenRegister-source\n\t\t * modes — the Wave-1 banner shape is the canonical one, extracted to\n\t\t * `utils/visibleWhen.js` in Wave 3 so manifest actions reuse it),\n\t\t * keeping the read VALUE for the `{value}` text placeholder.\n\t\t * Fail-safe: any fetch/shape error leaves the banner hidden. The\n\t\t * failure is kept (`conditionFailed`, with its reason) so an\n\t\t * attention card can say it could not check.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tasync evaluateCondition() {\n\t\t\tconst outcome = this.resolvedConditionOutcome\n\t\t\tif (outcome) {\n\t\t\t\tthis.conditionMet = outcome.met === true\n\t\t\t\tthis.conditionValue = outcome.value !== undefined ? outcome.value : null\n\t\t\t\tthis.conditionFailed = outcome.failed === true\n\t\t\t\tthis.conditionFailureReason = outcome.failed === true && typeof outcome.reason === 'string' ? outcome.reason : ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst cond = this.resolvedVisibleWhen\n\t\t\tif (!cond) {\n\t\t\t\tthis.conditionMet = null\n\t\t\t\tthis.conditionValue = null\n\t\t\t\tthis.conditionFailed = false\n\t\t\t\tthis.conditionFailureReason = ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst value = await readVisibleWhenValue(cond)\n\t\t\t\tthis.conditionValue = value\n\t\t\t\tthis.conditionMet = compareVisibleWhen(value, cond.op || 'eq', cond.value)\n\t\t\t\tthis.conditionFailed = false\n\t\t\t\tthis.conditionFailureReason = ''\n\t\t\t} catch (error) {\n\t\t\t\t// Still hidden: a banner that cannot tell whether it applies\n\t\t\t\t// should not claim attention. But not silently, because a\n\t\t\t\t// failed count and \"nothing to report\" look the same on screen.\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnBannerWidget] visibleWhen could not be evaluated, banner hidden:', error?.message || error)\n\t\t\t\tthis.conditionMet = false\n\t\t\t\tthis.conditionValue = null\n\t\t\t\tthis.conditionFailed = true\n\t\t\t\tthis.conditionFailureReason = typeof error?.message === 'string' ? error.message : String(error ?? '')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Route a plain click on the link; a modified click opens the href\n\t\t * in the browser.\n\t\t *\n\t\t * @param {MouseEvent} event The click event.\n\t\t * @return {void}\n\t\t */\n\t\tonClick(event) {\n\t\t\tif (!this.clickable) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowLinkClick(event, this.routeLocation, this.$router)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-banner-widget {\n\twidth: 100%;\n}\n\n.cn-banner-widget__card {\n\tmargin: 0;\n}\n\n.cn-banner-widget__text--clickable {\n\tcursor: pointer;\n\ttext-decoration: underline;\n\tcolor: inherit;\n}\n\n/* The check failed. Muted text, no fill, no severity colour: it is a note\n that something is not known, not a warning. */\n.cn-banner-widget--unchecked {\n\tbox-sizing: border-box;\n\tmargin: 0;\n\tpadding: 8px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tcursor: help;\n}\n\n/* The reason, for assistive technology; sighted readers get the tooltip. */\n.cn-banner-widget__unchecked-reason {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\tmargin: -1px;\n\tpadding: 0;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n\twhite-space: nowrap;\n\tborder: 0;\n}\n\n/* Attention card. The severity edge is an inset shadow, so it takes no width. */\n.cn-banner-widget--attention {\n\t--cn-banner-accent: var(--color-primary-element);\n\t--cn-banner-accent-text: var(--color-primary-element);\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 16px 24px;\n\tbox-sizing: border-box;\n\tpadding: 20px 24px 20px 28px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-container, var(--border-radius-large, 12px));\n\tbackground: var(--color-main-background);\n\tbox-shadow: inset 4px 0 0 0 var(--cn-banner-accent);\n}\n\n.cn-banner-widget--attention.cn-banner-widget--warning {\n\t--cn-banner-accent: var(--color-element-warning, var(--color-warning));\n\t--cn-banner-accent-text: var(--color-text-warning, var(--color-warning-text, var(--color-main-text)));\n}\n\n.cn-banner-widget--attention.cn-banner-widget--error {\n\t--cn-banner-accent: var(--color-element-error, var(--color-error));\n\t--cn-banner-accent-text: var(--color-text-error, var(--color-error-text));\n}\n\n.cn-banner-widget--attention.cn-banner-widget--success {\n\t--cn-banner-accent: var(--color-element-success, var(--color-success));\n\t--cn-banner-accent-text: var(--color-text-success, var(--color-success-text));\n}\n\n.cn-banner-widget__body {\n\tdisplay: flex;\n\tflex: 1 1 320px;\n\tflex-direction: column;\n\tgap: 6px;\n\tmin-width: 0;\n}\n\n.cn-banner-widget__kicker {\n\tmargin: 0;\n\tfont-size: 0.85em;\n\tfont-weight: 700;\n\tletter-spacing: 0.06em;\n\ttext-transform: uppercase;\n\tcolor: var(--cn-banner-accent-text);\n}\n\n.cn-banner-widget__title {\n\tmargin: 0;\n\tfont-size: 1.3em;\n\tfont-weight: 700;\n\tline-height: 1.3;\n\tcolor: var(--color-main-text);\n}\n\n.cn-banner-widget__reason {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-banner-widget__actions {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 10px;\n}\n\n.cn-banner-widget__action {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tbox-sizing: border-box;\n\tmin-height: var(--default-clickable-area, 40px);\n\tmargin: 0;\n\tpadding: 0 16px;\n\tborder: 1px solid var(--color-border-maxcontrast);\n\tborder-radius: var(--border-radius-element, var(--border-radius-large, 8px));\n\tfont: inherit;\n\tfont-weight: 600;\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.cn-banner-widget__action--secondary {\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n}\n\n.cn-banner-widget__action--secondary:hover {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-banner-widget__action--primary {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text);\n}\n\n.cn-banner-widget__action--primary:hover {\n\tborder-color: var(--color-primary-element-hover);\n\tbackground: var(--color-primary-element-hover);\n}\n\n.cn-banner-widget__action:focus-visible {\n\toutline: 2px solid var(--color-main-text);\n\toutline-offset: 2px;\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode","_toDisplayString","_openBlock","_Fragment","_renderList","_resolveDynamicComponent","_createTextVNode","_createVNode","_createBlock"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnBannerWidget.vue3.js","sources":["../../../../src/components/CnBannerWidget/CnBannerWidget.vue?vue&type=template&id=3531692f&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<section\n\t\tv-if=\"visible && isAttention\"\n\t\tclass=\"cn-banner-widget cn-banner-widget--attention\"\n\t\t:class=\"['cn-banner-widget--' + resolvedVariant, { 'cn-banner-widget--stacked': isStacked }]\"\n\t\t:aria-labelledby=\"headingId\"\n\t\tdata-testid=\"cn-banner-widget-attention\">\n\t\t<div class=\"cn-banner-widget__body\">\n\t\t\t<p v-if=\"resolvedKicker\" class=\"cn-banner-widget__kicker\">\n\t\t\t\t{{ resolvedKicker }}\n\t\t\t</p>\n\t\t\t<h3 :id=\"headingId\" class=\"cn-banner-widget__title\">\n\t\t\t\t{{ displayTitle }}\n\t\t\t</h3>\n\t\t\t<p v-if=\"displayReason\" class=\"cn-banner-widget__reason\">\n\t\t\t\t{{ displayReason }}\n\t\t\t</p>\n\t\t</div>\n\t\t<div v-if=\"resolvedActions.length > 0\" class=\"cn-banner-widget__actions\">\n\t\t\t<component\n\t\t\t\t:is=\"action.href ? 'a' : 'button'\"\n\t\t\t\tv-for=\"action in resolvedActions\"\n\t\t\t\t:key=\"action.key\"\n\t\t\t\tclass=\"cn-banner-widget__action\"\n\t\t\t\t:class=\"action.primary ? 'cn-banner-widget__action--primary' : 'cn-banner-widget__action--secondary'\"\n\t\t\t\t:href=\"action.href || null\"\n\t\t\t\t:type=\"action.href ? null : 'button'\"\n\t\t\t\tdata-testid=\"cn-banner-widget-action\"\n\t\t\t\t@click=\"onActionClick($event, action)\">\n\t\t\t\t{{ action.label }}\n\t\t\t</component>\n\t\t</div>\n\t</section>\n\t<!-- The count could not be read. One quiet line, not the card and not an\n\t alarm: hidden would read as \"nothing to report\", which is not known. -->\n\t<p\n\t\tv-else-if=\"checkFailed\"\n\t\tclass=\"cn-banner-widget cn-banner-widget--unchecked\"\n\t\t:title=\"failureTooltip\"\n\t\tdata-testid=\"cn-banner-widget-unchecked\">\n\t\t<span class=\"cn-banner-widget__unchecked-text\">{{ uncheckedText }}</span>\n\t\t<span class=\"cn-banner-widget__unchecked-reason\">{{ failureTooltip }}</span>\n\t</p>\n\t<div v-else-if=\"visible\" class=\"cn-banner-widget\">\n\t\t<NcNoteCard :type=\"resolvedVariant\" class=\"cn-banner-widget__card\">\n\t\t\t<!-- A named icon replaces the variant's own glyph. -->\n\t\t\t<template v-if=\"resolvedIcon\" #icon>\n\t\t\t\t<CnIcon :name=\"resolvedIcon\" :size=\"22\" data-testid=\"cn-banner-widget-icon\" />\n\t\t\t</template>\n\t\t\t<span class=\"cn-banner-widget__line\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"clickable ? 'a' : 'span'\"\n\t\t\t\t\tclass=\"cn-banner-widget__text\"\n\t\t\t\t\t:class=\"{ 'cn-banner-widget__text--clickable': clickable }\"\n\t\t\t\t\t:href=\"clickable ? routeHref : null\"\n\t\t\t\t\tdata-testid=\"cn-banner-widget-text\"\n\t\t\t\t\t@click=\"onClick\"><strong v-if=\"displayLead\" class=\"cn-banner-widget__lead\" data-testid=\"cn-banner-widget-lead\">{{ displayLead }}</strong>{{ displayLead && displayText ? ' ' : '' }}{{ displayText }}</component>\n\t\t\t\t<!-- Board look: the actions as links at the end of the line. -->\n\t\t\t\t<a\n\t\t\t\t\tv-for=\"action in noteActions\"\n\t\t\t\t\t:key=\"action.key\"\n\t\t\t\t\tclass=\"cn-banner-widget__link\"\n\t\t\t\t\t:href=\"action.href || null\"\n\t\t\t\t\tdata-testid=\"cn-banner-widget-link\"\n\t\t\t\t\t@click=\"onActionClick($event, action)\">{{ action.label }}</a>\n\t\t\t</span>\n\t\t</NcNoteCard>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcNoteCard } from '@nextcloud/vue'\nimport CnIcon from '../CnIcon/CnIcon.vue'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\nimport { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { safeHref } from '../../utils/safeHref.js'\nimport { nextUid } from '../../utils/uid.js'\nimport { compareVisibleWhen, readVisibleWhenValue } from '../../utils/visibleWhen.js'\n\n/** Variants understood by NcNoteCard. */\nconst VARIANTS = ['info', 'warning', 'error', 'success']\n\n/** Layouts: the note card (default) or the attention card. */\nconst LAYOUTS = ['banner', 'attention']\n\n/** An attention card offers a primary and a secondary action, no more. */\nconst MAX_ACTIONS = 2\n\n/**\n * Under the board look the card takes what the boards draw: DqMijnWerk's\n * \"Vandaag eerst\" has three secondary actions and the primary one.\n */\nconst MAX_ACTIONS_BOARD = 4\n\n/**\n * CnBannerWidget — declarative notice banner for dashboards and v2 pages\n * (`banner` widget type, Wave 1 of nextcloud-vue#91).\n *\n * Renders a themed NcNoteCard with a variant (`info | warning | error`),\n * a text, an optional click-through route, and an optional `visibleWhen`\n * condition: a simple `{ field, op, value }` predicate evaluated against\n * an endpoint response or an OpenRegister source (the doriath\n * migration-banner case — \"show while `pending > 0`\").\n *\n * Config arrives either as flat props (v2 grid spreads `props`) or as a\n * stored `content` blob (CnDashboardPage's registry branch) — explicit\n * flat props win on collision.\n *\n * ```json\n * {\n * \"widgetKey\": \"banner\",\n * \"props\": {\n * \"variant\": \"warning\",\n * \"text\": \"Migrations pending — open the migration overview.\",\n * \"visibleWhen\": { \"endpoint\": \"/apps/doriath/api/migrations/status\", \"field\": \"pending\", \"op\": \"gt\", \"value\": 0 },\n * \"route\": \"migrations\"\n * }\n * }\n * ```\n *\n * Fail-safe: with a `visibleWhen`, the banner stays HIDDEN until the\n * condition evaluates true — a failed fetch never breaks (or spams) a\n * dashboard.\n *\n * An attention card (`layout: \"attention\"`) whose `visibleWhen` request\n * FAILS renders one quiet line instead, \"Could not check\", with the reason as\n * a tooltip. A hidden card reads as \"nothing needs attention\", and a failed\n * count does not know that. A request that succeeds with a value that does\n * not meet the condition still renders nothing.\n *\n * `layout: \"attention\"` turns the banner into an attention card: a card with\n * a coloured edge by severity, a `kicker` label, a `title`, a `reason` line\n * and up to two `actions` (the first is the primary one unless an action says\n * `primary` itself). `visibleWhen` works the same way. Without `layout` the\n * note card above renders, as it always did.\n *\n * ```json\n * {\n * \"widgetKey\": \"banner\",\n * \"props\": {\n * \"layout\": \"attention\",\n * \"variant\": \"error\",\n * \"kicker\": \"First today\",\n * \"title\": \"Parking permits city centre\",\n * \"reason\": \"The deadline ends today.\",\n * \"actions\": [\n * { \"label\": \"Open case\", \"route\": { \"name\": \"CaseDetail\", \"params\": { \"id\": \"2026-0061\" } } },\n * { \"label\": \"Suspend deadline\", \"route\": \"CaseSuspend\" }\n * ]\n * }\n * }\n * ```\n */\nexport default {\n\tname: 'CnBannerWidget',\n\n\tcomponents: { CnIcon, NcNoteCard },\n\n\tinject: {\n\t\t/**\n\t\t * The host's label lookup (CnAppRoot provides it); identity without one, so a\n\t\t * label that is not a key renders as written.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The app's look, provided by CnAppRoot or CnPageRenderer (`nextcloud` or `board`). */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Banner severity variant: `info | warning | error` (plus `success`\n\t\t * for completeness — NcNoteCard's set). Empty falls back to the\n\t\t * `content` blob, then `info`.\n\t\t */\n\t\tvariant: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (v) => v === '' || VARIANTS.includes(v),\n\t\t},\n\n\t\t/** Pre-translated banner text. Empty falls back to the `content` blob. */\n\t\ttext: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * A bold lead in front of the text (\"2 aanmeldingen wachten op\n\t\t * goedkeuring.\" before the rest of the sentence). `{value}` is replaced\n\t\t * like in `text`, and it goes through the app's translate. Empty falls\n\t\t * back to the `content` blob.\n\t\t */\n\t\tlead: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * A named icon (`AccountPlus`, `AlertOutline`, ...) drawn before the\n\t\t * text instead of the variant's own glyph. Empty falls back to the\n\t\t * `content` blob, then to the variant glyph.\n\t\t */\n\t\ticon: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional visibility condition. Shape:\n\t\t * `{ endpoint?, source?, field?, op?, value }` — exactly one of\n\t\t * `endpoint` (a same-origin URL returning JSON) or `source`\n\t\t * (`{ register, schema, filter? }`, an OpenRegister object query whose\n\t\t * `filter` supports the shared @-token grammar). `field` is a\n\t\t * dot-path into the response (for a `source`, into the first result;\n\t\t * omit it — or use `@total` — to compare the collection total).\n\t\t * `op` is `eq | neq | gt | gte | lt | lte` (default `eq`); `value` is\n\t\t * the literal right-hand side. `null` (the default) shows the banner\n\t\t * unconditionally.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tvisibleWhen: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Optional click-through route: a vue-router route NAME (string) or\n\t\t * a full location object. When set, the banner text is a link to that\n\t\t * route. `null` renders static text.\n\t\t *\n\t\t * @type {string|object|null}\n\t\t */\n\t\troute: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * How the banner renders: `banner` (the note card, default) or\n\t\t * `attention` (a card with a severity edge, kicker, title, reason and\n\t\t * up to two actions). Empty falls back to the `content` blob, then\n\t\t * `banner`.\n\t\t *\n\t\t * @type {''|'banner'|'attention'}\n\t\t */\n\t\tlayout: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (v) => v === '' || LAYOUTS.includes(v),\n\t\t},\n\n\t\t/** Attention card: the small label above the title (\"First today\"). */\n\t\tkicker: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Attention card: the title. Falls back to `text` when empty. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Attention card: the line under the title that says why this needs attention. `{value}` is replaced like in `text`. */\n\t\treason: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Attention card: its actions, two at most (four under the board look,\n\t\t * or `maxActions`). Each is `{ label, route?,\n\t\t * href?, primary?, id? }`: `route` is a route name or location,\n\t\t * `href` an URL. An action with neither renders a button that emits\n\t\t * `action`. The first action is the primary one unless one sets\n\t\t * `primary: true`.\n\t\t *\n\t\t * @type {Array<{label: string, route?: (string|object), href?: string, primary?: boolean, id?: string}>|null}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Attention card: how many actions it shows at most. Null takes the\n\t\t * default: two, four under the board look.\n\t\t *\n\t\t * @spec openspec/changes/screens-banner-actions-parity/specs/banner-actions/spec.md#requirement-the-board-attention-card-shows-the-boards-actions\n\t\t */\n\t\tmaxActions: {\n\t\t\ttype: Number,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Attention card: stack the actions under the text instead of beside it,\n\t\t * the \"Begin hier\" card of PqMijnWerk (16px title, the action under the\n\t\t * reason). Falls back to the `content` blob.\n\t\t *\n\t\t * @spec openspec/changes/screens-start-here-banner/specs/start-here-banner/spec.md#requirement-a-card-can-stack-its-action-under-the-text\n\t\t */\n\t\tstacked: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Stored content blob (CnDashboardPage registry branch) carrying the\n\t\t * same keys as the flat props: `{ variant, text, visibleWhen, route,\n\t\t * layout, kicker, title, reason, actions }`. Explicit flat props win\n\t\t * on collision.\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Pre-evaluated `visibleWhen` outcome `{ met, value }`, injected by a\n\t\t * host that already ran the predicate (CnDashboardPage evaluates it\n\t\t * to know whether to collapse the banner's grid cell). When present,\n\t\t * the banner renders from this verdict instead of fetching again —\n\t\t * no duplicate request and no hidden-until-self-evaluated flash —\n\t\t * and `value` feeds the `{value}` text placeholder. `null` (the\n\t\t * default) keeps the banner self-evaluating. A host whose request\n\t\t * failed passes `{ met: false, value: null, failed: true, reason }`,\n\t\t * and an attention card then says it could not check.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tconditionOutcome: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted when an attention-card action without a `route` or `href`\n\t\t * is clicked, so the host can run it.\n\t\t *\n\t\t * @event action\n\t\t * @type {{id: (string|undefined), label: string, index: number}}\n\t\t */\n\t\t'action',\n\t],\n\n\t/**\n\t * The banner's own state: the evaluated condition, the value it read,\n\t * and whether the request for it failed.\n\t *\n\t * @return {object} The state.\n\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t */\n\tdata() {\n\t\treturn {\n\t\t\t/** Id that ties the attention card's region to its title. */\n\t\t\theadingId: `cn-banner-title-${nextUid()}`,\n\t\t\t/** Evaluated visibleWhen outcome (null = not yet evaluated). */\n\t\t\tconditionMet: null,\n\t\t\t/**\n\t\t\t * The raw field value the predicate read (from the injected\n\t\t\t * outcome or the banner's own evaluation) — interpolated into\n\t\t\t * the text wherever it says `{value}`.\n\t\t\t */\n\t\t\tconditionValue: null,\n\t\t\t/**\n\t\t\t * The first row of the `visibleWhen` source query (its `order`\n\t\t\t * applied), the card's subject: `{subject.<field>}` in the texts\n\t\t\t * and `@subject.<field>` in an action route read it.\n\t\t\t */\n\t\t\tconditionRow: null,\n\t\t\t/** Whether the visibleWhen request itself failed (not: answered \"no\"). */\n\t\t\tconditionFailed: false,\n\t\t\t/** Why it failed, as the request reported it. */\n\t\t\tconditionFailureReason: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** The effective variant (prop → content → 'info'). */\n\t\tresolvedVariant() {\n\t\t\tconst v = this.variant || (this.content && this.content.variant) || 'info'\n\t\t\treturn VARIANTS.includes(v) ? v : 'info'\n\t\t},\n\n\t\t/** The effective banner text (prop → content → ''), through the app's translate. */\n\t\tresolvedText() {\n\t\t\tconst text = this.text || (this.content && this.content.text) || ''\n\t\t\treturn text ? this.tl(text) : ''\n\t\t},\n\n\t\t/** The effective bold lead (prop → content → ''), through the app's translate. */\n\t\tresolvedLead() {\n\t\t\tconst lead = this.lead || (this.content && this.content.lead) || ''\n\t\t\treturn lead ? this.tl(lead) : ''\n\t\t},\n\n\t\t/** The lead with `{value}` filled in. */\n\t\tdisplayLead() {\n\t\t\treturn this.fillValue(this.resolvedLead)\n\t\t},\n\n\t\t/** The effective icon name (prop → content → ''). */\n\t\tresolvedIcon() {\n\t\t\treturn this.icon || (this.content && this.content.icon) || ''\n\t\t},\n\n\t\t/**\n\t\t * The note card's actions as links, under the board look only: the\n\t\t * first resolved action of `actions`, as the board draws \"Beleid\n\t\t * wijzigen\" at the end of the line. Without the look the note card\n\t\t * ignores `actions`, as it always did.\n\t\t *\n\t\t * @return {Array<object>} The resolved actions (at most one).\n\t\t */\n\t\tnoteActions() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board' ? this.resolvedActions.slice(0, 1) : []\n\t\t},\n\n\t\t/** The effective visibleWhen condition (prop → content → null). */\n\t\tresolvedVisibleWhen() {\n\t\t\treturn this.visibleWhen || (this.content && this.content.visibleWhen) || null\n\t\t},\n\n\t\t/** The effective injected outcome (prop → content → null). */\n\t\tresolvedConditionOutcome() {\n\t\t\treturn this.conditionOutcome || (this.content && this.content.conditionOutcome) || null\n\t\t},\n\n\t\t/**\n\t\t * The rendered text: `resolvedText` with `{value}` replaced by the\n\t\t * predicate's field value once one is known. Without a known value\n\t\t * the text renders as written — a `{value}` placeholder only makes\n\t\t * sense on a conditional banner, which stays hidden until its\n\t\t * predicate (and therefore its value) resolves.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayText() {\n\t\t\treturn this.fillValue(this.resolvedText)\n\t\t},\n\n\t\t/** The effective click-through route (prop → content → null). */\n\t\tresolvedRoute() {\n\t\t\treturn this.route || (this.content && this.content.route) || null\n\t\t},\n\n\t\t/**\n\t\t * Whether the attention card renders instead of the note card.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tisAttention() {\n\t\t\treturn (this.layout || (this.content && this.content.layout) || 'banner') === 'attention'\n\t\t},\n\n\t\t/**\n\t\t * Whether the actions sit under the text.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisStacked() {\n\t\t\treturn this.stacked || Boolean(this.content && this.content.stacked)\n\t\t},\n\n\t\t/**\n\t\t * The attention card's kicker (prop, else content).\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tresolvedKicker() {\n\t\t\tconst kicker = this.kicker || (this.content && this.content.kicker) || ''\n\t\t\treturn kicker ? this.fillValue(this.tl(kicker)) : ''\n\t\t},\n\n\t\t/**\n\t\t * The attention card's title: `title`, else `text`, with `{value}` filled in.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayTitle() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\treturn title ? this.fillValue(this.tl(title)) : this.displayText\n\t\t},\n\n\t\t/**\n\t\t * The attention card's reason line. When the title already used\n\t\t * `text`, the text is not repeated here.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayReason() {\n\t\t\tconst reason = this.reason || (this.content && this.content.reason) || ''\n\t\t\tif (reason) {\n\t\t\t\treturn this.fillValue(this.tl(reason))\n\t\t\t}\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\t// A card that sets `text` to the same words as its `title` says\n\t\t\t// them once.\n\t\t\treturn title && this.displayText !== this.displayTitle ? this.displayText : ''\n\t\t},\n\n\t\t/**\n\t\t * The attention card's actions: the first `actionCap` that have a label, each\n\t\t * with its href and whether it is the primary one.\n\t\t *\n\t\t * @return {Array<{key: string, id: (string|undefined), label: string, href: string, target: (object|null), primary: boolean, index: number}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\t/**\n\t\t * How many actions the attention card shows: `maxActions` (prop, then\n\t\t * content), else four under the board look and two without it.\n\t\t *\n\t\t * @return {number} The cap.\n\t\t * @spec openspec/changes/screens-banner-actions-parity/specs/banner-actions/spec.md#requirement-the-board-attention-card-shows-the-boards-actions\n\t\t */\n\t\tactionCap() {\n\t\t\tconst explicit = this.maxActions ?? (this.content ? this.content.maxActions : undefined)\n\t\t\tif (Number.isInteger(explicit) && explicit > 0) {\n\t\t\t\treturn explicit\n\t\t\t}\n\t\t\treturn normalizeLook(this.cnLook) === 'board' ? MAX_ACTIONS_BOARD : MAX_ACTIONS\n\t\t},\n\n\t\tresolvedActions() {\n\t\t\tconst raw = this.actions || (this.content && this.content.actions) || []\n\t\t\tif (!Array.isArray(raw)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst usable = raw\n\t\t\t\t.filter((action) => action && typeof action === 'object' && typeof action.label === 'string' && action.label !== '')\n\t\t\t\t.slice(0, this.actionCap)\n\t\t\tconst declaredPrimary = usable.findIndex((action) => action.primary === true)\n\t\t\tconst primaryIndex = declaredPrimary === -1 ? 0 : declaredPrimary\n\t\t\treturn usable.map((action, index) => {\n\t\t\t\tlet target = null\n\t\t\t\tlet href = ''\n\t\t\t\tif (action.route) {\n\t\t\t\t\ttarget = typeof action.route === 'string' ? { name: action.route } : this.resolveSubject(action.route)\n\t\t\t\t\thref = resolveHref(target, this.$router)\n\t\t\t\t} else if (typeof action.href === 'string' && action.href !== '') {\n\t\t\t\t\tconst safe = safeHref(action.href)\n\t\t\t\t\thref = safe === '#' ? '' : safe\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tkey: `${index}-${action.id || action.label}`,\n\t\t\t\t\tid: action.id,\n\t\t\t\t\tlabel: this.tl(action.label),\n\t\t\t\t\thref,\n\t\t\t\t\ttarget,\n\t\t\t\t\tprimary: index === primaryIndex,\n\t\t\t\t\tindex,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Whether the banner renders: no condition = always; else the evaluated outcome.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tvisible() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\tif (this.resolvedText === '' && this.resolvedLead === '' && !(this.isAttention && title !== '')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (!this.resolvedVisibleWhen) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn this.conditionMet === true\n\t\t},\n\n\t\t/**\n\t\t * Whether to say the check failed: an attention card, with words to\n\t\t * show, whose `visibleWhen` request failed. A plain banner stays\n\t\t * hidden on failure, as it always did.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tcheckFailed() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\treturn this.isAttention\n\t\t\t\t&& !!this.resolvedVisibleWhen\n\t\t\t\t&& this.conditionFailed\n\t\t\t\t&& (title !== '' || this.resolvedText !== '')\n\t\t},\n\n\t\t/**\n\t\t * The one line shown when the check failed. It names the card by its\n\t\t * title, unless the title needs the value that could not be read.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tuncheckedText() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || this.resolvedText\n\t\t\tif (!title || title.includes('{value}') || title.includes('{subject.')) {\n\t\t\t\treturn t('nextcloud-vue', 'Could not check')\n\t\t\t}\n\t\t\t// `escape: false`: the result is rendered through `{{ }}`, which escapes.\n\t\t\treturn t('nextcloud-vue', 'Could not check: {subject}', { subject: title }, undefined, { escape: false })\n\t\t},\n\n\t\t/**\n\t\t * The reason the check failed, for the tooltip and for assistive\n\t\t * technology.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tfailureTooltip() {\n\t\t\tif (!this.conditionFailureReason) {\n\t\t\t\treturn t('nextcloud-vue', 'The check failed.')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'The check failed: {reason}', { reason: this.conditionFailureReason }, undefined, { escape: false })\n\t\t},\n\n\t\t/** Whether the banner navigates on click (route set + router present). */\n\t\tclickable() {\n\t\t\treturn !!this.resolvedRoute && !!this.$router\n\t\t},\n\n\t\t/** The click-through route as a router location (a string is a route name). */\n\t\trouteLocation() {\n\t\t\tconst route = this.resolvedRoute\n\t\t\treturn typeof route === 'string' ? { name: route } : route\n\t\t},\n\n\t\t/** The href of the click-through link. */\n\t\trouteHref() {\n\t\t\treturn resolveHref(this.routeLocation, this.$router)\n\t\t},\n\t},\n\n\twatch: {\n\t\tresolvedVisibleWhen: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.evaluateCondition()\n\t\t\t},\n\t\t},\n\n\t\tresolvedConditionOutcome() {\n\t\t\tthis.evaluateCondition()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * A manifest string through the host's label lookup (`cnTranslate`), so a key\n\t\t * such as \"Mine\" reads in the user's language.\n\t\t *\n\t\t * @param {string} text The text as written in the manifest.\n\t\t * @return {string} The translated text.\n\t\t */\n\t\ttl(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * Replace `{value}` in a text by the value the predicate read, once\n\t\t * one is known.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The text with the value filled in.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tfillValue(text) {\n\t\t\tlet out = text\n\t\t\tif (this.conditionValue !== null && out.includes('{value}')) {\n\t\t\t\tout = out.replaceAll('{value}', String(this.conditionValue))\n\t\t\t}\n\t\t\treturn this.fillSubject(out)\n\t\t},\n\n\t\t/**\n\t\t * Fill `{subject.<field>}` (and `{subject.<field>|date}`, `|days`) from\n\t\t * the card's subject row. A token with no value empties the whole text,\n\t\t * as in a record list. Text without a subject token is untouched.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The filled text.\n\t\t * @spec openspec/changes/screens-start-here-banner/specs/start-here-banner/spec.md#requirement-a-banner-can-name-the-first-row-of-a-query\n\t\t */\n\t\tfillSubject(text) {\n\t\t\tif (typeof text !== 'string' || !text.includes('{subject.')) {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\t// `{value}` is not a subject token: keep it out of the template fill.\n\t\t\tconst guard = '\\u0000value\\u0000'\n\t\t\tconst filled = fillTemplate(text.replaceAll('{value}', guard), { subject: this.conditionRow || {} })\n\t\t\treturn filled.replaceAll(guard, '{value}')\n\t\t},\n\n\t\t/**\n\t\t * Replace `@subject.<field>` strings anywhere in an action route\n\t\t * (params, query) by the subject's value.\n\t\t *\n\t\t * @param {unknown} node A route name, location or any nested part of one.\n\t\t * @return {unknown} The same shape with the tokens resolved.\n\t\t * @spec openspec/changes/screens-start-here-banner/specs/start-here-banner/spec.md#requirement-a-banner-can-name-the-first-row-of-a-query\n\t\t */\n\t\tresolveSubject(node) {\n\t\t\tif (typeof node === 'string') {\n\t\t\t\tif (!node.startsWith('@subject.')) {\n\t\t\t\t\treturn node\n\t\t\t\t}\n\t\t\t\tconst value = this.conditionRow ? readPath(this.conditionRow, node.slice('@subject.'.length)) : undefined\n\t\t\t\treturn value === undefined || value === null ? '' : String(value)\n\t\t\t}\n\t\t\tif (Array.isArray(node)) {\n\t\t\t\treturn node.map((item) => this.resolveSubject(item))\n\t\t\t}\n\t\t\tif (node && typeof node === 'object') {\n\t\t\t\treturn Object.fromEntries(Object.entries(node).map(([key, value]) => [key, this.resolveSubject(value)]))\n\t\t\t}\n\t\t\treturn node\n\t\t},\n\n\t\t/**\n\t\t * Run an attention-card action: route an in-app link, leave an\n\t\t * external link to the browser, and emit `action` for a button.\n\t\t *\n\t\t * @param {MouseEvent} event The click.\n\t\t * @param {object} action The resolved action.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tonActionClick(event, action) {\n\t\t\tif (action.target) {\n\t\t\t\tfollowLinkClick(event, action.target, this.$router)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!action.href) {\n\t\t\t\tthis.$emit('action', { id: action.id, label: action.label, index: action.index })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve the `visibleWhen` verdict. A host-injected\n\t\t * `conditionOutcome` wins outright — the host already ran the\n\t\t * predicate, so re-fetching would double the request and flash the\n\t\t * banner hidden until its own copy resolved. Otherwise evaluate\n\t\t * through the shared util primitives (endpoint / OpenRegister-source\n\t\t * modes — the Wave-1 banner shape is the canonical one, extracted to\n\t\t * `utils/visibleWhen.js` in Wave 3 so manifest actions reuse it),\n\t\t * keeping the read VALUE for the `{value}` text placeholder.\n\t\t * Fail-safe: any fetch/shape error leaves the banner hidden. The\n\t\t * failure is kept (`conditionFailed`, with its reason) so an\n\t\t * attention card can say it could not check.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tasync evaluateCondition() {\n\t\t\tconst outcome = this.resolvedConditionOutcome\n\t\t\tif (outcome) {\n\t\t\t\tthis.conditionMet = outcome.met === true\n\t\t\t\tthis.conditionValue = outcome.value !== undefined ? outcome.value : null\n\t\t\t\tthis.conditionRow = outcome.row && typeof outcome.row === 'object' ? outcome.row : null\n\t\t\t\tthis.conditionFailed = outcome.failed === true\n\t\t\t\tthis.conditionFailureReason = outcome.failed === true && typeof outcome.reason === 'string' ? outcome.reason : ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst cond = this.resolvedVisibleWhen\n\t\t\tif (!cond) {\n\t\t\t\tthis.conditionMet = null\n\t\t\t\tthis.conditionValue = null\n\t\t\t\tthis.conditionRow = null\n\t\t\t\tthis.conditionFailed = false\n\t\t\t\tthis.conditionFailureReason = ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst sink = {}\n\t\t\t\tconst value = await readVisibleWhenValue(cond, undefined, sink)\n\t\t\t\tthis.conditionRow = sink.row || null\n\t\t\t\tthis.conditionValue = value\n\t\t\t\tthis.conditionMet = compareVisibleWhen(value, cond.op || 'eq', cond.value)\n\t\t\t\tthis.conditionFailed = false\n\t\t\t\tthis.conditionFailureReason = ''\n\t\t\t} catch (error) {\n\t\t\t\t// Still hidden: a banner that cannot tell whether it applies\n\t\t\t\t// should not claim attention. But not silently, because a\n\t\t\t\t// failed count and \"nothing to report\" look the same on screen.\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnBannerWidget] visibleWhen could not be evaluated, banner hidden:', error?.message || error)\n\t\t\t\tthis.conditionMet = false\n\t\t\t\tthis.conditionValue = null\n\t\t\t\tthis.conditionRow = null\n\t\t\t\tthis.conditionFailed = true\n\t\t\t\tthis.conditionFailureReason = typeof error?.message === 'string' ? error.message : String(error ?? '')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Route a plain click on the link; a modified click opens the href\n\t\t * in the browser.\n\t\t *\n\t\t * @param {MouseEvent} event The click event.\n\t\t * @return {void}\n\t\t */\n\t\tonClick(event) {\n\t\t\tif (!this.clickable) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowLinkClick(event, this.routeLocation, this.$router)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-banner-widget {\n\twidth: 100%;\n}\n\n.cn-banner-widget__card {\n\tmargin: 0;\n}\n\n.cn-banner-widget__line {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: baseline;\n\tgap: 4px 20px;\n}\n\n.cn-banner-widget__lead {\n\tfont-weight: 700;\n}\n\n.cn-banner-widget__link {\n\tmargin-inline-start: auto;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-banner-widget__text--clickable {\n\tcursor: pointer;\n\ttext-decoration: underline;\n\tcolor: inherit;\n}\n\n/* The check failed. Muted text, no fill, no severity colour: it is a note\n that something is not known, not a warning. */\n.cn-banner-widget--unchecked {\n\tbox-sizing: border-box;\n\tmargin: 0;\n\tpadding: 8px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tcursor: help;\n}\n\n/* The reason, for assistive technology; sighted readers get the tooltip. */\n.cn-banner-widget__unchecked-reason {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\tmargin: -1px;\n\tpadding: 0;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n\twhite-space: nowrap;\n\tborder: 0;\n}\n\n/* Attention card. The severity edge is an inset shadow, so it takes no width. */\n.cn-banner-widget--attention {\n\t--cn-banner-accent: var(--color-primary-element);\n\t--cn-banner-accent-text: var(--color-primary-element);\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 16px 24px;\n\tbox-sizing: border-box;\n\tpadding: 20px 24px 20px 28px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-container, var(--border-radius-large, 12px));\n\tbackground: var(--color-main-background);\n\tbox-shadow: inset 4px 0 0 0 var(--cn-banner-accent);\n}\n\n.cn-banner-widget--attention.cn-banner-widget--warning {\n\t--cn-banner-accent: var(--color-element-warning, var(--color-warning));\n\t--cn-banner-accent-text: var(--color-text-warning, var(--color-warning-text, var(--color-main-text)));\n}\n\n.cn-banner-widget--attention.cn-banner-widget--error {\n\t--cn-banner-accent: var(--color-element-error, var(--color-error));\n\t--cn-banner-accent-text: var(--color-text-error, var(--color-error-text));\n}\n\n.cn-banner-widget--attention.cn-banner-widget--success {\n\t--cn-banner-accent: var(--color-element-success, var(--color-success));\n\t--cn-banner-accent-text: var(--color-text-success, var(--color-success-text));\n}\n\n.cn-banner-widget__body {\n\tdisplay: flex;\n\tflex: 1 1 320px;\n\tflex-direction: column;\n\tgap: 6px;\n\tmin-width: 0;\n}\n\n.cn-banner-widget__kicker {\n\tmargin: 0;\n\tfont-size: 0.85em;\n\tfont-weight: 700;\n\tletter-spacing: 0.06em;\n\ttext-transform: uppercase;\n\tcolor: var(--cn-banner-accent-text);\n}\n\n.cn-banner-widget__title {\n\tmargin: 0;\n\tfont-size: 1.3em;\n\tfont-weight: 700;\n\tline-height: 1.3;\n\tcolor: var(--color-main-text);\n}\n\n.cn-banner-widget__reason {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* Stacked (PqMijnWerk \"Begin hier\"): the text, then the action under it. */\n.cn-banner-widget--stacked {\n\tflex-direction: column;\n\talign-items: flex-start;\n\tgap: 10px;\n}\n\n.cn-banner-widget--stacked .cn-banner-widget__body {\n\tflex: 0 0 auto;\n\tgap: 10px;\n}\n\n.cn-banner-widget--stacked .cn-banner-widget__title {\n\tfont-size: 16px;\n\tline-height: 1.4;\n}\n\n.cn-banner-widget--stacked .cn-banner-widget__reason {\n\tfont-size: 14px;\n}\n\n.cn-banner-widget--stacked .cn-banner-widget__action {\n\tmin-height: var(--cn-board-control-height, 40px);\n\tfont-size: 14px;\n}\n\n.cn-banner-widget__actions {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 10px;\n}\n\n.cn-banner-widget__action {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tbox-sizing: border-box;\n\tmin-height: var(--default-clickable-area, 40px);\n\tmargin: 0;\n\tpadding: 0 16px;\n\tborder: 1px solid var(--color-border-maxcontrast);\n\tborder-radius: var(--border-radius-element, var(--border-radius-large, 8px));\n\tfont: inherit;\n\tfont-weight: 600;\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.cn-banner-widget__action--secondary {\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n}\n\n.cn-banner-widget__action--secondary:hover {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-banner-widget__action--primary {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text);\n}\n\n.cn-banner-widget__action--primary:hover {\n\tborder-color: var(--color-primary-element-hover);\n\tbackground: var(--color-primary-element-hover);\n}\n\n.cn-banner-widget__action:focus-visible {\n\toutline: 2px solid var(--color-main-text);\n\toutline-offset: 2px;\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode","_toDisplayString","_openBlock","_Fragment","_renderList","_resolveDynamicComponent","_createTextVNode","_createVNode","_createBlock"],"mappings":"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"}
|
|
@@ -4,10 +4,11 @@ import CheckBold from 'vue-material-design-icons/CheckBold.vue';
|
|
|
4
4
|
import __sfc_main$1 from '../CnFkResolveCell/CnFkResolveCell.vue.js';
|
|
5
5
|
import __sfc_main$2 from './CnGroupNameCell.vue.js';
|
|
6
6
|
import { normalizeLook } from '../../composables/useLook.js';
|
|
7
|
-
import { formatBoardDate, formatAge, resolveAgeVariant } from '../../utils/boardDate.js';
|
|
7
|
+
import { formatBoardDate, formatAge, ageInDays, resolveAgeVariant } from '../../utils/boardDate.js';
|
|
8
8
|
import { resolveDateVariant, parseDateValue } from '../../utils/dateVariant.js';
|
|
9
9
|
import { safeCurrencyCode } from '../../utils/formatMetric.js';
|
|
10
10
|
import { objectFieldValue } from '../../utils/objectName.js';
|
|
11
|
+
import { readPath } from '../../utils/readPath.js';
|
|
11
12
|
import { safeHref } from '../../utils/safeHref.js';
|
|
12
13
|
import { isGroupProp, formatValue } from '../../utils/schema.js';
|
|
13
14
|
import __sfc_main from '../CnStatusBadge/CnStatusBadge.vue.js';
|
|
@@ -156,6 +157,16 @@ var script = {
|
|
|
156
157
|
default: () => ({}),
|
|
157
158
|
},
|
|
158
159
|
|
|
160
|
+
/**
|
|
161
|
+
* The column's secondary line (a muted second line under the value).
|
|
162
|
+
* The `avatar` widget draws it under the name, beside the picture;
|
|
163
|
+
* other cells ignore it, because the table draws it under the cell.
|
|
164
|
+
*/
|
|
165
|
+
secondary: {
|
|
166
|
+
type: String,
|
|
167
|
+
default: '',
|
|
168
|
+
},
|
|
169
|
+
|
|
159
170
|
/**
|
|
160
171
|
* Optional declarative cell-format spec — a no-code alternative to a
|
|
161
172
|
* registry `formatter`. Recognised `style` values:
|
|
@@ -310,14 +321,15 @@ var script = {
|
|
|
310
321
|
},
|
|
311
322
|
|
|
312
323
|
/**
|
|
313
|
-
* `avatar` widget: the picture size in pixels (`widgetProps.size`, default 24
|
|
324
|
+
* `avatar` widget: the picture size in pixels (`widgetProps.size`, default 24,
|
|
325
|
+
* 32 under the board look).
|
|
314
326
|
*
|
|
315
327
|
* @return {number}
|
|
316
328
|
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells
|
|
317
329
|
*/
|
|
318
330
|
avatarSize() {
|
|
319
331
|
const size = Number(this.widgetProps && this.widgetProps.size);
|
|
320
|
-
return Number.isFinite(size) && size > 0 ? size : 24
|
|
332
|
+
return Number.isFinite(size) && size > 0 ? size : (this.isBoardLook ? 32 : 24)
|
|
321
333
|
},
|
|
322
334
|
|
|
323
335
|
/**
|
|
@@ -384,6 +396,83 @@ var script = {
|
|
|
384
396
|
return formatAge(this.value)
|
|
385
397
|
},
|
|
386
398
|
|
|
399
|
+
/**
|
|
400
|
+
* `progress` widget: the value as a whole percentage of
|
|
401
|
+
* `widgetProps.max` (default 100), clamped to 0..100. An empty value
|
|
402
|
+
* falls back to the first `widgetProps.fallbackFields` row field that
|
|
403
|
+
* holds a number. Null when nothing holds one.
|
|
404
|
+
*
|
|
405
|
+
* @return {number|null}
|
|
406
|
+
* @spec openspec/changes/abstract-progress-cell/specs/progress-cell/spec.md#requirement-a-progress-cell-draws-a-toned-bar-with-its-percentage
|
|
407
|
+
*/
|
|
408
|
+
progressPercent() {
|
|
409
|
+
const props = this.widgetProps || {};
|
|
410
|
+
const fallbacks = Array.isArray(props.fallbackFields) ? props.fallbackFields : [];
|
|
411
|
+
const candidates = [this.value, ...fallbacks.map((field) => readPath(this.row || {}, field))];
|
|
412
|
+
const raw = candidates.find((v) => v !== null && v !== undefined && v !== '' && !Number.isNaN(Number(v)));
|
|
413
|
+
if (raw === undefined) {
|
|
414
|
+
return null
|
|
415
|
+
}
|
|
416
|
+
const max = Number(props.max) > 0 ? Number(props.max) : 100;
|
|
417
|
+
return Math.max(0, Math.min(100, Math.round((Number(raw) / max) * 100)))
|
|
418
|
+
},
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* `progress` widget: the tone. `widgetProps.thresholds`
|
|
422
|
+
* `{ good: 60, warn: 30 }` (the defaults): at or above `good` is
|
|
423
|
+
* success, at or above `warn` is warning, below is error. With
|
|
424
|
+
* `widgetProps.lowerIsBetter` the scale turns around.
|
|
425
|
+
*
|
|
426
|
+
* @return {'success'|'warning'|'error'}
|
|
427
|
+
* @spec openspec/changes/abstract-progress-cell/specs/progress-cell/spec.md#requirement-a-progress-cell-draws-a-toned-bar-with-its-percentage
|
|
428
|
+
*/
|
|
429
|
+
progressTone() {
|
|
430
|
+
const props = this.widgetProps || {};
|
|
431
|
+
const thresholds = props.thresholds || {};
|
|
432
|
+
const good = Number.isFinite(Number(thresholds.good)) ? Number(thresholds.good) : 60;
|
|
433
|
+
const warn = Number.isFinite(Number(thresholds.warn)) ? Number(thresholds.warn) : 30;
|
|
434
|
+
const value = props.lowerIsBetter === true ? 100 - this.progressPercent : this.progressPercent;
|
|
435
|
+
if (value >= good) {
|
|
436
|
+
return 'success'
|
|
437
|
+
}
|
|
438
|
+
return value >= warn ? 'warning' : 'error'
|
|
439
|
+
},
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* `progress` widget: the age badge, or null. `widgetProps.badge`
|
|
443
|
+
* `{ label, dateField, afterDays, when?, tone? }`: shown when the row's
|
|
444
|
+
* `dateField` (default `@self.updated`) lies `afterDays` or more days
|
|
445
|
+
* back and every `when` field equals its value. `label` is translated
|
|
446
|
+
* and takes `{days}`.
|
|
447
|
+
*
|
|
448
|
+
* @return {{label: string, tone: string}|null}
|
|
449
|
+
* @spec openspec/changes/abstract-progress-cell/specs/progress-cell/spec.md#requirement-a-progress-cell-can-carry-an-age-badge
|
|
450
|
+
*/
|
|
451
|
+
progressBadge() {
|
|
452
|
+
const badge = this.widgetProps && this.widgetProps.badge;
|
|
453
|
+
if (!badge || typeof badge !== 'object' || !badge.label) {
|
|
454
|
+
return null
|
|
455
|
+
}
|
|
456
|
+
const row = this.row || {};
|
|
457
|
+
const when = badge.when && typeof badge.when === 'object' ? badge.when : {};
|
|
458
|
+
for (const [field, expected] of Object.entries(when)) {
|
|
459
|
+
const actual = readPath(row, field);
|
|
460
|
+
if (actual !== undefined && actual !== null && actual !== '' && String(actual) !== String(expected)) {
|
|
461
|
+
return null
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
const days = ageInDays(readPath(row, badge.dateField || '@self.updated'));
|
|
465
|
+
const after = Number(badge.afterDays);
|
|
466
|
+
if (days === null || !Number.isFinite(after) || days < after) {
|
|
467
|
+
return null
|
|
468
|
+
}
|
|
469
|
+
const label = this.cnTranslate(badge.label);
|
|
470
|
+
return {
|
|
471
|
+
label: String(label).replace(/\{days\}/g, String(days)),
|
|
472
|
+
tone: ['error', 'warning', 'success'].includes(badge.tone) ? badge.tone : 'error',
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
|
|
387
476
|
/**
|
|
388
477
|
* `age` widget: the variant of the first `widgetProps.variantWhen`
|
|
389
478
|
* rule the shown days match, or '' for none.
|
|
@@ -555,11 +644,14 @@ var script = {
|
|
|
555
644
|
* a `{ value: variant }` map (e.g. `{ placed: 'primary', delivered: 'success' }`)
|
|
556
645
|
* that CnStatusBadge resolves per label. Null when unset (falls back to
|
|
557
646
|
* `badgeVariant`). Lets a manifest colour a status column without code.
|
|
647
|
+
* A column-level `colorMap` (it reaches the cell as the property's
|
|
648
|
+
* `colorMap`) works too, so `{ widget: 'badge', colorMap }` and
|
|
649
|
+
* `{ widget: 'badge', widgetProps: { colorMap } }` read the same.
|
|
558
650
|
*
|
|
559
651
|
* @return {object|null}
|
|
560
652
|
*/
|
|
561
653
|
badgeColorMap() {
|
|
562
|
-
return (this.widgetProps && this.widgetProps.colorMap) || null
|
|
654
|
+
return (this.widgetProps && this.widgetProps.colorMap) || this.enumColorMap || null
|
|
563
655
|
},
|
|
564
656
|
|
|
565
657
|
/**
|