@conduction/nextcloud-vue 2.78.0 → 2.80.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundled-packages.json +0 -2
- package/dist/esm/bundled-packages.json +0 -2
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js.map +1 -1
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js +3 -1
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +32 -3
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +115 -104
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +12 -2
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +39 -3
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +158 -0
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +157 -96
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +139 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +140 -92
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +77 -0
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +5 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +62 -4
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +85 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +43 -4
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +11 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +736 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +238 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnFactsWidget/index.js +30 -0
- package/dist/esm/components/CnFactsWidget/index.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +10 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +551 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +183 -0
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnFieldListWidget/index.js +29 -0
- package/dist/esm/components/CnFieldListWidget/index.js.map +1 -0
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +173 -5
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +181 -99
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js +29 -11
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/openRegisterSource.js +252 -0
- package/dist/esm/components/CnFilesBrowser/openRegisterSource.js.map +1 -0
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +66 -18
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +4 -4
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +18 -3
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +330 -74
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +34 -15
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +10 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js.map +1 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +99 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +48 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -0
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +100 -3
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +4 -2
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js +29 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +3 -2
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +10 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +387 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +169 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/index.js +31 -0
- package/dist/esm/components/CnRecordListWidget/index.js.map +1 -0
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js +10 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js +351 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js +71 -0
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSectionsWidget/index.js +35 -0
- package/dist/esm/components/CnSectionsWidget/index.js.map +1 -0
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +81 -2
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +1 -0
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +152 -8
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +84 -23
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +4 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -0
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +106 -2
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +105 -53
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
- package/dist/esm/composables/useAppManifest.js +8 -3
- package/dist/esm/composables/useAppManifest.js.map +1 -1
- package/dist/esm/composables/useFormDraft.js +140 -9
- package/dist/esm/composables/useFormDraft.js.map +1 -1
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js +28 -20
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js +9 -0
- package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/field-inspection.js +21 -0
- package/dist/esm/integrations/builtin/field-inspection.js.map +1 -1
- package/dist/esm/integrations/offline/checklistSubmission.js +141 -0
- package/dist/esm/integrations/offline/checklistSubmission.js.map +1 -0
- package/dist/esm/integrations/offline/fieldCollectionHelpers.js +70 -5
- package/dist/esm/integrations/offline/fieldCollectionHelpers.js.map +1 -1
- package/dist/esm/integrations/offline/index.js +2 -1
- package/dist/esm/integrations/offline/index.js.map +1 -1
- package/dist/esm/integrations/offline/offlineDb.js +6 -1
- package/dist/esm/integrations/offline/offlineDb.js.map +1 -1
- package/dist/esm/integrations/offline/syncReplayService.js +8 -2
- package/dist/esm/integrations/offline/syncReplayService.js.map +1 -1
- package/dist/esm/l10n/en.json.js +72 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +120 -2
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +1314 -74
- package/dist/esm/utils/boardDate.js +193 -0
- package/dist/esm/utils/boardDate.js.map +1 -0
- package/dist/esm/utils/detailActionModel.js +60 -3
- package/dist/esm/utils/detailActionModel.js.map +1 -1
- package/dist/esm/utils/dynamicProperties.js +5 -1
- package/dist/esm/utils/dynamicProperties.js.map +1 -1
- package/dist/esm/utils/fillTemplate.js +114 -0
- package/dist/esm/utils/fillTemplate.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +4 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/queryPresets.js +96 -0
- package/dist/esm/utils/queryPresets.js.map +1 -0
- package/dist/esm/utils/resolveFilterTokens.js +11 -0
- package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
- package/dist/esm/utils/schema.js +21 -0
- package/dist/esm/utils/schema.js.map +1 -1
- package/dist/esm/utils/sentinelTokens.js +3 -3
- package/dist/esm/utils/sentinelTokens.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +57 -6
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/widgetDispatch.js +7 -1
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +14278 -8169
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1314 -74
- package/l10n/en.json +72 -1
- package/l10n/nl.json +120 -2
- package/package.json +1 -1
- package/scripts/build-validators.js +26 -0
- package/src/components/CnActionButtons/CnActionButtons.vue +2 -0
- package/src/components/CnActionsBar/CnActionsBar.md +4 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +83 -42
- package/src/components/CnAppRoot/CnAppRoot.vue +12 -2
- package/src/components/CnBannerWidget/CnBannerWidget.vue +39 -3
- package/src/components/CnCellRenderer/CnCellRenderer.vue +306 -2
- package/src/components/CnDashboardPage/CnDashboardPage.vue +219 -4
- package/src/components/CnDataTable/CnDataTable.md +2 -0
- package/src/components/CnDataTable/CnDataTable.vue +82 -2
- package/src/components/CnDetailPage/CnDetailPage.md +1 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +63 -3
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +127 -1
- package/src/components/CnFactsWidget/CnFactsWidget.vue +1050 -0
- package/src/components/CnFactsWidget/index.js +29 -0
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +746 -0
- package/src/components/CnFieldListWidget/index.js +28 -0
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +232 -11
- package/src/components/CnFilesBrowser/filesBrowser.js +43 -2
- package/src/components/CnFilesBrowser/index.js +1 -0
- package/src/components/CnFilesBrowser/openRegisterSource.js +248 -0
- package/src/components/CnFormDialog/CnFormDialog.md +9 -3
- package/src/components/CnFormDialog/CnFormDialog.vue +70 -22
- package/src/components/CnFormPage/CnFormPage.vue +18 -3
- package/src/components/CnIndexPage/CnIndexPage.vue +281 -9
- package/src/components/CnIndexPage/CnIndexPageWidgets.vue +169 -0
- package/src/components/CnObjectSidebar/CnFilesTab.vue +103 -4
- package/src/components/CnPageRenderer/CnPageRenderer.vue +29 -1
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +3 -2
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +603 -0
- package/src/components/CnRecordListWidget/index.js +30 -0
- package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +1 -1
- package/src/components/CnSectionsWidget/CnSectionsWidget.vue +489 -0
- package/src/components/CnSectionsWidget/index.js +34 -0
- package/src/components/CnStagesWidget/CnStagesWidget.vue +81 -2
- package/src/components/CnStatWidget/CnStatWidget.vue +1 -1
- package/src/components/CnTabsWidget/CnTabsWidget.vue +214 -9
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +4 -0
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -0
- package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +188 -4
- package/src/components/index.js +5 -1
- package/src/composables/__tests__/useFormDraft.spec.js +69 -0
- package/src/composables/useAppManifest.js +8 -3
- package/src/composables/useFormDraft.js +139 -8
- package/src/css/form-field.css +14 -0
- package/src/css/index-page.css +71 -0
- package/src/css/kpi-card.css +32 -0
- package/src/css/look-board-detail.css +87 -0
- package/src/css/look-board-index.css +143 -14
- package/src/css/look-board.css +114 -2
- package/src/css/table.css +56 -0
- package/src/index.js +8 -0
- package/src/integrations/builtin/field-inspection/CnFieldInspectionCard.vue +35 -20
- package/src/integrations/builtin/field-inspection.js +21 -0
- package/src/integrations/offline/checklistSubmission.js +137 -0
- package/src/integrations/offline/fieldCollectionHelpers.js +69 -4
- package/src/integrations/offline/index.js +3 -0
- package/src/integrations/offline/offlineDb.js +6 -1
- package/src/integrations/offline/syncReplayService.js +8 -2
- package/src/schemas/app-manifest-v2.schema.json +149 -5
- package/src/utils/boardDate.js +189 -0
- package/src/utils/detailActionModel.js +59 -2
- package/src/utils/dynamicProperties.js +5 -1
- package/src/utils/fillTemplate.js +110 -0
- package/src/utils/libraryWidgetKeys.js +4 -0
- package/src/utils/queryPresets.js +93 -0
- package/src/utils/resolveFilterTokens.js +11 -0
- package/src/utils/schema.js +21 -0
- package/src/utils/sentinelTokens.js +3 -3
- package/src/utils/validateManifestV2.compiled.js +57 -2
- package/src/utils/widgetDispatch.js +7 -1
- package/dist/esm/_virtual/equal.js +0 -4
- package/dist/esm/_virtual/equal.js.map +0 -1
- package/dist/esm/_virtual/ucs2length.js +0 -4
- package/dist/esm/_virtual/ucs2length.js.map +0 -1
- package/dist/esm/node_modules/ajv/dist/runtime/equal.js +0 -9
- package/dist/esm/node_modules/ajv/dist/runtime/equal.js.map +0 -1
- package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js +0 -25
- package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js.map +0 -1
- package/dist/esm/node_modules/fast-deep-equal/index.js +0 -47
- package/dist/esm/node_modules/fast-deep-equal/index.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnRecordListWidget.vue3.js","sources":["../../../../src/components/CnRecordListWidget/CnRecordListWidget.vue?vue&type=template&id=7073b362&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tclass=\"cn-record-list\"\n\t\t:class=\"`cn-record-list--${presentation}`\"\n\t\tdata-testid=\"cn-record-list\">\n\t\t<p v-if=\"error\" class=\"cn-record-list__error\" data-testid=\"cn-record-list-error\">\n\t\t\t{{ errorLabel }}\n\t\t</p>\n\t\t<div v-else-if=\"loading && rows.length === 0\" class=\"cn-record-list__loading\">\n\t\t\t<NcLoadingIcon :size=\"20\" :name=\"loadingLabel\" />\n\t\t</div>\n\t\t<p v-else-if=\"rows.length === 0\" class=\"cn-record-list__empty\" data-testid=\"cn-record-list-empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t\t<ul v-else class=\"cn-record-list__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"item in items\"\n\t\t\t\t:key=\"item.key\"\n\t\t\t\tclass=\"cn-record-list__item\"\n\t\t\t\tdata-testid=\"cn-record-list-item\">\n\t\t\t\t<!-- rows: title and meta, then the pill, the date and a chevron. -->\n\t\t\t\t<component\n\t\t\t\t\t:is=\"item.tag\"\n\t\t\t\t\tv-if=\"presentation === 'rows'\"\n\t\t\t\t\tclass=\"cn-record-list__row\"\n\t\t\t\t\t:class=\"{ 'cn-record-list__row--link': item.tag !== 'div' }\"\n\t\t\t\t\tv-bind=\"item.attrs\">\n\t\t\t\t\t<span class=\"cn-record-list__main\">\n\t\t\t\t\t\t<span v-if=\"item.title\" class=\"cn-record-list__title\">{{ item.title }}</span>\n\t\t\t\t\t\t<span v-if=\"item.meta\" class=\"cn-record-list__meta\">{{ item.meta }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"item.pill || item.trailing\" class=\"cn-record-list__aside\">\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-if=\"item.pill\"\n\t\t\t\t\t\t\t:label=\"item.pill.label\"\n\t\t\t\t\t\t\t:variant=\"item.pill.variant\"\n\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t<span v-if=\"item.trailing\" class=\"cn-record-list__trailing\">{{ item.trailing }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<ChevronRight\n\t\t\t\t\t\tv-if=\"item.tag !== 'div'\"\n\t\t\t\t\t\tclass=\"cn-record-list__chevron\"\n\t\t\t\t\t\t:size=\"18\"\n\t\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t</component>\n\n\t\t\t\t<!-- feed: a round icon, a meta line and the text. -->\n\t\t\t\t<div v-else-if=\"presentation === 'feed'\" class=\"cn-record-list__feed-row\">\n\t\t\t\t\t<span class=\"cn-record-list__feed-icon\" aria-hidden=\"true\">\n\t\t\t\t\t\t<CnIcon :name=\"item.icon\" :size=\"18\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<component :is=\"item.tag\" class=\"cn-record-list__main\" v-bind=\"item.attrs\">\n\t\t\t\t\t\t<span v-if=\"item.meta\" class=\"cn-record-list__meta\">{{ item.meta }}</span>\n\t\t\t\t\t\t<span v-if=\"item.text\" class=\"cn-record-list__text\">{{ item.text }}</span>\n\t\t\t\t\t</component>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- inline: a label and the title, the title a link. -->\n\t\t\t\t<div v-else class=\"cn-record-list__inline-row\">\n\t\t\t\t\t<span v-if=\"item.label\" class=\"cn-record-list__label\">{{ item.label }}</span>\n\t\t\t\t\t<component :is=\"item.tag\" class=\"cn-record-list__inline-title\" v-bind=\"item.attrs\">\n\t\t\t\t\t\t{{ item.title }}\n\t\t\t\t\t</component>\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script>\nimport { subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport CnIcon from '../CnIcon/CnIcon.vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { isBadgeVariant } from '../../utils/badgeVariants.js'\nimport { fillTemplate, firstFilled } from '../../utils/fillTemplate.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\n\nconst PAGE_REFRESH_CHANNEL = 'cn:page:refresh'\nconst PRESENTATIONS = ['rows', 'feed', 'inline']\n\n/**\n * Unwrap a value that may be a ref.\n *\n * @param {unknown} value The value.\n * @return {unknown} The plain value.\n */\nfunction unwrap(value) {\n\treturn value && typeof value === 'object' && 'value' in value ? value.value : value\n}\n\n/**\n * CnRecordListWidget — the objects of another schema that point at this\n * record, drawn as the boards draw them (screens-relation-pages).\n *\n * A person page lists its open cases (\"Verlichting fietspad Lindelaan,\n * 2026-0082 · Woo-verzoek, In behandeling, Termijn 1 nov\"), its latest contact\n * moments (an icon by channel, a meta line, the summary) and its relations\n * (\"Secretaris van Buurtvereniging Oost\"). Each is a list of another schema's\n * objects filtered on a reference to this record. The source keys are those of\n * `object-list` (`register`, `schema`, `filter` with the `@objectId` /\n * `@object.<field>` / `@workspace.<key>` tokens, `sort`, `limit`, `extend`,\n * `rowRoute`); what differs is the drawing: `presentation` is `rows`, `feed`\n * or `inline`, and each line is a template (`\"{identifier} · {caseTypeName}\"`,\n * `\"Termijn {deadline|date}\"`).\n *\n * A content-only catalog type: on a card surface the detail host draws the\n * titled card. The widget emits `header` with `{ link, note }` (the\n * `headerLink` and `headerNote` with `{total}` filled in) for that card's\n * header.\n */\nexport default {\n\tname: 'CnRecordListWidget',\n\n\tcomponents: { ChevronRight, CnIcon, CnStatusBadge, NcLoadingIcon },\n\n\tinject: {\n\t\t/** Detail-page object context (`{ objectId, object, register, schema }`). */\n\t\tcnObjectContext: { default: null },\n\t\t/** Page workspace bag, for `@workspace.<key>` tokens. */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/** The host app's translate function. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The list config: `{ register, schema, filter?, sort?, limit?, extend?,\n\t\t * presentation?, title?, meta?, text?, label?, pill?, trailing?, icon?,\n\t\t * rowRoute?, rowIdField?, headerLink?, headerNote?, emptyText?, errorText? }`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The current record's id, for the `@objectId` token. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The current record, for `@object.<field>` tokens. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['header'],\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\ttotal: 0,\n\t\t\tloading: false,\n\t\t\terror: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {string} `rows`, `feed` or `inline`. */\n\t\tpresentation() {\n\t\t\tconst value = this.content?.presentation\n\t\t\treturn PRESENTATIONS.includes(value) ? value : 'rows'\n\t\t},\n\n\t\t/** @return {object} The token context: record, id and workspace. */\n\t\ttokenCtx() {\n\t\t\tconst injected = unwrap(this.cnObjectContext) || {}\n\t\t\treturn {\n\t\t\t\t...injected,\n\t\t\t\tobjectId: this.objectId ?? injected.objectId ?? this.objectData?.id ?? null,\n\t\t\t\tobject: this.objectData || injected.object || null,\n\t\t\t\tworkspace: unwrap(this.cnWorkspaceContext) || {},\n\t\t\t}\n\t\t},\n\n\t\t/** @return {object} The filter with its tokens resolved. */\n\t\tresolvedFilter() {\n\t\t\tconst filter = this.content?.filter\n\t\t\treturn dropOptionalUnresolved(resolveFilterTokens(filter && typeof filter === 'object' ? filter : {}, this.tokenCtx))\n\t\t},\n\n\t\t/** @return {boolean} A required token (the record id, say) has no value yet. */\n\t\twaiting() {\n\t\t\treturn hasUnresolvedTokens(this.resolvedFilter)\n\t\t},\n\n\t\t/** @return {number} The fetch cap. */\n\t\tlimit() {\n\t\t\tconst value = Number(this.content?.limit)\n\t\t\treturn Number.isFinite(value) && value > 0 ? Math.floor(value) : 5\n\t\t},\n\n\t\t/** @return {string} What a fetch depends on; a change fetches again. */\n\t\trequestKey() {\n\t\t\tconst c = this.content || {}\n\t\t\treturn JSON.stringify([c.register, c.schema, this.resolvedFilter, c.sort, this.limit, c.extend, this.waiting])\n\t\t},\n\n\t\t/** @return {Array<object>} The rows ready to draw. */\n\t\titems() {\n\t\t\tconst c = this.content || {}\n\t\t\treturn this.rows.slice(0, this.limit).map((row, index) => {\n\t\t\t\tconst link = this.rowLink(row)\n\t\t\t\treturn {\n\t\t\t\t\tkey: (row && (row.id || row['@self']?.id)) || `row-${index}`,\n\t\t\t\t\ttag: link ? 'router-link' : 'div',\n\t\t\t\t\tattrs: link ? { to: link } : {},\n\t\t\t\t\ttitle: this.fill(c.title, row),\n\t\t\t\t\tmeta: this.fill(c.meta, row),\n\t\t\t\t\ttext: this.fill(c.text, row),\n\t\t\t\t\tlabel: this.fill(c.label, row),\n\t\t\t\t\ttrailing: c.trailing ? firstFilled(c.trailing, row, this.translate) : '',\n\t\t\t\t\tpill: this.pillOf(row),\n\t\t\t\t\ticon: this.iconOf(row),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {object|null} The header link with `{total}` filled and tokens resolved. */\n\t\theaderLink() {\n\t\t\tconst link = this.content?.headerLink\n\t\t\tif (!link || typeof link !== 'object' || typeof link.label !== 'string' || link.label === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst resolved = { label: this.withTotal(link.label) }\n\t\t\tif (link.route) {\n\t\t\t\tresolved.route = link.route\n\t\t\t\tif (link.params && typeof link.params === 'object') {\n\t\t\t\t\tresolved.params = resolveFilterTokens(link.params, this.tokenCtx)\n\t\t\t\t}\n\t\t\t\tif (link.query && typeof link.query === 'object') {\n\t\t\t\t\tresolved.query = resolveFilterTokens(link.query, this.tokenCtx)\n\t\t\t\t}\n\t\t\t} else if (typeof link.href === 'string') {\n\t\t\t\tresolved.href = link.href\n\t\t\t}\n\t\t\treturn resolved\n\t\t},\n\n\t\t/** @return {string} The header note with `{total}` filled. */\n\t\theaderNote() {\n\t\t\tconst note = this.content?.headerNote\n\t\t\treturn typeof note === 'string' && note !== '' ? this.withTotal(note) : ''\n\t\t},\n\n\t\t/** @return {string} The empty line. */\n\t\temptyLabel() {\n\t\t\treturn this.content?.emptyText ? this.translate(this.content.emptyText) : t('nextcloud-vue', 'Nothing here yet')\n\t\t},\n\n\t\t/** @return {string} The error line. */\n\t\terrorLabel() {\n\t\t\treturn this.content?.errorText ? this.translate(this.content.errorText) : t('nextcloud-vue', 'This list could not be loaded.')\n\t\t},\n\n\t\t/** @return {string} The spinner's accessible name. */\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading …')\n\t\t},\n\t},\n\n\twatch: {\n\t\trequestKey() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.onRefresh = () => this.fetchRows()\n\t\tsubscribe(PAGE_REFRESH_CHANNEL, this.onRefresh)\n\t\tthis.fetchRows()\n\t},\n\n\tbeforeUnmount() {\n\t\tunsubscribe(PAGE_REFRESH_CHANNEL, this.onRefresh)\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Translate a manifest string through the host app.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation.\n\t\t */\n\t\ttranslate(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * Fill a template from a row, after translating it.\n\t\t *\n\t\t * @param {string} template The template.\n\t\t * @param {object} row The row.\n\t\t * @return {string} The text, '' when a token is empty.\n\t\t */\n\t\tfill(template, row) {\n\t\t\treturn typeof template === 'string' && template !== '' ? fillTemplate(this.translate(template), row) : ''\n\t\t},\n\n\t\t/**\n\t\t * Replace `{total}` in a translated label.\n\t\t *\n\t\t * @param {string} label The label.\n\t\t * @return {string} The text.\n\t\t */\n\t\twithTotal(label) {\n\t\t\treturn this.translate(label).replace(/\\{total\\}/g, String(this.total))\n\t\t},\n\n\t\t/**\n\t\t * The route a row opens: `rowRoute` with the id at `rowIdField`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {object|null} The router location, or null.\n\t\t */\n\t\trowLink(row) {\n\t\t\tconst route = this.content?.rowRoute\n\t\t\tif (!route || !this.$router || !row) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst field = this.content?.rowIdField\n\t\t\tconst id = field ? readPath(row, field) : (row.id || row['@self']?.id)\n\t\t\tif (id === null || id === undefined || id === '' || typeof id === 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn { name: route, params: { id: String(id) } }\n\t\t},\n\n\t\t/**\n\t\t * The row's pill: `{ field, labels?, colorMap?, variant? }`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {{label: string, variant: string}|null} The pill, or null.\n\t\t */\n\t\tpillOf(row) {\n\t\t\tconst pill = this.content?.pill\n\t\t\tif (!pill || typeof pill !== 'object' || !pill.field) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst raw = readPath(row, pill.field)\n\t\t\tif (raw === null || raw === undefined || raw === '' || typeof raw === 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst key = String(raw)\n\t\t\tconst labels = pill.labels && typeof pill.labels === 'object' ? pill.labels : {}\n\t\t\tconst map = pill.colorMap && typeof pill.colorMap === 'object' ? pill.colorMap : {}\n\t\t\tconst found = Object.keys(map).find((k) => k.toLowerCase() === key.toLowerCase())\n\t\t\tconst variant = found ? map[found] : pill.variant\n\t\t\treturn {\n\t\t\t\tlabel: labels[key] ? this.translate(labels[key]) : key,\n\t\t\t\tvariant: isBadgeVariant(variant) ? variant : 'default',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The feed icon: an icon name, or `{ field, map, default }`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {string} The MDI icon name.\n\t\t */\n\t\ticonOf(row) {\n\t\t\tconst icon = this.content?.icon\n\t\t\tif (typeof icon === 'string' && icon !== '') {\n\t\t\t\treturn icon\n\t\t\t}\n\t\t\tif (icon && typeof icon === 'object') {\n\t\t\t\tconst raw = icon.field ? readPath(row, icon.field) : undefined\n\t\t\t\tconst map = icon.map && typeof icon.map === 'object' ? icon.map : {}\n\t\t\t\tif (raw !== null && raw !== undefined && map[String(raw)]) {\n\t\t\t\t\treturn map[String(raw)]\n\t\t\t\t}\n\t\t\t\tif (typeof icon.default === 'string' && icon.default !== '') {\n\t\t\t\t\treturn icon.default\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 'CircleSmall'\n\t\t},\n\n\t\t/** Tell the card around this list what its header shows. */\n\t\temitHeader() {\n\t\t\t/**\n\t\t\t * @event header Emitted after every read with the card header's parts.\n\t\t\t * @type {{link: (object|null), note: string}} The header link (resolved, with `{total}` filled) and the note.\n\t\t\t */\n\t\t\tthis.$emit('header', { link: this.headerLink, note: this.headerNote })\n\t\t},\n\n\t\t/**\n\t\t * Fetch the list. Sends nothing while a required token is unresolved\n\t\t * or the source is incomplete.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchRows() {\n\t\t\tconst c = this.content || {}\n\t\t\tif (!c.register || !c.schema || this.waiting) {\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.total = 0\n\t\t\t\tthis.error = false\n\t\t\t\tthis.emitHeader()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = false\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', { register: c.register, schema: c.schema })\n\t\t\t\tconst params = { _limit: this.limit }\n\t\t\t\tif (c.sort && typeof c.sort === 'object' && c.sort.field) {\n\t\t\t\t\tparams[`_order[${c.sort.field}]`] = c.sort.dir === 'desc' ? 'desc' : 'asc'\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(c.extend)) {\n\t\t\t\t\tconst extend = c.extend.filter((e) => typeof e === 'string' && e !== '')\n\t\t\t\t\tif (extend.length > 0) {\n\t\t\t\t\t\tparams._extend = extend\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (const [key, value] of Object.entries(this.resolvedFilter)) {\n\t\t\t\t\tif (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\t\t\t\t\tfor (const [op, operand] of Object.entries(value)) {\n\t\t\t\t\t\t\tparams[`${key}[${op}]`] = operand\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (value !== '' && value !== null && value !== undefined) {\n\t\t\t\t\t\tparams[key] = value\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst response = await axios.get(url, { params })\n\t\t\t\tconst data = response && response.data ? response.data : {}\n\t\t\t\tthis.rows = Array.isArray(data.results) ? data.results : []\n\t\t\t\tthis.total = typeof data.total === 'number' ? data.total : this.rows.length\n\t\t\t} catch (e) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnRecordListWidget] failed to load objects:', e)\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.total = 0\n\t\t\t\tthis.error = true\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t\tthis.emitHeader()\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-record-list__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-record-list__empty,\n.cn-record-list__error {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0;\n}\n\n.cn-record-list__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 8px 0;\n}\n\n/* rows */\n.cn-record-list--rows .cn-record-list__item + .cn-record-list__item {\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-record-list__row {\n\talign-items: center;\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tgap: 16px;\n\tpadding: 12px 0;\n\ttext-decoration: none;\n}\n\n.cn-record-list__row--link:hover .cn-record-list__title,\n.cn-record-list__row--link:focus-visible .cn-record-list__title {\n\ttext-decoration: underline;\n}\n\n.cn-record-list__main {\n\tdisplay: flex;\n\tflex: 1 1 auto;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-record-list__title {\n\tfont-size: 15px;\n\tfont-weight: 700;\n}\n\n.cn-record-list__meta {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-record-list__aside {\n\talign-items: center;\n\tdisplay: flex;\n\tflex: none;\n\tgap: 16px;\n}\n\n.cn-record-list__trailing {\n\tfont-size: 14px;\n\twhite-space: nowrap;\n}\n\n.cn-record-list__chevron {\n\tcolor: var(--color-text-maxcontrast);\n\tflex: none;\n}\n\n/* feed */\n.cn-record-list--feed .cn-record-list__item + .cn-record-list__item {\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-record-list__feed-row {\n\talign-items: flex-start;\n\tdisplay: flex;\n\tgap: 14px;\n\tpadding: 12px 0;\n}\n\n.cn-record-list__feed-icon {\n\talign-items: center;\n\tbackground: var(--color-primary-element-light);\n\tborder-radius: 50%;\n\tcolor: var(--color-primary-element-light-text, var(--color-primary-element));\n\tdisplay: flex;\n\tflex: none;\n\theight: 36px;\n\tjustify-content: center;\n\twidth: 36px;\n}\n\n.cn-record-list__text {\n\tfont-size: 15px;\n}\n\n.cn-record-list--feed .cn-record-list__main {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n/* inline */\n.cn-record-list__inline-row {\n\tpadding: 4px 0;\n}\n\n.cn-record-list__label {\n\tmargin-inline-end: 4px;\n}\n\n.cn-record-list__inline-title {\n\tcolor: var(--color-main-text);\n}\n\na.cn-record-list__inline-title {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: underline;\n}\n</style>\n\n<style>\n/* In a narrow side column a row stacks: title, meta, then pill and date. */\n.cn-detail-page__side .cn-record-list__row {\n\talign-items: flex-start;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-detail-page__side .cn-record-list__row .cn-record-list__title {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: underline;\n}\n\n.cn-detail-page__side .cn-record-list__chevron {\n\tdisplay: none;\n}\n</style>\n"],"names":["_normalizeClass","_createElementBlock","_toDisplayString","_openBlock","_createVNode","_createBlock","_resolveDynamicComponent","_mergeProps","_createElementVNode","_createTextVNode"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import __sfc_main from './CnRecordListWidget.vue.js';
|
|
2
|
+
import { registerDashboardWidget } from '../CnWidgetGrid/dashboardWidgetRegistry.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
6
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
7
|
+
*
|
|
8
|
+
* CnRecordListWidget — self-registers the `record-list` widget type (the
|
|
9
|
+
* objects of another schema that point at this record, as board rows, a feed
|
|
10
|
+
* or inline links) into the shared dashboardWidgetRegistry at module load.
|
|
11
|
+
* Renderer only: the list is declared in the manifest.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
registerDashboardWidget('record-list', {
|
|
16
|
+
renderer: __sfc_main,
|
|
17
|
+
form: null,
|
|
18
|
+
defaultContent: {
|
|
19
|
+
register: '',
|
|
20
|
+
schema: '',
|
|
21
|
+
filter: {},
|
|
22
|
+
presentation: 'rows',
|
|
23
|
+
limit: 5,
|
|
24
|
+
},
|
|
25
|
+
displayName: 'Record list',
|
|
26
|
+
icon: 'FormatListBulleted',
|
|
27
|
+
surfaces: ['detail-page', 'app-dashboard'],
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export { __sfc_main as CnRecordListWidget, __sfc_main as default };
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/components/CnRecordListWidget/index.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * CnRecordListWidget — self-registers the `record-list` widget type (the\n * objects of another schema that point at this record, as board rows, a feed\n * or inline links) into the shared dashboardWidgetRegistry at module load.\n * Renderer only: the list is declared in the manifest.\n */\n\nimport CnRecordListWidget from './CnRecordListWidget.vue'\nimport { registerDashboardWidget } from '../CnWidgetGrid/dashboardWidgetRegistry.js'\n\nregisterDashboardWidget('record-list', {\n\trenderer: CnRecordListWidget,\n\tform: null,\n\tdefaultContent: {\n\t\tregister: '',\n\t\tschema: '',\n\t\tfilter: {},\n\t\tpresentation: 'rows',\n\t\tlimit: 5,\n\t},\n\tdisplayName: 'Record list',\n\ticon: 'FormatListBulleted',\n\tsurfaces: ['detail-page', 'app-dashboard'],\n})\n\nexport { CnRecordListWidget }\nexport default CnRecordListWidget\n"],"names":["CnRecordListWidget"],"mappings":";;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA,uBAAuB,CAAC,aAAa,EAAE;AACvC,CAAC,QAAQ,EAAEA,UAAkB;AAC7B,CAAC,IAAI,EAAE,IAAI;AACX,CAAC,cAAc,EAAE;AACjB,EAAE,QAAQ,EAAE,EAAE;AACd,EAAE,MAAM,EAAE,EAAE;AACZ,EAAE,MAAM,EAAE,EAAE;AACZ,EAAE,YAAY,EAAE,MAAM;AACtB,EAAE,KAAK,EAAE,CAAC;AACV,EAAE;AACF,CAAC,WAAW,EAAE,aAAa;AAC3B,CAAC,IAAI,EAAE,oBAAoB;AAC3B,CAAC,QAAQ,EAAE,CAAC,aAAa,EAAE,eAAe,CAAC;AAC3C,CAAC;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnSavedViewsControl.vue2.js","sources":["../../../../src/components/CnSavedViewsControl/CnSavedViewsControl.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<!--\n\t\tBoard look: the saved views are a row of chips on the ground, each with\n\t\tits count, the selected one filled. The menu stays for managing views\n\t\t(apply, rename, share, delete) and is reached from the \"Save view\"\n\t\tbutton. Without the look the control is the menu alone, as before.\n\t-->\n\t<div\n\t\tv-if=\"isBoardLook && chipRows.length > 0\"\n\t\tclass=\"cn-saved-views__chips\"\n\t\trole=\"group\"\n\t\t:aria-label=\"t('nextcloud-vue', 'Saved views')\"\n\t\tdata-testid=\"cn-saved-views-chips\">\n\t\t<button\n\t\t\tv-for=\"row in chipRows\"\n\t\t\t:key=\"`chip-${row.view.id || row.view.slug}`\"\n\t\t\ttype=\"button\"\n\t\t\tclass=\"cn-saved-views__chip\"\n\t\t\t:class=\"{ 'cn-saved-views__chip--selected': isChipSelected(row.view) }\"\n\t\t\t:aria-pressed=\"isChipSelected(row.view)\"\n\t\t\tdata-testid=\"cn-saved-views-chip\"\n\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t@click=\"onApply(row.view)\">\n\t\t\t<span class=\"cn-saved-views__chip-name\">{{ row.view.name }}</span>\n\t\t\t<span\n\t\t\t\tv-if=\"countOf(row.view) !== null\"\n\t\t\t\tclass=\"cn-saved-views__chip-count\"\n\t\t\t\tdata-testid=\"cn-saved-views-chip-count\">{{ countOf(row.view) }}</span>\n\t\t</button>\n\t</div>\n\t<NcActions\n\t\tv-bind=\"$attrs\"\n\t\t:class=\"{ 'cn-saved-views__save': isBoardLook }\"\n\t\t:forceMenu=\"true\"\n\t\t:forceName=\"true\"\n\t\t:menuName=\"isBoardLook ? t('nextcloud-vue', 'Save view') : menuLabel\"\n\t\tdata-testid=\"cn-saved-views-control\"\n\t\t:aria-label=\"isBoardLook ? t('nextcloud-vue', 'Save view') : menuLabel\">\n\t\t<template #icon>\n\t\t\t<ContentSaveOutline v-if=\"isBoardLook\" :size=\"18\" />\n\t\t\t<BookmarkOutline v-else :size=\"20\" />\n\t\t</template>\n\n\t\t<NcActionCaption :name=\"t('nextcloud-vue', 'Saved views')\" />\n\n\t\t<!-- Loading state -->\n\t\t<NcActionCaption\n\t\t\tv-if=\"loading\"\n\t\t\tdata-testid=\"cn-saved-views-loading\"\n\t\t\t:name=\"t('nextcloud-vue', 'Loading…')\" />\n\n\t\t<!-- Empty state -->\n\t\t<NcActionCaption\n\t\t\tv-else-if=\"views.length === 0\"\n\t\t\tdata-testid=\"cn-saved-views-empty\"\n\t\t\t:name=\"t('nextcloud-vue', 'No saved views yet')\" />\n\n\t\t<!-- The labels already in use, offered before anybody types a new one. -->\n\t\t<template v-if=\"!loading && offeredLabels.length > 0\">\n\t\t\t<NcActionButton\n\t\t\t\tv-for=\"label in offeredLabels\"\n\t\t\t\t:key=\"`label-${label}`\"\n\t\t\t\tdata-testid=\"cn-saved-views-label\"\n\t\t\t\t:data-label=\"label\"\n\t\t\t\t:modelValue=\"labelFilter === label\"\n\t\t\t\ttype=\"checkbox\"\n\t\t\t\t:aria-label=\"labelFilterLabel(label)\"\n\t\t\t\t@click=\"onLabelFilter(label)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<TagOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ label }}\n\t\t\t</NcActionButton>\n\t\t\t<NcActionSeparator />\n\t\t</template>\n\n\t\t<!--\n\t\t\tA label that matches nothing says so. Chained to the ROWS rather\n\t\t\tthan to the label list: written as an else-if of the labels block\n\t\t\tit only ever rendered on a page with no labels at all, which is\n\t\t\tthe one case it cannot be about.\n\t\t-->\n\t\t<NcActionCaption\n\t\t\tv-if=\"!loading && views.length > 0 && rows.length === 0\"\n\t\t\tdata-testid=\"cn-saved-views-none-for-label\"\n\t\t\t:name=\"t('nextcloud-vue', 'No views with this label')\" />\n\n\t\t<!-- One row per view, in tree order: the name applies it, a trailing\n\t\t icon button deletes it. NcActions only recognises NcAction* vnodes\n\t\t as menu items and silently drops anything else, so a row with more\n\t\t than one control has to be an NcActionButtonGroup. -->\n\t\t<template v-if=\"!loading\">\n\t\t\t<template v-for=\"section in sections\" :key=\"section.key\">\n\t\t\t\t<NcActionCaption\n\t\t\t\t\tv-if=\"section.label\"\n\t\t\t\t\t:data-testid=\"`cn-saved-views-section-${section.key}`\"\n\t\t\t\t\t:name=\"section.label\" />\n\t\t\t\t<NcActionButtonGroup\n\t\t\t\t\tv-for=\"row in section.rows\"\n\t\t\t\t\t:key=\"`view-${row.view.id || row.view.slug}`\"\n\t\t\t\t\tclass=\"cn-saved-view-row\">\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-item\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:data-depth=\"row.depth\"\n\t\t\t\t\t\t:data-group=\"row.group\"\n\t\t\t\t\t\t:data-access=\"accessOf(row)\"\n\t\t\t\t\t\t:aria-label=\"rowLabel(row)\"\n\t\t\t\t\t\t@click=\"onApply(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<EyeOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ rowName(row, section.key === 'shared') }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'owner' && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`share-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-share\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"shareLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onShareRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ShareVariantOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"(accessOf(row) === 'owner' || accessOf(row) === 'write') && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`presentation-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-presentation\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"presentationLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onPresentationRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ViewDashboardOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'write'\"\n\t\t\t\t\t\t:key=\"`update-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-update\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"updateLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onUpdateRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ContentSaveOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'read' && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`copy-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-copy\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"copyLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onCopyRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ContentCopy :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'owner' && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`delete-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-delete\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"deleteLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onDeleteRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<TrashCanOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</NcActionButtonGroup>\n\t\t\t</template>\n\t\t</template>\n\n\t\t<NcActionSeparator />\n\n\t\t<NcActionButton\n\t\t\tdata-testid=\"cn-saved-views-save\"\n\t\t\t:aria-label=\"t('nextcloud-vue', 'Save current view…')\"\n\t\t\t@click=\"onSaveRequest\">\n\t\t\t<template #icon>\n\t\t\t\t<ContentSaveOutline :size=\"20\" />\n\t\t\t</template>\n\t\t\t{{ t('nextcloud-vue', 'Save current view…') }}\n\t\t</NcActionButton>\n\t</NcActions>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActionButtonGroup, NcActionCaption, NcActions, NcActionSeparator } from '@nextcloud/vue'\nimport BookmarkOutline from 'vue-material-design-icons/BookmarkOutline.vue'\nimport ContentCopy from 'vue-material-design-icons/ContentCopy.vue'\nimport ContentSaveOutline from 'vue-material-design-icons/ContentSaveOutline.vue'\nimport EyeOutline from 'vue-material-design-icons/EyeOutline.vue'\nimport ShareVariantOutline from 'vue-material-design-icons/ShareVariantOutline.vue'\nimport TagOutline from 'vue-material-design-icons/TagOutline.vue'\nimport TrashCanOutline from 'vue-material-design-icons/TrashCanOutline.vue'\nimport ViewDashboardOutline from 'vue-material-design-icons/ViewDashboardOutline.vue'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { buildViewTree, labelsInUse, VIEW_GROUPS } from '../../utils/buildViewTree.js'\nimport { isOwnView, viewAccess } from '../../utils/savedViewHelpers.js'\n\n/**\n * CnSavedViewsControl — toolbar dropdown listing OpenRegister saved-search\n * views for the current index page (saved-views-ui).\n *\n * Purely presentational: the parent (CnIndexPage) owns fetching via\n * `useSavedViewsApi()` and all mutations; this control only lists the\n * given `views` and emits intents:\n *\n * - `@apply(view)` — a view entry was clicked; parent writes the view's\n * stored filters/search/sort into the route query.\n * - `@save-request()` — \"Save current view…\" clicked; parent opens\n * CnSaveViewDialog.\n * - `@delete-request(view)` — a view's trailing delete icon clicked; parent\n * opens a confirm dialog. Only rendered for views the current user owns\n * (`@self.access: owner`, else `view.owner === currentUserId`) — OpenRegister\n * refuses foreign deletes server-side anyway (owner-scoped 404).\n * - `@share-request(view)` — Share on an own view; parent opens the share dialog.\n * - `@update-request(view)` — Save on a view shared with write access; parent\n * saves the current state to it (never sending `sharedWith` or `owner`).\n * - `@presentation-request(view)` — Presentation on a view the user may edit\n * (`owner` or `write`); parent opens the presentation dialog (table, board, calendar).\n * - `@copy-request(view)` — \"Save as my view\" on a view shared read-only.\n *\n * Views shared with the user (`@self.access` `write` or `read`) list under\n * \"Shared with me\", next to \"My views\", once any exist; with none the list\n * renders as before.\n *\n * @event {object} apply — Apply the clicked view. Payload: the View API object.\n * @event {void} save-request — Open the save-current-view dialog.\n * @event {object} delete-request — Confirm-delete the clicked view. Payload: the View API object.\n * @event {object} share-request — Share the clicked own view. Payload: the View API object.\n * @event {object} update-request — Save the current state to the clicked writable shared view. Payload: the View API object.\n * @event {object} presentation-request — Change how the clicked view shows. Payload: the View API object.\n * @event {object} copy-request — Copy the clicked read-only shared view into a personal one. Payload: the View API object.\n */\nexport default {\n\tname: 'CnSavedViewsControl',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionButtonGroup,\n\t\tNcActionCaption,\n\t\tNcActionSeparator,\n\t\tBookmarkOutline,\n\t\tContentCopy,\n\t\tContentSaveOutline,\n\t\tEyeOutline,\n\t\tShareVariantOutline,\n\t\tTagOutline,\n\t\tTrashCanOutline,\n\t\tViewDashboardOutline,\n\t},\n\n\tinject: {\n\t\t/** The look CnAppRoot provides; `board` draws the views as chips. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\t// The chips and the menu are two roots under the board look.\n\tinheritAttrs: false,\n\n\tprops: {\n\t\t/** Views to list (View API objects from `GET /apps/openregister/api/views`). */\n\t\tviews: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** True while the parent is fetching the view list. */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** The signed-in NC user id — gates the per-view delete affordance. */\n\t\tcurrentUserId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * How deep the tree indents before it flattens. Mirrors\n\t\t * `savedViewTree.maxDepth` in the manifest. Flattening is about\n\t\t * indentation only: a view past the bound still renders.\n\t\t */\n\t\tmaxDepth: {\n\t\t\ttype: Number,\n\t\t\tdefault: 3,\n\t\t},\n\n\t\t/**\n\t\t * How many records each view matches, keyed by view id (or slug). A\n\t\t * view with a number here shows it after its name; a view can also\n\t\t * carry its own `count`. `null` (the default) shows no counts.\n\t\t *\n\t\t * @type {{[viewId: string]: number}|null}\n\t\t */\n\t\tcounts: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The id (or slug) of the applied view; its chip renders selected under\n\t\t * the board look.\n\t\t */\n\t\tselectedViewId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['apply', 'copy-request', 'delete-request', 'presentation-request', 'save-request', 'share-request', 'update-request'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** The label currently filtering the list, or '' for all of them. */\n\t\t\tlabelFilter: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the saved views draw as chips (the board look).\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisBoardLook() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/**\n\t\t * The views that get a chip: the top level of the tree, in tree order.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tchipRows() {\n\t\t\treturn this.rows.filter((row) => row.depth === 0)\n\t\t},\n\n\t\t/** @return {string} The dropdown trigger label. */\n\t\tmenuLabel() {\n\t\t\treturn t('nextcloud-vue', 'Views')\n\t\t},\n\n\t\t/** @return {string} The seeded group name, for the template. */\n\t\tSEEDED_GROUP() {\n\t\t\treturn VIEW_GROUPS.SEEDED\n\t\t},\n\n\t\t/**\n\t\t * The views in tree order, seeded first, filtered by the active label.\n\t\t *\n\t\t * @return {Array<object>} Rows of `{ view, depth, group, orphaned }`.\n\t\t */\n\t\trows() {\n\t\t\treturn buildViewTree({\n\t\t\t\tviews: this.views,\n\t\t\t\tlabelFilter: this.labelFilter,\n\t\t\t\tmaxDepth: this.maxDepth,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The rows in their sections. With no view shared with this user there\n\t\t * is one section without a caption, as the list always was. With\n\t\t * shared views: seeded views (no caption), \"My views\", \"Shared with me\".\n\t\t *\n\t\t * @return {Array<{key: string, label: string, rows: Array<object>}>} The sections.\n\t\t * @spec openspec/changes/saved-views-shared-by-role/tasks.md#task-1\n\t\t */\n\t\tsections() {\n\t\t\tconst shared = this.rows.filter((r) => r.group !== VIEW_GROUPS.SEEDED && this.accessOf(r) !== 'owner')\n\t\t\tif (shared.length === 0) {\n\t\t\t\treturn [{ key: 'all', label: '', rows: this.rows }]\n\t\t\t}\n\t\t\tconst seeded = this.rows.filter((r) => r.group === VIEW_GROUPS.SEEDED)\n\t\t\tconst mine = this.rows.filter((r) => r.group !== VIEW_GROUPS.SEEDED && this.accessOf(r) === 'owner')\n\t\t\treturn [\n\t\t\t\t{ key: 'seeded', label: '', rows: seeded },\n\t\t\t\t{ key: 'mine', label: t('nextcloud-vue', 'My views'), rows: mine },\n\t\t\t\t{ key: 'shared', label: t('nextcloud-vue', 'Shared with me'), rows: shared },\n\t\t\t].filter((section) => section.rows.length > 0)\n\t\t},\n\n\t\t/**\n\t\t * The labels already in use.\n\t\t *\n\t\t * Read from the UNFILTERED list on purpose: reading it from `rows`\n\t\t * would remove every other label from the menu the moment one was\n\t\t * chosen, and a filter you cannot change without clearing it first is\n\t\t * a filter people stop using.\n\t\t *\n\t\t * @return {Array<string>} The labels.\n\t\t */\n\t\tofferedLabels() {\n\t\t\treturn labelsInUse(this.views)\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Whether a view's chip is the selected one.\n\t\t *\n\t\t * @param {object} view The view.\n\t\t * @return {boolean}\n\t\t */\n\t\tisChipSelected(view) {\n\t\t\treturn this.selectedViewId !== '' && (this.selectedViewId === String(view.id) || this.selectedViewId === String(view.slug))\n\t\t},\n\n\t\t/**\n\t\t * The name a row shows, indented to its depth.\n\t\t *\n\t\t * Indented with figure spaces rather than CSS padding because the row\n\t\t * is an `NcActionButton` whose label is read out as text: a screen\n\t\t * reader gets the same shape a sighted reader does, and no stylesheet\n\t\t * has to know about the tree.\n\t\t *\n\t\t * @param {object} row The row from buildViewTree().\n\t\t * @param {boolean} [withGroups] Append the groups the view is shared with.\n\t\t * @return {string} The name.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\trowName(row, withGroups = false) {\n\t\t\tconst groups = withGroups ? this.groupsOf(row.view) : ''\n\t\t\treturn `${'\\u2007'.repeat(row.depth * 2)}${this.nameWithCount(row.view)}${groups !== '' ? ` · ${groups}` : ''}`\n\t\t},\n\n\t\t/**\n\t\t * What the current user may do with a row's view.\n\t\t *\n\t\t * @param {object} row The row from buildViewTree().\n\t\t * @return {'owner'|'write'|'read'} The access.\n\t\t * @spec openspec/changes/saved-views-shared-by-role/tasks.md#task-1\n\t\t */\n\t\taccessOf(row) {\n\t\t\treturn viewAccess(row.view, this.currentUserId)\n\t\t},\n\n\t\t/**\n\t\t * The groups a view is shared with, as text for the row.\n\t\t *\n\t\t * @param {object} view The View API object.\n\t\t * @return {string} The group ids, comma-separated; empty when none.\n\t\t */\n\t\tgroupsOf(view) {\n\t\t\treturn (Array.isArray(view.sharedWith) ? view.sharedWith : [])\n\t\t\t\t.map((e) => e && e.group)\n\t\t\t\t.filter((g) => typeof g === 'string' && g !== '')\n\t\t\t\t.join(', ')\n\t\t},\n\n\t\tshareLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Share \"{name}\"', { name: view.name })\n\t\t},\n\n\t\tupdateLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Save the current view to \"{name}\"', { name: view.name })\n\t\t},\n\n\t\tcopyLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Save \"{name}\" as my view', { name: view.name })\n\t\t},\n\n\t\tpresentationLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Change how \"{name}\" shows', { name: view.name })\n\t\t},\n\n\t\tonPresentationRequest(view) {\n\t\t\t/**\n\t\t\t * @event presentation-request Presentation on an editable view; open the presentation dialog.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('presentation-request', view)\n\t\t},\n\n\t\tonShareRequest(view) {\n\t\t\t/**\n\t\t\t * @event share-request Share on an own view; open the share dialog.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('share-request', view)\n\t\t},\n\n\t\tonUpdateRequest(view) {\n\t\t\t/**\n\t\t\t * @event update-request Save on a writable shared view; save the current state to it.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('update-request', view)\n\t\t},\n\n\t\tonCopyRequest(view) {\n\t\t\t/**\n\t\t\t * @event copy-request \"Save as my view\" on a read-only shared view; store a personal copy.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('copy-request', view)\n\t\t},\n\n\t\t/**\n\t\t * The number of records a view matches, or null when none is known:\n\t\t * `counts[id]` (or `counts[slug]`) first, else the view's own `count`.\n\t\t *\n\t\t * @param {object} view The saved view.\n\t\t * @return {number|null}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\tcountOf(view) {\n\t\t\tconst counts = this.counts || {}\n\t\t\tlet value = counts[view.id]\n\t\t\tif (value === undefined || value === null) {\n\t\t\t\tvalue = counts[view.slug]\n\t\t\t}\n\t\t\tif (value === undefined || value === null) {\n\t\t\t\tvalue = view.count\n\t\t\t}\n\t\t\treturn (typeof value === 'number' && Number.isFinite(value)) ? value : null\n\t\t},\n\n\t\t/**\n\t\t * A view's name, followed by its count when one is known.\n\t\t *\n\t\t * @param {object} view The saved view.\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\tnameWithCount(view) {\n\t\t\tconst count = this.countOf(view)\n\t\t\treturn count === null ? view.name : `${view.name} (${count})`\n\t\t},\n\n\t\t/**\n\t\t * What a row is called to somebody who cannot see the indentation.\n\t\t *\n\t\t * Says the level in words, and says when a view's parent is one this\n\t\t * reader cannot see. A child that silently sits at the root looks\n\t\t * exactly like a view that never had a parent.\n\t\t *\n\t\t * @param {object} row The row from buildViewTree().\n\t\t * @return {string} The accessible label.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\trowLabel(row) {\n\t\t\tconst viewName = this.nameWithCount(row.view)\n\t\t\tconst name = row.depth === 0\n\t\t\t\t? viewName\n\t\t\t\t: t('nextcloud-vue', '{name}, level {level}', { name: viewName, level: row.depth + 1 })\n\n\t\t\tif (row.orphaned !== true) {\n\t\t\t\treturn name\n\t\t\t}\n\n\t\t\treturn t('nextcloud-vue', '{name}. Parts of this view come from one you cannot see.', { name })\n\t\t},\n\n\t\t/**\n\t\t * The accessible label of a label filter entry.\n\t\t *\n\t\t * @param {string} label The label.\n\t\t * @return {string} The label.\n\t\t */\n\t\tlabelFilterLabel(label) {\n\t\t\treturn this.labelFilter === label\n\t\t\t\t? t('nextcloud-vue', 'Stop filtering on \"{label}\"', { label })\n\t\t\t\t: t('nextcloud-vue', 'Show only views labelled \"{label}\"', { label })\n\t\t},\n\n\t\t/**\n\t\t * Turn a label filter on, or off when it is already on.\n\t\t *\n\t\t * Clicking the active label clears it, so the way out is the way in\n\t\t * rather than a second control somebody has to find.\n\t\t *\n\t\t * @param {string} label The label.\n\t\t */\n\t\tonLabelFilter(label) {\n\t\t\tthis.labelFilter = this.labelFilter === label ? '' : label\n\t\t},\n\n\t\t/**\n\t\t * Whether the delete entry renders for a view.\n\t\t *\n\t\t * @param {object} view The View API object.\n\t\t * @return {boolean} True when the current user owns the view.\n\t\t */\n\t\tisOwn(view) {\n\t\t\treturn isOwnView(view, this.currentUserId)\n\t\t},\n\n\t\t/**\n\t\t * Accessible label for a view's delete entry.\n\t\t *\n\t\t * @param {object} view The View API object.\n\t\t * @return {string} The label.\n\t\t */\n\t\tdeleteLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Delete \"{name}\"', { name: view.name })\n\t\t},\n\n\t\t/**\n\t\t * View-entry click: hand the view to the parent to apply.\n\t\t *\n\t\t * @param {object} view The clicked View API object.\n\t\t */\n\t\tonApply(view) {\n\t\t\t/**\n\t\t\t * @event apply A view entry was clicked; apply its stored state.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('apply', view)\n\t\t},\n\n\t\t/**\n\t\t * \"Save current view…\" click: ask the parent to open the save dialog.\n\t\t */\n\t\tonSaveRequest() {\n\t\t\t/**\n\t\t\t * @event save-request \"Save current view…\" clicked; open the save dialog.\n\t\t\t */\n\t\t\tthis.$emit('save-request')\n\t\t},\n\n\t\t/**\n\t\t * Delete-entry click: hand the view to the parent to confirm-delete.\n\t\t *\n\t\t * @param {object} view The View API object to delete.\n\t\t */\n\t\tonDeleteRequest(view) {\n\t\t\t/**\n\t\t\t * @event delete-request A view's delete entry was clicked; confirm and delete.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('delete-request', view)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* NcActionButtonGroup gives every child `flex: 1 1` (equal width) by\n default; override so the name button grows and the delete icon stays\n compact. :deep() is required — these li's belong to NcActionButtonGroup's\n own scope, not this component's. */\n.cn-saved-view-row :deep(.nc-button-group-content) {\n\tgap: 0;\n}\n\n/* Anchored on the FIRST child, not the last: a row carries one or two buttons\n depending on ownership, so \"the last one\" is the name button on a row that\n has only that. */\n.cn-saved-view-row :deep(.nc-button-group-content > li) {\n\tflex: 0 0 auto;\n}\n\n.cn-saved-view-row :deep(.nc-button-group-content > li:first-child) {\n\tflex: 1 1 auto;\n}\n\n/* NcActionButtonGroup also centers .action-button content (fine for an\n icon-only toolbar button, wrong for a row with a name) — restore the\n normal left-aligned NcActionButton look for the name button. */\n.cn-saved-view-row :deep(.nc-button-group-content > li:first-child .action-button) {\n\tjustify-content: flex-start;\n}\n</style>\n"],"names":["t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnSavedViewsControl.vue2.js","sources":["../../../../src/components/CnSavedViewsControl/CnSavedViewsControl.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<!--\n\t\tBoard look: the saved views are a row of chips on the ground, each with\n\t\tits count, the selected one filled. The menu stays for managing views\n\t\t(apply, rename, share, delete) and is reached from the \"Save view\"\n\t\tbutton. Without the look the control is the menu alone, as before.\n\t-->\n\t<div\n\t\tv-if=\"isBoardLook && chipRows.length > 0\"\n\t\tclass=\"cn-saved-views__chips\"\n\t\trole=\"group\"\n\t\t:aria-label=\"t('nextcloud-vue', 'Saved views')\"\n\t\tdata-testid=\"cn-saved-views-chips\">\n\t\t<button\n\t\t\tv-for=\"row in chipRows\"\n\t\t\t:key=\"`chip-${row.view.id || row.view.slug}`\"\n\t\t\ttype=\"button\"\n\t\t\tclass=\"cn-saved-views__chip\"\n\t\t\t:class=\"{ 'cn-saved-views__chip--selected': isChipSelected(row.view) }\"\n\t\t\t:aria-pressed=\"isChipSelected(row.view)\"\n\t\t\tdata-testid=\"cn-saved-views-chip\"\n\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t@click=\"onApply(row.view)\">\n\t\t\t<span class=\"cn-saved-views__chip-name\">{{ row.view.name }}</span>\n\t\t\t<span\n\t\t\t\tv-if=\"countOf(row.view) !== null\"\n\t\t\t\tclass=\"cn-saved-views__chip-count\"\n\t\t\t\tdata-testid=\"cn-saved-views-chip-count\">{{ countOf(row.view) }}</span>\n\t\t</button>\n\t</div>\n\t<NcActions\n\t\tv-bind=\"$attrs\"\n\t\t:class=\"{ 'cn-saved-views__save': isBoardLook }\"\n\t\t:forceMenu=\"true\"\n\t\t:forceName=\"true\"\n\t\t:menuName=\"isBoardLook ? t('nextcloud-vue', 'Save view') : menuLabel\"\n\t\tdata-testid=\"cn-saved-views-control\"\n\t\t:aria-label=\"isBoardLook ? t('nextcloud-vue', 'Save view') : menuLabel\">\n\t\t<template #icon>\n\t\t\t<ContentSaveOutline v-if=\"isBoardLook\" :size=\"14\" />\n\t\t\t<BookmarkOutline v-else :size=\"20\" />\n\t\t</template>\n\n\t\t<NcActionCaption :name=\"t('nextcloud-vue', 'Saved views')\" />\n\n\t\t<!-- Loading state -->\n\t\t<NcActionCaption\n\t\t\tv-if=\"loading\"\n\t\t\tdata-testid=\"cn-saved-views-loading\"\n\t\t\t:name=\"t('nextcloud-vue', 'Loading…')\" />\n\n\t\t<!-- Empty state -->\n\t\t<NcActionCaption\n\t\t\tv-else-if=\"views.length === 0\"\n\t\t\tdata-testid=\"cn-saved-views-empty\"\n\t\t\t:name=\"t('nextcloud-vue', 'No saved views yet')\" />\n\n\t\t<!-- The labels already in use, offered before anybody types a new one. -->\n\t\t<template v-if=\"!loading && offeredLabels.length > 0\">\n\t\t\t<NcActionButton\n\t\t\t\tv-for=\"label in offeredLabels\"\n\t\t\t\t:key=\"`label-${label}`\"\n\t\t\t\tdata-testid=\"cn-saved-views-label\"\n\t\t\t\t:data-label=\"label\"\n\t\t\t\t:modelValue=\"labelFilter === label\"\n\t\t\t\ttype=\"checkbox\"\n\t\t\t\t:aria-label=\"labelFilterLabel(label)\"\n\t\t\t\t@click=\"onLabelFilter(label)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<TagOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ label }}\n\t\t\t</NcActionButton>\n\t\t\t<NcActionSeparator />\n\t\t</template>\n\n\t\t<!--\n\t\t\tA label that matches nothing says so. Chained to the ROWS rather\n\t\t\tthan to the label list: written as an else-if of the labels block\n\t\t\tit only ever rendered on a page with no labels at all, which is\n\t\t\tthe one case it cannot be about.\n\t\t-->\n\t\t<NcActionCaption\n\t\t\tv-if=\"!loading && views.length > 0 && rows.length === 0\"\n\t\t\tdata-testid=\"cn-saved-views-none-for-label\"\n\t\t\t:name=\"t('nextcloud-vue', 'No views with this label')\" />\n\n\t\t<!-- One row per view, in tree order: the name applies it, a trailing\n\t\t icon button deletes it. NcActions only recognises NcAction* vnodes\n\t\t as menu items and silently drops anything else, so a row with more\n\t\t than one control has to be an NcActionButtonGroup. -->\n\t\t<template v-if=\"!loading\">\n\t\t\t<template v-for=\"section in sections\" :key=\"section.key\">\n\t\t\t\t<NcActionCaption\n\t\t\t\t\tv-if=\"section.label\"\n\t\t\t\t\t:data-testid=\"`cn-saved-views-section-${section.key}`\"\n\t\t\t\t\t:name=\"section.label\" />\n\t\t\t\t<NcActionButtonGroup\n\t\t\t\t\tv-for=\"row in section.rows\"\n\t\t\t\t\t:key=\"`view-${row.view.id || row.view.slug}`\"\n\t\t\t\t\tclass=\"cn-saved-view-row\">\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-item\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:data-depth=\"row.depth\"\n\t\t\t\t\t\t:data-group=\"row.group\"\n\t\t\t\t\t\t:data-access=\"accessOf(row)\"\n\t\t\t\t\t\t:aria-label=\"rowLabel(row)\"\n\t\t\t\t\t\t@click=\"onApply(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<EyeOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ rowName(row, section.key === 'shared') }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'owner' && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`share-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-share\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"shareLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onShareRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ShareVariantOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"(accessOf(row) === 'owner' || accessOf(row) === 'write') && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`presentation-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-presentation\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"presentationLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onPresentationRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ViewDashboardOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'write'\"\n\t\t\t\t\t\t:key=\"`update-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-update\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"updateLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onUpdateRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ContentSaveOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'read' && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`copy-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-copy\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"copyLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onCopyRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ContentCopy :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'owner' && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`delete-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-delete\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"deleteLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onDeleteRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<TrashCanOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</NcActionButtonGroup>\n\t\t\t</template>\n\t\t</template>\n\n\t\t<NcActionSeparator />\n\n\t\t<NcActionButton\n\t\t\tdata-testid=\"cn-saved-views-save\"\n\t\t\t:aria-label=\"t('nextcloud-vue', 'Save current view…')\"\n\t\t\t@click=\"onSaveRequest\">\n\t\t\t<template #icon>\n\t\t\t\t<ContentSaveOutline :size=\"20\" />\n\t\t\t</template>\n\t\t\t{{ t('nextcloud-vue', 'Save current view…') }}\n\t\t</NcActionButton>\n\t</NcActions>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActionButtonGroup, NcActionCaption, NcActions, NcActionSeparator } from '@nextcloud/vue'\nimport BookmarkOutline from 'vue-material-design-icons/BookmarkOutline.vue'\nimport ContentCopy from 'vue-material-design-icons/ContentCopy.vue'\nimport ContentSaveOutline from 'vue-material-design-icons/ContentSaveOutline.vue'\nimport EyeOutline from 'vue-material-design-icons/EyeOutline.vue'\nimport ShareVariantOutline from 'vue-material-design-icons/ShareVariantOutline.vue'\nimport TagOutline from 'vue-material-design-icons/TagOutline.vue'\nimport TrashCanOutline from 'vue-material-design-icons/TrashCanOutline.vue'\nimport ViewDashboardOutline from 'vue-material-design-icons/ViewDashboardOutline.vue'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { buildViewTree, labelsInUse, VIEW_GROUPS } from '../../utils/buildViewTree.js'\nimport { isOwnView, viewAccess } from '../../utils/savedViewHelpers.js'\n\n/**\n * CnSavedViewsControl — toolbar dropdown listing OpenRegister saved-search\n * views for the current index page (saved-views-ui).\n *\n * Purely presentational: the parent (CnIndexPage) owns fetching via\n * `useSavedViewsApi()` and all mutations; this control only lists the\n * given `views` and emits intents:\n *\n * - `@apply(view)` — a view entry was clicked; parent writes the view's\n * stored filters/search/sort into the route query.\n * - `@save-request()` — \"Save current view…\" clicked; parent opens\n * CnSaveViewDialog.\n * - `@delete-request(view)` — a view's trailing delete icon clicked; parent\n * opens a confirm dialog. Only rendered for views the current user owns\n * (`@self.access: owner`, else `view.owner === currentUserId`) — OpenRegister\n * refuses foreign deletes server-side anyway (owner-scoped 404).\n * - `@share-request(view)` — Share on an own view; parent opens the share dialog.\n * - `@update-request(view)` — Save on a view shared with write access; parent\n * saves the current state to it (never sending `sharedWith` or `owner`).\n * - `@presentation-request(view)` — Presentation on a view the user may edit\n * (`owner` or `write`); parent opens the presentation dialog (table, board, calendar).\n * - `@copy-request(view)` — \"Save as my view\" on a view shared read-only.\n *\n * Views shared with the user (`@self.access` `write` or `read`) list under\n * \"Shared with me\", next to \"My views\", once any exist; with none the list\n * renders as before.\n *\n * @event {object} apply — Apply the clicked view. Payload: the View API object.\n * @event {void} save-request — Open the save-current-view dialog.\n * @event {object} delete-request — Confirm-delete the clicked view. Payload: the View API object.\n * @event {object} share-request — Share the clicked own view. Payload: the View API object.\n * @event {object} update-request — Save the current state to the clicked writable shared view. Payload: the View API object.\n * @event {object} presentation-request — Change how the clicked view shows. Payload: the View API object.\n * @event {object} copy-request — Copy the clicked read-only shared view into a personal one. Payload: the View API object.\n */\nexport default {\n\tname: 'CnSavedViewsControl',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionButtonGroup,\n\t\tNcActionCaption,\n\t\tNcActionSeparator,\n\t\tBookmarkOutline,\n\t\tContentCopy,\n\t\tContentSaveOutline,\n\t\tEyeOutline,\n\t\tShareVariantOutline,\n\t\tTagOutline,\n\t\tTrashCanOutline,\n\t\tViewDashboardOutline,\n\t},\n\n\tinject: {\n\t\t/** The look CnAppRoot provides; `board` draws the views as chips. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\t// The chips and the menu are two roots under the board look.\n\tinheritAttrs: false,\n\n\tprops: {\n\t\t/** Views to list (View API objects from `GET /apps/openregister/api/views`). */\n\t\tviews: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** True while the parent is fetching the view list. */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** The signed-in NC user id — gates the per-view delete affordance. */\n\t\tcurrentUserId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * How deep the tree indents before it flattens. Mirrors\n\t\t * `savedViewTree.maxDepth` in the manifest. Flattening is about\n\t\t * indentation only: a view past the bound still renders.\n\t\t */\n\t\tmaxDepth: {\n\t\t\ttype: Number,\n\t\t\tdefault: 3,\n\t\t},\n\n\t\t/**\n\t\t * How many records each view matches, keyed by view id (or slug). A\n\t\t * view with a number here shows it after its name; a view can also\n\t\t * carry its own `count`. `null` (the default) shows no counts.\n\t\t *\n\t\t * @type {{[viewId: string]: number}|null}\n\t\t */\n\t\tcounts: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The id (or slug) of the applied view; its chip renders selected under\n\t\t * the board look.\n\t\t */\n\t\tselectedViewId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['apply', 'copy-request', 'delete-request', 'presentation-request', 'save-request', 'share-request', 'update-request'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** The label currently filtering the list, or '' for all of them. */\n\t\t\tlabelFilter: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the saved views draw as chips (the board look).\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisBoardLook() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/**\n\t\t * The views that get a chip: the top level of the tree, in tree order.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tchipRows() {\n\t\t\treturn this.rows.filter((row) => row.depth === 0)\n\t\t},\n\n\t\t/** @return {string} The dropdown trigger label. */\n\t\tmenuLabel() {\n\t\t\treturn t('nextcloud-vue', 'Views')\n\t\t},\n\n\t\t/** @return {string} The seeded group name, for the template. */\n\t\tSEEDED_GROUP() {\n\t\t\treturn VIEW_GROUPS.SEEDED\n\t\t},\n\n\t\t/**\n\t\t * The views in tree order, seeded first, filtered by the active label.\n\t\t *\n\t\t * @return {Array<object>} Rows of `{ view, depth, group, orphaned }`.\n\t\t */\n\t\trows() {\n\t\t\treturn buildViewTree({\n\t\t\t\tviews: this.views,\n\t\t\t\tlabelFilter: this.labelFilter,\n\t\t\t\tmaxDepth: this.maxDepth,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The rows in their sections. With no view shared with this user there\n\t\t * is one section without a caption, as the list always was. With\n\t\t * shared views: seeded views (no caption), \"My views\", \"Shared with me\".\n\t\t *\n\t\t * @return {Array<{key: string, label: string, rows: Array<object>}>} The sections.\n\t\t * @spec openspec/changes/saved-views-shared-by-role/tasks.md#task-1\n\t\t */\n\t\tsections() {\n\t\t\tconst shared = this.rows.filter((r) => r.group !== VIEW_GROUPS.SEEDED && this.accessOf(r) !== 'owner')\n\t\t\tif (shared.length === 0) {\n\t\t\t\treturn [{ key: 'all', label: '', rows: this.rows }]\n\t\t\t}\n\t\t\tconst seeded = this.rows.filter((r) => r.group === VIEW_GROUPS.SEEDED)\n\t\t\tconst mine = this.rows.filter((r) => r.group !== VIEW_GROUPS.SEEDED && this.accessOf(r) === 'owner')\n\t\t\treturn [\n\t\t\t\t{ key: 'seeded', label: '', rows: seeded },\n\t\t\t\t{ key: 'mine', label: t('nextcloud-vue', 'My views'), rows: mine },\n\t\t\t\t{ key: 'shared', label: t('nextcloud-vue', 'Shared with me'), rows: shared },\n\t\t\t].filter((section) => section.rows.length > 0)\n\t\t},\n\n\t\t/**\n\t\t * The labels already in use.\n\t\t *\n\t\t * Read from the UNFILTERED list on purpose: reading it from `rows`\n\t\t * would remove every other label from the menu the moment one was\n\t\t * chosen, and a filter you cannot change without clearing it first is\n\t\t * a filter people stop using.\n\t\t *\n\t\t * @return {Array<string>} The labels.\n\t\t */\n\t\tofferedLabels() {\n\t\t\treturn labelsInUse(this.views)\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Whether a view's chip is the selected one.\n\t\t *\n\t\t * @param {object} view The view.\n\t\t * @return {boolean}\n\t\t */\n\t\tisChipSelected(view) {\n\t\t\treturn this.selectedViewId !== '' && (this.selectedViewId === String(view.id) || this.selectedViewId === String(view.slug))\n\t\t},\n\n\t\t/**\n\t\t * The name a row shows, indented to its depth.\n\t\t *\n\t\t * Indented with figure spaces rather than CSS padding because the row\n\t\t * is an `NcActionButton` whose label is read out as text: a screen\n\t\t * reader gets the same shape a sighted reader does, and no stylesheet\n\t\t * has to know about the tree.\n\t\t *\n\t\t * @param {object} row The row from buildViewTree().\n\t\t * @param {boolean} [withGroups] Append the groups the view is shared with.\n\t\t * @return {string} The name.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\trowName(row, withGroups = false) {\n\t\t\tconst groups = withGroups ? this.groupsOf(row.view) : ''\n\t\t\treturn `${'\\u2007'.repeat(row.depth * 2)}${this.nameWithCount(row.view)}${groups !== '' ? ` · ${groups}` : ''}`\n\t\t},\n\n\t\t/**\n\t\t * What the current user may do with a row's view.\n\t\t *\n\t\t * @param {object} row The row from buildViewTree().\n\t\t * @return {'owner'|'write'|'read'} The access.\n\t\t * @spec openspec/changes/saved-views-shared-by-role/tasks.md#task-1\n\t\t */\n\t\taccessOf(row) {\n\t\t\treturn viewAccess(row.view, this.currentUserId)\n\t\t},\n\n\t\t/**\n\t\t * The groups a view is shared with, as text for the row.\n\t\t *\n\t\t * @param {object} view The View API object.\n\t\t * @return {string} The group ids, comma-separated; empty when none.\n\t\t */\n\t\tgroupsOf(view) {\n\t\t\treturn (Array.isArray(view.sharedWith) ? view.sharedWith : [])\n\t\t\t\t.map((e) => e && e.group)\n\t\t\t\t.filter((g) => typeof g === 'string' && g !== '')\n\t\t\t\t.join(', ')\n\t\t},\n\n\t\tshareLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Share \"{name}\"', { name: view.name })\n\t\t},\n\n\t\tupdateLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Save the current view to \"{name}\"', { name: view.name })\n\t\t},\n\n\t\tcopyLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Save \"{name}\" as my view', { name: view.name })\n\t\t},\n\n\t\tpresentationLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Change how \"{name}\" shows', { name: view.name })\n\t\t},\n\n\t\tonPresentationRequest(view) {\n\t\t\t/**\n\t\t\t * @event presentation-request Presentation on an editable view; open the presentation dialog.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('presentation-request', view)\n\t\t},\n\n\t\tonShareRequest(view) {\n\t\t\t/**\n\t\t\t * @event share-request Share on an own view; open the share dialog.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('share-request', view)\n\t\t},\n\n\t\tonUpdateRequest(view) {\n\t\t\t/**\n\t\t\t * @event update-request Save on a writable shared view; save the current state to it.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('update-request', view)\n\t\t},\n\n\t\tonCopyRequest(view) {\n\t\t\t/**\n\t\t\t * @event copy-request \"Save as my view\" on a read-only shared view; store a personal copy.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('copy-request', view)\n\t\t},\n\n\t\t/**\n\t\t * The number of records a view matches, or null when none is known:\n\t\t * `counts[id]` (or `counts[slug]`) first, else the view's own `count`.\n\t\t *\n\t\t * @param {object} view The saved view.\n\t\t * @return {number|null}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\tcountOf(view) {\n\t\t\tconst counts = this.counts || {}\n\t\t\tlet value = counts[view.id]\n\t\t\tif (value === undefined || value === null) {\n\t\t\t\tvalue = counts[view.slug]\n\t\t\t}\n\t\t\tif (value === undefined || value === null) {\n\t\t\t\tvalue = view.count\n\t\t\t}\n\t\t\treturn (typeof value === 'number' && Number.isFinite(value)) ? value : null\n\t\t},\n\n\t\t/**\n\t\t * A view's name, followed by its count when one is known.\n\t\t *\n\t\t * @param {object} view The saved view.\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\tnameWithCount(view) {\n\t\t\tconst count = this.countOf(view)\n\t\t\treturn count === null ? view.name : `${view.name} (${count})`\n\t\t},\n\n\t\t/**\n\t\t * What a row is called to somebody who cannot see the indentation.\n\t\t *\n\t\t * Says the level in words, and says when a view's parent is one this\n\t\t * reader cannot see. A child that silently sits at the root looks\n\t\t * exactly like a view that never had a parent.\n\t\t *\n\t\t * @param {object} row The row from buildViewTree().\n\t\t * @return {string} The accessible label.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\trowLabel(row) {\n\t\t\tconst viewName = this.nameWithCount(row.view)\n\t\t\tconst name = row.depth === 0\n\t\t\t\t? viewName\n\t\t\t\t: t('nextcloud-vue', '{name}, level {level}', { name: viewName, level: row.depth + 1 })\n\n\t\t\tif (row.orphaned !== true) {\n\t\t\t\treturn name\n\t\t\t}\n\n\t\t\treturn t('nextcloud-vue', '{name}. Parts of this view come from one you cannot see.', { name })\n\t\t},\n\n\t\t/**\n\t\t * The accessible label of a label filter entry.\n\t\t *\n\t\t * @param {string} label The label.\n\t\t * @return {string} The label.\n\t\t */\n\t\tlabelFilterLabel(label) {\n\t\t\treturn this.labelFilter === label\n\t\t\t\t? t('nextcloud-vue', 'Stop filtering on \"{label}\"', { label })\n\t\t\t\t: t('nextcloud-vue', 'Show only views labelled \"{label}\"', { label })\n\t\t},\n\n\t\t/**\n\t\t * Turn a label filter on, or off when it is already on.\n\t\t *\n\t\t * Clicking the active label clears it, so the way out is the way in\n\t\t * rather than a second control somebody has to find.\n\t\t *\n\t\t * @param {string} label The label.\n\t\t */\n\t\tonLabelFilter(label) {\n\t\t\tthis.labelFilter = this.labelFilter === label ? '' : label\n\t\t},\n\n\t\t/**\n\t\t * Whether the delete entry renders for a view.\n\t\t *\n\t\t * @param {object} view The View API object.\n\t\t * @return {boolean} True when the current user owns the view.\n\t\t */\n\t\tisOwn(view) {\n\t\t\treturn isOwnView(view, this.currentUserId)\n\t\t},\n\n\t\t/**\n\t\t * Accessible label for a view's delete entry.\n\t\t *\n\t\t * @param {object} view The View API object.\n\t\t * @return {string} The label.\n\t\t */\n\t\tdeleteLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Delete \"{name}\"', { name: view.name })\n\t\t},\n\n\t\t/**\n\t\t * View-entry click: hand the view to the parent to apply.\n\t\t *\n\t\t * @param {object} view The clicked View API object.\n\t\t */\n\t\tonApply(view) {\n\t\t\t/**\n\t\t\t * @event apply A view entry was clicked; apply its stored state.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('apply', view)\n\t\t},\n\n\t\t/**\n\t\t * \"Save current view…\" click: ask the parent to open the save dialog.\n\t\t */\n\t\tonSaveRequest() {\n\t\t\t/**\n\t\t\t * @event save-request \"Save current view…\" clicked; open the save dialog.\n\t\t\t */\n\t\t\tthis.$emit('save-request')\n\t\t},\n\n\t\t/**\n\t\t * Delete-entry click: hand the view to the parent to confirm-delete.\n\t\t *\n\t\t * @param {object} view The View API object to delete.\n\t\t */\n\t\tonDeleteRequest(view) {\n\t\t\t/**\n\t\t\t * @event delete-request A view's delete entry was clicked; confirm and delete.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('delete-request', view)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* NcActionButtonGroup gives every child `flex: 1 1` (equal width) by\n default; override so the name button grows and the delete icon stays\n compact. :deep() is required — these li's belong to NcActionButtonGroup's\n own scope, not this component's. */\n.cn-saved-view-row :deep(.nc-button-group-content) {\n\tgap: 0;\n}\n\n/* Anchored on the FIRST child, not the last: a row carries one or two buttons\n depending on ownership, so \"the last one\" is the name button on a row that\n has only that. */\n.cn-saved-view-row :deep(.nc-button-group-content > li) {\n\tflex: 0 0 auto;\n}\n\n.cn-saved-view-row :deep(.nc-button-group-content > li:first-child) {\n\tflex: 1 1 auto;\n}\n\n/* NcActionButtonGroup also centers .action-button content (fine for an\n icon-only toolbar button, wrong for a row with a name) — restore the\n normal left-aligned NcActionButton look for the name button. */\n.cn-saved-view-row :deep(.nc-button-group-content > li:first-child .action-button) {\n\tjustify-content: flex-start;\n}\n</style>\n"],"names":["t"],"mappings":"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"}
|
|
@@ -63,7 +63,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
63
63
|
($options.isBoardLook)
|
|
64
64
|
? (openBlock(), createBlock(_component_ContentSaveOutline, {
|
|
65
65
|
key: 0,
|
|
66
|
-
size:
|
|
66
|
+
size: 14
|
|
67
67
|
}))
|
|
68
68
|
: (openBlock(), createBlock(_component_BookmarkOutline, {
|
|
69
69
|
key: 1,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnSavedViewsControl.vue3.js","sources":["../../../../src/components/CnSavedViewsControl/CnSavedViewsControl.vue?vue&type=template&id=9dd8cb32&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<!--\n\t\tBoard look: the saved views are a row of chips on the ground, each with\n\t\tits count, the selected one filled. The menu stays for managing views\n\t\t(apply, rename, share, delete) and is reached from the \"Save view\"\n\t\tbutton. Without the look the control is the menu alone, as before.\n\t-->\n\t<div\n\t\tv-if=\"isBoardLook && chipRows.length > 0\"\n\t\tclass=\"cn-saved-views__chips\"\n\t\trole=\"group\"\n\t\t:aria-label=\"t('nextcloud-vue', 'Saved views')\"\n\t\tdata-testid=\"cn-saved-views-chips\">\n\t\t<button\n\t\t\tv-for=\"row in chipRows\"\n\t\t\t:key=\"`chip-${row.view.id || row.view.slug}`\"\n\t\t\ttype=\"button\"\n\t\t\tclass=\"cn-saved-views__chip\"\n\t\t\t:class=\"{ 'cn-saved-views__chip--selected': isChipSelected(row.view) }\"\n\t\t\t:aria-pressed=\"isChipSelected(row.view)\"\n\t\t\tdata-testid=\"cn-saved-views-chip\"\n\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t@click=\"onApply(row.view)\">\n\t\t\t<span class=\"cn-saved-views__chip-name\">{{ row.view.name }}</span>\n\t\t\t<span\n\t\t\t\tv-if=\"countOf(row.view) !== null\"\n\t\t\t\tclass=\"cn-saved-views__chip-count\"\n\t\t\t\tdata-testid=\"cn-saved-views-chip-count\">{{ countOf(row.view) }}</span>\n\t\t</button>\n\t</div>\n\t<NcActions\n\t\tv-bind=\"$attrs\"\n\t\t:class=\"{ 'cn-saved-views__save': isBoardLook }\"\n\t\t:forceMenu=\"true\"\n\t\t:forceName=\"true\"\n\t\t:menuName=\"isBoardLook ? t('nextcloud-vue', 'Save view') : menuLabel\"\n\t\tdata-testid=\"cn-saved-views-control\"\n\t\t:aria-label=\"isBoardLook ? t('nextcloud-vue', 'Save view') : menuLabel\">\n\t\t<template #icon>\n\t\t\t<ContentSaveOutline v-if=\"isBoardLook\" :size=\"18\" />\n\t\t\t<BookmarkOutline v-else :size=\"20\" />\n\t\t</template>\n\n\t\t<NcActionCaption :name=\"t('nextcloud-vue', 'Saved views')\" />\n\n\t\t<!-- Loading state -->\n\t\t<NcActionCaption\n\t\t\tv-if=\"loading\"\n\t\t\tdata-testid=\"cn-saved-views-loading\"\n\t\t\t:name=\"t('nextcloud-vue', 'Loading…')\" />\n\n\t\t<!-- Empty state -->\n\t\t<NcActionCaption\n\t\t\tv-else-if=\"views.length === 0\"\n\t\t\tdata-testid=\"cn-saved-views-empty\"\n\t\t\t:name=\"t('nextcloud-vue', 'No saved views yet')\" />\n\n\t\t<!-- The labels already in use, offered before anybody types a new one. -->\n\t\t<template v-if=\"!loading && offeredLabels.length > 0\">\n\t\t\t<NcActionButton\n\t\t\t\tv-for=\"label in offeredLabels\"\n\t\t\t\t:key=\"`label-${label}`\"\n\t\t\t\tdata-testid=\"cn-saved-views-label\"\n\t\t\t\t:data-label=\"label\"\n\t\t\t\t:modelValue=\"labelFilter === label\"\n\t\t\t\ttype=\"checkbox\"\n\t\t\t\t:aria-label=\"labelFilterLabel(label)\"\n\t\t\t\t@click=\"onLabelFilter(label)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<TagOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ label }}\n\t\t\t</NcActionButton>\n\t\t\t<NcActionSeparator />\n\t\t</template>\n\n\t\t<!--\n\t\t\tA label that matches nothing says so. Chained to the ROWS rather\n\t\t\tthan to the label list: written as an else-if of the labels block\n\t\t\tit only ever rendered on a page with no labels at all, which is\n\t\t\tthe one case it cannot be about.\n\t\t-->\n\t\t<NcActionCaption\n\t\t\tv-if=\"!loading && views.length > 0 && rows.length === 0\"\n\t\t\tdata-testid=\"cn-saved-views-none-for-label\"\n\t\t\t:name=\"t('nextcloud-vue', 'No views with this label')\" />\n\n\t\t<!-- One row per view, in tree order: the name applies it, a trailing\n\t\t icon button deletes it. NcActions only recognises NcAction* vnodes\n\t\t as menu items and silently drops anything else, so a row with more\n\t\t than one control has to be an NcActionButtonGroup. -->\n\t\t<template v-if=\"!loading\">\n\t\t\t<template v-for=\"section in sections\" :key=\"section.key\">\n\t\t\t\t<NcActionCaption\n\t\t\t\t\tv-if=\"section.label\"\n\t\t\t\t\t:data-testid=\"`cn-saved-views-section-${section.key}`\"\n\t\t\t\t\t:name=\"section.label\" />\n\t\t\t\t<NcActionButtonGroup\n\t\t\t\t\tv-for=\"row in section.rows\"\n\t\t\t\t\t:key=\"`view-${row.view.id || row.view.slug}`\"\n\t\t\t\t\tclass=\"cn-saved-view-row\">\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-item\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:data-depth=\"row.depth\"\n\t\t\t\t\t\t:data-group=\"row.group\"\n\t\t\t\t\t\t:data-access=\"accessOf(row)\"\n\t\t\t\t\t\t:aria-label=\"rowLabel(row)\"\n\t\t\t\t\t\t@click=\"onApply(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<EyeOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ rowName(row, section.key === 'shared') }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'owner' && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`share-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-share\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"shareLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onShareRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ShareVariantOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"(accessOf(row) === 'owner' || accessOf(row) === 'write') && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`presentation-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-presentation\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"presentationLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onPresentationRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ViewDashboardOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'write'\"\n\t\t\t\t\t\t:key=\"`update-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-update\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"updateLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onUpdateRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ContentSaveOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'read' && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`copy-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-copy\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"copyLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onCopyRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ContentCopy :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'owner' && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`delete-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-delete\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"deleteLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onDeleteRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<TrashCanOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</NcActionButtonGroup>\n\t\t\t</template>\n\t\t</template>\n\n\t\t<NcActionSeparator />\n\n\t\t<NcActionButton\n\t\t\tdata-testid=\"cn-saved-views-save\"\n\t\t\t:aria-label=\"t('nextcloud-vue', 'Save current view…')\"\n\t\t\t@click=\"onSaveRequest\">\n\t\t\t<template #icon>\n\t\t\t\t<ContentSaveOutline :size=\"20\" />\n\t\t\t</template>\n\t\t\t{{ t('nextcloud-vue', 'Save current view…') }}\n\t\t</NcActionButton>\n\t</NcActions>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActionButtonGroup, NcActionCaption, NcActions, NcActionSeparator } from '@nextcloud/vue'\nimport BookmarkOutline from 'vue-material-design-icons/BookmarkOutline.vue'\nimport ContentCopy from 'vue-material-design-icons/ContentCopy.vue'\nimport ContentSaveOutline from 'vue-material-design-icons/ContentSaveOutline.vue'\nimport EyeOutline from 'vue-material-design-icons/EyeOutline.vue'\nimport ShareVariantOutline from 'vue-material-design-icons/ShareVariantOutline.vue'\nimport TagOutline from 'vue-material-design-icons/TagOutline.vue'\nimport TrashCanOutline from 'vue-material-design-icons/TrashCanOutline.vue'\nimport ViewDashboardOutline from 'vue-material-design-icons/ViewDashboardOutline.vue'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { buildViewTree, labelsInUse, VIEW_GROUPS } from '../../utils/buildViewTree.js'\nimport { isOwnView, viewAccess } from '../../utils/savedViewHelpers.js'\n\n/**\n * CnSavedViewsControl — toolbar dropdown listing OpenRegister saved-search\n * views for the current index page (saved-views-ui).\n *\n * Purely presentational: the parent (CnIndexPage) owns fetching via\n * `useSavedViewsApi()` and all mutations; this control only lists the\n * given `views` and emits intents:\n *\n * - `@apply(view)` — a view entry was clicked; parent writes the view's\n * stored filters/search/sort into the route query.\n * - `@save-request()` — \"Save current view…\" clicked; parent opens\n * CnSaveViewDialog.\n * - `@delete-request(view)` — a view's trailing delete icon clicked; parent\n * opens a confirm dialog. Only rendered for views the current user owns\n * (`@self.access: owner`, else `view.owner === currentUserId`) — OpenRegister\n * refuses foreign deletes server-side anyway (owner-scoped 404).\n * - `@share-request(view)` — Share on an own view; parent opens the share dialog.\n * - `@update-request(view)` — Save on a view shared with write access; parent\n * saves the current state to it (never sending `sharedWith` or `owner`).\n * - `@presentation-request(view)` — Presentation on a view the user may edit\n * (`owner` or `write`); parent opens the presentation dialog (table, board, calendar).\n * - `@copy-request(view)` — \"Save as my view\" on a view shared read-only.\n *\n * Views shared with the user (`@self.access` `write` or `read`) list under\n * \"Shared with me\", next to \"My views\", once any exist; with none the list\n * renders as before.\n *\n * @event {object} apply — Apply the clicked view. Payload: the View API object.\n * @event {void} save-request — Open the save-current-view dialog.\n * @event {object} delete-request — Confirm-delete the clicked view. Payload: the View API object.\n * @event {object} share-request — Share the clicked own view. Payload: the View API object.\n * @event {object} update-request — Save the current state to the clicked writable shared view. Payload: the View API object.\n * @event {object} presentation-request — Change how the clicked view shows. Payload: the View API object.\n * @event {object} copy-request — Copy the clicked read-only shared view into a personal one. Payload: the View API object.\n */\nexport default {\n\tname: 'CnSavedViewsControl',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionButtonGroup,\n\t\tNcActionCaption,\n\t\tNcActionSeparator,\n\t\tBookmarkOutline,\n\t\tContentCopy,\n\t\tContentSaveOutline,\n\t\tEyeOutline,\n\t\tShareVariantOutline,\n\t\tTagOutline,\n\t\tTrashCanOutline,\n\t\tViewDashboardOutline,\n\t},\n\n\tinject: {\n\t\t/** The look CnAppRoot provides; `board` draws the views as chips. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\t// The chips and the menu are two roots under the board look.\n\tinheritAttrs: false,\n\n\tprops: {\n\t\t/** Views to list (View API objects from `GET /apps/openregister/api/views`). */\n\t\tviews: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** True while the parent is fetching the view list. */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** The signed-in NC user id — gates the per-view delete affordance. */\n\t\tcurrentUserId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * How deep the tree indents before it flattens. Mirrors\n\t\t * `savedViewTree.maxDepth` in the manifest. Flattening is about\n\t\t * indentation only: a view past the bound still renders.\n\t\t */\n\t\tmaxDepth: {\n\t\t\ttype: Number,\n\t\t\tdefault: 3,\n\t\t},\n\n\t\t/**\n\t\t * How many records each view matches, keyed by view id (or slug). A\n\t\t * view with a number here shows it after its name; a view can also\n\t\t * carry its own `count`. `null` (the default) shows no counts.\n\t\t *\n\t\t * @type {{[viewId: string]: number}|null}\n\t\t */\n\t\tcounts: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The id (or slug) of the applied view; its chip renders selected under\n\t\t * the board look.\n\t\t */\n\t\tselectedViewId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['apply', 'copy-request', 'delete-request', 'presentation-request', 'save-request', 'share-request', 'update-request'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** The label currently filtering the list, or '' for all of them. */\n\t\t\tlabelFilter: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the saved views draw as chips (the board look).\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisBoardLook() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/**\n\t\t * The views that get a chip: the top level of the tree, in tree order.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tchipRows() {\n\t\t\treturn this.rows.filter((row) => row.depth === 0)\n\t\t},\n\n\t\t/** @return {string} The dropdown trigger label. */\n\t\tmenuLabel() {\n\t\t\treturn t('nextcloud-vue', 'Views')\n\t\t},\n\n\t\t/** @return {string} The seeded group name, for the template. */\n\t\tSEEDED_GROUP() {\n\t\t\treturn VIEW_GROUPS.SEEDED\n\t\t},\n\n\t\t/**\n\t\t * The views in tree order, seeded first, filtered by the active label.\n\t\t *\n\t\t * @return {Array<object>} Rows of `{ view, depth, group, orphaned }`.\n\t\t */\n\t\trows() {\n\t\t\treturn buildViewTree({\n\t\t\t\tviews: this.views,\n\t\t\t\tlabelFilter: this.labelFilter,\n\t\t\t\tmaxDepth: this.maxDepth,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The rows in their sections. With no view shared with this user there\n\t\t * is one section without a caption, as the list always was. With\n\t\t * shared views: seeded views (no caption), \"My views\", \"Shared with me\".\n\t\t *\n\t\t * @return {Array<{key: string, label: string, rows: Array<object>}>} The sections.\n\t\t * @spec openspec/changes/saved-views-shared-by-role/tasks.md#task-1\n\t\t */\n\t\tsections() {\n\t\t\tconst shared = this.rows.filter((r) => r.group !== VIEW_GROUPS.SEEDED && this.accessOf(r) !== 'owner')\n\t\t\tif (shared.length === 0) {\n\t\t\t\treturn [{ key: 'all', label: '', rows: this.rows }]\n\t\t\t}\n\t\t\tconst seeded = this.rows.filter((r) => r.group === VIEW_GROUPS.SEEDED)\n\t\t\tconst mine = this.rows.filter((r) => r.group !== VIEW_GROUPS.SEEDED && this.accessOf(r) === 'owner')\n\t\t\treturn [\n\t\t\t\t{ key: 'seeded', label: '', rows: seeded },\n\t\t\t\t{ key: 'mine', label: t('nextcloud-vue', 'My views'), rows: mine },\n\t\t\t\t{ key: 'shared', label: t('nextcloud-vue', 'Shared with me'), rows: shared },\n\t\t\t].filter((section) => section.rows.length > 0)\n\t\t},\n\n\t\t/**\n\t\t * The labels already in use.\n\t\t *\n\t\t * Read from the UNFILTERED list on purpose: reading it from `rows`\n\t\t * would remove every other label from the menu the moment one was\n\t\t * chosen, and a filter you cannot change without clearing it first is\n\t\t * a filter people stop using.\n\t\t *\n\t\t * @return {Array<string>} The labels.\n\t\t */\n\t\tofferedLabels() {\n\t\t\treturn labelsInUse(this.views)\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Whether a view's chip is the selected one.\n\t\t *\n\t\t * @param {object} view The view.\n\t\t * @return {boolean}\n\t\t */\n\t\tisChipSelected(view) {\n\t\t\treturn this.selectedViewId !== '' && (this.selectedViewId === String(view.id) || this.selectedViewId === String(view.slug))\n\t\t},\n\n\t\t/**\n\t\t * The name a row shows, indented to its depth.\n\t\t *\n\t\t * Indented with figure spaces rather than CSS padding because the row\n\t\t * is an `NcActionButton` whose label is read out as text: a screen\n\t\t * reader gets the same shape a sighted reader does, and no stylesheet\n\t\t * has to know about the tree.\n\t\t *\n\t\t * @param {object} row The row from buildViewTree().\n\t\t * @param {boolean} [withGroups] Append the groups the view is shared with.\n\t\t * @return {string} The name.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\trowName(row, withGroups = false) {\n\t\t\tconst groups = withGroups ? this.groupsOf(row.view) : ''\n\t\t\treturn `${'\\u2007'.repeat(row.depth * 2)}${this.nameWithCount(row.view)}${groups !== '' ? ` · ${groups}` : ''}`\n\t\t},\n\n\t\t/**\n\t\t * What the current user may do with a row's view.\n\t\t *\n\t\t * @param {object} row The row from buildViewTree().\n\t\t * @return {'owner'|'write'|'read'} The access.\n\t\t * @spec openspec/changes/saved-views-shared-by-role/tasks.md#task-1\n\t\t */\n\t\taccessOf(row) {\n\t\t\treturn viewAccess(row.view, this.currentUserId)\n\t\t},\n\n\t\t/**\n\t\t * The groups a view is shared with, as text for the row.\n\t\t *\n\t\t * @param {object} view The View API object.\n\t\t * @return {string} The group ids, comma-separated; empty when none.\n\t\t */\n\t\tgroupsOf(view) {\n\t\t\treturn (Array.isArray(view.sharedWith) ? view.sharedWith : [])\n\t\t\t\t.map((e) => e && e.group)\n\t\t\t\t.filter((g) => typeof g === 'string' && g !== '')\n\t\t\t\t.join(', ')\n\t\t},\n\n\t\tshareLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Share \"{name}\"', { name: view.name })\n\t\t},\n\n\t\tupdateLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Save the current view to \"{name}\"', { name: view.name })\n\t\t},\n\n\t\tcopyLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Save \"{name}\" as my view', { name: view.name })\n\t\t},\n\n\t\tpresentationLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Change how \"{name}\" shows', { name: view.name })\n\t\t},\n\n\t\tonPresentationRequest(view) {\n\t\t\t/**\n\t\t\t * @event presentation-request Presentation on an editable view; open the presentation dialog.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('presentation-request', view)\n\t\t},\n\n\t\tonShareRequest(view) {\n\t\t\t/**\n\t\t\t * @event share-request Share on an own view; open the share dialog.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('share-request', view)\n\t\t},\n\n\t\tonUpdateRequest(view) {\n\t\t\t/**\n\t\t\t * @event update-request Save on a writable shared view; save the current state to it.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('update-request', view)\n\t\t},\n\n\t\tonCopyRequest(view) {\n\t\t\t/**\n\t\t\t * @event copy-request \"Save as my view\" on a read-only shared view; store a personal copy.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('copy-request', view)\n\t\t},\n\n\t\t/**\n\t\t * The number of records a view matches, or null when none is known:\n\t\t * `counts[id]` (or `counts[slug]`) first, else the view's own `count`.\n\t\t *\n\t\t * @param {object} view The saved view.\n\t\t * @return {number|null}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\tcountOf(view) {\n\t\t\tconst counts = this.counts || {}\n\t\t\tlet value = counts[view.id]\n\t\t\tif (value === undefined || value === null) {\n\t\t\t\tvalue = counts[view.slug]\n\t\t\t}\n\t\t\tif (value === undefined || value === null) {\n\t\t\t\tvalue = view.count\n\t\t\t}\n\t\t\treturn (typeof value === 'number' && Number.isFinite(value)) ? value : null\n\t\t},\n\n\t\t/**\n\t\t * A view's name, followed by its count when one is known.\n\t\t *\n\t\t * @param {object} view The saved view.\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\tnameWithCount(view) {\n\t\t\tconst count = this.countOf(view)\n\t\t\treturn count === null ? view.name : `${view.name} (${count})`\n\t\t},\n\n\t\t/**\n\t\t * What a row is called to somebody who cannot see the indentation.\n\t\t *\n\t\t * Says the level in words, and says when a view's parent is one this\n\t\t * reader cannot see. A child that silently sits at the root looks\n\t\t * exactly like a view that never had a parent.\n\t\t *\n\t\t * @param {object} row The row from buildViewTree().\n\t\t * @return {string} The accessible label.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\trowLabel(row) {\n\t\t\tconst viewName = this.nameWithCount(row.view)\n\t\t\tconst name = row.depth === 0\n\t\t\t\t? viewName\n\t\t\t\t: t('nextcloud-vue', '{name}, level {level}', { name: viewName, level: row.depth + 1 })\n\n\t\t\tif (row.orphaned !== true) {\n\t\t\t\treturn name\n\t\t\t}\n\n\t\t\treturn t('nextcloud-vue', '{name}. Parts of this view come from one you cannot see.', { name })\n\t\t},\n\n\t\t/**\n\t\t * The accessible label of a label filter entry.\n\t\t *\n\t\t * @param {string} label The label.\n\t\t * @return {string} The label.\n\t\t */\n\t\tlabelFilterLabel(label) {\n\t\t\treturn this.labelFilter === label\n\t\t\t\t? t('nextcloud-vue', 'Stop filtering on \"{label}\"', { label })\n\t\t\t\t: t('nextcloud-vue', 'Show only views labelled \"{label}\"', { label })\n\t\t},\n\n\t\t/**\n\t\t * Turn a label filter on, or off when it is already on.\n\t\t *\n\t\t * Clicking the active label clears it, so the way out is the way in\n\t\t * rather than a second control somebody has to find.\n\t\t *\n\t\t * @param {string} label The label.\n\t\t */\n\t\tonLabelFilter(label) {\n\t\t\tthis.labelFilter = this.labelFilter === label ? '' : label\n\t\t},\n\n\t\t/**\n\t\t * Whether the delete entry renders for a view.\n\t\t *\n\t\t * @param {object} view The View API object.\n\t\t * @return {boolean} True when the current user owns the view.\n\t\t */\n\t\tisOwn(view) {\n\t\t\treturn isOwnView(view, this.currentUserId)\n\t\t},\n\n\t\t/**\n\t\t * Accessible label for a view's delete entry.\n\t\t *\n\t\t * @param {object} view The View API object.\n\t\t * @return {string} The label.\n\t\t */\n\t\tdeleteLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Delete \"{name}\"', { name: view.name })\n\t\t},\n\n\t\t/**\n\t\t * View-entry click: hand the view to the parent to apply.\n\t\t *\n\t\t * @param {object} view The clicked View API object.\n\t\t */\n\t\tonApply(view) {\n\t\t\t/**\n\t\t\t * @event apply A view entry was clicked; apply its stored state.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('apply', view)\n\t\t},\n\n\t\t/**\n\t\t * \"Save current view…\" click: ask the parent to open the save dialog.\n\t\t */\n\t\tonSaveRequest() {\n\t\t\t/**\n\t\t\t * @event save-request \"Save current view…\" clicked; open the save dialog.\n\t\t\t */\n\t\t\tthis.$emit('save-request')\n\t\t},\n\n\t\t/**\n\t\t * Delete-entry click: hand the view to the parent to confirm-delete.\n\t\t *\n\t\t * @param {object} view The View API object to delete.\n\t\t */\n\t\tonDeleteRequest(view) {\n\t\t\t/**\n\t\t\t * @event delete-request A view's delete entry was clicked; confirm and delete.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('delete-request', view)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* NcActionButtonGroup gives every child `flex: 1 1` (equal width) by\n default; override so the name button grows and the delete icon stays\n compact. :deep() is required — these li's belong to NcActionButtonGroup's\n own scope, not this component's. */\n.cn-saved-view-row :deep(.nc-button-group-content) {\n\tgap: 0;\n}\n\n/* Anchored on the FIRST child, not the last: a row carries one or two buttons\n depending on ownership, so \"the last one\" is the name button on a row that\n has only that. */\n.cn-saved-view-row :deep(.nc-button-group-content > li) {\n\tflex: 0 0 auto;\n}\n\n.cn-saved-view-row :deep(.nc-button-group-content > li:first-child) {\n\tflex: 1 1 auto;\n}\n\n/* NcActionButtonGroup also centers .action-button content (fine for an\n icon-only toolbar button, wrong for a row with a name) — restore the\n normal left-aligned NcActionButton look for the name button. */\n.cn-saved-view-row :deep(.nc-button-group-content > li:first-child .action-button) {\n\tjustify-content: flex-start;\n}\n</style>\n"],"names":["_createElementBlock","_Fragment","_renderList","_createElementVNode","_toDisplayString","_openBlock","_createVNode","_mergeProps","_createBlock","_createTextVNode"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnSavedViewsControl.vue3.js","sources":["../../../../src/components/CnSavedViewsControl/CnSavedViewsControl.vue?vue&type=template&id=9dd8cb32&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<!--\n\t\tBoard look: the saved views are a row of chips on the ground, each with\n\t\tits count, the selected one filled. The menu stays for managing views\n\t\t(apply, rename, share, delete) and is reached from the \"Save view\"\n\t\tbutton. Without the look the control is the menu alone, as before.\n\t-->\n\t<div\n\t\tv-if=\"isBoardLook && chipRows.length > 0\"\n\t\tclass=\"cn-saved-views__chips\"\n\t\trole=\"group\"\n\t\t:aria-label=\"t('nextcloud-vue', 'Saved views')\"\n\t\tdata-testid=\"cn-saved-views-chips\">\n\t\t<button\n\t\t\tv-for=\"row in chipRows\"\n\t\t\t:key=\"`chip-${row.view.id || row.view.slug}`\"\n\t\t\ttype=\"button\"\n\t\t\tclass=\"cn-saved-views__chip\"\n\t\t\t:class=\"{ 'cn-saved-views__chip--selected': isChipSelected(row.view) }\"\n\t\t\t:aria-pressed=\"isChipSelected(row.view)\"\n\t\t\tdata-testid=\"cn-saved-views-chip\"\n\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t@click=\"onApply(row.view)\">\n\t\t\t<span class=\"cn-saved-views__chip-name\">{{ row.view.name }}</span>\n\t\t\t<span\n\t\t\t\tv-if=\"countOf(row.view) !== null\"\n\t\t\t\tclass=\"cn-saved-views__chip-count\"\n\t\t\t\tdata-testid=\"cn-saved-views-chip-count\">{{ countOf(row.view) }}</span>\n\t\t</button>\n\t</div>\n\t<NcActions\n\t\tv-bind=\"$attrs\"\n\t\t:class=\"{ 'cn-saved-views__save': isBoardLook }\"\n\t\t:forceMenu=\"true\"\n\t\t:forceName=\"true\"\n\t\t:menuName=\"isBoardLook ? t('nextcloud-vue', 'Save view') : menuLabel\"\n\t\tdata-testid=\"cn-saved-views-control\"\n\t\t:aria-label=\"isBoardLook ? t('nextcloud-vue', 'Save view') : menuLabel\">\n\t\t<template #icon>\n\t\t\t<ContentSaveOutline v-if=\"isBoardLook\" :size=\"14\" />\n\t\t\t<BookmarkOutline v-else :size=\"20\" />\n\t\t</template>\n\n\t\t<NcActionCaption :name=\"t('nextcloud-vue', 'Saved views')\" />\n\n\t\t<!-- Loading state -->\n\t\t<NcActionCaption\n\t\t\tv-if=\"loading\"\n\t\t\tdata-testid=\"cn-saved-views-loading\"\n\t\t\t:name=\"t('nextcloud-vue', 'Loading…')\" />\n\n\t\t<!-- Empty state -->\n\t\t<NcActionCaption\n\t\t\tv-else-if=\"views.length === 0\"\n\t\t\tdata-testid=\"cn-saved-views-empty\"\n\t\t\t:name=\"t('nextcloud-vue', 'No saved views yet')\" />\n\n\t\t<!-- The labels already in use, offered before anybody types a new one. -->\n\t\t<template v-if=\"!loading && offeredLabels.length > 0\">\n\t\t\t<NcActionButton\n\t\t\t\tv-for=\"label in offeredLabels\"\n\t\t\t\t:key=\"`label-${label}`\"\n\t\t\t\tdata-testid=\"cn-saved-views-label\"\n\t\t\t\t:data-label=\"label\"\n\t\t\t\t:modelValue=\"labelFilter === label\"\n\t\t\t\ttype=\"checkbox\"\n\t\t\t\t:aria-label=\"labelFilterLabel(label)\"\n\t\t\t\t@click=\"onLabelFilter(label)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<TagOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ label }}\n\t\t\t</NcActionButton>\n\t\t\t<NcActionSeparator />\n\t\t</template>\n\n\t\t<!--\n\t\t\tA label that matches nothing says so. Chained to the ROWS rather\n\t\t\tthan to the label list: written as an else-if of the labels block\n\t\t\tit only ever rendered on a page with no labels at all, which is\n\t\t\tthe one case it cannot be about.\n\t\t-->\n\t\t<NcActionCaption\n\t\t\tv-if=\"!loading && views.length > 0 && rows.length === 0\"\n\t\t\tdata-testid=\"cn-saved-views-none-for-label\"\n\t\t\t:name=\"t('nextcloud-vue', 'No views with this label')\" />\n\n\t\t<!-- One row per view, in tree order: the name applies it, a trailing\n\t\t icon button deletes it. NcActions only recognises NcAction* vnodes\n\t\t as menu items and silently drops anything else, so a row with more\n\t\t than one control has to be an NcActionButtonGroup. -->\n\t\t<template v-if=\"!loading\">\n\t\t\t<template v-for=\"section in sections\" :key=\"section.key\">\n\t\t\t\t<NcActionCaption\n\t\t\t\t\tv-if=\"section.label\"\n\t\t\t\t\t:data-testid=\"`cn-saved-views-section-${section.key}`\"\n\t\t\t\t\t:name=\"section.label\" />\n\t\t\t\t<NcActionButtonGroup\n\t\t\t\t\tv-for=\"row in section.rows\"\n\t\t\t\t\t:key=\"`view-${row.view.id || row.view.slug}`\"\n\t\t\t\t\tclass=\"cn-saved-view-row\">\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-item\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:data-depth=\"row.depth\"\n\t\t\t\t\t\t:data-group=\"row.group\"\n\t\t\t\t\t\t:data-access=\"accessOf(row)\"\n\t\t\t\t\t\t:aria-label=\"rowLabel(row)\"\n\t\t\t\t\t\t@click=\"onApply(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<EyeOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ rowName(row, section.key === 'shared') }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'owner' && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`share-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-share\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"shareLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onShareRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ShareVariantOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"(accessOf(row) === 'owner' || accessOf(row) === 'write') && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`presentation-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-presentation\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"presentationLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onPresentationRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ViewDashboardOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'write'\"\n\t\t\t\t\t\t:key=\"`update-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-update\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"updateLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onUpdateRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ContentSaveOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'read' && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`copy-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-copy\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"copyLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onCopyRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ContentCopy :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"accessOf(row) === 'owner' && row.group !== SEEDED_GROUP\"\n\t\t\t\t\t\t:key=\"`delete-${row.view.id || row.view.slug}`\"\n\t\t\t\t\t\tdata-testid=\"cn-saved-views-delete\"\n\t\t\t\t\t\t:data-view-id=\"row.view.id || row.view.slug\"\n\t\t\t\t\t\t:aria-label=\"deleteLabel(row.view)\"\n\t\t\t\t\t\t@click=\"onDeleteRequest(row.view)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<TrashCanOutline :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</NcActionButtonGroup>\n\t\t\t</template>\n\t\t</template>\n\n\t\t<NcActionSeparator />\n\n\t\t<NcActionButton\n\t\t\tdata-testid=\"cn-saved-views-save\"\n\t\t\t:aria-label=\"t('nextcloud-vue', 'Save current view…')\"\n\t\t\t@click=\"onSaveRequest\">\n\t\t\t<template #icon>\n\t\t\t\t<ContentSaveOutline :size=\"20\" />\n\t\t\t</template>\n\t\t\t{{ t('nextcloud-vue', 'Save current view…') }}\n\t\t</NcActionButton>\n\t</NcActions>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActionButtonGroup, NcActionCaption, NcActions, NcActionSeparator } from '@nextcloud/vue'\nimport BookmarkOutline from 'vue-material-design-icons/BookmarkOutline.vue'\nimport ContentCopy from 'vue-material-design-icons/ContentCopy.vue'\nimport ContentSaveOutline from 'vue-material-design-icons/ContentSaveOutline.vue'\nimport EyeOutline from 'vue-material-design-icons/EyeOutline.vue'\nimport ShareVariantOutline from 'vue-material-design-icons/ShareVariantOutline.vue'\nimport TagOutline from 'vue-material-design-icons/TagOutline.vue'\nimport TrashCanOutline from 'vue-material-design-icons/TrashCanOutline.vue'\nimport ViewDashboardOutline from 'vue-material-design-icons/ViewDashboardOutline.vue'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { buildViewTree, labelsInUse, VIEW_GROUPS } from '../../utils/buildViewTree.js'\nimport { isOwnView, viewAccess } from '../../utils/savedViewHelpers.js'\n\n/**\n * CnSavedViewsControl — toolbar dropdown listing OpenRegister saved-search\n * views for the current index page (saved-views-ui).\n *\n * Purely presentational: the parent (CnIndexPage) owns fetching via\n * `useSavedViewsApi()` and all mutations; this control only lists the\n * given `views` and emits intents:\n *\n * - `@apply(view)` — a view entry was clicked; parent writes the view's\n * stored filters/search/sort into the route query.\n * - `@save-request()` — \"Save current view…\" clicked; parent opens\n * CnSaveViewDialog.\n * - `@delete-request(view)` — a view's trailing delete icon clicked; parent\n * opens a confirm dialog. Only rendered for views the current user owns\n * (`@self.access: owner`, else `view.owner === currentUserId`) — OpenRegister\n * refuses foreign deletes server-side anyway (owner-scoped 404).\n * - `@share-request(view)` — Share on an own view; parent opens the share dialog.\n * - `@update-request(view)` — Save on a view shared with write access; parent\n * saves the current state to it (never sending `sharedWith` or `owner`).\n * - `@presentation-request(view)` — Presentation on a view the user may edit\n * (`owner` or `write`); parent opens the presentation dialog (table, board, calendar).\n * - `@copy-request(view)` — \"Save as my view\" on a view shared read-only.\n *\n * Views shared with the user (`@self.access` `write` or `read`) list under\n * \"Shared with me\", next to \"My views\", once any exist; with none the list\n * renders as before.\n *\n * @event {object} apply — Apply the clicked view. Payload: the View API object.\n * @event {void} save-request — Open the save-current-view dialog.\n * @event {object} delete-request — Confirm-delete the clicked view. Payload: the View API object.\n * @event {object} share-request — Share the clicked own view. Payload: the View API object.\n * @event {object} update-request — Save the current state to the clicked writable shared view. Payload: the View API object.\n * @event {object} presentation-request — Change how the clicked view shows. Payload: the View API object.\n * @event {object} copy-request — Copy the clicked read-only shared view into a personal one. Payload: the View API object.\n */\nexport default {\n\tname: 'CnSavedViewsControl',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionButtonGroup,\n\t\tNcActionCaption,\n\t\tNcActionSeparator,\n\t\tBookmarkOutline,\n\t\tContentCopy,\n\t\tContentSaveOutline,\n\t\tEyeOutline,\n\t\tShareVariantOutline,\n\t\tTagOutline,\n\t\tTrashCanOutline,\n\t\tViewDashboardOutline,\n\t},\n\n\tinject: {\n\t\t/** The look CnAppRoot provides; `board` draws the views as chips. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\t// The chips and the menu are two roots under the board look.\n\tinheritAttrs: false,\n\n\tprops: {\n\t\t/** Views to list (View API objects from `GET /apps/openregister/api/views`). */\n\t\tviews: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** True while the parent is fetching the view list. */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** The signed-in NC user id — gates the per-view delete affordance. */\n\t\tcurrentUserId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * How deep the tree indents before it flattens. Mirrors\n\t\t * `savedViewTree.maxDepth` in the manifest. Flattening is about\n\t\t * indentation only: a view past the bound still renders.\n\t\t */\n\t\tmaxDepth: {\n\t\t\ttype: Number,\n\t\t\tdefault: 3,\n\t\t},\n\n\t\t/**\n\t\t * How many records each view matches, keyed by view id (or slug). A\n\t\t * view with a number here shows it after its name; a view can also\n\t\t * carry its own `count`. `null` (the default) shows no counts.\n\t\t *\n\t\t * @type {{[viewId: string]: number}|null}\n\t\t */\n\t\tcounts: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The id (or slug) of the applied view; its chip renders selected under\n\t\t * the board look.\n\t\t */\n\t\tselectedViewId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['apply', 'copy-request', 'delete-request', 'presentation-request', 'save-request', 'share-request', 'update-request'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** The label currently filtering the list, or '' for all of them. */\n\t\t\tlabelFilter: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the saved views draw as chips (the board look).\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisBoardLook() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/**\n\t\t * The views that get a chip: the top level of the tree, in tree order.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tchipRows() {\n\t\t\treturn this.rows.filter((row) => row.depth === 0)\n\t\t},\n\n\t\t/** @return {string} The dropdown trigger label. */\n\t\tmenuLabel() {\n\t\t\treturn t('nextcloud-vue', 'Views')\n\t\t},\n\n\t\t/** @return {string} The seeded group name, for the template. */\n\t\tSEEDED_GROUP() {\n\t\t\treturn VIEW_GROUPS.SEEDED\n\t\t},\n\n\t\t/**\n\t\t * The views in tree order, seeded first, filtered by the active label.\n\t\t *\n\t\t * @return {Array<object>} Rows of `{ view, depth, group, orphaned }`.\n\t\t */\n\t\trows() {\n\t\t\treturn buildViewTree({\n\t\t\t\tviews: this.views,\n\t\t\t\tlabelFilter: this.labelFilter,\n\t\t\t\tmaxDepth: this.maxDepth,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The rows in their sections. With no view shared with this user there\n\t\t * is one section without a caption, as the list always was. With\n\t\t * shared views: seeded views (no caption), \"My views\", \"Shared with me\".\n\t\t *\n\t\t * @return {Array<{key: string, label: string, rows: Array<object>}>} The sections.\n\t\t * @spec openspec/changes/saved-views-shared-by-role/tasks.md#task-1\n\t\t */\n\t\tsections() {\n\t\t\tconst shared = this.rows.filter((r) => r.group !== VIEW_GROUPS.SEEDED && this.accessOf(r) !== 'owner')\n\t\t\tif (shared.length === 0) {\n\t\t\t\treturn [{ key: 'all', label: '', rows: this.rows }]\n\t\t\t}\n\t\t\tconst seeded = this.rows.filter((r) => r.group === VIEW_GROUPS.SEEDED)\n\t\t\tconst mine = this.rows.filter((r) => r.group !== VIEW_GROUPS.SEEDED && this.accessOf(r) === 'owner')\n\t\t\treturn [\n\t\t\t\t{ key: 'seeded', label: '', rows: seeded },\n\t\t\t\t{ key: 'mine', label: t('nextcloud-vue', 'My views'), rows: mine },\n\t\t\t\t{ key: 'shared', label: t('nextcloud-vue', 'Shared with me'), rows: shared },\n\t\t\t].filter((section) => section.rows.length > 0)\n\t\t},\n\n\t\t/**\n\t\t * The labels already in use.\n\t\t *\n\t\t * Read from the UNFILTERED list on purpose: reading it from `rows`\n\t\t * would remove every other label from the menu the moment one was\n\t\t * chosen, and a filter you cannot change without clearing it first is\n\t\t * a filter people stop using.\n\t\t *\n\t\t * @return {Array<string>} The labels.\n\t\t */\n\t\tofferedLabels() {\n\t\t\treturn labelsInUse(this.views)\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Whether a view's chip is the selected one.\n\t\t *\n\t\t * @param {object} view The view.\n\t\t * @return {boolean}\n\t\t */\n\t\tisChipSelected(view) {\n\t\t\treturn this.selectedViewId !== '' && (this.selectedViewId === String(view.id) || this.selectedViewId === String(view.slug))\n\t\t},\n\n\t\t/**\n\t\t * The name a row shows, indented to its depth.\n\t\t *\n\t\t * Indented with figure spaces rather than CSS padding because the row\n\t\t * is an `NcActionButton` whose label is read out as text: a screen\n\t\t * reader gets the same shape a sighted reader does, and no stylesheet\n\t\t * has to know about the tree.\n\t\t *\n\t\t * @param {object} row The row from buildViewTree().\n\t\t * @param {boolean} [withGroups] Append the groups the view is shared with.\n\t\t * @return {string} The name.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\trowName(row, withGroups = false) {\n\t\t\tconst groups = withGroups ? this.groupsOf(row.view) : ''\n\t\t\treturn `${'\\u2007'.repeat(row.depth * 2)}${this.nameWithCount(row.view)}${groups !== '' ? ` · ${groups}` : ''}`\n\t\t},\n\n\t\t/**\n\t\t * What the current user may do with a row's view.\n\t\t *\n\t\t * @param {object} row The row from buildViewTree().\n\t\t * @return {'owner'|'write'|'read'} The access.\n\t\t * @spec openspec/changes/saved-views-shared-by-role/tasks.md#task-1\n\t\t */\n\t\taccessOf(row) {\n\t\t\treturn viewAccess(row.view, this.currentUserId)\n\t\t},\n\n\t\t/**\n\t\t * The groups a view is shared with, as text for the row.\n\t\t *\n\t\t * @param {object} view The View API object.\n\t\t * @return {string} The group ids, comma-separated; empty when none.\n\t\t */\n\t\tgroupsOf(view) {\n\t\t\treturn (Array.isArray(view.sharedWith) ? view.sharedWith : [])\n\t\t\t\t.map((e) => e && e.group)\n\t\t\t\t.filter((g) => typeof g === 'string' && g !== '')\n\t\t\t\t.join(', ')\n\t\t},\n\n\t\tshareLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Share \"{name}\"', { name: view.name })\n\t\t},\n\n\t\tupdateLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Save the current view to \"{name}\"', { name: view.name })\n\t\t},\n\n\t\tcopyLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Save \"{name}\" as my view', { name: view.name })\n\t\t},\n\n\t\tpresentationLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Change how \"{name}\" shows', { name: view.name })\n\t\t},\n\n\t\tonPresentationRequest(view) {\n\t\t\t/**\n\t\t\t * @event presentation-request Presentation on an editable view; open the presentation dialog.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('presentation-request', view)\n\t\t},\n\n\t\tonShareRequest(view) {\n\t\t\t/**\n\t\t\t * @event share-request Share on an own view; open the share dialog.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('share-request', view)\n\t\t},\n\n\t\tonUpdateRequest(view) {\n\t\t\t/**\n\t\t\t * @event update-request Save on a writable shared view; save the current state to it.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('update-request', view)\n\t\t},\n\n\t\tonCopyRequest(view) {\n\t\t\t/**\n\t\t\t * @event copy-request \"Save as my view\" on a read-only shared view; store a personal copy.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('copy-request', view)\n\t\t},\n\n\t\t/**\n\t\t * The number of records a view matches, or null when none is known:\n\t\t * `counts[id]` (or `counts[slug]`) first, else the view's own `count`.\n\t\t *\n\t\t * @param {object} view The saved view.\n\t\t * @return {number|null}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\tcountOf(view) {\n\t\t\tconst counts = this.counts || {}\n\t\t\tlet value = counts[view.id]\n\t\t\tif (value === undefined || value === null) {\n\t\t\t\tvalue = counts[view.slug]\n\t\t\t}\n\t\t\tif (value === undefined || value === null) {\n\t\t\t\tvalue = view.count\n\t\t\t}\n\t\t\treturn (typeof value === 'number' && Number.isFinite(value)) ? value : null\n\t\t},\n\n\t\t/**\n\t\t * A view's name, followed by its count when one is known.\n\t\t *\n\t\t * @param {object} view The saved view.\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\tnameWithCount(view) {\n\t\t\tconst count = this.countOf(view)\n\t\t\treturn count === null ? view.name : `${view.name} (${count})`\n\t\t},\n\n\t\t/**\n\t\t * What a row is called to somebody who cannot see the indentation.\n\t\t *\n\t\t * Says the level in words, and says when a view's parent is one this\n\t\t * reader cannot see. A child that silently sits at the root looks\n\t\t * exactly like a view that never had a parent.\n\t\t *\n\t\t * @param {object} row The row from buildViewTree().\n\t\t * @return {string} The accessible label.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\trowLabel(row) {\n\t\t\tconst viewName = this.nameWithCount(row.view)\n\t\t\tconst name = row.depth === 0\n\t\t\t\t? viewName\n\t\t\t\t: t('nextcloud-vue', '{name}, level {level}', { name: viewName, level: row.depth + 1 })\n\n\t\t\tif (row.orphaned !== true) {\n\t\t\t\treturn name\n\t\t\t}\n\n\t\t\treturn t('nextcloud-vue', '{name}. Parts of this view come from one you cannot see.', { name })\n\t\t},\n\n\t\t/**\n\t\t * The accessible label of a label filter entry.\n\t\t *\n\t\t * @param {string} label The label.\n\t\t * @return {string} The label.\n\t\t */\n\t\tlabelFilterLabel(label) {\n\t\t\treturn this.labelFilter === label\n\t\t\t\t? t('nextcloud-vue', 'Stop filtering on \"{label}\"', { label })\n\t\t\t\t: t('nextcloud-vue', 'Show only views labelled \"{label}\"', { label })\n\t\t},\n\n\t\t/**\n\t\t * Turn a label filter on, or off when it is already on.\n\t\t *\n\t\t * Clicking the active label clears it, so the way out is the way in\n\t\t * rather than a second control somebody has to find.\n\t\t *\n\t\t * @param {string} label The label.\n\t\t */\n\t\tonLabelFilter(label) {\n\t\t\tthis.labelFilter = this.labelFilter === label ? '' : label\n\t\t},\n\n\t\t/**\n\t\t * Whether the delete entry renders for a view.\n\t\t *\n\t\t * @param {object} view The View API object.\n\t\t * @return {boolean} True when the current user owns the view.\n\t\t */\n\t\tisOwn(view) {\n\t\t\treturn isOwnView(view, this.currentUserId)\n\t\t},\n\n\t\t/**\n\t\t * Accessible label for a view's delete entry.\n\t\t *\n\t\t * @param {object} view The View API object.\n\t\t * @return {string} The label.\n\t\t */\n\t\tdeleteLabel(view) {\n\t\t\treturn t('nextcloud-vue', 'Delete \"{name}\"', { name: view.name })\n\t\t},\n\n\t\t/**\n\t\t * View-entry click: hand the view to the parent to apply.\n\t\t *\n\t\t * @param {object} view The clicked View API object.\n\t\t */\n\t\tonApply(view) {\n\t\t\t/**\n\t\t\t * @event apply A view entry was clicked; apply its stored state.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('apply', view)\n\t\t},\n\n\t\t/**\n\t\t * \"Save current view…\" click: ask the parent to open the save dialog.\n\t\t */\n\t\tonSaveRequest() {\n\t\t\t/**\n\t\t\t * @event save-request \"Save current view…\" clicked; open the save dialog.\n\t\t\t */\n\t\t\tthis.$emit('save-request')\n\t\t},\n\n\t\t/**\n\t\t * Delete-entry click: hand the view to the parent to confirm-delete.\n\t\t *\n\t\t * @param {object} view The View API object to delete.\n\t\t */\n\t\tonDeleteRequest(view) {\n\t\t\t/**\n\t\t\t * @event delete-request A view's delete entry was clicked; confirm and delete.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('delete-request', view)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* NcActionButtonGroup gives every child `flex: 1 1` (equal width) by\n default; override so the name button grows and the delete icon stays\n compact. :deep() is required — these li's belong to NcActionButtonGroup's\n own scope, not this component's. */\n.cn-saved-view-row :deep(.nc-button-group-content) {\n\tgap: 0;\n}\n\n/* Anchored on the FIRST child, not the last: a row carries one or two buttons\n depending on ownership, so \"the last one\" is the name button on a row that\n has only that. */\n.cn-saved-view-row :deep(.nc-button-group-content > li) {\n\tflex: 0 0 auto;\n}\n\n.cn-saved-view-row :deep(.nc-button-group-content > li:first-child) {\n\tflex: 1 1 auto;\n}\n\n/* NcActionButtonGroup also centers .action-button content (fine for an\n icon-only toolbar button, wrong for a row with a name) — restore the\n normal left-aligned NcActionButton look for the name button. */\n.cn-saved-view-row :deep(.nc-button-group-content > li:first-child .action-button) {\n\tjustify-content: flex-start;\n}\n</style>\n"],"names":["_createElementBlock","_Fragment","_renderList","_createElementVNode","_toDisplayString","_openBlock","_createVNode","_mergeProps","_createBlock","_createTextVNode"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import script from './CnSectionsWidget.vue2.js';
|
|
2
|
+
import { render } from './CnSectionsWidget.vue3.js';
|
|
3
|
+
|
|
4
|
+
script.render = render;
|
|
5
|
+
script.__scopeId = "data-v-ea4f8722";
|
|
6
|
+
script.__file = "src/components/CnSectionsWidget/CnSectionsWidget.vue";
|
|
7
|
+
const __sfc_main = script;
|
|
8
|
+
|
|
9
|
+
export { __sfc_main as default };
|
|
10
|
+
//# sourceMappingURL=CnSectionsWidget.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnSectionsWidget.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
|