@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
|
@@ -16,6 +16,13 @@
|
|
|
16
16
|
:rowActions="rowActions"
|
|
17
17
|
:newActions="newActions"
|
|
18
18
|
:linkedItems="linkedItems"
|
|
19
|
+
:columns="columns"
|
|
20
|
+
:rowData="rowDataSource"
|
|
21
|
+
:preferenceApp="preferenceApp"
|
|
22
|
+
:preferenceKey="preferenceKey"
|
|
23
|
+
:bulkActions="bulkActions"
|
|
24
|
+
:groupBy="groupBy"
|
|
25
|
+
:facets="facets"
|
|
19
26
|
@changed="onBrowserChanged" />
|
|
20
27
|
<template v-else>
|
|
21
28
|
<!-- Upload error -->
|
|
@@ -240,7 +247,7 @@ import CnFilePreview from '../CnFilePreview/CnFilePreview.vue'
|
|
|
240
247
|
import CnFilesBrowser from '../CnFilesBrowser/CnFilesBrowser.vue'
|
|
241
248
|
import { useFileOpener } from '../../composables/useFileOpener.js'
|
|
242
249
|
import { buildHeaders, prefixUrl } from '../../utils/index.js'
|
|
243
|
-
import { objectDisplayName, resolveObjectFolderInfo } from '../CnFilesBrowser/filesBrowser.js'
|
|
250
|
+
import { indexRowData, objectDisplayName, resolveObjectFolderInfo } from '../CnFilesBrowser/filesBrowser.js'
|
|
244
251
|
import { createOpenRegisterSource } from '../CnFilesBrowser/openRegisterSource.js'
|
|
245
252
|
|
|
246
253
|
/**
|
|
@@ -359,6 +366,47 @@ export default {
|
|
|
359
366
|
* Null fetches nothing.
|
|
360
367
|
*/
|
|
361
368
|
linkedItemsUrl: { type: String, default: null },
|
|
369
|
+
/**
|
|
370
|
+
* Forwarded to CnFilesBrowser: the columns the browser shows, built-in
|
|
371
|
+
* names or declared columns (`{ key, label, source, formatter }`).
|
|
372
|
+
* A `source: 'row'` column reads the host's record for the file from
|
|
373
|
+
* `rowDataUrl`. None keeps name, size and modified.
|
|
374
|
+
*
|
|
375
|
+
* @type {Array<string|object>}
|
|
376
|
+
*/
|
|
377
|
+
columns: { type: Array, default: () => [] },
|
|
378
|
+
/** Forwarded to CnFilesBrowser: the app id the user's column choice is stored under; unset offers no Columns chooser. */
|
|
379
|
+
preferenceApp: { type: String, default: '' },
|
|
380
|
+
/** Forwarded to CnFilesBrowser: the key the column choice is stored under. */
|
|
381
|
+
preferenceKey: { type: String, default: 'files-browser-columns' },
|
|
382
|
+
/**
|
|
383
|
+
* An app endpoint that answers the host's own record per file, for the
|
|
384
|
+
* browser's `source: 'row'` columns. App-relative; `{objectId}` is
|
|
385
|
+
* replaced. Read once per listing. The answer is a list of records
|
|
386
|
+
* (or the list at `rowDataPath`, or under `items` or `results`), each
|
|
387
|
+
* naming its file in `rowDataKey`. Null reads nothing.
|
|
388
|
+
*/
|
|
389
|
+
rowDataUrl: { type: String, default: null },
|
|
390
|
+
/**
|
|
391
|
+
* Forwarded to CnFilesBrowser: the host's actions on a selection of
|
|
392
|
+
* files. With at least one, files get checkboxes and a bulk bar.
|
|
393
|
+
*
|
|
394
|
+
* @type {Array<{id: string, label: string, icon?: string, type?: string, target?: string, props?: object}>}
|
|
395
|
+
*/
|
|
396
|
+
bulkActions: { type: Array, default: () => [] },
|
|
397
|
+
/** Forwarded to CnFilesBrowser: the key of a declared column to group the files on. */
|
|
398
|
+
groupBy: { type: String, default: '' },
|
|
399
|
+
/**
|
|
400
|
+
* Forwarded to CnFilesBrowser: keys of declared columns to filter on,
|
|
401
|
+
* a chip per value in use.
|
|
402
|
+
*
|
|
403
|
+
* @type {string[]}
|
|
404
|
+
*/
|
|
405
|
+
facets: { type: Array, default: () => [] },
|
|
406
|
+
/** A dotted path to the list of records in the `rowDataUrl` answer; empty reads the answer itself. */
|
|
407
|
+
rowDataPath: { type: String, default: '' },
|
|
408
|
+
/** The record field that holds the Nextcloud file id. */
|
|
409
|
+
rowDataKey: { type: String, default: 'fileId' },
|
|
362
410
|
/** Label of the Download action. */
|
|
363
411
|
downloadLabel: { type: String, default: () => t('nextcloud-vue', 'Download') },
|
|
364
412
|
/** Label of the action that opens the Files sidebar on the file. */
|
|
@@ -424,6 +472,27 @@ export default {
|
|
|
424
472
|
},
|
|
425
473
|
|
|
426
474
|
computed: {
|
|
475
|
+
/**
|
|
476
|
+
* The browser's `rowData`: a function that reads the host's records
|
|
477
|
+
* from `rowDataUrl` and keys them by file id, or null without a url.
|
|
478
|
+
* The browser calls it once per listing.
|
|
479
|
+
*
|
|
480
|
+
* @return {(function(): Promise<object>)|null} The row data source.
|
|
481
|
+
* @spec openspec/changes/files-browser-hosts-a-documents-list/specs/files-browser/spec.md
|
|
482
|
+
*/
|
|
483
|
+
rowDataSource() {
|
|
484
|
+
if (!this.rowDataUrl || !this.objectId) {
|
|
485
|
+
return null
|
|
486
|
+
}
|
|
487
|
+
const url = generateUrl(this.rowDataUrl.replace('{objectId}', encodeURIComponent(this.objectId)))
|
|
488
|
+
const path = this.rowDataPath
|
|
489
|
+
const key = this.rowDataKey
|
|
490
|
+
return async () => {
|
|
491
|
+
const { data } = await axios.get(url)
|
|
492
|
+
return indexRowData(data, { path, key })
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
|
|
427
496
|
/**
|
|
428
497
|
* The root crumb: the host's label, else the object's name (its folder
|
|
429
498
|
* on disk is called after its uuid), else the folder's own name.
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
{{ cancelLabel }}
|
|
19
19
|
</NcButton>
|
|
20
20
|
</p>
|
|
21
|
+
<!-- @slot composer-before An app's own control above the composer, e.g. a template picker. Scope: `{ setText, text }`; `setText(text)` fills the composer, `text` is what it holds now. -->
|
|
22
|
+
<slot name="composer-before" :setText="setComposerText" :text="newNoteText" />
|
|
21
23
|
<CnNoteComposer
|
|
22
24
|
class="cn-sidebar-tab__composer"
|
|
23
25
|
:modelValue="newNoteText"
|
|
@@ -481,6 +483,20 @@ export default {
|
|
|
481
483
|
}
|
|
482
484
|
},
|
|
483
485
|
|
|
486
|
+
/**
|
|
487
|
+
* Fill the composer from outside, for the `composer-before` slot.
|
|
488
|
+
*
|
|
489
|
+
* The app decides what goes in (a template body, a quoted line); the
|
|
490
|
+
* composer stays the one place the text lives, so sending, editing and
|
|
491
|
+
* the mention picker all see it exactly as if it had been typed.
|
|
492
|
+
*
|
|
493
|
+
* @param {string} text The text the composer should hold.
|
|
494
|
+
* @return {void}
|
|
495
|
+
*/
|
|
496
|
+
setComposerText(text) {
|
|
497
|
+
this.newNoteText = (text === null || text === undefined) ? '' : String(text)
|
|
498
|
+
},
|
|
499
|
+
|
|
484
500
|
startEdit(note) {
|
|
485
501
|
this.replyTo = null
|
|
486
502
|
this.editingNoteId = note.id
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
- SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
- SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<fieldset class="cn-radio-cards" data-testid="cn-radio-cards" :aria-busy="busy ? 'true' : 'false'">
|
|
8
|
+
<legend v-if="label" class="cn-radio-cards__legend">
|
|
9
|
+
{{ label }}
|
|
10
|
+
</legend>
|
|
11
|
+
<div class="cn-radio-cards__grid" :class="`cn-radio-cards__grid--${columns}`">
|
|
12
|
+
<label
|
|
13
|
+
v-for="option in options"
|
|
14
|
+
:key="String(option.value)"
|
|
15
|
+
class="cn-radio-cards__card"
|
|
16
|
+
:class="{ 'cn-radio-cards__card--selected': isSelected(option) }"
|
|
17
|
+
:data-testid="`cn-radio-cards-option-${option.value}`">
|
|
18
|
+
<input
|
|
19
|
+
class="cn-radio-cards__input"
|
|
20
|
+
type="radio"
|
|
21
|
+
:name="groupName"
|
|
22
|
+
:value="String(option.value)"
|
|
23
|
+
:checked="isSelected(option)"
|
|
24
|
+
:disabled="readOnly || busy"
|
|
25
|
+
@change="choose(option)">
|
|
26
|
+
<span class="cn-radio-cards__dot" aria-hidden="true" />
|
|
27
|
+
<span class="cn-radio-cards__text">
|
|
28
|
+
<span class="cn-radio-cards__title">{{ option.label }}</span>
|
|
29
|
+
<span v-if="option.description" class="cn-radio-cards__description">{{ option.description }}</span>
|
|
30
|
+
</span>
|
|
31
|
+
</label>
|
|
32
|
+
</div>
|
|
33
|
+
<NcNoteCard v-if="notice" type="error" data-testid="cn-radio-cards-notice">
|
|
34
|
+
{{ notice }}
|
|
35
|
+
</NcNoteCard>
|
|
36
|
+
</fieldset>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<script>
|
|
40
|
+
import { NcNoteCard } from '@nextcloud/vue'
|
|
41
|
+
import { objectUrlOf, patchObject } from '../../utils/objectWrite.js'
|
|
42
|
+
import { readPath } from '../../utils/readPath.js'
|
|
43
|
+
import { serverErrorMessage } from '../../utils/serverErrorMessage.js'
|
|
44
|
+
|
|
45
|
+
let uid = 0
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* CnRadioCardsWidget — the `radio-cards` widget: one choice out of a few cards,
|
|
49
|
+
* each with a title and a description, bound to an enum field of the CURRENT
|
|
50
|
+
* object. Choosing a card saves at once with ONE PATCH of that field; a refused
|
|
51
|
+
* PATCH puts the choice back and shows the server's words. Real radio inputs
|
|
52
|
+
* underneath, so the choice is perceivable without colour and works from the
|
|
53
|
+
* keyboard.
|
|
54
|
+
*
|
|
55
|
+
* @spec openspec/changes/abstract-radio-cards/specs/radio-cards/spec.md
|
|
56
|
+
*/
|
|
57
|
+
export default {
|
|
58
|
+
name: 'CnRadioCardsWidget',
|
|
59
|
+
|
|
60
|
+
components: { NcNoteCard },
|
|
61
|
+
|
|
62
|
+
inject: {
|
|
63
|
+
cnTranslate: { default: () => (key) => key },
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
props: {
|
|
67
|
+
/**
|
|
68
|
+
* The widget configuration.
|
|
69
|
+
*
|
|
70
|
+
* - `field` (required) — the top-level enum field.
|
|
71
|
+
* - `options` (required) — `[{ value, label, description? }]`.
|
|
72
|
+
* - `label` — a legend over the cards (visually shown).
|
|
73
|
+
* - `columns` — 1, 2 or 3 (default 2).
|
|
74
|
+
* - `readOnly`.
|
|
75
|
+
*
|
|
76
|
+
* @type {object}
|
|
77
|
+
*/
|
|
78
|
+
content: {
|
|
79
|
+
type: Object,
|
|
80
|
+
default: () => ({}),
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
/** The current detail-page object. */
|
|
84
|
+
objectData: {
|
|
85
|
+
type: Object,
|
|
86
|
+
default: null,
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
/** The current object id. */
|
|
90
|
+
objectId: {
|
|
91
|
+
type: [String, Number],
|
|
92
|
+
default: '',
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
/** OpenRegister register slug of the surface. */
|
|
96
|
+
register: {
|
|
97
|
+
type: [String, Object],
|
|
98
|
+
default: '',
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
/** OpenRegister schema slug of the surface. */
|
|
102
|
+
schema: {
|
|
103
|
+
type: [String, Object],
|
|
104
|
+
default: '',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
emits: ['saved'],
|
|
109
|
+
|
|
110
|
+
data() {
|
|
111
|
+
uid += 1
|
|
112
|
+
return {
|
|
113
|
+
groupName: `cn-radio-cards-${uid}`,
|
|
114
|
+
saved: '',
|
|
115
|
+
value: '',
|
|
116
|
+
busy: false,
|
|
117
|
+
notice: '',
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
computed: {
|
|
122
|
+
options() {
|
|
123
|
+
const list = Array.isArray(this.content.options) ? this.content.options : []
|
|
124
|
+
return list
|
|
125
|
+
.filter((option) => option !== null && option !== undefined)
|
|
126
|
+
.map((option) => (typeof option === 'object'
|
|
127
|
+
? { value: option.value, label: this.tr(String(option.label ?? option.value)), description: this.tr(option.description || '') }
|
|
128
|
+
: { value: option, label: this.tr(String(option)), description: '' }))
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
label() {
|
|
132
|
+
return this.tr(this.content.label || '')
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
columns() {
|
|
136
|
+
return [1, 3].includes(this.content.columns) ? this.content.columns : 2
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
readOnly() {
|
|
140
|
+
return this.content.readOnly === true
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
objectUrl() {
|
|
144
|
+
return objectUrlOf(this.register, this.schema, this.objectId || (this.objectData && this.objectData.id))
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
watch: {
|
|
149
|
+
objectData() {
|
|
150
|
+
this.adopt()
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
created() {
|
|
155
|
+
this.adopt()
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
methods: {
|
|
159
|
+
tr(text) {
|
|
160
|
+
if (typeof text !== 'string' || text === '') {
|
|
161
|
+
return ''
|
|
162
|
+
}
|
|
163
|
+
return typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
/** Read the choice off the object. */
|
|
167
|
+
adopt() {
|
|
168
|
+
const stored = readPath(this.objectData || {}, this.content.field)
|
|
169
|
+
this.saved = stored === undefined || stored === null ? '' : String(stored)
|
|
170
|
+
this.value = this.saved
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
isSelected(option) {
|
|
174
|
+
return this.value === String(option.value)
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Choose a card and save that one field.
|
|
179
|
+
*
|
|
180
|
+
* @param {object} option The chosen option.
|
|
181
|
+
* @return {Promise<void>}
|
|
182
|
+
* @spec openspec/changes/abstract-radio-cards/specs/radio-cards/spec.md#requirement-choosing-a-card-saves-that-one-field
|
|
183
|
+
*/
|
|
184
|
+
async choose(option) {
|
|
185
|
+
if (!this.objectUrl || !this.content.field) {
|
|
186
|
+
this.notice = this.tr('This choice cannot be saved here.')
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
this.notice = ''
|
|
190
|
+
this.busy = true
|
|
191
|
+
this.value = String(option.value)
|
|
192
|
+
try {
|
|
193
|
+
const data = await patchObject(this.objectUrl, { [this.content.field]: option.value })
|
|
194
|
+
const written = readPath(data, this.content.field)
|
|
195
|
+
this.saved = String(written !== undefined && written !== null ? written : option.value)
|
|
196
|
+
this.value = this.saved
|
|
197
|
+
/**
|
|
198
|
+
* @event saved The choice was written.
|
|
199
|
+
* @type {{field: string, value: unknown}}
|
|
200
|
+
*/
|
|
201
|
+
this.$emit('saved', { field: this.content.field, value: option.value })
|
|
202
|
+
} catch (e) {
|
|
203
|
+
this.value = this.saved
|
|
204
|
+
this.notice = serverErrorMessage(e)
|
|
205
|
+
} finally {
|
|
206
|
+
this.busy = false
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
}
|
|
211
|
+
</script>
|
|
212
|
+
|
|
213
|
+
<style scoped>
|
|
214
|
+
.cn-radio-cards {
|
|
215
|
+
display: flex;
|
|
216
|
+
flex-direction: column;
|
|
217
|
+
gap: 8px;
|
|
218
|
+
min-width: 0;
|
|
219
|
+
margin: 0;
|
|
220
|
+
padding: 0;
|
|
221
|
+
border: 0;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.cn-radio-cards__legend {
|
|
225
|
+
padding: 0;
|
|
226
|
+
margin-bottom: 6px;
|
|
227
|
+
font-weight: 600;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.cn-radio-cards__grid {
|
|
231
|
+
display: grid;
|
|
232
|
+
gap: 10px;
|
|
233
|
+
grid-template-columns: minmax(0, 1fr);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.cn-radio-cards__grid--2 {
|
|
237
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.cn-radio-cards__grid--3 {
|
|
241
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.cn-radio-cards__card {
|
|
245
|
+
position: relative;
|
|
246
|
+
display: flex;
|
|
247
|
+
align-items: flex-start;
|
|
248
|
+
gap: 10px;
|
|
249
|
+
padding: 12px 14px;
|
|
250
|
+
border: 1px solid var(--color-border-maxcontrast);
|
|
251
|
+
border-radius: var(--cn-board-control-radius, 8px);
|
|
252
|
+
background: var(--color-main-background);
|
|
253
|
+
cursor: pointer;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.cn-radio-cards__card--selected {
|
|
257
|
+
border-color: var(--color-primary-element);
|
|
258
|
+
background: var(--color-primary-element-light);
|
|
259
|
+
box-shadow: 0 0 0 1px var(--color-primary-element);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.cn-radio-cards__card:focus-within {
|
|
263
|
+
outline: 2px solid var(--color-primary-element);
|
|
264
|
+
outline-offset: 2px;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/* The real input stays in the DOM and in the accessibility tree; the dot is its picture. */
|
|
268
|
+
.cn-radio-cards__input {
|
|
269
|
+
position: absolute;
|
|
270
|
+
opacity: 0;
|
|
271
|
+
inset: 0;
|
|
272
|
+
margin: 0;
|
|
273
|
+
cursor: pointer;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.cn-radio-cards__dot {
|
|
277
|
+
flex: 0 0 auto;
|
|
278
|
+
width: 14px;
|
|
279
|
+
height: 14px;
|
|
280
|
+
margin-top: 3px;
|
|
281
|
+
box-sizing: border-box;
|
|
282
|
+
border: 1px solid var(--color-border-maxcontrast);
|
|
283
|
+
border-radius: 50%;
|
|
284
|
+
background: var(--color-main-background);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.cn-radio-cards__card--selected .cn-radio-cards__dot {
|
|
288
|
+
border: 4px solid var(--color-primary-element);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.cn-radio-cards__text {
|
|
292
|
+
display: flex;
|
|
293
|
+
flex-direction: column;
|
|
294
|
+
gap: 2px;
|
|
295
|
+
min-width: 0;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.cn-radio-cards__title {
|
|
299
|
+
font-weight: 600;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.cn-radio-cards__description {
|
|
303
|
+
font-size: 12px;
|
|
304
|
+
color: var(--color-text-maxcontrast);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
@media (max-width: 640px) {
|
|
308
|
+
.cn-radio-cards__grid--2,
|
|
309
|
+
.cn-radio-cards__grid--3 {
|
|
310
|
+
grid-template-columns: minmax(0, 1fr);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
*
|
|
5
|
+
* CnRadioCardsWidget — self-registers the `radio-cards` widget type (one
|
|
6
|
+
* choice out of a few cards with a title and a description, bound to an enum
|
|
7
|
+
* field of the current object and saved on selection) into the shared
|
|
8
|
+
* dashboardWidgetRegistry at module load.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import CnRadioCardsWidget from './CnRadioCardsWidget.vue'
|
|
12
|
+
import { registerDashboardWidget } from '../CnWidgetGrid/dashboardWidgetRegistry.js'
|
|
13
|
+
|
|
14
|
+
registerDashboardWidget('radio-cards', {
|
|
15
|
+
renderer: CnRadioCardsWidget,
|
|
16
|
+
form: null,
|
|
17
|
+
defaultContent: {
|
|
18
|
+
field: '',
|
|
19
|
+
options: [],
|
|
20
|
+
},
|
|
21
|
+
displayName: 'Radio cards',
|
|
22
|
+
icon: 'RadioboxMarked',
|
|
23
|
+
surfaces: ['detail-page'],
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export { CnRadioCardsWidget }
|
|
27
|
+
export default CnRadioCardsWidget
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
class="cn-record-list__row"
|
|
31
31
|
:class="{ 'cn-record-list__row--link': item.tag !== 'div' }"
|
|
32
32
|
v-bind="item.attrs">
|
|
33
|
+
<span v-if="item.initials"
|
|
34
|
+
class="cn-record-list__avatar"
|
|
35
|
+
aria-hidden="true"
|
|
36
|
+
data-testid="cn-record-list-avatar">{{ item.initials }}</span>
|
|
33
37
|
<span class="cn-record-list__main">
|
|
34
38
|
<span v-if="item.title" class="cn-record-list__title">{{ item.title }}</span>
|
|
35
39
|
<span v-if="item.meta" class="cn-record-list__meta">{{ item.meta }}</span>
|
|
@@ -81,6 +85,8 @@ import CnIcon from '../CnIcon/CnIcon.vue'
|
|
|
81
85
|
import CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'
|
|
82
86
|
import { isBadgeVariant } from '../../utils/badgeVariants.js'
|
|
83
87
|
import { fillTemplate, firstFilled } from '../../utils/fillTemplate.js'
|
|
88
|
+
import { initialsOf } from '../../utils/initials.js'
|
|
89
|
+
import { pageFilter, pageSort } from '../../utils/pageControls.js'
|
|
84
90
|
import { readPath } from '../../utils/readPath.js'
|
|
85
91
|
import { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
86
92
|
|
|
@@ -108,7 +114,8 @@ function unwrap(value) {
|
|
|
108
114
|
* objects filtered on a reference to this record. The source keys are those of
|
|
109
115
|
* `object-list` (`register`, `schema`, `filter` with the `@objectId` /
|
|
110
116
|
* `@object.<field>` / `@workspace.<key>` tokens, `sort`, `limit`, `extend`,
|
|
111
|
-
* `rowRoute`);
|
|
117
|
+
* `rowRoute`, `pageControls`); `avatar` puts a circle with the initials of a name
|
|
118
|
+
* template before the title; what differs is the drawing: `presentation` is `rows`, `feed`
|
|
112
119
|
* or `inline`, and each line is a template (`"{identifier} · {caseTypeName}"`,
|
|
113
120
|
* `"Termijn {deadline|date}"`).
|
|
114
121
|
*
|
|
@@ -188,8 +195,8 @@ export default {
|
|
|
188
195
|
|
|
189
196
|
/** @return {object} The filter with its tokens resolved. */
|
|
190
197
|
resolvedFilter() {
|
|
191
|
-
const filter = this.content?.filter
|
|
192
|
-
return dropOptionalUnresolved(resolveFilterTokens(filter
|
|
198
|
+
const filter = pageFilter(this.content?.pageControls, this.tokenCtx.workspace, this.content?.filter)
|
|
199
|
+
return dropOptionalUnresolved(resolveFilterTokens(filter, this.tokenCtx))
|
|
193
200
|
},
|
|
194
201
|
|
|
195
202
|
/** @return {boolean} A required token (the record id, say) has no value yet. */
|
|
@@ -197,6 +204,11 @@ export default {
|
|
|
197
204
|
return hasUnresolvedTokens(this.resolvedFilter)
|
|
198
205
|
},
|
|
199
206
|
|
|
207
|
+
/** @return {object|null} The sort: the page's sort control when the widget listens to one, else its own. */
|
|
208
|
+
effectiveSort() {
|
|
209
|
+
return pageSort(this.content?.pageControls, this.tokenCtx.workspace, this.content?.sort)
|
|
210
|
+
},
|
|
211
|
+
|
|
200
212
|
/** @return {number} The fetch cap. */
|
|
201
213
|
limit() {
|
|
202
214
|
const value = Number(this.content?.limit)
|
|
@@ -206,7 +218,7 @@ export default {
|
|
|
206
218
|
/** @return {string} What a fetch depends on; a change fetches again. */
|
|
207
219
|
requestKey() {
|
|
208
220
|
const c = this.content || {}
|
|
209
|
-
return JSON.stringify([c.register, c.schema, this.resolvedFilter,
|
|
221
|
+
return JSON.stringify([c.register, c.schema, this.resolvedFilter, this.effectiveSort, this.limit, c.extend, this.waiting])
|
|
210
222
|
},
|
|
211
223
|
|
|
212
224
|
/** @return {Array<object>} The rows ready to draw. */
|
|
@@ -218,6 +230,7 @@ export default {
|
|
|
218
230
|
key: (row && (row.id || row['@self']?.id)) || `row-${index}`,
|
|
219
231
|
tag: link ? 'router-link' : 'div',
|
|
220
232
|
attrs: link ? { to: link } : {},
|
|
233
|
+
initials: this.initialsFor(row),
|
|
221
234
|
title: this.fill(c.title, row),
|
|
222
235
|
meta: this.fill(c.meta, row),
|
|
223
236
|
text: this.fill(c.text, row),
|
|
@@ -299,6 +312,20 @@ export default {
|
|
|
299
312
|
return typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text
|
|
300
313
|
},
|
|
301
314
|
|
|
315
|
+
/**
|
|
316
|
+
* The initials of the row's avatar (`avatar`: a name template such as
|
|
317
|
+
* `"{client.name}"`, or `{ text }`), or '' without one.
|
|
318
|
+
*
|
|
319
|
+
* @param {object} row The row.
|
|
320
|
+
* @return {string} One or two letters.
|
|
321
|
+
* @spec openspec/changes/screens-record-list-avatar/specs/record-list-avatar/spec.md#requirement-a-record-list-row-can-carry-an-avatar
|
|
322
|
+
*/
|
|
323
|
+
initialsFor(row) {
|
|
324
|
+
const avatar = this.content?.avatar
|
|
325
|
+
const template = typeof avatar === 'string' ? avatar : (avatar && typeof avatar === 'object' ? avatar.text : '')
|
|
326
|
+
return initialsOf(this.fill(template, row))
|
|
327
|
+
},
|
|
328
|
+
|
|
302
329
|
/**
|
|
303
330
|
* Fill a template from a row, after translating it.
|
|
304
331
|
*
|
|
@@ -422,8 +449,9 @@ export default {
|
|
|
422
449
|
])
|
|
423
450
|
const url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', { register: c.register, schema: c.schema })
|
|
424
451
|
const params = { _limit: this.limit }
|
|
425
|
-
|
|
426
|
-
|
|
452
|
+
const sort = this.effectiveSort
|
|
453
|
+
if (sort && typeof sort === 'object' && sort.field) {
|
|
454
|
+
params[`_order[${sort.field}]`] = sort.dir === 'desc' ? 'desc' : 'asc'
|
|
427
455
|
}
|
|
428
456
|
if (Array.isArray(c.extend)) {
|
|
429
457
|
const extend = c.extend.filter((e) => typeof e === 'string' && e !== '')
|
|
@@ -497,6 +525,20 @@ export default {
|
|
|
497
525
|
text-decoration: underline;
|
|
498
526
|
}
|
|
499
527
|
|
|
528
|
+
.cn-record-list__avatar {
|
|
529
|
+
align-items: center;
|
|
530
|
+
background: var(--color-background-hover);
|
|
531
|
+
border-radius: 50%;
|
|
532
|
+
color: var(--color-main-text);
|
|
533
|
+
display: flex;
|
|
534
|
+
flex: none;
|
|
535
|
+
font-size: 12px;
|
|
536
|
+
font-weight: 700;
|
|
537
|
+
height: 36px;
|
|
538
|
+
justify-content: center;
|
|
539
|
+
width: 36px;
|
|
540
|
+
}
|
|
541
|
+
|
|
500
542
|
.cn-record-list__main {
|
|
501
543
|
display: flex;
|
|
502
544
|
flex: 1 1 auto;
|
|
@@ -600,4 +642,11 @@ a.cn-record-list__inline-title {
|
|
|
600
642
|
.cn-detail-page__side .cn-record-list__chevron {
|
|
601
643
|
display: none;
|
|
602
644
|
}
|
|
645
|
+
|
|
646
|
+
/* A row is a touch target of at least 44px on a phone. */
|
|
647
|
+
@media (max-width: 640px) {
|
|
648
|
+
.cn-record-list__row {
|
|
649
|
+
min-height: 44px;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
603
652
|
</style>
|