@conduction/nextcloud-vue 2.56.0-beta.30 → 2.56.0-beta.32
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
|
@@ -147,6 +147,61 @@
|
|
|
147
147
|
</li>
|
|
148
148
|
</ul>
|
|
149
149
|
|
|
150
|
+
<!-- The host's filter: per declared column, a chip per value in use
|
|
151
|
+
with how many files carry it. Chips narrow the list. -->
|
|
152
|
+
<div v-if="facetGroups.length > 0 && !loading && error === ''"
|
|
153
|
+
class="cn-files-browser__facets"
|
|
154
|
+
data-testid="cn-files-browser-facets">
|
|
155
|
+
<div v-for="facet in facetGroups"
|
|
156
|
+
:key="facet.key"
|
|
157
|
+
class="cn-files-browser__facet"
|
|
158
|
+
role="group"
|
|
159
|
+
:aria-label="t('nextcloud-vue', 'Filter by {column}', { column: facet.label })">
|
|
160
|
+
<button v-for="option in facet.values"
|
|
161
|
+
:key="option.value"
|
|
162
|
+
type="button"
|
|
163
|
+
class="cn-files-browser__facet-chip"
|
|
164
|
+
:class="{ 'cn-files-browser__facet-chip--active': option.active }"
|
|
165
|
+
:aria-pressed="option.active ? 'true' : 'false'"
|
|
166
|
+
data-testid="cn-files-browser-facet-chip"
|
|
167
|
+
@click="toggleFacet(facet.key, option.value)">
|
|
168
|
+
{{ option.value }}
|
|
169
|
+
<strong class="cn-files-browser__facet-count">{{ option.count }}</strong>
|
|
170
|
+
</button>
|
|
171
|
+
</div>
|
|
172
|
+
<NcButton v-if="facetActive"
|
|
173
|
+
variant="tertiary"
|
|
174
|
+
data-testid="cn-files-browser-facets-clear"
|
|
175
|
+
@click="clearFacets">
|
|
176
|
+
{{ t('nextcloud-vue', 'Clear filters') }}
|
|
177
|
+
</NcButton>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<!-- The host's bulk actions, once something is selected. -->
|
|
181
|
+
<div v-if="bulkEnabled && selectedIds.length > 0"
|
|
182
|
+
class="cn-files-browser__bulk-bar"
|
|
183
|
+
role="toolbar"
|
|
184
|
+
:aria-label="t('nextcloud-vue', 'Actions for the selected files')"
|
|
185
|
+
data-testid="cn-files-browser-bulk-bar">
|
|
186
|
+
<span class="cn-files-browser__bulk-count" aria-live="polite">
|
|
187
|
+
{{ t('nextcloud-vue', '{count} selected', { count: selectedIds.length }) }}
|
|
188
|
+
</span>
|
|
189
|
+
<NcButton v-for="action in bulkActions"
|
|
190
|
+
:key="action.id"
|
|
191
|
+
:data-testid="`cn-files-browser-bulk-action-${action.id}`"
|
|
192
|
+
@click="runBulkAction(action)">
|
|
193
|
+
<template #icon>
|
|
194
|
+
<CnIcon :name="action.icon || 'CheckboxMultipleMarkedOutline'" :size="20" />
|
|
195
|
+
</template>
|
|
196
|
+
{{ action.label }}
|
|
197
|
+
</NcButton>
|
|
198
|
+
<NcButton variant="tertiary"
|
|
199
|
+
data-testid="cn-files-browser-bulk-clear"
|
|
200
|
+
@click="clearSelection">
|
|
201
|
+
{{ t('nextcloud-vue', 'Clear selection') }}
|
|
202
|
+
</NcButton>
|
|
203
|
+
</div>
|
|
204
|
+
|
|
150
205
|
<NcLoadingIcon v-if="loading" :size="32" class="cn-files-browser__loading" />
|
|
151
206
|
|
|
152
207
|
<CnEmptyContent v-else-if="error !== ''"
|
|
@@ -181,6 +236,16 @@
|
|
|
181
236
|
<table v-else class="cn-files-browser__table" data-testid="cn-files-browser-table">
|
|
182
237
|
<thead>
|
|
183
238
|
<tr>
|
|
239
|
+
<th v-if="bulkEnabled" class="cn-files-browser__col-select" scope="col">
|
|
240
|
+
<input type="checkbox"
|
|
241
|
+
class="cn-files-browser__select"
|
|
242
|
+
:checked="allSelected"
|
|
243
|
+
:indeterminate.prop="someSelected && !allSelected"
|
|
244
|
+
:disabled="selectableIds.length === 0"
|
|
245
|
+
:aria-label="t('nextcloud-vue', 'Select all files')"
|
|
246
|
+
data-testid="cn-files-browser-select-all"
|
|
247
|
+
@change="toggleAll($event.target.checked)">
|
|
248
|
+
</th>
|
|
184
249
|
<th class="cn-files-browser__col-icon" scope="col">
|
|
185
250
|
<span class="hidden-visually">{{ t('nextcloud-vue', 'Type') }}</span>
|
|
186
251
|
</th>
|
|
@@ -207,126 +272,155 @@
|
|
|
207
272
|
</tr>
|
|
208
273
|
</thead>
|
|
209
274
|
<tbody>
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
<
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
<!--
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
<
|
|
275
|
+
<!-- A heading row per group when the host groups on a column; the
|
|
276
|
+
destructured entry is either a group or a node. -->
|
|
277
|
+
<template v-for="{ key, group, node } in displayRows" :key="key">
|
|
278
|
+
<tr v-if="group"
|
|
279
|
+
class="cn-files-browser__group"
|
|
280
|
+
data-testid="cn-files-browser-group">
|
|
281
|
+
<th :colspan="columnCount" scope="rowgroup" class="cn-files-browser__group-heading">
|
|
282
|
+
{{ group.label }}
|
|
283
|
+
<span class="cn-files-browser__group-count">{{ group.count }}</span>
|
|
284
|
+
</th>
|
|
285
|
+
</tr>
|
|
286
|
+
<tr
|
|
287
|
+
v-else
|
|
288
|
+
:key="key"
|
|
289
|
+
class="cn-files-browser__row"
|
|
290
|
+
:class="{ 'cn-files-browser__row--folder': isFolder(node) }"
|
|
291
|
+
data-testid="cn-files-browser-row"
|
|
292
|
+
:data-name="node.basename"
|
|
293
|
+
@click="onRowClick(node, $event)"
|
|
294
|
+
@mousedown="preventMiddleClickAutoscroll"
|
|
295
|
+
@auxclick="onRowAuxClick(node, $event)">
|
|
296
|
+
<td v-if="bulkEnabled"
|
|
297
|
+
class="cn-files-browser__col-select"
|
|
298
|
+
@click.stop
|
|
299
|
+
@auxclick.stop>
|
|
300
|
+
<input v-if="!isFolder(node)"
|
|
301
|
+
type="checkbox"
|
|
302
|
+
class="cn-files-browser__select"
|
|
303
|
+
:checked="isSelected(node)"
|
|
304
|
+
:aria-label="t('nextcloud-vue', 'Select {name}', { name: node.basename })"
|
|
305
|
+
data-testid="cn-files-browser-select"
|
|
306
|
+
@change="toggleSelected(node, $event.target.checked)">
|
|
307
|
+
</td>
|
|
308
|
+
<td class="cn-files-browser__col-icon">
|
|
309
|
+
<img
|
|
310
|
+
v-if="previewUrlFor(node)"
|
|
311
|
+
class="cn-files-browser__thumb"
|
|
312
|
+
:src="previewUrlFor(node)"
|
|
313
|
+
alt=""
|
|
314
|
+
loading="lazy"
|
|
315
|
+
@error="onPreviewError(node)">
|
|
316
|
+
<img
|
|
317
|
+
v-else-if="mimeIconFor(node)"
|
|
318
|
+
class="cn-files-browser__mime"
|
|
319
|
+
:src="mimeIconFor(node)"
|
|
320
|
+
alt="">
|
|
321
|
+
<FolderOutline v-else-if="isFolder(node)" :size="32" />
|
|
322
|
+
<FileOutline v-else :size="32" />
|
|
323
|
+
</td>
|
|
324
|
+
<td v-for="column in renderedColumns"
|
|
325
|
+
:key="column.key"
|
|
326
|
+
:class="`cn-files-browser__col-${column.key}`"
|
|
327
|
+
:data-testid="`cn-files-browser-cell-${column.key}`">
|
|
328
|
+
<span v-if="column.key === 'name'" class="cn-files-browser__name">{{ node.basename }}</span>
|
|
329
|
+
<template v-else-if="column.key === 'size'">
|
|
330
|
+
{{ isFolder(node) ? '' : formatSize(node.size) }}
|
|
331
|
+
</template>
|
|
332
|
+
<NcDateTime v-else-if="column.key === 'modified'" :timestamp="node.mtime" :ignoreSeconds="true" />
|
|
333
|
+
<!-- Everything the host declared renders through the same
|
|
334
|
+
cell renderer the tables use, so a `formatter` means
|
|
335
|
+
here what it means there. -->
|
|
336
|
+
<CnCellRenderer v-else
|
|
337
|
+
:value="cellValue(column, node)"
|
|
338
|
+
:formatter="column.formatter || null"
|
|
339
|
+
:formatterOptions="column.formatterOptions || null"
|
|
340
|
+
:widget="column.widget || null"
|
|
341
|
+
:row="node" />
|
|
342
|
+
</td>
|
|
343
|
+
<td class="cn-files-browser__col-actions" @click.stop @auxclick.stop>
|
|
344
|
+
<NcActions :forceMenu="true" :ariaLabel="t('nextcloud-vue', 'Actions for {name}', { name: node.basename })">
|
|
272
345
|
<NcActionButton
|
|
273
|
-
v-for="action in
|
|
274
|
-
:key="
|
|
346
|
+
v-for="action in actionsFor(node)"
|
|
347
|
+
:key="action.id"
|
|
275
348
|
:closeAfterClick="true"
|
|
276
|
-
:data-testid="`cn-files-browser-
|
|
277
|
-
@click="
|
|
349
|
+
:data-testid="`cn-files-browser-action-${action.id}`"
|
|
350
|
+
@click="run(action, node)">
|
|
278
351
|
<template #icon>
|
|
279
|
-
<
|
|
352
|
+
<NcIconSvgWrapper :svg="iconOf(action, node)" />
|
|
280
353
|
</template>
|
|
281
|
-
{{ action
|
|
354
|
+
{{ labelOf(action, node) }}
|
|
282
355
|
</NcActionButton>
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
</template>
|
|
299
|
-
{{ downloadLabel }}
|
|
300
|
-
</NcActionLink>
|
|
301
|
-
<NcActionButton v-if="source && canChange"
|
|
302
|
-
:closeAfterClick="true"
|
|
303
|
-
data-testid="cn-files-browser-action-delete"
|
|
304
|
-
@click="askRemove(node)">
|
|
305
|
-
<template #icon>
|
|
306
|
-
<TrashCanOutline :size="20" />
|
|
307
|
-
</template>
|
|
308
|
-
{{ deleteLabel }}
|
|
309
|
-
</NcActionButton>
|
|
310
|
-
<NcActionButton v-if="canChange"
|
|
311
|
-
:closeAfterClick="true"
|
|
312
|
-
data-testid="cn-files-browser-action-rename"
|
|
313
|
-
@click="askRename(node)">
|
|
314
|
-
<template #icon>
|
|
315
|
-
<Pencil :size="20" />
|
|
316
|
-
</template>
|
|
317
|
-
{{ renameLabel }}
|
|
318
|
-
</NcActionButton>
|
|
319
|
-
<NcActionLink
|
|
320
|
-
v-if="node.fileid && !source"
|
|
321
|
-
:href="permalink(node)"
|
|
322
|
-
target="_blank"
|
|
323
|
-
:closeAfterClick="true">
|
|
324
|
-
<template #icon>
|
|
325
|
-
<OpenInNew :size="20" />
|
|
356
|
+
<!-- The host's own actions on a file (never on a folder): declared
|
|
357
|
+
in its manifest, dispatched through the page's action runner
|
|
358
|
+
with the node's file id, name and path merged in. -->
|
|
359
|
+
<template v-if="!isFolder(node)">
|
|
360
|
+
<NcActionButton
|
|
361
|
+
v-for="action in hostActionsFor(node)"
|
|
362
|
+
:key="`host-${action.id}`"
|
|
363
|
+
:closeAfterClick="true"
|
|
364
|
+
:data-testid="`cn-files-browser-host-action-${action.id}`"
|
|
365
|
+
@click="runHostAction(action, node)">
|
|
366
|
+
<template #icon>
|
|
367
|
+
<CnIcon :name="action.icon || 'FileDocumentEditOutline'" :size="20" />
|
|
368
|
+
</template>
|
|
369
|
+
{{ action.label }}
|
|
370
|
+
</NcActionButton>
|
|
326
371
|
</template>
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
372
|
+
<NcActionSeparator v-if="actionsFor(node).length > 0 || (!isFolder(node) && hostActionsFor(node).length > 0)" />
|
|
373
|
+
<!-- The Files app's own rename is its list's inline input,
|
|
374
|
+
which is not here; this one is a dialog over a DAV move. -->
|
|
375
|
+
<!-- With a `source`, the Files app's actions (which act over
|
|
376
|
+
WebDAV) are not offered; download and delete go through
|
|
377
|
+
the source instead. -->
|
|
378
|
+
<NcActionLink
|
|
379
|
+
v-if="source && sourceDownloadUrl(node)"
|
|
380
|
+
:href="sourceDownloadUrl(node)"
|
|
381
|
+
:download="node.basename"
|
|
382
|
+
:closeAfterClick="true"
|
|
383
|
+
data-testid="cn-files-browser-action-download">
|
|
384
|
+
<template #icon>
|
|
385
|
+
<Download :size="20" />
|
|
386
|
+
</template>
|
|
387
|
+
{{ downloadLabel }}
|
|
388
|
+
</NcActionLink>
|
|
389
|
+
<NcActionButton v-if="source && canChange"
|
|
390
|
+
:closeAfterClick="true"
|
|
391
|
+
data-testid="cn-files-browser-action-delete"
|
|
392
|
+
@click="askRemove(node)">
|
|
393
|
+
<template #icon>
|
|
394
|
+
<TrashCanOutline :size="20" />
|
|
395
|
+
</template>
|
|
396
|
+
{{ deleteLabel }}
|
|
397
|
+
</NcActionButton>
|
|
398
|
+
<NcActionButton v-if="canChange"
|
|
399
|
+
:closeAfterClick="true"
|
|
400
|
+
data-testid="cn-files-browser-action-rename"
|
|
401
|
+
@click="askRename(node)">
|
|
402
|
+
<template #icon>
|
|
403
|
+
<Pencil :size="20" />
|
|
404
|
+
</template>
|
|
405
|
+
{{ renameLabel }}
|
|
406
|
+
</NcActionButton>
|
|
407
|
+
<NcActionLink
|
|
408
|
+
v-if="node.fileid && !source"
|
|
409
|
+
:href="permalink(node)"
|
|
410
|
+
target="_blank"
|
|
411
|
+
:closeAfterClick="true">
|
|
412
|
+
<template #icon>
|
|
413
|
+
<OpenInNew :size="20" />
|
|
414
|
+
</template>
|
|
415
|
+
{{ showInFilesLabel }}
|
|
416
|
+
</NcActionLink>
|
|
417
|
+
</NcActions>
|
|
418
|
+
</td>
|
|
419
|
+
</tr>
|
|
420
|
+
</template>
|
|
421
|
+
<tr v-if="facetActive && filteredFiles.length === 0" class="cn-files-browser__no-match-row">
|
|
422
|
+
<td :colspan="columnCount" class="cn-files-browser__no-match" data-testid="cn-files-browser-no-match">
|
|
423
|
+
{{ t('nextcloud-vue', 'No files match the filter.') }}
|
|
330
424
|
</td>
|
|
331
425
|
</tr>
|
|
332
426
|
<!-- Rows that are not nodes of this folder: documents the host
|
|
@@ -341,6 +435,7 @@
|
|
|
341
435
|
@click="openLinked(item)"
|
|
342
436
|
@mousedown="preventMiddleClickAutoscroll"
|
|
343
437
|
@auxclick="onLinkedAuxClick(item, $event)">
|
|
438
|
+
<td v-if="bulkEnabled" class="cn-files-browser__col-select" />
|
|
344
439
|
<td class="cn-files-browser__col-icon">
|
|
345
440
|
<img
|
|
346
441
|
v-if="mimeIconFor({ mime: item.mime })"
|
|
@@ -548,10 +643,13 @@ import CnIcon from '../CnIcon/CnIcon.vue'
|
|
|
548
643
|
import { readUserPreference, writeUserPreference } from '../../composables/useUserPreferences.js'
|
|
549
644
|
import { dispatchAction } from '../../utils/actionsDispatcher.js'
|
|
550
645
|
import { isNewTabClick, isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'
|
|
551
|
-
import { ACTIONS_NEEDING_THE_FILES_PAGE, crumbsFor, joinPath } from './filesBrowser.js'
|
|
646
|
+
import { ACTIONS_NEEDING_THE_FILES_PAGE, crumbsFor, hostActionApplies, joinPath } from './filesBrowser.js'
|
|
552
647
|
import {
|
|
553
648
|
attributePropertiesFor,
|
|
649
|
+
facetCounts,
|
|
554
650
|
fileColumnValue,
|
|
651
|
+
groupNodes,
|
|
652
|
+
nodeMatchesFacets,
|
|
555
653
|
normaliseFileColumns,
|
|
556
654
|
sortNodesByColumn,
|
|
557
655
|
visibleFileColumns,
|
|
@@ -631,9 +729,11 @@ export default {
|
|
|
631
729
|
* dispatched through the page's action runner with the node's
|
|
632
730
|
* `fileId`, `fileName` and `path` merged into an `open-modal` action's
|
|
633
731
|
* props (or appended as the node to a `handler` action's args). Folders
|
|
634
|
-
* get none. `icon` is an MDI icon name.
|
|
732
|
+
* get none. `icon` is an MDI icon name. `visibleIf` offers the action
|
|
733
|
+
* only on some files: `extension` (a list, no case) and `mime` (a list;
|
|
734
|
+
* `image/` or `image/*` names the family), each that is named must hold.
|
|
635
735
|
*
|
|
636
|
-
* @type {Array<{id: string, label: string, icon?: string, type?: string, target?: string, props?: object, handler?: string, args?: Array}>}
|
|
736
|
+
* @type {Array<{id: string, label: string, icon?: string, type?: string, target?: string, props?: object, handler?: string, args?: Array, visibleIf?: {extension?: string[], mime?: string[]}}>}
|
|
637
737
|
*/
|
|
638
738
|
rowActions: {
|
|
639
739
|
type: Array,
|
|
@@ -853,6 +953,52 @@ export default {
|
|
|
853
953
|
default: 'files-browser-columns',
|
|
854
954
|
},
|
|
855
955
|
|
|
956
|
+
/**
|
|
957
|
+
* The host's actions on a selection of files, declared like
|
|
958
|
+
* `rowActions`. With at least one, every file row (never a folder or a
|
|
959
|
+
* linked row) gets a checkbox, the header a select-all, and a bar with
|
|
960
|
+
* these actions shows while files are selected. An `open-modal`
|
|
961
|
+
* action's props gain `files` (each `{ fileId, fileName, path }`) and
|
|
962
|
+
* `fileIds`; a `handler` action's args gain the selected nodes. The
|
|
963
|
+
* selection clears after an action and when another folder opens.
|
|
964
|
+
*
|
|
965
|
+
* @type {Array<{id: string, label: string, icon?: string, type?: string, target?: string, props?: object, handler?: string, args?: Array}>}
|
|
966
|
+
*/
|
|
967
|
+
bulkActions: {
|
|
968
|
+
type: Array,
|
|
969
|
+
default: () => [],
|
|
970
|
+
},
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* The key of a declared column to group the files on: a heading row
|
|
974
|
+
* per value, in the order each is first met, with its count. Files
|
|
975
|
+
* with no value come last under `groupEmptyLabel`; folders stay above.
|
|
976
|
+
* Empty (the default) does not group.
|
|
977
|
+
*/
|
|
978
|
+
groupBy: {
|
|
979
|
+
type: String,
|
|
980
|
+
default: '',
|
|
981
|
+
},
|
|
982
|
+
|
|
983
|
+
/** The heading of the group of files with no value in the `groupBy` column. */
|
|
984
|
+
groupEmptyLabel: {
|
|
985
|
+
type: String,
|
|
986
|
+
default: () => t('nextcloud-vue', 'Other'),
|
|
987
|
+
},
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Keys of declared columns to filter on. Each gets a chip per value in
|
|
991
|
+
* use among the folder's files (a list counts per entry) with how many
|
|
992
|
+
* files carry it; chosen chips keep the files with any chosen value of
|
|
993
|
+
* that column, across every column with a choice.
|
|
994
|
+
*
|
|
995
|
+
* @type {string[]}
|
|
996
|
+
*/
|
|
997
|
+
facets: {
|
|
998
|
+
type: Array,
|
|
999
|
+
default: () => [],
|
|
1000
|
+
},
|
|
1001
|
+
|
|
856
1002
|
/** Label of the toolbar's Columns chooser. */
|
|
857
1003
|
columnsLabel: {
|
|
858
1004
|
type: String,
|
|
@@ -875,6 +1021,10 @@ export default {
|
|
|
875
1021
|
sortAsc: true,
|
|
876
1022
|
/** The host's per-file data for `row` columns, by file id. */
|
|
877
1023
|
resolvedRowData: null,
|
|
1024
|
+
/** File ids (as strings) of the selected files. */
|
|
1025
|
+
selectedIds: [],
|
|
1026
|
+
/** Chosen facet values by column key. */
|
|
1027
|
+
facetSelections: {},
|
|
878
1028
|
/**
|
|
879
1029
|
* The column keys this user chose to see, or null while they have
|
|
880
1030
|
* chosen nothing. Never a source of membership: it is intersected
|
|
@@ -1005,6 +1155,149 @@ export default {
|
|
|
1005
1155
|
return visibleFileColumns(this.declaredColumns, this.visibleColumnKeys)
|
|
1006
1156
|
},
|
|
1007
1157
|
|
|
1158
|
+
/**
|
|
1159
|
+
* The declared columns by key, for grouping and facets.
|
|
1160
|
+
*
|
|
1161
|
+
* @return {object} Resolved columns by key.
|
|
1162
|
+
*/
|
|
1163
|
+
columnsByKey() {
|
|
1164
|
+
const byKey = {}
|
|
1165
|
+
this.declaredColumns.forEach((column) => {
|
|
1166
|
+
byKey[column.key] = column
|
|
1167
|
+
})
|
|
1168
|
+
return byKey
|
|
1169
|
+
},
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* How many cells a row has, for a heading or message spanning the row.
|
|
1173
|
+
*
|
|
1174
|
+
* @return {number} The column count.
|
|
1175
|
+
*/
|
|
1176
|
+
columnCount() {
|
|
1177
|
+
return this.renderedColumns.length + 2 + (this.bulkEnabled ? 1 : 0)
|
|
1178
|
+
},
|
|
1179
|
+
|
|
1180
|
+
/**
|
|
1181
|
+
* Whether the host gave bulk actions, which turns on the selection.
|
|
1182
|
+
*
|
|
1183
|
+
* @return {boolean} True with at least one bulk action.
|
|
1184
|
+
* @spec openspec/changes/files-browser-hosts-a-documents-list/specs/files-browser/spec.md
|
|
1185
|
+
*/
|
|
1186
|
+
bulkEnabled() {
|
|
1187
|
+
return Array.isArray(this.bulkActions) && this.bulkActions.length > 0
|
|
1188
|
+
},
|
|
1189
|
+
|
|
1190
|
+
/**
|
|
1191
|
+
* The listed folders, in display order.
|
|
1192
|
+
*
|
|
1193
|
+
* @return {Array<object>} The folder nodes.
|
|
1194
|
+
*/
|
|
1195
|
+
folderNodes() {
|
|
1196
|
+
return this.sorted.filter((node) => this.isFolder(node))
|
|
1197
|
+
},
|
|
1198
|
+
|
|
1199
|
+
/**
|
|
1200
|
+
* The listed files, in display order, before the filter.
|
|
1201
|
+
*
|
|
1202
|
+
* @return {Array<object>} The file nodes.
|
|
1203
|
+
*/
|
|
1204
|
+
fileNodes() {
|
|
1205
|
+
return this.sorted.filter((node) => !this.isFolder(node))
|
|
1206
|
+
},
|
|
1207
|
+
|
|
1208
|
+
/**
|
|
1209
|
+
* Whether any facet value is chosen.
|
|
1210
|
+
*
|
|
1211
|
+
* @return {boolean} True when the filter narrows the list.
|
|
1212
|
+
*/
|
|
1213
|
+
facetActive() {
|
|
1214
|
+
return Object.values(this.facetSelections).some((values) => Array.isArray(values) && values.length > 0)
|
|
1215
|
+
},
|
|
1216
|
+
|
|
1217
|
+
/**
|
|
1218
|
+
* The chips: per facet column, the values in use with their counts.
|
|
1219
|
+
*
|
|
1220
|
+
* @return {Array<{key: string, label: string, values: Array<{value: string, count: number, active: boolean}>}>} The facets.
|
|
1221
|
+
* @spec openspec/changes/files-browser-hosts-a-documents-list/specs/files-browser/spec.md
|
|
1222
|
+
*/
|
|
1223
|
+
facetGroups() {
|
|
1224
|
+
const keys = Array.isArray(this.facets) ? this.facets : []
|
|
1225
|
+
return keys
|
|
1226
|
+
.map((key) => this.columnsByKey[key])
|
|
1227
|
+
.filter(Boolean)
|
|
1228
|
+
.map((column) => {
|
|
1229
|
+
const chosen = this.facetSelections[column.key] || []
|
|
1230
|
+
return {
|
|
1231
|
+
key: column.key,
|
|
1232
|
+
label: this.columnLabel(column),
|
|
1233
|
+
values: facetCounts(this.fileNodes, column, this.resolvedRowData)
|
|
1234
|
+
.map((entry) => ({ ...entry, active: chosen.includes(entry.value) })),
|
|
1235
|
+
}
|
|
1236
|
+
})
|
|
1237
|
+
.filter((facet) => facet.values.length > 0)
|
|
1238
|
+
},
|
|
1239
|
+
|
|
1240
|
+
/**
|
|
1241
|
+
* The files the filter keeps.
|
|
1242
|
+
*
|
|
1243
|
+
* @return {Array<object>} The file nodes.
|
|
1244
|
+
*/
|
|
1245
|
+
filteredFiles() {
|
|
1246
|
+
if (!this.facetActive) {
|
|
1247
|
+
return this.fileNodes
|
|
1248
|
+
}
|
|
1249
|
+
return this.fileNodes.filter((node) => nodeMatchesFacets(node, this.facetSelections, this.columnsByKey, this.resolvedRowData))
|
|
1250
|
+
},
|
|
1251
|
+
|
|
1252
|
+
/**
|
|
1253
|
+
* What the table body renders, in order: folders, then the files,
|
|
1254
|
+
* under a heading per group when the host groups.
|
|
1255
|
+
*
|
|
1256
|
+
* @return {Array<{key: string, group?: object, node?: object}>} The rows.
|
|
1257
|
+
* @spec openspec/changes/files-browser-hosts-a-documents-list/specs/files-browser/spec.md
|
|
1258
|
+
*/
|
|
1259
|
+
displayRows() {
|
|
1260
|
+
const rows = this.folderNodes.map((node) => ({ key: node.source, node }))
|
|
1261
|
+
const column = this.groupBy ? this.columnsByKey[this.groupBy] : null
|
|
1262
|
+
if (!column) {
|
|
1263
|
+
this.filteredFiles.forEach((node) => rows.push({ key: node.source, node }))
|
|
1264
|
+
return rows
|
|
1265
|
+
}
|
|
1266
|
+
groupNodes(this.filteredFiles, column, this.resolvedRowData).forEach((group) => {
|
|
1267
|
+
const label = group.value === null ? this.groupEmptyLabel : group.value
|
|
1268
|
+
rows.push({ key: `group-${group.value === null ? '' : group.value}`, group: { label, count: group.nodes.length } })
|
|
1269
|
+
group.nodes.forEach((node) => rows.push({ key: node.source, node }))
|
|
1270
|
+
})
|
|
1271
|
+
return rows
|
|
1272
|
+
},
|
|
1273
|
+
|
|
1274
|
+
/**
|
|
1275
|
+
* The ids of the files a select-all takes: the files the filter keeps.
|
|
1276
|
+
*
|
|
1277
|
+
* @return {string[]} File ids as strings.
|
|
1278
|
+
*/
|
|
1279
|
+
selectableIds() {
|
|
1280
|
+
return this.filteredFiles.map((node) => String(node.fileid))
|
|
1281
|
+
},
|
|
1282
|
+
|
|
1283
|
+
/**
|
|
1284
|
+
* Whether every selectable file is selected.
|
|
1285
|
+
*
|
|
1286
|
+
* @return {boolean} True when all are.
|
|
1287
|
+
*/
|
|
1288
|
+
allSelected() {
|
|
1289
|
+
return this.selectableIds.length > 0 && this.selectableIds.every((id) => this.selectedIds.includes(id))
|
|
1290
|
+
},
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
* Whether some file is selected.
|
|
1294
|
+
*
|
|
1295
|
+
* @return {boolean} True when one or more are.
|
|
1296
|
+
*/
|
|
1297
|
+
someSelected() {
|
|
1298
|
+
return this.selectedIds.length > 0
|
|
1299
|
+
},
|
|
1300
|
+
|
|
1008
1301
|
/**
|
|
1009
1302
|
* The column the table is currently sorted on, or null when the sort
|
|
1010
1303
|
* key belongs to no declared column.
|
|
@@ -1053,6 +1346,11 @@ export default {
|
|
|
1053
1346
|
this.currentPath = next
|
|
1054
1347
|
this.refresh()
|
|
1055
1348
|
},
|
|
1349
|
+
|
|
1350
|
+
// A selection belongs to the folder it was made in.
|
|
1351
|
+
currentPath() {
|
|
1352
|
+
this.selectedIds = []
|
|
1353
|
+
},
|
|
1056
1354
|
},
|
|
1057
1355
|
|
|
1058
1356
|
mounted() {
|
|
@@ -1949,6 +2247,104 @@ export default {
|
|
|
1949
2247
|
return generateUrl('/f/{fileid}', { fileid: node.fileid })
|
|
1950
2248
|
},
|
|
1951
2249
|
|
|
2250
|
+
/**
|
|
2251
|
+
* Whether a file is selected.
|
|
2252
|
+
*
|
|
2253
|
+
* @param {object} node The file.
|
|
2254
|
+
* @return {boolean} True when selected.
|
|
2255
|
+
*/
|
|
2256
|
+
isSelected(node) {
|
|
2257
|
+
return this.selectedIds.includes(String(node.fileid))
|
|
2258
|
+
},
|
|
2259
|
+
|
|
2260
|
+
/**
|
|
2261
|
+
* Select or deselect one file.
|
|
2262
|
+
*
|
|
2263
|
+
* @param {object} node The file.
|
|
2264
|
+
* @param {boolean} checked Whether it is now selected.
|
|
2265
|
+
* @return {void}
|
|
2266
|
+
*/
|
|
2267
|
+
toggleSelected(node, checked) {
|
|
2268
|
+
const id = String(node.fileid)
|
|
2269
|
+
const rest = this.selectedIds.filter((entry) => entry !== id)
|
|
2270
|
+
this.selectedIds = checked ? [...rest, id] : rest
|
|
2271
|
+
},
|
|
2272
|
+
|
|
2273
|
+
/**
|
|
2274
|
+
* Select every file the filter keeps, or none.
|
|
2275
|
+
*
|
|
2276
|
+
* @param {boolean} checked Whether to select all.
|
|
2277
|
+
* @return {void}
|
|
2278
|
+
*/
|
|
2279
|
+
toggleAll(checked) {
|
|
2280
|
+
this.selectedIds = checked ? [...this.selectableIds] : []
|
|
2281
|
+
},
|
|
2282
|
+
|
|
2283
|
+
/** Drop the selection. */
|
|
2284
|
+
clearSelection() {
|
|
2285
|
+
this.selectedIds = []
|
|
2286
|
+
},
|
|
2287
|
+
|
|
2288
|
+
/**
|
|
2289
|
+
* Run one of the host's bulk actions on the selected files, then drop
|
|
2290
|
+
* the selection. An `open-modal` action's props gain `files` and
|
|
2291
|
+
* `fileIds`; a `handler` action's args gain the selected nodes.
|
|
2292
|
+
*
|
|
2293
|
+
* @param {object} action The declared bulk action.
|
|
2294
|
+
* @return {void}
|
|
2295
|
+
* @spec openspec/changes/files-browser-hosts-a-documents-list/specs/files-browser/spec.md
|
|
2296
|
+
*/
|
|
2297
|
+
runBulkAction(action) {
|
|
2298
|
+
if (!action || typeof action !== 'object') {
|
|
2299
|
+
return
|
|
2300
|
+
}
|
|
2301
|
+
const nodes = this.fileNodes.filter((node) => this.selectedIds.includes(String(node.fileid)))
|
|
2302
|
+
const type = action.type || 'handler'
|
|
2303
|
+
let wrapped = action
|
|
2304
|
+
if (type === 'open-modal') {
|
|
2305
|
+
const files = nodes.map((node) => ({ fileId: node.fileid, fileName: node.basename, path: node.path }))
|
|
2306
|
+
wrapped = { ...action, props: { ...(action.props || {}), files, fileIds: files.map((file) => file.fileId) } }
|
|
2307
|
+
} else if (type === 'handler') {
|
|
2308
|
+
wrapped = { ...action, args: [...(action.args || []), nodes] }
|
|
2309
|
+
}
|
|
2310
|
+
this.selectedIds = []
|
|
2311
|
+
if (typeof this.cnDispatchAction === 'function') {
|
|
2312
|
+
this.cnDispatchAction(wrapped)
|
|
2313
|
+
return
|
|
2314
|
+
}
|
|
2315
|
+
dispatchAction(wrapped, { router: this.$router || null })
|
|
2316
|
+
},
|
|
2317
|
+
|
|
2318
|
+
/**
|
|
2319
|
+
* Choose or drop one facet value.
|
|
2320
|
+
*
|
|
2321
|
+
* @param {string} key The column key.
|
|
2322
|
+
* @param {string} value The value.
|
|
2323
|
+
* @return {void}
|
|
2324
|
+
*/
|
|
2325
|
+
toggleFacet(key, value) {
|
|
2326
|
+
const chosen = this.facetSelections[key] || []
|
|
2327
|
+
const next = chosen.includes(value) ? chosen.filter((entry) => entry !== value) : [...chosen, value]
|
|
2328
|
+
this.facetSelections = { ...this.facetSelections, [key]: next }
|
|
2329
|
+
},
|
|
2330
|
+
|
|
2331
|
+
/** Drop every facet choice. */
|
|
2332
|
+
clearFacets() {
|
|
2333
|
+
this.facetSelections = {}
|
|
2334
|
+
},
|
|
2335
|
+
|
|
2336
|
+
/**
|
|
2337
|
+
* The host's own row actions that belong on this file: those whose
|
|
2338
|
+
* `visibleIf` (extension, mime) holds for it, or that name none.
|
|
2339
|
+
*
|
|
2340
|
+
* @param {object} node The file.
|
|
2341
|
+
* @return {Array<object>} The actions to offer.
|
|
2342
|
+
* @spec openspec/changes/files-browser-hosts-a-documents-list/specs/files-browser/spec.md
|
|
2343
|
+
*/
|
|
2344
|
+
hostActionsFor(node) {
|
|
2345
|
+
return this.rowActions.filter((action) => hostActionApplies(action, node))
|
|
2346
|
+
},
|
|
2347
|
+
|
|
1952
2348
|
/**
|
|
1953
2349
|
* Run one of the host's declared actions on a file.
|
|
1954
2350
|
*
|
|
@@ -2195,6 +2591,81 @@ export default {
|
|
|
2195
2591
|
color: var(--color-text-maxcontrast);
|
|
2196
2592
|
}
|
|
2197
2593
|
|
|
2594
|
+
.cn-files-browser__col-select {
|
|
2595
|
+
width: 36px;
|
|
2596
|
+
text-align: center;
|
|
2597
|
+
}
|
|
2598
|
+
|
|
2599
|
+
.cn-files-browser__select {
|
|
2600
|
+
width: 18px;
|
|
2601
|
+
height: 18px;
|
|
2602
|
+
margin: 0;
|
|
2603
|
+
accent-color: var(--color-primary-element);
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2606
|
+
.cn-files-browser__bulk-bar {
|
|
2607
|
+
display: flex;
|
|
2608
|
+
flex-wrap: wrap;
|
|
2609
|
+
align-items: center;
|
|
2610
|
+
gap: 8px;
|
|
2611
|
+
padding: 8px 12px;
|
|
2612
|
+
border-radius: var(--border-radius-large);
|
|
2613
|
+
background: var(--color-primary-element-light);
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
.cn-files-browser__bulk-count {
|
|
2617
|
+
font-weight: 600;
|
|
2618
|
+
margin-inline-end: 8px;
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
.cn-files-browser__facets {
|
|
2622
|
+
display: flex;
|
|
2623
|
+
flex-wrap: wrap;
|
|
2624
|
+
align-items: center;
|
|
2625
|
+
gap: 8px 16px;
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
.cn-files-browser__facet {
|
|
2629
|
+
display: flex;
|
|
2630
|
+
flex-wrap: wrap;
|
|
2631
|
+
gap: 6px;
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
.cn-files-browser__facet-chip {
|
|
2635
|
+
display: inline-flex;
|
|
2636
|
+
align-items: center;
|
|
2637
|
+
gap: 6px;
|
|
2638
|
+
min-height: 32px;
|
|
2639
|
+
padding: 0 12px;
|
|
2640
|
+
border: 1px solid var(--color-border-dark);
|
|
2641
|
+
border-radius: var(--border-radius-pill);
|
|
2642
|
+
background: var(--color-main-background);
|
|
2643
|
+
color: var(--color-main-text);
|
|
2644
|
+
cursor: pointer;
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2647
|
+
.cn-files-browser__facet-chip--active {
|
|
2648
|
+
border-color: var(--color-primary-element);
|
|
2649
|
+
background: var(--color-primary-element-light);
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
.cn-files-browser__group-heading {
|
|
2653
|
+
padding: 12px 8px 4px;
|
|
2654
|
+
text-align: start;
|
|
2655
|
+
font-weight: 700;
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
.cn-files-browser__group-count {
|
|
2659
|
+
margin-inline-start: 6px;
|
|
2660
|
+
font-weight: 400;
|
|
2661
|
+
color: var(--color-text-maxcontrast);
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2664
|
+
.cn-files-browser__no-match {
|
|
2665
|
+
padding: 16px 8px;
|
|
2666
|
+
color: var(--color-text-maxcontrast);
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2198
2669
|
.cn-files-browser__col-actions {
|
|
2199
2670
|
width: 48px;
|
|
2200
2671
|
}
|