@conduction/nextcloud-vue 2.80.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/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +3 -3
- 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 +140 -10
- 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 +17 -3
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +63 -52
- 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 +3 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +72 -0
- 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/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.vue2.js +131 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +80 -5
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +282 -69
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
- 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/CnFormPage/CnFormPage.vue2.js +60 -0
- 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 +29 -2
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +48 -4
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- 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/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.vue2.js +30 -6
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +34 -25
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- 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/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/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.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 +47 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +16 -14
- 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 +7 -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 +197 -6
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +96 -50
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.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 +17 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +9 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +55 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +1041 -12
- package/dist/esm/utils/contentHeaderLink.js +51 -0
- package/dist/esm/utils/contentHeaderLink.js.map +1 -0
- 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 +7 -1
- package/dist/esm/utils/fillTemplate.js.map +1 -1
- package/dist/esm/utils/initials.js +32 -0
- package/dist/esm/utils/initials.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +7 -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/resolveFilterTokens.js +1 -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/sentinelTokens.js +19 -1
- 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 +1 -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 +13589 -7833
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1041 -12
- package/l10n/en.json +9 -1
- package/l10n/nl.json +55 -1
- package/package.json +2 -1
- package/scripts/check-docs-mdx.mjs +108 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +6 -1
- package/src/components/CnAppRoot/CnAppRoot.md +4 -2
- package/src/components/CnAppRoot/CnAppRoot.vue +151 -59
- package/src/components/CnBannerWidget/CnBannerWidget.vue +205 -20
- package/src/components/CnCellRenderer/CnCellRenderer.md +2 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +47 -4
- 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 +21 -12
- package/src/components/CnDetailPage/CnDetailPage.vue +89 -3
- package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +1 -1
- package/src/components/CnFacetSidebar/CnFacetSidebar.vue +7 -7
- package/src/components/CnFactsWidget/CnFactsWidget.vue +142 -2
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +268 -6
- 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/CnFormPage/CnFormPage.vue +81 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +56 -4
- 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/CnRadioCardsWidget/CnRadioCardsWidget.vue +313 -0
- package/src/components/CnRadioCardsWidget/index.js +27 -0
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +55 -6
- package/src/components/CnSavedViewsWidget/CnSavedViewsWidget.vue +296 -0
- package/src/components/CnSavedViewsWidget/index.js +29 -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/CnStatWidget/CnStatWidget.vue +87 -6
- 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 +82 -3
- 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 +7 -0
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +7 -2
- package/src/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
- package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +309 -11
- package/src/components/index.js +15 -0
- package/src/composables/useListView.js +4 -4
- package/src/css/look-board-detail.css +36 -2
- package/src/css/look-board-index.css +39 -8
- package/src/css/look-board.css +72 -0
- package/src/index.js +15 -0
- package/src/schemas/app-manifest-v2.schema.json +93 -9
- package/src/utils/contentHeaderLink.js +48 -0
- package/src/utils/facets.js +74 -2
- package/src/utils/fetchFilterCounts.js +39 -4
- package/src/utils/fillTemplate.js +7 -1
- package/src/utils/initials.js +29 -0
- package/src/utils/libraryWidgetKeys.js +7 -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/resolveFilterTokens.js +3 -1
- package/src/utils/resolveManifestTokens.js +2 -0
- package/src/utils/sentinelTokens.js +19 -1
- 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 +1 -1
- package/src/utils/widgetVisibility.js +7 -56
|
@@ -7,7 +7,7 @@ import { readPath } from '../../utils/readPath.js';
|
|
|
7
7
|
* Renders CnDocumentReviewList from a list on the bound record:
|
|
8
8
|
* `content: { field, title?, statusField?, statuses?, rowAction?: { label, route } }`.
|
|
9
9
|
* A row action with a `route` opens that route with the row's key as `id`.
|
|
10
|
-
* Resolved by its registry key,
|
|
10
|
+
* Resolved by its registry key, and exported for an app that renders it itself.
|
|
11
11
|
*/
|
|
12
12
|
var script = {
|
|
13
13
|
name: 'CnDocumentReviewWidget',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnDocumentReviewWidget.vue2.js","sources":["../../../../src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-case-widget-card\">\n\t\t<CnDocumentReviewList\n\t\t\t:documents=\"documents\"\n\t\t\t:title=\"content.title || ''\"\n\t\t\t:nameField=\"content.nameField || 'name'\"\n\t\t\t:metaField=\"content.metaField || 'meta'\"\n\t\t\t:statusField=\"content.statusField || 'reviewStatus'\"\n\t\t\t:hrefField=\"content.hrefField || 'href'\"\n\t\t\t:rowKey=\"content.rowKey || 'id'\"\n\t\t\t:statuses=\"content.statuses || []\"\n\t\t\t:defaultStatus=\"content.defaultStatus || ''\"\n\t\t\t:rowActionLabel=\"rowActionLabel\"\n\t\t\t:emptyText=\"content.emptyText || undefined\"\n\t\t\t@rowAction=\"onRowAction\" />\n\t</div>\n</template>\n\n<script>\nimport CnDocumentReviewList from './CnDocumentReviewList.vue'\nimport { readPath } from '../../utils/readPath.js'\n\n/**\n * CnDocumentReviewWidget: the `document-review` detail widget type.\n *\n * Renders CnDocumentReviewList from a list on the bound record:\n * `content: { field, title?, statusField?, statuses?, rowAction?: { label, route } }`.\n * A row action with a `route` opens that route with the row's key as `id`.\n * Resolved by its registry key,
|
|
1
|
+
{"version":3,"file":"CnDocumentReviewWidget.vue2.js","sources":["../../../../src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-case-widget-card\">\n\t\t<CnDocumentReviewList\n\t\t\t:documents=\"documents\"\n\t\t\t:title=\"content.title || ''\"\n\t\t\t:nameField=\"content.nameField || 'name'\"\n\t\t\t:metaField=\"content.metaField || 'meta'\"\n\t\t\t:statusField=\"content.statusField || 'reviewStatus'\"\n\t\t\t:hrefField=\"content.hrefField || 'href'\"\n\t\t\t:rowKey=\"content.rowKey || 'id'\"\n\t\t\t:statuses=\"content.statuses || []\"\n\t\t\t:defaultStatus=\"content.defaultStatus || ''\"\n\t\t\t:rowActionLabel=\"rowActionLabel\"\n\t\t\t:emptyText=\"content.emptyText || undefined\"\n\t\t\t@rowAction=\"onRowAction\" />\n\t</div>\n</template>\n\n<script>\nimport CnDocumentReviewList from './CnDocumentReviewList.vue'\nimport { readPath } from '../../utils/readPath.js'\n\n/**\n * CnDocumentReviewWidget: the `document-review` detail widget type.\n *\n * Renders CnDocumentReviewList from a list on the bound record:\n * `content: { field, title?, statusField?, statuses?, rowAction?: { label, route } }`.\n * A row action with a `route` opens that route with the row's key as `id`.\n * Resolved by its registry key, and exported for an app that renders it itself.\n */\nexport default {\n\tname: 'CnDocumentReviewWidget',\n\n\tcomponents: {\n\t\tCnDocumentReviewList,\n\t},\n\n\tprops: {\n\t\t/** The widget's content. `field` is the dot-path to the documents on the record. */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The bound record. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The documents read off the record.\n\t\t *\n\t\t * @return {Array<object>} The documents, or an empty list.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list\n\t\t */\n\t\tdocuments() {\n\t\t\tconst value = readPath(this.objectData || {}, this.content.field || 'documents')\n\t\t\treturn Array.isArray(value) ? value : []\n\t\t},\n\n\t\t/**\n\t\t * The row action's label. Empty, and so no button, unless the content declares one.\n\t\t *\n\t\t * @return {string} The label.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list\n\t\t */\n\t\trowActionLabel() {\n\t\t\tconst label = this.content.rowAction?.label\n\t\t\treturn typeof label === 'string' ? label : ''\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Open the route the row action names, with the row's key as `id`.\n\t\t *\n\t\t * @param {object} document The row's document.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list\n\t\t */\n\t\tonRowAction(document) {\n\t\t\tconst route = this.content.rowAction?.route\n\t\t\tconst id = readPath(document, this.content.rowKey || 'id')\n\t\t\tif (typeof route !== 'string' || route === '' || !this.$router || id === undefined || id === null) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$router.push({ name: route, params: { id: String(id) } })\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-case-widget-card {\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large);\n\tpadding: calc(5 * var(--default-grid-baseline));\n}\n</style>\n"],"names":["CnDocumentReviewList"],"mappings":";;;AA2BA;;AAEC;;AAEA;;AAEA;;AAED,aAAe;CACd,IAAI,EAAE,wBAAwB;;CAE9B,UAAU,EAAE;AACX,wBAAAA,UAAoB;EACpB;;AAED,CAAA,KAAK,EAAE;AACN;AACA,EAAA,OAAO,EAAE;GACR,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,OAAO,EAAE,CAAC;GACnB;;AAED;EACA,UAAU,EAAE;GACX,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,IAAI;GACb;EACD;;AAED,CAAA,QAAQ,EAAE;AACT;;AAEC;;AAEA;;EAED,SAAS,GAAG;AACX,GAAA,MAAM,KAAI,GAAI,QAAQ,CAAC,IAAI,CAAC,UAAS,IAAK,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,KAAM,IAAG,WAAW,EAAA;AAC/E,GAAA,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAA,GAAI,KAAI,GAAI,EAAC;GACvC;;AAED;AACC;AACA;;AAEA;;AAED,EAAA,cAAc,GAAG;AAChB,GAAA,MAAM,KAAM,GAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAI;AAC1C,GAAA,OAAO,OAAO,KAAM,KAAI,QAAS,GAAE,KAAI,GAAI,EAAC;GAC5C;EACD;;AAED,CAAA,OAAO,EAAE;AACR;;AAEC;AACA;;AAEA;;AAED,EAAA,WAAW,CAAC,QAAQ,EAAE;AACrB,GAAA,MAAM,KAAM,GAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAI;GAC1C,MAAM,EAAG,GAAE,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,MAAK,IAAK,IAAI,EAAA;AACzD,GAAA,IAAI,OAAO,KAAI,KAAM,QAAS,IAAG,UAAU,EAAC,IAAK,CAAC,IAAI,CAAC,OAAM,IAAK,EAAG,KAAI,aAAa,EAAC,KAAM,IAAI,EAAE;IAClG,MAAK;AACN,IAAA;GACA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAE,EAAA,EAAG,EAAA;GAC7D;EACD;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnDocumentReviewWidget.vue3.js","sources":["../../../../src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue?vue&type=template&id=22291596&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-case-widget-card\">\n\t\t<CnDocumentReviewList\n\t\t\t:documents=\"documents\"\n\t\t\t:title=\"content.title || ''\"\n\t\t\t:nameField=\"content.nameField || 'name'\"\n\t\t\t:metaField=\"content.metaField || 'meta'\"\n\t\t\t:statusField=\"content.statusField || 'reviewStatus'\"\n\t\t\t:hrefField=\"content.hrefField || 'href'\"\n\t\t\t:rowKey=\"content.rowKey || 'id'\"\n\t\t\t:statuses=\"content.statuses || []\"\n\t\t\t:defaultStatus=\"content.defaultStatus || ''\"\n\t\t\t:rowActionLabel=\"rowActionLabel\"\n\t\t\t:emptyText=\"content.emptyText || undefined\"\n\t\t\t@rowAction=\"onRowAction\" />\n\t</div>\n</template>\n\n<script>\nimport CnDocumentReviewList from './CnDocumentReviewList.vue'\nimport { readPath } from '../../utils/readPath.js'\n\n/**\n * CnDocumentReviewWidget: the `document-review` detail widget type.\n *\n * Renders CnDocumentReviewList from a list on the bound record:\n * `content: { field, title?, statusField?, statuses?, rowAction?: { label, route } }`.\n * A row action with a `route` opens that route with the row's key as `id`.\n * Resolved by its registry key,
|
|
1
|
+
{"version":3,"file":"CnDocumentReviewWidget.vue3.js","sources":["../../../../src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue?vue&type=template&id=22291596&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-case-widget-card\">\n\t\t<CnDocumentReviewList\n\t\t\t:documents=\"documents\"\n\t\t\t:title=\"content.title || ''\"\n\t\t\t:nameField=\"content.nameField || 'name'\"\n\t\t\t:metaField=\"content.metaField || 'meta'\"\n\t\t\t:statusField=\"content.statusField || 'reviewStatus'\"\n\t\t\t:hrefField=\"content.hrefField || 'href'\"\n\t\t\t:rowKey=\"content.rowKey || 'id'\"\n\t\t\t:statuses=\"content.statuses || []\"\n\t\t\t:defaultStatus=\"content.defaultStatus || ''\"\n\t\t\t:rowActionLabel=\"rowActionLabel\"\n\t\t\t:emptyText=\"content.emptyText || undefined\"\n\t\t\t@rowAction=\"onRowAction\" />\n\t</div>\n</template>\n\n<script>\nimport CnDocumentReviewList from './CnDocumentReviewList.vue'\nimport { readPath } from '../../utils/readPath.js'\n\n/**\n * CnDocumentReviewWidget: the `document-review` detail widget type.\n *\n * Renders CnDocumentReviewList from a list on the bound record:\n * `content: { field, title?, statusField?, statuses?, rowAction?: { label, route } }`.\n * A row action with a `route` opens that route with the row's key as `id`.\n * Resolved by its registry key, and exported for an app that renders it itself.\n */\nexport default {\n\tname: 'CnDocumentReviewWidget',\n\n\tcomponents: {\n\t\tCnDocumentReviewList,\n\t},\n\n\tprops: {\n\t\t/** The widget's content. `field` is the dot-path to the documents on the record. */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The bound record. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The documents read off the record.\n\t\t *\n\t\t * @return {Array<object>} The documents, or an empty list.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list\n\t\t */\n\t\tdocuments() {\n\t\t\tconst value = readPath(this.objectData || {}, this.content.field || 'documents')\n\t\t\treturn Array.isArray(value) ? value : []\n\t\t},\n\n\t\t/**\n\t\t * The row action's label. Empty, and so no button, unless the content declares one.\n\t\t *\n\t\t * @return {string} The label.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list\n\t\t */\n\t\trowActionLabel() {\n\t\t\tconst label = this.content.rowAction?.label\n\t\t\treturn typeof label === 'string' ? label : ''\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Open the route the row action names, with the row's key as `id`.\n\t\t *\n\t\t * @param {object} document The row's document.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list\n\t\t */\n\t\tonRowAction(document) {\n\t\t\tconst route = this.content.rowAction?.route\n\t\t\tconst id = readPath(document, this.content.rowKey || 'id')\n\t\t\tif (typeof route !== 'string' || route === '' || !this.$router || id === undefined || id === null) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$router.push({ name: route, params: { id: String(id) } })\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-case-widget-card {\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large);\n\tpadding: calc(5 * var(--default-grid-baseline));\n}\n</style>\n"],"names":["_openBlock","_createElementBlock"],"mappings":";;AAWM,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,qBAAqB,GAAA;;;;;AAA9B,EAAA,QAAAA,SAAA,EAAA,EAAAC,kBAAA,QAAA;;AAEC,MAAA,SAAS,EAAE,QAAS,CAAA,SAAA;MACpB,KAAK,EAAE,MAAO,CAAA,OAAA,CAAC,KAAK,IAAA,EAAA;MACpB,SAAS,EAAE,MAAO,CAAA,OAAA,CAAC,SAAS,IAAA,MAAA;MAC5B,SAAS,EAAE,MAAO,CAAA,OAAA,CAAC,SAAS,IAAA,MAAA;MAC5B,WAAW,EAAE,MAAO,CAAA,OAAA,CAAC,WAAS,IAAA,cAAA;MAC9B,SAAS,EAAE,MAAO,CAAA,OAAA,CAAC,SAAU,IAAA,MAAA;MAC7B,MAAM,EAAE,MAAO,CAAA,OAAA,CAAC,MAAM,IAAA,IAAA;MACtB,QAAE,EAAA,MAAA,CAAA,OAAA,CAAA,QAAA,IAAA,EAAA;;;;;;;;;;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { translate } from '@nextcloud/l10n';
|
|
2
2
|
import { NcButton, NcSelect, NcTextField, NcCheckboxRadioSwitch, NcLoadingIcon } from '@nextcloud/vue';
|
|
3
|
-
import {
|
|
3
|
+
import { facetOptions } from '../../utils/facets.js';
|
|
4
4
|
import { filtersFromSchema } from '../../utils/schema.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -113,12 +113,12 @@ var script = {
|
|
|
113
113
|
|
|
114
114
|
getFilterOptions(filter) {
|
|
115
115
|
// Use facet data if available (live values with counts)
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
const live = facetOptions(
|
|
117
|
+
this.facetData[filter.key],
|
|
118
|
+
(count) => translate('nextcloud-vue', 'No value ({count})', { count }),
|
|
119
|
+
);
|
|
120
|
+
if (live !== null) {
|
|
121
|
+
return live
|
|
122
122
|
}
|
|
123
123
|
// Fall back to static enum options from schema
|
|
124
124
|
return filter.options || []
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnFacetSidebar.vue2.js","sources":["../../../../src/components/CnFacetSidebar/CnFacetSidebar.vue"],"sourcesContent":["<template>\n\t<div class=\"cn-facet-sidebar\">\n\t\t<div class=\"cn-facet-sidebar__header\">\n\t\t\t<h3 class=\"cn-facet-sidebar__title\">\n\t\t\t\t{{ title }}\n\t\t\t</h3>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"hasActiveFilters\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\tclass=\"cn-facet-sidebar__clear\"\n\t\t\t\t@click=\"$emit('clear-all')\">\n\t\t\t\t{{ clearLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<!-- Loading state -->\n\t\t<div v-if=\"loading\" class=\"cn-facet-sidebar__loading\">\n\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t</div>\n\n\t\t<!-- Filter groups -->\n\t\t<div v-else class=\"cn-facet-sidebar__filters\">\n\t\t\t<div\n\t\t\t\tv-for=\"filter in effectiveFilters\"\n\t\t\t\t:key=\"filter.key\"\n\t\t\t\tclass=\"cn-facet-sidebar__group\">\n\t\t\t\t<label class=\"cn-facet-sidebar__label\">{{ filter.label }}</label>\n\n\t\t\t\t<!-- Checkbox filter (boolean) -->\n\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\tv-if=\"filter.type === 'checkbox'\"\n\t\t\t\t\t:modelValue=\"getFilterValue(filter.key) === true\"\n\t\t\t\t\t@update:modelValue=\"onFilterChange(filter.key, $event)\">\n\t\t\t\t\t{{ filter.label }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\n\t\t\t\t<!-- Select filter (enum / facet values) -->\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-else-if=\"filter.type === 'select'\"\n\t\t\t\t\tclass=\"cn-facet-sidebar__select\"\n\t\t\t\t\t:modelValue=\"getSelectedOptions(filter)\"\n\t\t\t\t\t:options=\"getFilterOptions(filter)\"\n\t\t\t\t\t:placeholder=\"filter.label\"\n\t\t\t\t\t:inputLabel=\"filter.label\"\n\t\t\t\t\t:multiple=\"true\"\n\t\t\t\t\t:keepOpen=\"true\"\n\t\t\t\t\t:clearable=\"true\"\n\t\t\t\t\t@update:modelValue=\"onSelectChange(filter.key, $event)\" />\n\n\t\t\t\t<!-- Text filter (fallback) -->\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else\n\t\t\t\t\t:modelValue=\"getFilterValue(filter.key) || ''\"\n\t\t\t\t\t:placeholder=\"filter.label\"\n\t\t\t\t\t:label=\"filter.label\"\n\t\t\t\t\t@update:modelValue=\"onFilterChange(filter.key, $event)\" />\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcLoadingIcon, NcSelect, NcTextField } from '@nextcloud/vue'\nimport {
|
|
1
|
+
{"version":3,"file":"CnFacetSidebar.vue2.js","sources":["../../../../src/components/CnFacetSidebar/CnFacetSidebar.vue"],"sourcesContent":["<template>\n\t<div class=\"cn-facet-sidebar\">\n\t\t<div class=\"cn-facet-sidebar__header\">\n\t\t\t<h3 class=\"cn-facet-sidebar__title\">\n\t\t\t\t{{ title }}\n\t\t\t</h3>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"hasActiveFilters\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\tclass=\"cn-facet-sidebar__clear\"\n\t\t\t\t@click=\"$emit('clear-all')\">\n\t\t\t\t{{ clearLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<!-- Loading state -->\n\t\t<div v-if=\"loading\" class=\"cn-facet-sidebar__loading\">\n\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t</div>\n\n\t\t<!-- Filter groups -->\n\t\t<div v-else class=\"cn-facet-sidebar__filters\">\n\t\t\t<div\n\t\t\t\tv-for=\"filter in effectiveFilters\"\n\t\t\t\t:key=\"filter.key\"\n\t\t\t\tclass=\"cn-facet-sidebar__group\">\n\t\t\t\t<label class=\"cn-facet-sidebar__label\">{{ filter.label }}</label>\n\n\t\t\t\t<!-- Checkbox filter (boolean) -->\n\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\tv-if=\"filter.type === 'checkbox'\"\n\t\t\t\t\t:modelValue=\"getFilterValue(filter.key) === true\"\n\t\t\t\t\t@update:modelValue=\"onFilterChange(filter.key, $event)\">\n\t\t\t\t\t{{ filter.label }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\n\t\t\t\t<!-- Select filter (enum / facet values) -->\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-else-if=\"filter.type === 'select'\"\n\t\t\t\t\tclass=\"cn-facet-sidebar__select\"\n\t\t\t\t\t:modelValue=\"getSelectedOptions(filter)\"\n\t\t\t\t\t:options=\"getFilterOptions(filter)\"\n\t\t\t\t\t:placeholder=\"filter.label\"\n\t\t\t\t\t:inputLabel=\"filter.label\"\n\t\t\t\t\t:multiple=\"true\"\n\t\t\t\t\t:keepOpen=\"true\"\n\t\t\t\t\t:clearable=\"true\"\n\t\t\t\t\t@update:modelValue=\"onSelectChange(filter.key, $event)\" />\n\n\t\t\t\t<!-- Text filter (fallback) -->\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else\n\t\t\t\t\t:modelValue=\"getFilterValue(filter.key) || ''\"\n\t\t\t\t\t:placeholder=\"filter.label\"\n\t\t\t\t\t:label=\"filter.label\"\n\t\t\t\t\t@update:modelValue=\"onFilterChange(filter.key, $event)\" />\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcLoadingIcon, NcSelect, NcTextField } from '@nextcloud/vue'\nimport { facetOptions } from '../../utils/facets.js'\nimport { filtersFromSchema } from '../../utils/schema.js'\n\n/**\n * CnFacetSidebar — Auto-generated faceted search sidebar from schema properties.\n *\n * Reads properties marked `facetable: true` in the schema and generates\n * appropriate filter widgets. Accepts live facet data from the API for\n * dynamic option values with counts.\n *\n * ```vue\n * <CnFacetSidebar\n * :schema=\"schema\"\n * :facet-data=\"facetData\"\n * :active-filters=\"filters\"\n * @filter-change=\"onFilterChange\"\n * @clear-all=\"clearFilters\" />\n * ```\n */\nexport default {\n\tname: 'CnFacetSidebar',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcSelect,\n\t\tNcTextField,\n\t\tNcCheckboxRadioSwitch,\n\t\tNcLoadingIcon,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Consumer translation function, provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). Filter\n\t\t * labels come from schema property titles, authored in English as the\n\t\t * canonical source; the visible label is resolved through this function\n\t\t * so it follows the user's language. Defaults to identity when used\n\t\t * standalone (no CnAppRoot ancestor).\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** Schema definition — reads facetable properties */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Live facet data from the API: `{ fieldName: { values: [{ value, count?, label? }] } }` */\n\t\tfacetData: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Current active filters: { fieldName: value | [values] } */\n\t\tactiveFilters: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Whether facet data is loading */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Sidebar title */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Filters'),\n\t\t},\n\n\t\t/** Clear all button label */\n\t\tclearLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Clear all'),\n\t\t},\n\n\t\t/**\n\t\t * Whether the current user is an admin.\n\t\t * When false, schema properties with `adminOnly: true` are hidden from filters.\n\t\t */\n\t\tuserIsAdmin: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\t},\n\n\temits: ['clear-all', 'filter-change'],\n\n\tcomputed: {\n\t\teffectiveFilters() {\n\t\t\treturn filtersFromSchema(this.schema, { isAdmin: this.userIsAdmin, translate: this.cnTranslate })\n\t\t},\n\n\t\thasActiveFilters() {\n\t\t\treturn Object.values(this.activeFilters).some((v) => {\n\t\t\t\tif (Array.isArray(v)) {\n\t\t\t\t\treturn v.length > 0\n\t\t\t\t}\n\t\t\t\treturn v !== null && v !== undefined && v !== '' && v !== false\n\t\t\t})\n\t\t},\n\t},\n\n\tmethods: {\n\t\tgetFilterValue(key) {\n\t\t\treturn this.activeFilters[key] ?? null\n\t\t},\n\n\t\tgetFilterOptions(filter) {\n\t\t\t// Use facet data if available (live values with counts)\n\t\t\tconst live = facetOptions(\n\t\t\t\tthis.facetData[filter.key],\n\t\t\t\t(count) => t('nextcloud-vue', 'No value ({count})', { count }),\n\t\t\t)\n\t\t\tif (live !== null) {\n\t\t\t\treturn live\n\t\t\t}\n\t\t\t// Fall back to static enum options from schema\n\t\t\treturn filter.options || []\n\t\t},\n\n\t\tgetSelectedOptions(filter) {\n\t\t\tconst value = this.getFilterValue(filter.key)\n\t\t\tif (!value) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst values = Array.isArray(value) ? value : [value]\n\t\t\tconst options = this.getFilterOptions(filter)\n\t\t\treturn values.map((v) => options.find((o) => o.id === v) || { id: v, label: String(v) })\n\t\t},\n\n\t\tonSelectChange(key, selected) {\n\t\t\tconst values = selected ? selected.map((o) => o.id) : []\n\t\t\tthis.$emit('filter-change', { key, values })\n\t\t},\n\n\t\tonFilterChange(key, value) {\n\t\t\tthis.$emit('filter-change', { key, values: value })\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-facet-sidebar {\n\tpadding: 16px;\n\tborder-right: 1px solid var(--color-border);\n\tmin-width: 240px;\n\tmax-width: 300px;\n}\n\n.cn-facet-sidebar__header {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: center;\n\tmargin-bottom: 16px;\n}\n\n.cn-facet-sidebar__title {\n\tmargin: 0;\n\tfont-size: 15px;\n\tfont-weight: 600;\n}\n\n.cn-facet-sidebar__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 20px;\n}\n\n.cn-facet-sidebar__filters {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n}\n\n.cn-facet-sidebar__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-facet-sidebar__label {\n\tfont-size: 12px;\n\tfont-weight: 500;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.3px;\n}\n\n.cn-facet-sidebar__select {\n\twidth: 100%;\n}\n</style>\n"],"names":["t"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnFacetSidebar.vue3.js","sources":["../../../../src/components/CnFacetSidebar/CnFacetSidebar.vue?vue&type=template&id=202f66e2&lang.js"],"sourcesContent":["<template>\n\t<div class=\"cn-facet-sidebar\">\n\t\t<div class=\"cn-facet-sidebar__header\">\n\t\t\t<h3 class=\"cn-facet-sidebar__title\">\n\t\t\t\t{{ title }}\n\t\t\t</h3>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"hasActiveFilters\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\tclass=\"cn-facet-sidebar__clear\"\n\t\t\t\t@click=\"$emit('clear-all')\">\n\t\t\t\t{{ clearLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<!-- Loading state -->\n\t\t<div v-if=\"loading\" class=\"cn-facet-sidebar__loading\">\n\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t</div>\n\n\t\t<!-- Filter groups -->\n\t\t<div v-else class=\"cn-facet-sidebar__filters\">\n\t\t\t<div\n\t\t\t\tv-for=\"filter in effectiveFilters\"\n\t\t\t\t:key=\"filter.key\"\n\t\t\t\tclass=\"cn-facet-sidebar__group\">\n\t\t\t\t<label class=\"cn-facet-sidebar__label\">{{ filter.label }}</label>\n\n\t\t\t\t<!-- Checkbox filter (boolean) -->\n\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\tv-if=\"filter.type === 'checkbox'\"\n\t\t\t\t\t:modelValue=\"getFilterValue(filter.key) === true\"\n\t\t\t\t\t@update:modelValue=\"onFilterChange(filter.key, $event)\">\n\t\t\t\t\t{{ filter.label }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\n\t\t\t\t<!-- Select filter (enum / facet values) -->\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-else-if=\"filter.type === 'select'\"\n\t\t\t\t\tclass=\"cn-facet-sidebar__select\"\n\t\t\t\t\t:modelValue=\"getSelectedOptions(filter)\"\n\t\t\t\t\t:options=\"getFilterOptions(filter)\"\n\t\t\t\t\t:placeholder=\"filter.label\"\n\t\t\t\t\t:inputLabel=\"filter.label\"\n\t\t\t\t\t:multiple=\"true\"\n\t\t\t\t\t:keepOpen=\"true\"\n\t\t\t\t\t:clearable=\"true\"\n\t\t\t\t\t@update:modelValue=\"onSelectChange(filter.key, $event)\" />\n\n\t\t\t\t<!-- Text filter (fallback) -->\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else\n\t\t\t\t\t:modelValue=\"getFilterValue(filter.key) || ''\"\n\t\t\t\t\t:placeholder=\"filter.label\"\n\t\t\t\t\t:label=\"filter.label\"\n\t\t\t\t\t@update:modelValue=\"onFilterChange(filter.key, $event)\" />\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcLoadingIcon, NcSelect, NcTextField } from '@nextcloud/vue'\nimport {
|
|
1
|
+
{"version":3,"file":"CnFacetSidebar.vue3.js","sources":["../../../../src/components/CnFacetSidebar/CnFacetSidebar.vue?vue&type=template&id=202f66e2&lang.js"],"sourcesContent":["<template>\n\t<div class=\"cn-facet-sidebar\">\n\t\t<div class=\"cn-facet-sidebar__header\">\n\t\t\t<h3 class=\"cn-facet-sidebar__title\">\n\t\t\t\t{{ title }}\n\t\t\t</h3>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"hasActiveFilters\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\tclass=\"cn-facet-sidebar__clear\"\n\t\t\t\t@click=\"$emit('clear-all')\">\n\t\t\t\t{{ clearLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<!-- Loading state -->\n\t\t<div v-if=\"loading\" class=\"cn-facet-sidebar__loading\">\n\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t</div>\n\n\t\t<!-- Filter groups -->\n\t\t<div v-else class=\"cn-facet-sidebar__filters\">\n\t\t\t<div\n\t\t\t\tv-for=\"filter in effectiveFilters\"\n\t\t\t\t:key=\"filter.key\"\n\t\t\t\tclass=\"cn-facet-sidebar__group\">\n\t\t\t\t<label class=\"cn-facet-sidebar__label\">{{ filter.label }}</label>\n\n\t\t\t\t<!-- Checkbox filter (boolean) -->\n\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\tv-if=\"filter.type === 'checkbox'\"\n\t\t\t\t\t:modelValue=\"getFilterValue(filter.key) === true\"\n\t\t\t\t\t@update:modelValue=\"onFilterChange(filter.key, $event)\">\n\t\t\t\t\t{{ filter.label }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\n\t\t\t\t<!-- Select filter (enum / facet values) -->\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-else-if=\"filter.type === 'select'\"\n\t\t\t\t\tclass=\"cn-facet-sidebar__select\"\n\t\t\t\t\t:modelValue=\"getSelectedOptions(filter)\"\n\t\t\t\t\t:options=\"getFilterOptions(filter)\"\n\t\t\t\t\t:placeholder=\"filter.label\"\n\t\t\t\t\t:inputLabel=\"filter.label\"\n\t\t\t\t\t:multiple=\"true\"\n\t\t\t\t\t:keepOpen=\"true\"\n\t\t\t\t\t:clearable=\"true\"\n\t\t\t\t\t@update:modelValue=\"onSelectChange(filter.key, $event)\" />\n\n\t\t\t\t<!-- Text filter (fallback) -->\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else\n\t\t\t\t\t:modelValue=\"getFilterValue(filter.key) || ''\"\n\t\t\t\t\t:placeholder=\"filter.label\"\n\t\t\t\t\t:label=\"filter.label\"\n\t\t\t\t\t@update:modelValue=\"onFilterChange(filter.key, $event)\" />\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcLoadingIcon, NcSelect, NcTextField } from '@nextcloud/vue'\nimport { facetOptions } from '../../utils/facets.js'\nimport { filtersFromSchema } from '../../utils/schema.js'\n\n/**\n * CnFacetSidebar — Auto-generated faceted search sidebar from schema properties.\n *\n * Reads properties marked `facetable: true` in the schema and generates\n * appropriate filter widgets. Accepts live facet data from the API for\n * dynamic option values with counts.\n *\n * ```vue\n * <CnFacetSidebar\n * :schema=\"schema\"\n * :facet-data=\"facetData\"\n * :active-filters=\"filters\"\n * @filter-change=\"onFilterChange\"\n * @clear-all=\"clearFilters\" />\n * ```\n */\nexport default {\n\tname: 'CnFacetSidebar',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcSelect,\n\t\tNcTextField,\n\t\tNcCheckboxRadioSwitch,\n\t\tNcLoadingIcon,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Consumer translation function, provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). Filter\n\t\t * labels come from schema property titles, authored in English as the\n\t\t * canonical source; the visible label is resolved through this function\n\t\t * so it follows the user's language. Defaults to identity when used\n\t\t * standalone (no CnAppRoot ancestor).\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** Schema definition — reads facetable properties */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Live facet data from the API: `{ fieldName: { values: [{ value, count?, label? }] } }` */\n\t\tfacetData: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Current active filters: { fieldName: value | [values] } */\n\t\tactiveFilters: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Whether facet data is loading */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Sidebar title */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Filters'),\n\t\t},\n\n\t\t/** Clear all button label */\n\t\tclearLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Clear all'),\n\t\t},\n\n\t\t/**\n\t\t * Whether the current user is an admin.\n\t\t * When false, schema properties with `adminOnly: true` are hidden from filters.\n\t\t */\n\t\tuserIsAdmin: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\t},\n\n\temits: ['clear-all', 'filter-change'],\n\n\tcomputed: {\n\t\teffectiveFilters() {\n\t\t\treturn filtersFromSchema(this.schema, { isAdmin: this.userIsAdmin, translate: this.cnTranslate })\n\t\t},\n\n\t\thasActiveFilters() {\n\t\t\treturn Object.values(this.activeFilters).some((v) => {\n\t\t\t\tif (Array.isArray(v)) {\n\t\t\t\t\treturn v.length > 0\n\t\t\t\t}\n\t\t\t\treturn v !== null && v !== undefined && v !== '' && v !== false\n\t\t\t})\n\t\t},\n\t},\n\n\tmethods: {\n\t\tgetFilterValue(key) {\n\t\t\treturn this.activeFilters[key] ?? null\n\t\t},\n\n\t\tgetFilterOptions(filter) {\n\t\t\t// Use facet data if available (live values with counts)\n\t\t\tconst live = facetOptions(\n\t\t\t\tthis.facetData[filter.key],\n\t\t\t\t(count) => t('nextcloud-vue', 'No value ({count})', { count }),\n\t\t\t)\n\t\t\tif (live !== null) {\n\t\t\t\treturn live\n\t\t\t}\n\t\t\t// Fall back to static enum options from schema\n\t\t\treturn filter.options || []\n\t\t},\n\n\t\tgetSelectedOptions(filter) {\n\t\t\tconst value = this.getFilterValue(filter.key)\n\t\t\tif (!value) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst values = Array.isArray(value) ? value : [value]\n\t\t\tconst options = this.getFilterOptions(filter)\n\t\t\treturn values.map((v) => options.find((o) => o.id === v) || { id: v, label: String(v) })\n\t\t},\n\n\t\tonSelectChange(key, selected) {\n\t\t\tconst values = selected ? selected.map((o) => o.id) : []\n\t\t\tthis.$emit('filter-change', { key, values })\n\t\t},\n\n\t\tonFilterChange(key, value) {\n\t\t\tthis.$emit('filter-change', { key, values: value })\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-facet-sidebar {\n\tpadding: 16px;\n\tborder-right: 1px solid var(--color-border);\n\tmin-width: 240px;\n\tmax-width: 300px;\n}\n\n.cn-facet-sidebar__header {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: center;\n\tmargin-bottom: 16px;\n}\n\n.cn-facet-sidebar__title {\n\tmargin: 0;\n\tfont-size: 15px;\n\tfont-weight: 600;\n}\n\n.cn-facet-sidebar__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 20px;\n}\n\n.cn-facet-sidebar__filters {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n}\n\n.cn-facet-sidebar__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-facet-sidebar__label {\n\tfont-size: 12px;\n\tfont-weight: 500;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.3px;\n}\n\n.cn-facet-sidebar__select {\n\twidth: 100%;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_createBlock","_createVNode","_Fragment","_renderList","_createTextVNode"],"mappings":";;AACM,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,kBAAkB,GAAA;AACvB,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,0BAA0B,GAAA;AAChC,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,yBAAyB,GAAA;;;AAahB,EAAA,KAAK,EAAC,2BAA2B;;;;AAKzC,EAAA,KAAK,EAAC,2BAA2B;;AAKpC,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,yBAAyB,GAAA;;;;;;;;;AAzBzC,EAAA,QAAAA,SAAA,EAAA,EAAAC,kBAAA,CAyDK,OAzDL,UAyDK,EAAA;AAxDJ,IAAAC,kBAAA,CAWK,OAXL,UAWK,EAAA;MAVJA,kBAEI,CAAA,IAAA,EAFJ,UAEI,EAAAC,eAAA,CADA;OAGG,QAAgB,CAAA,gBAAA;wBADvBC,WAMU,CAAA,mBAAA,EAAA;;AAJT,YAAA,OAAO,EAAC,UAAS;AACjB,YAAA,KAAK,EAAC,yBAAwB;AAC7B,YAAA,OAAK,uCAAE,IAAK,CAAA,KAAA,CAAA,WAAA,CAAA,CAAA,CAAA;;6BACb,MAAe;8CAAZ,MAAS,CAAA,UAAA,CAAA,EAAA,CAAA,YAAA;;;;;;KAKH,MAAO,CAAA,OAAA;AAAlB,SAAAJ,SAAA,EAAA,EAAAC,kBAAA,CAEK,OAFL,UAEK,EAAA;UADJI,WAA2B,CAAA,wBAAA,EAAA,EAAX,IAAI,EAAE,EAAE,EAAA,CAAA;;AAIzB,SAAAL,SAAA,EAAA,EAAAC,kBAAA,CAoCK,OApCL,UAoCK,EAAA;4BAnCJA,kBAkCK,CAAAK,QAAA,EAAA,IAAA,EAAAC,UAAA,CAjCa,QAAgB,CAAA,gBAAA,EAAA,CAA1B,MAAK,KAAA;iCADbN,kBAkCK,CAAA,KAAA,EAAA;cAhCH,GAAG,EAAE,MAAM,CAAC,GAAG;AAChB,cAAA,KAAK,EAAC,yBAAyB;;AAC/B,cAAAC,kBAAA,CAAgE,OAAhE,EAAA,UAAgE,EAAtBC,eAAA,CAAA,MAAM,CAAC,KAAI,CAAA,EAAA,CAAA,YAAA;AAI9C,cAAA,CAAA,MAAM,CAAC,IAAG,KAAA,UAAA;gCADjBC,WAKuB,CAAA,gCAAA,EAAA;;AAHrB,oBAAA,UAAU,EAAE,QAAA,CAAA,cAAc,CAAC,MAAM,CAAC,GAAG,CAAA,KAAA,IAAA;AACrC,oBAAA,qBAAiB,aAAE,QAAc,CAAA,cAAA,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAA,CAAA;;qCACrD,MAAiB;AAAd,sBAAAI,eAAA,CAAAL,eAAA,CAAA,MAAM,CAAC;;;;AAKC,kBAAA,CAAA,MAAM,CAAC,IAAG,KAAA,QAAA;kCADtBC,WAU0D,CAAA,mBAAA,EAAA;;AARzD,sBAAA,KAAK,EAAC,0BAAyB;sBAC9B,UAAU,EAAE,QAAkB,CAAA,kBAAA,CAAC,MAAM,CAAA;sBACrC,OAAO,EAAE,QAAgB,CAAA,gBAAA,CAAC,MAAM,CAAA;sBAChC,WAAW,EAAE,MAAM,CAAC,KAAK;sBACzB,UAAU,EAAE,MAAM,CAAC,KAAK;AACxB,sBAAA,QAAQ,EAAE,IAAI;AACd,sBAAA,QAAQ,EAAE,IAAI;AACd,sBAAA,SAAS,EAAE,IAAI;AACf,sBAAA,qBAAiB,aAAE,QAAc,CAAA,cAAA,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAA,CAAA;;kCAGtDA,WAK0D,CAAA,sBAAA,EAAA;;AAHxD,sBAAA,UAAU,EAAE,QAAA,CAAA,cAAc,CAAC,MAAM,CAAC,GAAG,CAAA,IAAA,EAAA;sBACrC,WAAW,EAAE,MAAM,CAAC,KAAK;sBACzB,KAAK,EAAE,MAAM,CAAC,KAAK;AACnB,sBAAA,qBAAiB,aAAE,QAAc,CAAA,cAAA,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAA,CAAA;;;;;;;;;;"}
|
|
@@ -7,6 +7,7 @@ import EyeOutline from 'vue-material-design-icons/EyeOutline.vue';
|
|
|
7
7
|
import __sfc_main from '../CnIcon/CnIcon.vue.js';
|
|
8
8
|
import __sfc_main$1 from '../CnStatusBadge/CnStatusBadge.vue.js';
|
|
9
9
|
import { resolveEndpointRequest } from '../../composables/useEndpointSource.js';
|
|
10
|
+
import { useRefLabels } from '../../composables/useRefLabels.js';
|
|
10
11
|
import { dispatchAction } from '../../utils/actionsDispatcher.js';
|
|
11
12
|
import { isBadgeVariant } from '../../utils/badgeVariants.js';
|
|
12
13
|
import { formatBoardDate } from '../../utils/boardDate.js';
|
|
@@ -17,6 +18,35 @@ import { resolveDeepTokens, hasUnresolvedDeepTokens } from '../../utils/resolveF
|
|
|
17
18
|
/** Kinds of entry a `fields[]` item can be. A labelled item without `as` is a row. */
|
|
18
19
|
const KINDS = Object.freeze(['row', 'title', 'line', 'pill', 'lead', 'text']);
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* The label resolver every facts widget on the page shares, made on first use
|
|
23
|
+
* (the object store needs an active pinia). One batched request per register and
|
|
24
|
+
* schema for the ids it has not seen; a label it has read stays until the page
|
|
25
|
+
* reloads, and an object the store already holds is not fetched at all.
|
|
26
|
+
*
|
|
27
|
+
* @type {{resolve: Function}|null}
|
|
28
|
+
*/
|
|
29
|
+
let refLabels = null;
|
|
30
|
+
|
|
31
|
+
/** The page-wide reference label resolver. */
|
|
32
|
+
function sharedRefLabels() {
|
|
33
|
+
if (!refLabels) {
|
|
34
|
+
refLabels = useRefLabels();
|
|
35
|
+
}
|
|
36
|
+
return refLabels
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Key of a resolved reference in the widget's `labels` data.
|
|
41
|
+
*
|
|
42
|
+
* @param {{register: string, schema: string, labelField?: string}} resolve The entry's `resolve` config.
|
|
43
|
+
* @param {string} id The referenced object's id.
|
|
44
|
+
* @return {string} The key.
|
|
45
|
+
*/
|
|
46
|
+
function refKey(resolve, id) {
|
|
47
|
+
return `${resolve.register}/${resolve.schema}/${resolve.labelField || ''}/${id}`
|
|
48
|
+
}
|
|
49
|
+
|
|
20
50
|
/** Event-bus channel a reveal of a masked value is announced on, so an app can log it. */
|
|
21
51
|
const FACTS_REVEAL_CHANNEL = 'cn:facts:reveal';
|
|
22
52
|
|
|
@@ -80,6 +110,7 @@ var script = {
|
|
|
80
110
|
* `as` is `row` (default when `label` or `icon` is set), `title`, `line`, `text` (a line in the normal ink), `lead` (an icon tile or initials beside `title` and `subtitle`) or `pill`.
|
|
81
111
|
* A row's `format` may also be `email`, `phone` or `url` (a link) or `masked` (the last four characters; `reveal: true` adds Show). `copy: true` adds a copy button; `icon` replaces the label.
|
|
82
112
|
* A part may be `{ text }`, a constant; a `line` or `text` entry without `path` or `parts` shows its `text`.
|
|
113
|
+
* `resolve: { register, schema, labelField? }` on an entry or a part turns a value that is a reference (an id, or a list of ids) into the label of the referenced object (`labelField`, else `title`, then `name`): the case type or status of a task shows its name, not its uuid. The ids are fetched once per register and schema through the object store and shared by every facts widget on the page; the entry stays empty until its label is known, and when no label can be read.
|
|
83
114
|
* - `action` — an action drawn as a button under the facts; `headerAction` — one drawn in the card header (reported through the `header` event).
|
|
84
115
|
* `format` is `text` (default), `date` (board short date), `date-long` (always with the year) or `list` (an array joined by ", ", each item read at `itemPath`).
|
|
85
116
|
* `map` turns a raw value into a label (translated). `parts` (on a `line`) lists paths, or `{ path, label?, format? }`, joined by `separator` (default " · "); empty parts are dropped.
|
|
@@ -122,6 +153,8 @@ var script = {
|
|
|
122
153
|
revealed: {},
|
|
123
154
|
/** Key of the row whose value was just copied. */
|
|
124
155
|
copied: '',
|
|
156
|
+
/** Labels of the referenced objects read so far, by `refKey`. */
|
|
157
|
+
labels: {},
|
|
125
158
|
}
|
|
126
159
|
},
|
|
127
160
|
|
|
@@ -297,6 +330,13 @@ var script = {
|
|
|
297
330
|
},
|
|
298
331
|
},
|
|
299
332
|
|
|
333
|
+
record: {
|
|
334
|
+
immediate: true,
|
|
335
|
+
handler() {
|
|
336
|
+
this.loadLabels();
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
|
|
300
340
|
headerAction: {
|
|
301
341
|
immediate: true,
|
|
302
342
|
handler() {
|
|
@@ -535,6 +575,94 @@ var script = {
|
|
|
535
575
|
});
|
|
536
576
|
},
|
|
537
577
|
|
|
578
|
+
/**
|
|
579
|
+
* The text of a reference: the labels of the objects its id(s) name, joined
|
|
580
|
+
* by ", ". An id whose label is not known yet (or cannot be read) adds nothing.
|
|
581
|
+
*
|
|
582
|
+
* @param {unknown} raw The id, or a list of ids (or of `{ id }` objects).
|
|
583
|
+
* @param {object} spec `{ resolve: { register, schema, labelField? }, itemPath? }`.
|
|
584
|
+
* @return {string}
|
|
585
|
+
*/
|
|
586
|
+
refText(raw, spec) {
|
|
587
|
+
return this.refIds(raw, spec)
|
|
588
|
+
.map((id) => this.labels[refKey(spec.resolve, id)])
|
|
589
|
+
.filter((label) => typeof label === 'string' && label !== '')
|
|
590
|
+
.join(', ')
|
|
591
|
+
},
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* The ids a reference value holds.
|
|
595
|
+
*
|
|
596
|
+
* @param {unknown} raw The value.
|
|
597
|
+
* @param {object} spec `{ itemPath? }`.
|
|
598
|
+
* @return {string[]}
|
|
599
|
+
*/
|
|
600
|
+
refIds(raw, spec) {
|
|
601
|
+
return (Array.isArray(raw) ? raw : [raw])
|
|
602
|
+
.map((item) => (item && typeof item === 'object' ? (spec.itemPath ? readPath(item, spec.itemPath) : (item.id ?? item.uuid)) : item))
|
|
603
|
+
.filter((id) => id !== null && id !== undefined && id !== '' && typeof id !== 'object')
|
|
604
|
+
.map(String)
|
|
605
|
+
},
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Every entry or part that carries `resolve`, with the record's value at its path.
|
|
609
|
+
*
|
|
610
|
+
* @param {object} record The object.
|
|
611
|
+
* @return {Array<{spec: object, raw: unknown}>}
|
|
612
|
+
*/
|
|
613
|
+
refSpecs(record) {
|
|
614
|
+
const fields = Array.isArray(this.content?.fields) ? this.content.fields : [];
|
|
615
|
+
const specs = [];
|
|
616
|
+
const visit = (spec) => {
|
|
617
|
+
if (!spec || typeof spec !== 'object') {
|
|
618
|
+
return
|
|
619
|
+
}
|
|
620
|
+
if (spec.resolve && typeof spec.resolve === 'object' && spec.path) {
|
|
621
|
+
specs.push({ spec, raw: readPath(record, spec.path) });
|
|
622
|
+
}
|
|
623
|
+
const nested = [...(Array.isArray(spec.parts) ? spec.parts : []), ...(Array.isArray(spec.title) ? spec.title : [spec.title]), ...(Array.isArray(spec.subtitle) ? spec.subtitle : [spec.subtitle])];
|
|
624
|
+
nested.forEach(visit);
|
|
625
|
+
};
|
|
626
|
+
fields.forEach(visit);
|
|
627
|
+
return specs
|
|
628
|
+
},
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Read the labels of the referenced objects, one batched request per
|
|
632
|
+
* register and schema, and keep them in `labels`.
|
|
633
|
+
*
|
|
634
|
+
* @param {object|null} record The object shown (defaults to the current record).
|
|
635
|
+
* @return {Promise<void>}
|
|
636
|
+
*/
|
|
637
|
+
async loadLabels(record = this.record) {
|
|
638
|
+
if (!record) {
|
|
639
|
+
return
|
|
640
|
+
}
|
|
641
|
+
const groups = new Map();
|
|
642
|
+
for (const { spec, raw } of this.refSpecs(record)) {
|
|
643
|
+
const { register, schema, labelField } = spec.resolve;
|
|
644
|
+
if (!register || !schema) {
|
|
645
|
+
continue
|
|
646
|
+
}
|
|
647
|
+
const groupKey = `${register}/${schema}/${labelField || ''}`;
|
|
648
|
+
const group = groups.get(groupKey) || { resolve: spec.resolve, ids: new Set() };
|
|
649
|
+
this.refIds(raw, spec).forEach((id) => group.ids.add(id));
|
|
650
|
+
groups.set(groupKey, group);
|
|
651
|
+
}
|
|
652
|
+
if (groups.size === 0) {
|
|
653
|
+
return
|
|
654
|
+
}
|
|
655
|
+
const resolver = sharedRefLabels();
|
|
656
|
+
const next = { ...this.labels };
|
|
657
|
+
await Promise.all([...groups.values()].map(async ({ resolve, ids }) => {
|
|
658
|
+
const found = await resolver.resolve(resolve.register, resolve.schema, [...ids], resolve.labelField);
|
|
659
|
+
Object.entries(found).forEach(([id, label]) => {
|
|
660
|
+
next[refKey(resolve, id)] = label;
|
|
661
|
+
});
|
|
662
|
+
}));
|
|
663
|
+
this.labels = next;
|
|
664
|
+
},
|
|
665
|
+
|
|
538
666
|
/**
|
|
539
667
|
* Read the source object.
|
|
540
668
|
*
|
|
@@ -641,6 +769,9 @@ var script = {
|
|
|
641
769
|
if (raw === null || raw === undefined || raw === '') {
|
|
642
770
|
return ''
|
|
643
771
|
}
|
|
772
|
+
if (spec.resolve && typeof spec.resolve === 'object') {
|
|
773
|
+
return this.refText(raw, spec)
|
|
774
|
+
}
|
|
644
775
|
const format = spec.format || 'text';
|
|
645
776
|
if (format === 'list' || Array.isArray(raw)) {
|
|
646
777
|
const items = (Array.isArray(raw) ? raw : [raw])
|