@conduction/nextcloud-vue 2.97.0 → 2.99.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js +12 -0
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +11 -2
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +17 -0
- 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/CnBreadcrumbs/CnBreadcrumbs.vue.js +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js.map +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +106 -2
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue.js +9 -0
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue2.js +964 -0
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue3.js +282 -0
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChecklistRunWidget/index.js +28 -0
- package/dist/esm/components/CnChecklistRunWidget/index.js.map +1 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +1 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +26 -8
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +188 -5
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +2 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue.js +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js +3 -3
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue.js +1 -1
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js +27 -0
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js +16 -2
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +43 -3
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js +9 -0
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js.map +1 -0
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js +239 -0
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js.map +1 -0
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js +182 -0
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +9 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js.map +1 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js +265 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +191 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue.js +9 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue.js.map +1 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue2.js +508 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue2.js.map +1 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue3.js +99 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue.js +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js +18 -70
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js +6 -44
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue.js +1 -1
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js +83 -9
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js +80 -11
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +2 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue.js +9 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue.js.map +1 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue2.js +472 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue3.js +231 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnWorkingCalendarWidget/index.js +27 -0
- package/dist/esm/components/CnWorkingCalendarWidget/index.js.map +1 -0
- package/dist/esm/composables/indexSources.js +238 -2
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/composables/useFlowStore.js +2 -1
- package/dist/esm/composables/useFlowStore.js.map +1 -1
- package/dist/esm/composables/useOrganisationStore.js +115 -0
- package/dist/esm/composables/useOrganisationStore.js.map +1 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +109 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +138 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +1071 -274
- package/dist/esm/utils/builtInFormatters.js +34 -1
- package/dist/esm/utils/builtInFormatters.js.map +1 -1
- package/dist/esm/utils/libraryWidgetKeys.js +2 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/matrixCells.js +158 -0
- package/dist/esm/utils/matrixCells.js.map +1 -0
- package/dist/esm/utils/nextcloudDirectory.js +121 -0
- package/dist/esm/utils/nextcloudDirectory.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/workingCalendarMonth.js +85 -0
- package/dist/esm/utils/workingCalendarMonth.js.map +1 -0
- package/dist/nextcloud-vue.cjs.js +14888 -10014
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1071 -274
- package/l10n/en.json +109 -1
- package/l10n/nl.json +138 -1
- package/package.json +2 -2
- package/scripts/merge-l10n.js +196 -0
- package/src/components/CnActionsMenu/CnActionsMenu.vue +14 -1
- package/src/components/CnAppRoot/CnAppRoot.md +1 -0
- package/src/components/CnAppRoot/CnAppRoot.vue +17 -0
- package/src/components/CnBreadcrumbs/CnBreadcrumbs.vue +1 -0
- package/src/components/CnChartWidget/CnChartWidget.md +2 -1
- package/src/components/CnChartWidget/CnChartWidget.vue +106 -2
- package/src/components/CnChecklistRunWidget/CnChecklistRunWidget.vue +1269 -0
- package/src/components/CnChecklistRunWidget/index.js +27 -0
- package/src/components/CnDashboardPage/CnDashboardPage.vue +1 -0
- package/src/components/CnDataTable/CnDataTable.vue +26 -8
- package/src/components/CnDetailPage/CnDetailPage.md +48 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +189 -5
- package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +3 -3
- package/src/components/CnFeaturesBoard/CnFeaturesBoard.vue +38 -2
- package/src/components/CnIndexPage/CnIndexPage.md +28 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +43 -3
- package/src/components/CnOrderedListField/CnOrderedListField.vue +458 -0
- package/src/components/CnOrderedListField/index.js +7 -0
- package/src/components/CnSettingsField/CnSettingsField.vue +449 -0
- package/src/components/CnSettingsField/index.js +4 -0
- package/src/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue +613 -0
- package/src/components/CnSettingsFieldsSection/index.js +4 -0
- package/src/components/CnSettingsPage/CnSettingsPage.vue +6 -86
- package/src/components/CnUserSettingsDialog/CnUserSettingsDialog.vue +139 -10
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +2 -0
- package/src/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue +725 -0
- package/src/components/CnWorkingCalendarWidget/index.js +26 -0
- package/src/components/index.js +5 -0
- package/src/composables/indexSources.js +237 -1
- package/src/composables/useFlowStore.js +2 -1
- package/src/composables/useOrganisationStore.js +111 -0
- package/src/css/badge.css +32 -16
- package/src/css/look-board-detail.css +80 -8
- package/src/css/look-board-index.css +52 -8
- package/src/css/look-board.css +219 -1
- package/src/index.js +5 -0
- package/src/schemas/app-manifest-v2.schema.json +33 -3
- package/src/utils/builtInFormatters.js +33 -0
- package/src/utils/libraryWidgetKeys.js +2 -0
- package/src/utils/matrixCells.js +154 -0
- package/src/utils/nextcloudDirectory.js +117 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/workingCalendarMonth.js +82 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnFeaturesBoard.vue2.js","sources":["../../../../src/components/CnFeaturesBoard/CnFeaturesBoard.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-features-board\" :class=\"`cn-features-board--${layout}`\" data-testid=\"cn-features-board\">\n\t\t<header class=\"cn-features-board__header\">\n\t\t\t<div class=\"cn-features-board__title-row\">\n\t\t\t\t<h1 class=\"cn-features-board__title\">\n\t\t\t\t\t{{ resolvedTitle }}\n\t\t\t\t</h1>\n\t\t\t\t<div class=\"cn-features-board__header-actions\">\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"documentationButton && documentationUrl\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t:href=\"documentationUrl\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-docs\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<BookOpenVariant :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Documentation') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<CnBuildiqEditButton />\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"suggestUrl && suggestButton\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:href=\"suggestUrl\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-suggest\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Suggest feature') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcActions\n\t\t\t\t\t\tv-if=\"!tabs\"\n\t\t\t\t\t\t:menuName=\"t('nextcloud-vue', 'Actions')\"\n\t\t\t\t\t\t:forceName=\"true\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-actions\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<DotsHorizontal :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<NcActionLink\n\t\t\t\t\t\t\tv-for=\"action in menuLinks\"\n\t\t\t\t\t\t\t:key=\"action.key\"\n\t\t\t\t\t\t\t:href=\"action.href\"\n\t\t\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t\t<NcActionButton @click=\"onSupport\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Support this app') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</NcActions>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<p v-if=\"resolvedSubtitle\" class=\"cn-features-board__subtitle\" data-testid=\"cn-features-board-subtitle\">\n\t\t\t\t{{ resolvedSubtitle }}\n\t\t\t</p>\n\t\t</header>\n\n\t\t<div v-if=\"tabs\" class=\"cn-features-board__tab-row\">\n\t\t\t<div\n\t\t\t\tclass=\"cn-features-board__tabs\"\n\t\t\t\trole=\"tablist\"\n\t\t\t\t:aria-label=\"resolvedTitle\"\n\t\t\t\t@keydown=\"onTabKeydown\">\n\t\t\t\t<button\n\t\t\t\t\tv-for=\"tab in tabList\"\n\t\t\t\t\t:id=\"`${uid}-tab-${tab.key}`\"\n\t\t\t\t\t:key=\"tab.key\"\n\t\t\t\t\tref=\"tabButtons\"\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\trole=\"tab\"\n\t\t\t\t\tclass=\"cn-features-board__tab\"\n\t\t\t\t\t:class=\"{ 'cn-features-board__tab--active': activeTab === tab.key }\"\n\t\t\t\t\t:aria-selected=\"activeTab === tab.key ? 'true' : 'false'\"\n\t\t\t\t\t:aria-controls=\"`${uid}-panel`\"\n\t\t\t\t\t:tabindex=\"activeTab === tab.key ? 0 : -1\"\n\t\t\t\t\t:data-testid=\"`cn-features-board-tab-${tab.key}`\"\n\t\t\t\t\t@click=\"activeTab = tab.key\">\n\t\t\t\t\t{{ tab.label }}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t<NcActions\n\t\t\t\t:menuName=\"t('nextcloud-vue', 'Actions')\"\n\t\t\t\t:forceName=\"true\"\n\t\t\t\tclass=\"cn-features-board__tab-actions\"\n\t\t\t\tdata-testid=\"cn-features-board-actions\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<DotsHorizontal :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t<NcActionLink\n\t\t\t\t\tv-for=\"action in menuLinks\"\n\t\t\t\t\t:key=\"action.key\"\n\t\t\t\t\t:href=\"action.href\"\n\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionLink>\n\t\t\t\t<NcActionButton @click=\"onSupport\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Support this app') }}\n\t\t\t\t</NcActionButton>\n\t\t\t</NcActions>\n\t\t</div>\n\n\t\t<div\n\t\t\t:id=\"`${uid}-panel`\"\n\t\t\tclass=\"cn-features-board__panel\"\n\t\t\t:class=\"{ 'cn-features-board__panel--tabbed': tabs }\"\n\t\t\t:role=\"tabs ? 'tabpanel' : null\"\n\t\t\t:aria-labelledby=\"tabs ? `${uid}-tab-${activeTab}` : null\">\n\t\t\t<CnRoadmapTab v-if=\"tabs && activeTab === 'roadmap'\" :repo=\"repo\" />\n\t\t\t<template v-else>\n\t\t\t\t<div\n\t\t\t\t\tclass=\"cn-features-board__filter\"\n\t\t\t\t\t:class=\"`cn-features-board__filter--${filter}`\"\n\t\t\t\t\trole=\"group\"\n\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Status')\"\n\t\t\t\t\tdata-testid=\"cn-features-board-filter\">\n\t\t\t\t\t<button\n\t\t\t\t\t\tv-for=\"option in filterOptions\"\n\t\t\t\t\t\t:key=\"option.key\"\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclass=\"cn-features-board__filter-option\"\n\t\t\t\t\t\t:class=\"{ 'cn-features-board__filter-option--active': status === option.key }\"\n\t\t\t\t\t\t:aria-pressed=\"status === option.key ? 'true' : 'false'\"\n\t\t\t\t\t\t:data-status=\"option.key\"\n\t\t\t\t\t\t@click=\"status = option.key\">\n\t\t\t\t\t\t{{ option.label }}<span\n\t\t\t\t\t\t\tv-if=\"filter === 'chips'\"\n\t\t\t\t\t\t\tclass=\"cn-features-board__filter-count\"\n\t\t\t\t\t\t\tdata-testid=\"cn-features-board-filter-count\">{{ ' ' + option.count }}</span>\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\n\t\t\t\t<div class=\"cn-features-board__body\">\n\t\t\t\t\t<div class=\"cn-features-board__groups\" :class=\"`cn-features-board__groups--${layout}`\">\n\t\t\t\t\t\t<p v-if=\"shownGroups.length === 0\" class=\"cn-features-board__empty\" data-testid=\"cn-features-board-empty\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'No features with this status') }}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<section\n\t\t\t\t\t\t\tv-for=\"group in shownGroups\"\n\t\t\t\t\t\t\t:key=\"group.key\"\n\t\t\t\t\t\t\tclass=\"cn-features-board__group\"\n\t\t\t\t\t\t\t:aria-labelledby=\"group.label ? `${uid}-group-${group.key}` : null\"\n\t\t\t\t\t\t\t:aria-label=\"group.label ? null : resolvedTitle\"\n\t\t\t\t\t\t\tdata-testid=\"cn-features-board-group\"\n\t\t\t\t\t\t\t:data-group=\"group.key\">\n\t\t\t\t\t\t\t<div v-if=\"group.label\" class=\"cn-features-board__group-head\">\n\t\t\t\t\t\t\t\t<h2 :id=\"`${uid}-group-${group.key}`\" class=\"cn-features-board__group-title\">\n\t\t\t\t\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"layout === 'cards'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__group-caption\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-group-caption\">{{ groupCaption(group) }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div v-if=\"layout === 'cards'\" class=\"cn-features-board__cards\">\n\t\t\t\t\t\t\t\t<article\n\t\t\t\t\t\t\t\t\tv-for=\"feature in group.shown\"\n\t\t\t\t\t\t\t\t\t:key=\"feature.slug\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__card\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-feature\">\n\t\t\t\t\t\t\t\t\t<div class=\"cn-features-board__card-head\">\n\t\t\t\t\t\t\t\t\t\t<h3 class=\"cn-features-board__card-title\">\n\t\t\t\t\t\t\t\t\t\t\t{{ feature.title }}\n\t\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\t\t\t:label=\"statusText(feature)\"\n\t\t\t\t\t\t\t\t\t\t\t:variant=\"variantOf(feature.status)\"\n\t\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-status\" />\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<p v-if=\"feature.summary\" class=\"cn-features-board__card-summary\">\n\t\t\t\t\t\t\t\t\t\t{{ feature.summary }}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t<div v-if=\"readMoreHref(feature)\" class=\"cn-features-board__card-footer\">\n\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__read-more\"\n\t\t\t\t\t\t\t\t\t\t\t:href=\"readMoreHref(feature)\"\n\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Learn more about {title}', { title: feature.title })\">\n\t\t\t\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Learn more') }}\n\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</article>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<ul v-else class=\"cn-features-board__rows\">\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tv-for=\"feature in group.shown\"\n\t\t\t\t\t\t\t\t\t:key=\"feature.slug\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__row\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-feature\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-features-board__row-text\">\n\t\t\t\t\t\t\t\t\t\t<span class=\"cn-features-board__row-title\">{{ feature.title }}</span>\n\t\t\t\t\t\t\t\t\t\t<span v-if=\"feature.summary\" class=\"cn-features-board__row-summary\">{{ feature.summary }}</span>\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\t\t:label=\"statusText(feature)\"\n\t\t\t\t\t\t\t\t\t\t:variant=\"variantOf(feature.status)\"\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-status\" />\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</section>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<aside\n\t\t\t\t\t\tv-if=\"next > 0 && repo\"\n\t\t\t\t\t\tclass=\"cn-features-board__next\"\n\t\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Next on the roadmap')\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-next\">\n\t\t\t\t\t\t<section class=\"cn-features-board__next-card\">\n\t\t\t\t\t\t\t<div class=\"cn-features-board__next-head\">\n\t\t\t\t\t\t\t\t<RoadVariant :size=\"20\" />\n\t\t\t\t\t\t\t\t<h2 class=\"cn-features-board__next-title\">\n\t\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Coming next') }}\n\t\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<p class=\"cn-features-board__next-lead\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'The most requested items from the issue tracker.') }}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<ol v-if=\"nextIssues.length > 0\" class=\"cn-features-board__next-list\">\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tv-for=\"issue in nextIssues\"\n\t\t\t\t\t\t\t\t\t:key=\"issue.number\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-item\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-next-item\">\n\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-link\"\n\t\t\t\t\t\t\t\t\t\t:href=\"safeHref(issue.html_url)\"\n\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\">{{ issue.title }}</a>\n\t\t\t\t\t\t\t\t\t<div class=\"cn-features-board__next-meta\">\n\t\t\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\t\t\tv-if=\"issue.status\"\n\t\t\t\t\t\t\t\t\t\t\t:label=\"statusLabel(issue.status)\"\n\t\t\t\t\t\t\t\t\t\t\t:variant=\"variantOf(issue.status)\" />\n\t\t\t\t\t\t\t\t\t\t<span v-if=\"issue.opened\" class=\"cn-features-board__next-opened\">\n\t\t\t\t\t\t\t\t\t\t\t<CalendarBlankOutline :size=\"14\" />{{ t('nextcloud-vue', 'Opened {date}', { date: issue.opened }) }}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t<span>#{{ issue.number }}</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t</ol>\n\t\t\t\t\t\t\t<p v-else-if=\"!issuesLoading\" class=\"cn-features-board__next-lead\" data-testid=\"cn-features-board-next-empty\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'No roadmap items yet') }}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\tv-if=\"tabs\"\n\t\t\t\t\t\t\t\thref=\"#\"\n\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-more\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-whole-roadmap\"\n\t\t\t\t\t\t\t\t@click.prevent=\"activeTab = 'roadmap'\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'See the whole roadmap') }}<ChevronRight :size=\"16\" />\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\tv-if=\"documentationUrl\"\n\t\t\t\t\t\t\t\t:href=\"documentationUrl\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-more\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Documentation') }}<ChevronRight :size=\"16\" />\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</section>\n\t\t\t\t\t</aside>\n\t\t\t\t</div>\n\n\t\t\t\t<NcNoteCard v-if=\"notice\"\n\t\t\t\t\ttype=\"info\"\n\t\t\t\t\tclass=\"cn-features-board__notice\"\n\t\t\t\t\tdata-testid=\"cn-features-board-notice\">\n\t\t\t\t\t{{ tr(notice) }}\n\t\t\t\t</NcNoteCard>\n\t\t\t</template>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { getLanguage, translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActionLink, NcActions, NcButton, NcNoteCard } from '@nextcloud/vue'\nimport BookOpenVariant from 'vue-material-design-icons/BookOpenVariant.vue'\nimport CalendarBlankOutline from 'vue-material-design-icons/CalendarBlankOutline.vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport DotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport RoadVariant from 'vue-material-design-icons/RoadVariant.vue'\nimport CnBuildiqEditButton from '../CnBuildiqEditButton/CnBuildiqEditButton.vue'\nimport CnRoadmapTab from '../CnRoadmapTab/CnRoadmapTab.vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { formatBoardDate } from '../../utils/boardDate.js'\nimport { countByStatus, FEATURE_STATUS_VARIANTS, FEATURE_STATUSES, groupFeatures } from '../../utils/featureCatalog.js'\nimport { fetchRoadmapIssues, issueStatus } from '../../utils/roadmapIssues.js'\nimport { safeHref } from '../../utils/safeHref.js'\n\nlet featuresBoardUid = 0\n\n/**\n * Read the reader's language, which @nextcloud/l10n throws on outside a\n * Nextcloud page.\n *\n * @return {string} The language tag, or ''.\n */\nfunction readerLanguage() {\n\ttry {\n\t\treturn getLanguage() || ''\n\t} catch {\n\t\treturn ''\n\t}\n}\n\n/**\n * CnFeaturesBoard - the Features and roadmap page in the board look.\n *\n * A title with a subtitle, optional folder tabs (Features, Roadmap), a status\n * filter (count chips or a segmented control), the features grouped by\n * category (as two-up cards or as rows inside one card per group), an\n * optional \"Next\" card with the most requested forge issues, and an optional\n * notice under the groups. The links the default look puts in its right-hand\n * sidebar (suggest, buildiq, AI skills, support) sit in an Actions menu.\n *\n * Rendered by CnFeaturesAndRoadmapView when the page is in the board look.\n *\n * @spec openspec/changes/screens-roadmap-board-look/specs/features-roadmap-board-look/spec.md\n *\n * @event support Emitted when the reader picks \"Support this app\" in the Actions menu.\n */\nexport default {\n\tname: 'CnFeaturesBoard',\n\n\tcomponents: {\n\t\tBookOpenVariant,\n\t\tCalendarBlankOutline,\n\t\tChevronRight,\n\t\tCnBuildiqEditButton,\n\t\tCnRoadmapTab,\n\t\tCnStatusBadge,\n\t\tDotsHorizontal,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcActions,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tPlus,\n\t\tRoadVariant,\n\t},\n\n\tinject: {\n\t\t/** The app's translate function, provided by CnAppRoot. Identity outside one. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The features: `{ slug, title, summary, status, category, expected, docsUrl, title_<lang>, summary_<lang> }`. */\n\t\tfeatures: { type: Array, default: () => [] },\n\t\t/** Category key to caption; sets the order of the groups. */\n\t\tcategories: { type: Object, default: () => ({}) },\n\t\t/** `cards`: a section per category with two-up feature cards. `rows`: one card per category with a row per feature. */\n\t\tlayout: {\n\t\t\ttype: String,\n\t\t\tdefault: 'cards',\n\t\t\tvalidator: (value) => ['cards', 'rows'].includes(value),\n\t\t},\n\n\t\t/** Show the Features and Roadmap folder tabs. */\n\t\ttabs: { type: Boolean, default: true },\n\t\t/** `chips`: pill buttons with a count. `segmented`: a segmented control without counts. */\n\t\tfilter: {\n\t\t\ttype: String,\n\t\t\tdefault: 'chips',\n\t\t\tvalidator: (value) => ['chips', 'segmented'].includes(value),\n\t\t},\n\n\t\t/** Overrides of the status labels, by key (`all`, `available`, `beta`, `in_progress`, `planned`). */\n\t\tstatusLabels: { type: Object, default: () => ({}) },\n\t\t/** How many issues the Next card lists; 0 hides the card. */\n\t\tnext: { type: Number, default: 3 },\n\t\t/** Forge label to issue status (`in_progress`, `planned`) for the badges in the Next card. */\n\t\tissueLabels: { type: Object, default: () => ({}) },\n\t\t/** `<owner>/<repo>` on the forge, for the Roadmap tab and the Next card. */\n\t\trepo: { type: String, default: '' },\n\t\t/** Page title. */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Subtitle template; `{count}` is the number of features and `{appVersion}` the app version. */\n\t\tsubtitle: { type: String, default: '' },\n\t\t/** The app version, for `{appVersion}` in the subtitle. */\n\t\tappVersion: { type: String, default: '' },\n\t\t/** An info notice under the groups. */\n\t\tnotice: { type: String, default: '' },\n\t\t/** The app's documentation site: the Next card's link and the target of \"Learn more\". */\n\t\tdocumentationUrl: { type: String, default: '' },\n\t\t/** Show a Documentation button in the header. */\n\t\tdocumentationButton: { type: Boolean, default: false },\n\t\t/** The feature-request form; the header's primary button. */\n\t\tsuggestUrl: { type: String, default: '' },\n\t\t/** Show the feature-request form as the header's primary button; without it, it is an entry in the Actions menu. */\n\t\tsuggestButton: { type: Boolean, default: true },\n\t\t/** Target of the OpenBuilt entry in the Actions menu. */\n\t\topenbuiltUrl: { type: String, default: '' },\n\t\t/** Target of the AI skills entry in the Actions menu. */\n\t\tllmSkillsUrl: { type: String, default: '' },\n\t},\n\n\temits: ['support'],\n\n\tdata() {\n\t\tfeaturesBoardUid += 1\n\t\treturn {\n\t\t\tuid: `cn-features-board-${featuresBoardUid}`,\n\t\t\tactiveTab: 'features',\n\t\t\tstatus: 'all',\n\t\t\tissues: [],\n\t\t\tissuesLoading: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tlanguage() {\n\t\t\treturn readerLanguage()\n\t\t},\n\n\t\tgroups() {\n\t\t\treturn groupFeatures(this.features, this.categories, this.language)\n\t\t},\n\n\t\tallFeatures() {\n\t\t\treturn this.groups.flatMap((group) => group.features)\n\t\t},\n\n\t\tcounts() {\n\t\t\treturn countByStatus(this.allFeatures)\n\t\t},\n\n\t\t/** The groups with the features the filter lets through; an emptied group is left out. */\n\t\tshownGroups() {\n\t\t\treturn this.groups\n\t\t\t\t.map((group) => ({\n\t\t\t\t\t...group,\n\t\t\t\t\tshown: this.status === 'all' ? group.features : group.features.filter((feature) => feature.status === this.status),\n\t\t\t\t}))\n\t\t\t\t.filter((group) => group.shown.length > 0)\n\t\t},\n\n\t\t/** All, then each status; `in_progress` only when a feature has it. */\n\t\tfilterOptions() {\n\t\t\tconst options = [{ key: 'all', label: this.statusLabel('all'), count: this.counts.all }]\n\t\t\tfor (const key of FEATURE_STATUSES) {\n\t\t\t\tif (key === 'in_progress' && this.counts.in_progress === 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\toptions.push({ key, label: this.statusLabel(key), count: this.counts[key] })\n\t\t\t}\n\t\t\treturn options\n\t\t},\n\n\t\ttabList() {\n\t\t\treturn [\n\t\t\t\t{ key: 'features', label: t('nextcloud-vue', 'Features') },\n\t\t\t\t{ key: 'roadmap', label: t('nextcloud-vue', 'Roadmap') },\n\t\t\t]\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\treturn this.title ? this.tr(this.title) : t('nextcloud-vue', 'Features and roadmap')\n\t\t},\n\n\t\tresolvedSubtitle() {\n\t\t\tif (!this.subtitle) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.tr(this.subtitle)\n\t\t\t\t.replace(/\\{count\\}/g, String(this.counts.all))\n\t\t\t\t.replace(/\\{appVersion\\}/g, this.appVersion)\n\t\t},\n\n\t\tmenuLinks() {\n\t\t\tconst links = []\n\t\t\tif (this.suggestUrl && !this.suggestButton) {\n\t\t\t\tlinks.push({ key: 'suggest', href: this.suggestUrl, label: t('nextcloud-vue', 'Suggest feature') })\n\t\t\t}\n\t\t\tif (this.openbuiltUrl) {\n\t\t\t\tlinks.push({ key: 'openbuilt', href: this.openbuiltUrl, label: t('nextcloud-vue', 'Build on this app') })\n\t\t\t}\n\t\t\tif (this.llmSkillsUrl) {\n\t\t\t\tlinks.push({ key: 'llm', href: this.llmSkillsUrl, label: t('nextcloud-vue', 'AI skills') })\n\t\t\t}\n\t\t\treturn links\n\t\t},\n\n\t\tnextIssues() {\n\t\t\treturn this.issues.slice(0, this.next).map((issue) => ({\n\t\t\t\tnumber: issue.number,\n\t\t\t\ttitle: issue.title,\n\t\t\t\thtml_url: issue.html_url,\n\t\t\t\tstatus: issueStatus(issue, this.issueLabels),\n\t\t\t\topened: issue.created_at ? formatBoardDate(issue.created_at) : '',\n\t\t\t}))\n\t\t},\n\t},\n\n\tmounted() {\n\t\tif (this.next > 0 && this.repo) {\n\t\t\tthis.loadIssues()\n\t\t}\n\t},\n\n\tmethods: {\n\t\tt,\n\t\tsafeHref,\n\n\t\t/**\n\t\t * Run an app-supplied string through the app's translate function.\n\t\t *\n\t\t * @param {string} key The string.\n\t\t * @return {string} The translated string.\n\t\t */\n\t\ttr(key) {\n\t\t\treturn this.cnTranslate(key)\n\t\t},\n\n\t\t/**\n\t\t * The label of a status, or of `all`: the app's override, else the library's.\n\t\t *\n\t\t * @param {string} key The status key.\n\t\t * @return {string} The label.\n\t\t */\n\t\tstatusLabel(key) {\n\t\t\tif (typeof this.statusLabels[key] === 'string' && this.statusLabels[key] !== '') {\n\t\t\t\treturn this.tr(this.statusLabels[key])\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tall: t('nextcloud-vue', 'All'),\n\t\t\t\tavailable: t('nextcloud-vue', 'Available'),\n\t\t\t\tbeta: t('nextcloud-vue', 'Beta'),\n\t\t\t\tin_progress: t('nextcloud-vue', 'In progress'),\n\t\t\t\tplanned: t('nextcloud-vue', 'Planned'),\n\t\t\t}[key] || key\n\t\t},\n\n\t\t/**\n\t\t * The badge text of a feature: its status, with the expected moment\n\t\t * when it has one (\"Planned, Q4 2026\").\n\t\t *\n\t\t * @param {{status: string, expected: string}} feature The feature.\n\t\t * @return {string} The badge text.\n\t\t */\n\t\tstatusText(feature) {\n\t\t\tconst label = this.statusLabel(feature.status)\n\t\t\treturn feature.expected ? t('nextcloud-vue', '{status}, {expected}', { status: label, expected: feature.expected }) : label\n\t\t},\n\n\t\t/**\n\t\t * The badge variant of a status.\n\t\t *\n\t\t * @param {string} status The status key.\n\t\t * @return {string} The CnStatusBadge variant.\n\t\t */\n\t\tvariantOf(status) {\n\t\t\treturn FEATURE_STATUS_VARIANTS[status] || 'default'\n\t\t},\n\n\t\t/**\n\t\t * \"14 features, 14 available\": the group's size, then each status it holds.\n\t\t *\n\t\t * @param {{features: Array<{status: string}>}} group The group.\n\t\t * @return {string} The caption.\n\t\t */\n\t\tgroupCaption(group) {\n\t\t\tconst counts = countByStatus(group.features)\n\t\t\tconst parts = [n('nextcloud-vue', '%n feature', '%n features', counts.all)]\n\t\t\tfor (const key of FEATURE_STATUSES) {\n\t\t\t\tif (counts[key] > 0) {\n\t\t\t\t\tparts.push(`${counts[key]} ${this.statusLabel(key).toLocaleLowerCase()}`)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn parts.join(', ')\n\t\t},\n\n\t\t/**\n\t\t * Where \"Learn more\" goes: the feature's own page when it is a web\n\t\t * address, else the documentation site, else nowhere.\n\t\t *\n\t\t * @param {{docsUrl: string}} feature The feature.\n\t\t * @return {string} The address, or ''.\n\t\t */\n\t\treadMoreHref(feature) {\n\t\t\tif (/^https?:\\/\\//i.test(feature.docsUrl)) {\n\t\t\t\treturn safeHref(feature.docsUrl)\n\t\t\t}\n\t\t\treturn this.documentationUrl ? safeHref(this.documentationUrl) : ''\n\t\t},\n\n\t\t/** Ask the host to open the support dialog. */\n\t\tonSupport() {\n\t\t\t/**\n\t\t\t * @event support Emitted when the reader picks \"Support this app\" in the Actions menu.\n\t\t\t */\n\t\t\tthis.$emit('support')\n\t\t},\n\n\t\t/** Fetch the forge issues for the Next card. */\n\t\tasync loadIssues() {\n\t\t\tthis.issuesLoading = true\n\t\t\tconst { items } = await fetchRoadmapIssues(this.repo)\n\t\t\tthis.issues = items\n\t\t\tthis.issuesLoading = false\n\t\t},\n\n\t\t/**\n\t\t * Arrow keys, Home and End move between the folder tabs.\n\t\t *\n\t\t * @param {KeyboardEvent} event The key press.\n\t\t */\n\t\tonTabKeydown(event) {\n\t\t\tconst keys = this.tabList.map((tab) => tab.key)\n\t\t\tconst index = keys.indexOf(this.activeTab)\n\t\t\tconst target = {\n\t\t\t\tArrowRight: (index + 1) % keys.length,\n\t\t\t\tArrowLeft: (index - 1 + keys.length) % keys.length,\n\t\t\t\tHome: 0,\n\t\t\t\tEnd: keys.length - 1,\n\t\t\t}[event.key]\n\t\t\tif (target === undefined) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tthis.activeTab = keys[target]\n\t\t\tthis.$nextTick(() => this.$refs.tabButtons?.[target]?.focus())\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-features-board {\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--cn-board-section-gap, 20px);\n\tpadding: var(--cn-board-content-padding, 24px 28px);\n\tmax-width: 1240px;\n\twidth: 100%;\n}\n\n.cn-features-board__header {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 4px 0 8px;\n}\n\n.cn-features-board__title-row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tjustify-content: space-between;\n\talign-items: flex-start;\n\tgap: 12px 24px;\n}\n\n.cn-features-board__title {\n\tflex: 1 1 320px;\n\tmin-width: 0;\n\tmargin: 0;\n\tfont-size: 28px;\n\tfont-weight: 700;\n\tline-height: 1.2;\n}\n\n.cn-features-board__header-actions {\n\tflex: none;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 10px;\n}\n\n.cn-features-board__subtitle {\n\tmargin: 0;\n\tfont-size: 15px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__tab-row {\n\tdisplay: flex;\n\talign-items: flex-end;\n\tgap: 8px;\n\tmargin-bottom: calc(-1 * var(--cn-board-section-gap, 20px));\n}\n\n.cn-features-board__tabs {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 4px;\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-features-board__tab {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tbox-sizing: border-box;\n\tmin-height: 34px;\n\tmargin: 0 0 -1px;\n\tpadding: 8px 12px;\n\tborder: 1px solid var(--cn-board-hairline, var(--color-border));\n\tborder-bottom: none;\n\tborder-radius: 8px 8px 0 0;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n\tfont-weight: 400;\n\tcursor: pointer;\n\twhite-space: nowrap;\n}\n\n.cn-features-board__tab--active {\n\tposition: relative;\n\tbackground: var(--color-main-background);\n\tborder-bottom: 1px solid var(--color-main-background);\n\tfont-weight: 700;\n}\n\n.cn-features-board__tab-actions {\n\tflex: none;\n\talign-self: flex-start;\n\tmargin: 0 8px 8px 0;\n}\n\n.cn-features-board__panel {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 22px;\n}\n\n.cn-features-board__panel--tabbed {\n\tpadding: 22px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 0 var(--cn-board-card-radius, 12px) var(--cn-board-card-radius, 12px) var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__filter {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-features-board__filter-option {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin: 0;\n\tfont-size: 14px;\n\tfont-weight: 600;\n\tcursor: pointer;\n}\n\n.cn-features-board__filter--chips .cn-features-board__filter-option {\n\theight: 36px;\n\tpadding: 0 14px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: 18px;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-board__filter--chips .cn-features-board__filter-option--active {\n\tborder-color: var(--color-main-text);\n\tbackground: var(--color-main-text);\n\tcolor: var(--color-main-background);\n}\n\n.cn-features-board__filter-count {\n\tfont-weight: 400;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__filter--chips .cn-features-board__filter-option--active .cn-features-board__filter-count {\n\tcolor: var(--color-border-dark);\n}\n\n.cn-features-board__filter--segmented {\n\tdisplay: inline-flex;\n\talign-self: flex-start;\n\tgap: 2px;\n\tpadding: 3px;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n}\n\n.cn-features-board__filter--segmented .cn-features-board__filter-option {\n\theight: 34px;\n\tpadding: 0 14px;\n\tborder: 0;\n\tborder-radius: 6px;\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-board__filter--segmented .cn-features-board__filter-option--active {\n\tbackground: var(--color-main-background);\n\tbox-shadow: 0 1px 2px rgba(27, 28, 29, 0.12);\n}\n\n.cn-features-board__body {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tgap: 24px;\n}\n\n.cn-features-board__groups {\n\tflex: 999 1 520px;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 26px;\n}\n\n.cn-features-board__groups--rows {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(340px, 1fr));\n\tgap: 20px;\n}\n\n.cn-features-board__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n\tmax-width: none;\n\tmargin: 0;\n}\n\n.cn-features-board__groups--rows .cn-features-board__group {\n\tpadding: var(--cn-board-card-padding, 20px 22px);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__group-head {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: baseline;\n\tgap: 10px;\n}\n\n.cn-features-board__group-title {\n\tmargin: 0;\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n.cn-features-board__groups--rows .cn-features-board__group-title {\n\tfont-size: 17px;\n}\n\n.cn-features-board__group-caption {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__cards {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n\tgap: 16px;\n}\n\n.cn-features-board__card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 10px;\n\tpadding: 18px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__card-head {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: flex-start;\n\tgap: 4px;\n}\n\n.cn-features-board__card-title {\n\tmargin: 0;\n\tfont-size: 17px;\n\tfont-weight: 700;\n\tline-height: 1.3;\n}\n\n.cn-features-board__card-summary {\n\tmargin: 0;\n\tfont-size: 14px;\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-board__card-footer {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\talign-items: center;\n\tmin-height: 34px;\n\tmargin-top: auto;\n\tpadding-top: 10px;\n\tborder-top: 1px solid var(--color-background-dark);\n}\n\n.cn-features-board__read-more {\n\tdisplay: inline-flex;\n\talign-items: center;\n\theight: 34px;\n\tpadding: 0 12px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: 8px;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n\tfont-weight: 600;\n\ttext-decoration: none;\n}\n\n.cn-features-board__rows {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-features-board__row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 6px 16px;\n\tpadding: 10px 0;\n\tborder-top: 1px solid var(--color-background-dark);\n}\n\n.cn-features-board__row-text {\n\tflex: 1 1 240px;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-features-board__row-title {\n\tfont-size: 15px;\n\tfont-weight: 500;\n}\n\n.cn-features-board__row-summary {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__next {\n\tflex: 1 1 280px;\n\tmin-width: 0;\n\tmax-width: 340px;\n}\n\n.cn-features-board__next-card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n\tpadding: 18px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-features-board__next-head {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-features-board__next-title {\n\tmargin: 0;\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n.cn-features-board__next-lead {\n\tmargin: 0;\n\tfont-size: 14px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__next-list {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 10px;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-features-board__next-item {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 12px 14px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 10px;\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__next-link {\n\tfont-size: 15px;\n\tfont-weight: 600;\n\tline-height: 1.3;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-features-board__next-meta {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__next-opened {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n}\n\n.cn-features-board__next-more {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-features-board__empty {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__notice {\n\tmargin: 0;\n}\n</style>\n"],"names":["CnBuildiqEditButton","CnRoadmapTab","CnStatusBadge","t","n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnFeaturesBoard.vue2.js","sources":["../../../../src/components/CnFeaturesBoard/CnFeaturesBoard.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-features-board\" :class=\"`cn-features-board--${layout}`\" data-testid=\"cn-features-board\">\n\t\t<header class=\"cn-features-board__header\">\n\t\t\t<div class=\"cn-features-board__title-row\">\n\t\t\t\t<h1 class=\"cn-features-board__title\">\n\t\t\t\t\t{{ resolvedTitle }}\n\t\t\t\t</h1>\n\t\t\t\t<div class=\"cn-features-board__header-actions\">\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"documentationButton && documentationUrl\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t:href=\"documentationUrl\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-docs\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<BookOpenVariant :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Documentation') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<CnBuildiqEditButton />\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"suggestUrl && suggestButton\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:href=\"suggestUrl\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-suggest\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Suggest feature') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcActions\n\t\t\t\t\t\tv-if=\"!tabs\"\n\t\t\t\t\t\t:menuName=\"t('nextcloud-vue', 'Actions')\"\n\t\t\t\t\t\t:forceName=\"true\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-actions\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<DotsHorizontal :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<NcActionLink\n\t\t\t\t\t\t\tv-for=\"action in menuLinks\"\n\t\t\t\t\t\t\t:key=\"action.key\"\n\t\t\t\t\t\t\t:href=\"action.href\"\n\t\t\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t\t<NcActionButton @click=\"onSupport\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Support this app') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</NcActions>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<p v-if=\"resolvedSubtitle\" class=\"cn-features-board__subtitle\" data-testid=\"cn-features-board-subtitle\">\n\t\t\t\t{{ resolvedSubtitle }}\n\t\t\t</p>\n\t\t</header>\n\n\t\t<div v-if=\"tabs\" class=\"cn-features-board__tab-row\">\n\t\t\t<div\n\t\t\t\tclass=\"cn-features-board__tabs\"\n\t\t\t\trole=\"tablist\"\n\t\t\t\t:aria-label=\"resolvedTitle\"\n\t\t\t\t@keydown=\"onTabKeydown\">\n\t\t\t\t<button\n\t\t\t\t\tv-for=\"tab in tabList\"\n\t\t\t\t\t:id=\"`${uid}-tab-${tab.key}`\"\n\t\t\t\t\t:key=\"tab.key\"\n\t\t\t\t\tref=\"tabButtons\"\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\trole=\"tab\"\n\t\t\t\t\tclass=\"cn-features-board__tab\"\n\t\t\t\t\t:class=\"{ 'cn-features-board__tab--active': activeTab === tab.key }\"\n\t\t\t\t\t:aria-selected=\"activeTab === tab.key ? 'true' : 'false'\"\n\t\t\t\t\t:aria-controls=\"`${uid}-panel`\"\n\t\t\t\t\t:tabindex=\"activeTab === tab.key ? 0 : -1\"\n\t\t\t\t\t:data-testid=\"`cn-features-board-tab-${tab.key}`\"\n\t\t\t\t\t@click=\"activeTab = tab.key\">\n\t\t\t\t\t{{ tab.label }}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t<NcActions\n\t\t\t\t:menuName=\"t('nextcloud-vue', 'Actions')\"\n\t\t\t\t:forceName=\"true\"\n\t\t\t\tclass=\"cn-features-board__tab-actions\"\n\t\t\t\tdata-testid=\"cn-features-board-actions\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<DotsHorizontal :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t<NcActionLink\n\t\t\t\t\tv-for=\"action in menuLinks\"\n\t\t\t\t\t:key=\"action.key\"\n\t\t\t\t\t:href=\"action.href\"\n\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionLink>\n\t\t\t\t<NcActionButton @click=\"onSupport\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Support this app') }}\n\t\t\t\t</NcActionButton>\n\t\t\t</NcActions>\n\t\t</div>\n\n\t\t<div\n\t\t\t:id=\"`${uid}-panel`\"\n\t\t\tclass=\"cn-features-board__panel\"\n\t\t\t:class=\"{ 'cn-features-board__panel--tabbed': tabs }\"\n\t\t\t:role=\"tabs ? 'tabpanel' : null\"\n\t\t\t:aria-labelledby=\"tabs ? `${uid}-tab-${activeTab}` : null\">\n\t\t\t<CnRoadmapTab v-if=\"tabs && activeTab === 'roadmap'\" :repo=\"repo\" />\n\t\t\t<template v-else>\n\t\t\t\t<div\n\t\t\t\t\tclass=\"cn-features-board__filter\"\n\t\t\t\t\t:class=\"`cn-features-board__filter--${filter}`\"\n\t\t\t\t\trole=\"group\"\n\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Status')\"\n\t\t\t\t\tdata-testid=\"cn-features-board-filter\">\n\t\t\t\t\t<button\n\t\t\t\t\t\tv-for=\"option in filterOptions\"\n\t\t\t\t\t\t:key=\"option.key\"\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclass=\"cn-features-board__filter-option\"\n\t\t\t\t\t\t:class=\"{ 'cn-features-board__filter-option--active': status === option.key }\"\n\t\t\t\t\t\t:aria-pressed=\"status === option.key ? 'true' : 'false'\"\n\t\t\t\t\t\t:data-status=\"option.key\"\n\t\t\t\t\t\t@click=\"status = option.key\">\n\t\t\t\t\t\t{{ option.label }}<span\n\t\t\t\t\t\t\tv-if=\"filter === 'chips'\"\n\t\t\t\t\t\t\tclass=\"cn-features-board__filter-count\"\n\t\t\t\t\t\t\tdata-testid=\"cn-features-board-filter-count\">{{ ' ' + option.count }}</span>\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\n\t\t\t\t<div class=\"cn-features-board__body\">\n\t\t\t\t\t<div class=\"cn-features-board__groups\" :class=\"`cn-features-board__groups--${layout}`\">\n\t\t\t\t\t\t<p v-if=\"shownGroups.length === 0\" class=\"cn-features-board__empty\" data-testid=\"cn-features-board-empty\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'No features with this status') }}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<section\n\t\t\t\t\t\t\tv-for=\"group in shownGroups\"\n\t\t\t\t\t\t\t:key=\"group.key\"\n\t\t\t\t\t\t\tclass=\"cn-features-board__group\"\n\t\t\t\t\t\t\t:aria-labelledby=\"group.label ? `${uid}-group-${group.key}` : null\"\n\t\t\t\t\t\t\t:aria-label=\"group.label ? null : resolvedTitle\"\n\t\t\t\t\t\t\tdata-testid=\"cn-features-board-group\"\n\t\t\t\t\t\t\t:data-group=\"group.key\">\n\t\t\t\t\t\t\t<div v-if=\"group.label\" class=\"cn-features-board__group-head\">\n\t\t\t\t\t\t\t\t<h2 :id=\"`${uid}-group-${group.key}`\" class=\"cn-features-board__group-title\">\n\t\t\t\t\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"layout === 'cards'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__group-caption\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-group-caption\">{{ groupCaption(group) }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div v-if=\"layout === 'cards'\" class=\"cn-features-board__cards\">\n\t\t\t\t\t\t\t\t<article\n\t\t\t\t\t\t\t\t\tv-for=\"feature in group.shown\"\n\t\t\t\t\t\t\t\t\t:key=\"feature.slug\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__card\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-feature\">\n\t\t\t\t\t\t\t\t\t<div class=\"cn-features-board__card-head\">\n\t\t\t\t\t\t\t\t\t\t<h3 class=\"cn-features-board__card-title\">\n\t\t\t\t\t\t\t\t\t\t\t{{ feature.title }}\n\t\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\t\t\t:label=\"statusText(feature)\"\n\t\t\t\t\t\t\t\t\t\t\t:variant=\"variantOf(feature.status)\"\n\t\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-status\" />\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<p v-if=\"feature.summary\" class=\"cn-features-board__card-summary\">\n\t\t\t\t\t\t\t\t\t\t{{ feature.summary }}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t<div v-if=\"readMoreHref(feature)\" class=\"cn-features-board__card-footer\">\n\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__read-more\"\n\t\t\t\t\t\t\t\t\t\t\t:href=\"readMoreHref(feature)\"\n\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Learn more about {title}', { title: feature.title })\">\n\t\t\t\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Learn more') }}\n\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</article>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<ul v-else class=\"cn-features-board__rows\">\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tv-for=\"feature in group.shown\"\n\t\t\t\t\t\t\t\t\t:key=\"feature.slug\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__row\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-feature\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-features-board__row-text\">\n\t\t\t\t\t\t\t\t\t\t<span class=\"cn-features-board__row-title\">{{ feature.title }}</span>\n\t\t\t\t\t\t\t\t\t\t<span v-if=\"summaries && feature.summary\" class=\"cn-features-board__row-summary\">{{ feature.summary }}</span>\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\t\t:label=\"statusText(feature)\"\n\t\t\t\t\t\t\t\t\t\t:variant=\"variantOf(feature.status)\"\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-status\" />\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</section>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<aside\n\t\t\t\t\t\tv-if=\"next > 0 && repo\"\n\t\t\t\t\t\tclass=\"cn-features-board__next\"\n\t\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Next on the roadmap')\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-next\">\n\t\t\t\t\t\t<section class=\"cn-features-board__next-card\">\n\t\t\t\t\t\t\t<div class=\"cn-features-board__next-head\">\n\t\t\t\t\t\t\t\t<RoadVariant :size=\"20\" />\n\t\t\t\t\t\t\t\t<h2 class=\"cn-features-board__next-title\">\n\t\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Coming next') }}\n\t\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<p class=\"cn-features-board__next-lead\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'The most requested items from the issue tracker.') }}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<ol v-if=\"nextIssues.length > 0\" class=\"cn-features-board__next-list\">\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tv-for=\"issue in nextIssues\"\n\t\t\t\t\t\t\t\t\t:key=\"issue.number\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-item\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-next-item\">\n\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-link\"\n\t\t\t\t\t\t\t\t\t\t:href=\"safeHref(issue.html_url)\"\n\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\">{{ issue.title }}</a>\n\t\t\t\t\t\t\t\t\t<div class=\"cn-features-board__next-meta\">\n\t\t\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\t\t\tv-if=\"issue.status\"\n\t\t\t\t\t\t\t\t\t\t\t:label=\"statusLabel(issue.status)\"\n\t\t\t\t\t\t\t\t\t\t\t:variant=\"variantOf(issue.status)\" />\n\t\t\t\t\t\t\t\t\t\t<span v-if=\"issue.opened\" class=\"cn-features-board__next-opened\">\n\t\t\t\t\t\t\t\t\t\t\t<CalendarBlankOutline :size=\"14\" />{{ t('nextcloud-vue', 'Opened {date}', { date: issue.opened }) }}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t<span>#{{ issue.number }}</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t</ol>\n\t\t\t\t\t\t\t<p v-else-if=\"!issuesLoading\" class=\"cn-features-board__next-lead\" data-testid=\"cn-features-board-next-empty\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'No roadmap items yet') }}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\tv-if=\"tabs\"\n\t\t\t\t\t\t\t\thref=\"#\"\n\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-more\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-whole-roadmap\"\n\t\t\t\t\t\t\t\t@click.prevent=\"activeTab = 'roadmap'\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'See the whole roadmap') }}<ChevronRight :size=\"16\" />\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\tv-if=\"documentationUrl\"\n\t\t\t\t\t\t\t\t:href=\"documentationUrl\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-more\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Documentation') }}<ChevronRight :size=\"16\" />\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</section>\n\t\t\t\t\t</aside>\n\t\t\t\t</div>\n\n\t\t\t\t<NcNoteCard v-if=\"notice\"\n\t\t\t\t\ttype=\"info\"\n\t\t\t\t\tclass=\"cn-features-board__notice\"\n\t\t\t\t\tdata-testid=\"cn-features-board-notice\">\n\t\t\t\t\t<template v-if=\"noticeParts.link\">\n\t\t\t\t\t\t{{ noticeParts.before }}<a\n\t\t\t\t\t\t\t:href=\"noticeParts.link.href\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\tdata-testid=\"cn-features-board-notice-link\">{{ noticeParts.link.text }}</a>{{ noticeParts.after }}\n\t\t\t\t\t</template>\n\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t{{ noticeParts.before }}\n\t\t\t\t\t</template>\n\t\t\t\t</NcNoteCard>\n\t\t\t</template>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { getLanguage, translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActionLink, NcActions, NcButton, NcNoteCard } from '@nextcloud/vue'\nimport BookOpenVariant from 'vue-material-design-icons/BookOpenVariant.vue'\nimport CalendarBlankOutline from 'vue-material-design-icons/CalendarBlankOutline.vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport DotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport RoadVariant from 'vue-material-design-icons/RoadVariant.vue'\nimport CnBuildiqEditButton from '../CnBuildiqEditButton/CnBuildiqEditButton.vue'\nimport CnRoadmapTab from '../CnRoadmapTab/CnRoadmapTab.vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { formatBoardDate } from '../../utils/boardDate.js'\nimport { countByStatus, FEATURE_STATUS_VARIANTS, FEATURE_STATUSES, groupFeatures } from '../../utils/featureCatalog.js'\nimport { fetchRoadmapIssues, issueStatus } from '../../utils/roadmapIssues.js'\nimport { safeHref } from '../../utils/safeHref.js'\n\nlet featuresBoardUid = 0\n\n/**\n * Read the reader's language, which @nextcloud/l10n throws on outside a\n * Nextcloud page.\n *\n * @return {string} The language tag, or ''.\n */\nfunction readerLanguage() {\n\ttry {\n\t\treturn getLanguage() || ''\n\t} catch {\n\t\treturn ''\n\t}\n}\n\n/**\n * CnFeaturesBoard - the Features and roadmap page in the board look.\n *\n * A title with a subtitle, optional folder tabs (Features, Roadmap), a status\n * filter (count chips or a segmented control), the features grouped by\n * category (as two-up cards or as rows inside one card per group), an\n * optional \"Next\" card with the most requested forge issues, and an optional\n * notice under the groups. The links the default look puts in its right-hand\n * sidebar (suggest, buildiq, AI skills, support) sit in an Actions menu.\n *\n * Rendered by CnFeaturesAndRoadmapView when the page is in the board look.\n *\n * @spec openspec/changes/screens-roadmap-board-look/specs/features-roadmap-board-look/spec.md\n *\n * @event support Emitted when the reader picks \"Support this app\" in the Actions menu.\n */\nexport default {\n\tname: 'CnFeaturesBoard',\n\n\tcomponents: {\n\t\tBookOpenVariant,\n\t\tCalendarBlankOutline,\n\t\tChevronRight,\n\t\tCnBuildiqEditButton,\n\t\tCnRoadmapTab,\n\t\tCnStatusBadge,\n\t\tDotsHorizontal,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcActions,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tPlus,\n\t\tRoadVariant,\n\t},\n\n\tinject: {\n\t\t/** The app's translate function, provided by CnAppRoot. Identity outside one. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The features: `{ slug, title, summary, status, category, expected, docsUrl, title_<lang>, summary_<lang> }`. */\n\t\tfeatures: { type: Array, default: () => [] },\n\t\t/** Category key to caption; sets the order of the groups. */\n\t\tcategories: { type: Object, default: () => ({}) },\n\t\t/** `cards`: a section per category with two-up feature cards. `rows`: one card per category with a row per feature. */\n\t\tlayout: {\n\t\t\ttype: String,\n\t\t\tdefault: 'cards',\n\t\t\tvalidator: (value) => ['cards', 'rows'].includes(value),\n\t\t},\n\n\t\t/** Show the Features and Roadmap folder tabs. */\n\t\ttabs: { type: Boolean, default: true },\n\t\t/** `chips`: pill buttons with a count. `segmented`: a segmented control without counts. */\n\t\tfilter: {\n\t\t\ttype: String,\n\t\t\tdefault: 'chips',\n\t\t\tvalidator: (value) => ['chips', 'segmented'].includes(value),\n\t\t},\n\n\t\t/** Overrides of the status labels, by key (`all`, `available`, `beta`, `in_progress`, `planned`). */\n\t\tstatusLabels: { type: Object, default: () => ({}) },\n\t\t/** How many issues the Next card lists; 0 hides the card. */\n\t\tnext: { type: Number, default: 3 },\n\t\t/** Forge label to issue status (`in_progress`, `planned`) for the badges in the Next card. */\n\t\tissueLabels: { type: Object, default: () => ({}) },\n\t\t/** `<owner>/<repo>` on the forge, for the Roadmap tab and the Next card. */\n\t\trepo: { type: String, default: '' },\n\t\t/** Page title. */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Subtitle template; `{count}` is the number of features and `{appVersion}` the app version. */\n\t\tsubtitle: { type: String, default: '' },\n\t\t/** The app version, for `{appVersion}` in the subtitle. */\n\t\tappVersion: { type: String, default: '' },\n\t\t/** An info notice under the groups. */\n\t\tnotice: { type: String, default: '' },\n\t\t/** The app's documentation site: the Next card's link and the target of \"Learn more\". */\n\t\tdocumentationUrl: { type: String, default: '' },\n\t\t/** Show a Documentation button in the header. */\n\t\tdocumentationButton: { type: Boolean, default: false },\n\t\t/** The feature-request form; the header's primary button. */\n\t\tsuggestUrl: { type: String, default: '' },\n\t\t/** Show the feature-request form as the header's primary button; without it, it is an entry in the Actions menu. */\n\t\tsuggestButton: { type: Boolean, default: true },\n\t\t/** In the `rows` layout, draw each feature's summary under its title; false shows the title and status only. */\n\t\tsummaries: { type: Boolean, default: true },\n\t\t/** A link inside the notice: `{ text, href }`; the first occurrence of `text` in the translated notice opens `href` in a new tab. Only http(s) and site-relative hrefs link. */\n\t\tnoticeLink: { type: Object, default: null },\n\t\t/** Target of the OpenBuilt entry in the Actions menu. */\n\t\topenbuiltUrl: { type: String, default: '' },\n\t\t/** Target of the AI skills entry in the Actions menu. */\n\t\tllmSkillsUrl: { type: String, default: '' },\n\t},\n\n\temits: ['support'],\n\n\tdata() {\n\t\tfeaturesBoardUid += 1\n\t\treturn {\n\t\t\tuid: `cn-features-board-${featuresBoardUid}`,\n\t\t\tactiveTab: 'features',\n\t\t\tstatus: 'all',\n\t\t\tissues: [],\n\t\t\tissuesLoading: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tlanguage() {\n\t\t\treturn readerLanguage()\n\t\t},\n\n\t\t/** The groups, their captions through the app's translate function. */\n\t\tgroups() {\n\t\t\treturn groupFeatures(this.features, this.categories, this.language)\n\t\t\t\t.map((group) => ({ ...group, label: group.label ? this.tr(group.label) : '' }))\n\t\t},\n\n\t\tallFeatures() {\n\t\t\treturn this.groups.flatMap((group) => group.features)\n\t\t},\n\n\t\tcounts() {\n\t\t\treturn countByStatus(this.allFeatures)\n\t\t},\n\n\t\t/** The groups with the features the filter lets through; an emptied group is left out. */\n\t\tshownGroups() {\n\t\t\treturn this.groups\n\t\t\t\t.map((group) => ({\n\t\t\t\t\t...group,\n\t\t\t\t\tshown: this.status === 'all' ? group.features : group.features.filter((feature) => feature.status === this.status),\n\t\t\t\t}))\n\t\t\t\t.filter((group) => group.shown.length > 0)\n\t\t},\n\n\t\t/** All, then each status; `in_progress` only when a feature has it. */\n\t\tfilterOptions() {\n\t\t\tconst options = [{ key: 'all', label: this.statusLabel('all'), count: this.counts.all }]\n\t\t\tfor (const key of FEATURE_STATUSES) {\n\t\t\t\tif (key === 'in_progress' && this.counts.in_progress === 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\toptions.push({ key, label: this.statusLabel(key), count: this.counts[key] })\n\t\t\t}\n\t\t\treturn options\n\t\t},\n\n\t\ttabList() {\n\t\t\treturn [\n\t\t\t\t{ key: 'features', label: t('nextcloud-vue', 'Features') },\n\t\t\t\t{ key: 'roadmap', label: t('nextcloud-vue', 'Roadmap') },\n\t\t\t]\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\treturn this.title ? this.tr(this.title) : t('nextcloud-vue', 'Features and roadmap')\n\t\t},\n\n\t\t/**\n\t\t * The notice split around its link: the text before, the link and the\n\t\t * text after. Without a usable `noticeLink` (no match in the\n\t\t * translated notice, or an href that is not http(s) or site-relative)\n\t\t * `before` is the whole notice.\n\t\t *\n\t\t * @return {{before: string, link: ({text: string, href: string}|null), after: string}} The parts.\n\t\t */\n\t\tnoticeParts() {\n\t\t\tconst text = this.notice ? this.tr(this.notice) : ''\n\t\t\tconst link = this.noticeLink\n\t\t\tconst label = link && typeof link.text === 'string' ? this.tr(link.text) : ''\n\t\t\tconst href = link && typeof link.href === 'string' ? link.href.trim() : ''\n\t\t\tconst index = label ? text.indexOf(label) : -1\n\t\t\tconst allowed = (/^https?:\\/\\//i.test(href) || href.startsWith('/')) && safeHref(href) === href\n\t\t\tif (index === -1 || !allowed) {\n\t\t\t\treturn { before: text, link: null, after: '' }\n\t\t\t}\n\t\t\treturn { before: text.slice(0, index), link: { text: label, href }, after: text.slice(index + label.length) }\n\t\t},\n\n\t\tresolvedSubtitle() {\n\t\t\tif (!this.subtitle) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.tr(this.subtitle)\n\t\t\t\t.replace(/\\{count\\}/g, String(this.counts.all))\n\t\t\t\t.replace(/\\{appVersion\\}/g, this.appVersion)\n\t\t},\n\n\t\tmenuLinks() {\n\t\t\tconst links = []\n\t\t\tif (this.suggestUrl && !this.suggestButton) {\n\t\t\t\tlinks.push({ key: 'suggest', href: this.suggestUrl, label: t('nextcloud-vue', 'Suggest feature') })\n\t\t\t}\n\t\t\tif (this.openbuiltUrl) {\n\t\t\t\tlinks.push({ key: 'openbuilt', href: this.openbuiltUrl, label: t('nextcloud-vue', 'Build on this app') })\n\t\t\t}\n\t\t\tif (this.llmSkillsUrl) {\n\t\t\t\tlinks.push({ key: 'llm', href: this.llmSkillsUrl, label: t('nextcloud-vue', 'AI skills') })\n\t\t\t}\n\t\t\treturn links\n\t\t},\n\n\t\tnextIssues() {\n\t\t\treturn this.issues.slice(0, this.next).map((issue) => ({\n\t\t\t\tnumber: issue.number,\n\t\t\t\ttitle: issue.title,\n\t\t\t\thtml_url: issue.html_url,\n\t\t\t\tstatus: issueStatus(issue, this.issueLabels),\n\t\t\t\topened: issue.created_at ? formatBoardDate(issue.created_at) : '',\n\t\t\t}))\n\t\t},\n\t},\n\n\tmounted() {\n\t\tif (this.next > 0 && this.repo) {\n\t\t\tthis.loadIssues()\n\t\t}\n\t},\n\n\tmethods: {\n\t\tt,\n\t\tsafeHref,\n\n\t\t/**\n\t\t * Run an app-supplied string through the app's translate function.\n\t\t *\n\t\t * @param {string} key The string.\n\t\t * @return {string} The translated string.\n\t\t */\n\t\ttr(key) {\n\t\t\treturn this.cnTranslate(key)\n\t\t},\n\n\t\t/**\n\t\t * The label of a status, or of `all`: the app's override, else the library's.\n\t\t *\n\t\t * @param {string} key The status key.\n\t\t * @return {string} The label.\n\t\t */\n\t\tstatusLabel(key) {\n\t\t\tif (typeof this.statusLabels[key] === 'string' && this.statusLabels[key] !== '') {\n\t\t\t\treturn this.tr(this.statusLabels[key])\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tall: t('nextcloud-vue', 'All'),\n\t\t\t\tavailable: t('nextcloud-vue', 'Available'),\n\t\t\t\tbeta: t('nextcloud-vue', 'Beta'),\n\t\t\t\tin_progress: t('nextcloud-vue', 'In progress'),\n\t\t\t\tplanned: t('nextcloud-vue', 'Planned'),\n\t\t\t}[key] || key\n\t\t},\n\n\t\t/**\n\t\t * The badge text of a feature: its status, with the expected moment\n\t\t * when it has one (\"Planned, Q4 2026\").\n\t\t *\n\t\t * @param {{status: string, expected: string}} feature The feature.\n\t\t * @return {string} The badge text.\n\t\t */\n\t\tstatusText(feature) {\n\t\t\tconst label = this.statusLabel(feature.status)\n\t\t\treturn feature.expected ? t('nextcloud-vue', '{status}, {expected}', { status: label, expected: feature.expected }) : label\n\t\t},\n\n\t\t/**\n\t\t * The badge variant of a status.\n\t\t *\n\t\t * @param {string} status The status key.\n\t\t * @return {string} The CnStatusBadge variant.\n\t\t */\n\t\tvariantOf(status) {\n\t\t\treturn FEATURE_STATUS_VARIANTS[status] || 'default'\n\t\t},\n\n\t\t/**\n\t\t * \"14 features, 14 available\": the group's size, then each status it holds.\n\t\t *\n\t\t * @param {{features: Array<{status: string}>}} group The group.\n\t\t * @return {string} The caption.\n\t\t */\n\t\tgroupCaption(group) {\n\t\t\tconst counts = countByStatus(group.features)\n\t\t\tconst parts = [n('nextcloud-vue', '%n feature', '%n features', counts.all)]\n\t\t\tfor (const key of FEATURE_STATUSES) {\n\t\t\t\tif (counts[key] > 0) {\n\t\t\t\t\tparts.push(`${counts[key]} ${this.statusLabel(key).toLocaleLowerCase()}`)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn parts.join(', ')\n\t\t},\n\n\t\t/**\n\t\t * Where \"Learn more\" goes: the feature's own page when it is a web\n\t\t * address, else the documentation site, else nowhere.\n\t\t *\n\t\t * @param {{docsUrl: string}} feature The feature.\n\t\t * @return {string} The address, or ''.\n\t\t */\n\t\treadMoreHref(feature) {\n\t\t\tif (/^https?:\\/\\//i.test(feature.docsUrl)) {\n\t\t\t\treturn safeHref(feature.docsUrl)\n\t\t\t}\n\t\t\treturn this.documentationUrl ? safeHref(this.documentationUrl) : ''\n\t\t},\n\n\t\t/** Ask the host to open the support dialog. */\n\t\tonSupport() {\n\t\t\t/**\n\t\t\t * @event support Emitted when the reader picks \"Support this app\" in the Actions menu.\n\t\t\t */\n\t\t\tthis.$emit('support')\n\t\t},\n\n\t\t/** Fetch the forge issues for the Next card. */\n\t\tasync loadIssues() {\n\t\t\tthis.issuesLoading = true\n\t\t\tconst { items } = await fetchRoadmapIssues(this.repo)\n\t\t\tthis.issues = items\n\t\t\tthis.issuesLoading = false\n\t\t},\n\n\t\t/**\n\t\t * Arrow keys, Home and End move between the folder tabs.\n\t\t *\n\t\t * @param {KeyboardEvent} event The key press.\n\t\t */\n\t\tonTabKeydown(event) {\n\t\t\tconst keys = this.tabList.map((tab) => tab.key)\n\t\t\tconst index = keys.indexOf(this.activeTab)\n\t\t\tconst target = {\n\t\t\t\tArrowRight: (index + 1) % keys.length,\n\t\t\t\tArrowLeft: (index - 1 + keys.length) % keys.length,\n\t\t\t\tHome: 0,\n\t\t\t\tEnd: keys.length - 1,\n\t\t\t}[event.key]\n\t\t\tif (target === undefined) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tthis.activeTab = keys[target]\n\t\t\tthis.$nextTick(() => this.$refs.tabButtons?.[target]?.focus())\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-features-board {\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--cn-board-section-gap, 20px);\n\tpadding: var(--cn-board-content-padding, 24px 28px);\n\tmax-width: 1240px;\n\twidth: 100%;\n}\n\n.cn-features-board__header {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 4px 0 8px;\n}\n\n.cn-features-board__title-row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tjustify-content: space-between;\n\talign-items: flex-start;\n\tgap: 12px 24px;\n}\n\n.cn-features-board__title {\n\tflex: 1 1 320px;\n\tmin-width: 0;\n\tmargin: 0;\n\tfont-size: 28px;\n\tfont-weight: 700;\n\tline-height: 1.2;\n}\n\n.cn-features-board__header-actions {\n\tflex: none;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 10px;\n}\n\n.cn-features-board__subtitle {\n\tmargin: 0;\n\tfont-size: 15px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__tab-row {\n\tdisplay: flex;\n\talign-items: flex-end;\n\tgap: 8px;\n\tmargin-bottom: calc(-1 * var(--cn-board-section-gap, 20px));\n}\n\n.cn-features-board__tabs {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 4px;\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-features-board__tab {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tbox-sizing: border-box;\n\tmin-height: 34px;\n\tmargin: 0 0 -1px;\n\tpadding: 8px 12px;\n\tborder: 1px solid var(--cn-board-hairline, var(--color-border));\n\tborder-bottom: none;\n\tborder-radius: 8px 8px 0 0;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n\tfont-weight: 400;\n\tcursor: pointer;\n\twhite-space: nowrap;\n}\n\n.cn-features-board__tab--active {\n\tposition: relative;\n\tbackground: var(--color-main-background);\n\tborder-bottom: 1px solid var(--color-main-background);\n\tfont-weight: 700;\n}\n\n.cn-features-board__tab-actions {\n\tflex: none;\n\talign-self: flex-start;\n\tmargin: 0 8px 8px 0;\n}\n\n.cn-features-board__panel {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 22px;\n}\n\n.cn-features-board__panel--tabbed {\n\tpadding: 22px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 0 var(--cn-board-card-radius, 12px) var(--cn-board-card-radius, 12px) var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__filter {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-features-board__filter-option {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin: 0;\n\tfont-size: 14px;\n\tfont-weight: 600;\n\tcursor: pointer;\n}\n\n.cn-features-board__filter--chips .cn-features-board__filter-option {\n\theight: 36px;\n\tpadding: 0 14px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: 18px;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-board__filter--chips .cn-features-board__filter-option--active {\n\tborder-color: var(--color-main-text);\n\tbackground: var(--color-main-text);\n\tcolor: var(--color-main-background);\n}\n\n.cn-features-board__filter-count {\n\tfont-weight: 400;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__filter--chips .cn-features-board__filter-option--active .cn-features-board__filter-count {\n\tcolor: var(--color-border-dark);\n}\n\n.cn-features-board__filter--segmented {\n\tdisplay: inline-flex;\n\talign-self: flex-start;\n\tgap: 2px;\n\tpadding: 3px;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n}\n\n.cn-features-board__filter--segmented .cn-features-board__filter-option {\n\theight: 34px;\n\tpadding: 0 14px;\n\tborder: 0;\n\tborder-radius: 6px;\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-board__filter--segmented .cn-features-board__filter-option--active {\n\tbackground: var(--color-main-background);\n\tbox-shadow: 0 1px 2px rgba(27, 28, 29, 0.12);\n}\n\n.cn-features-board__body {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tgap: 24px;\n}\n\n.cn-features-board__groups {\n\tflex: 999 1 520px;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 26px;\n}\n\n.cn-features-board__groups--rows {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(340px, 1fr));\n\tgap: 20px;\n}\n\n.cn-features-board__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n\tmax-width: none;\n\tmargin: 0;\n}\n\n.cn-features-board__groups--rows .cn-features-board__group {\n\tpadding: var(--cn-board-card-padding, 20px 22px);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__group-head {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: baseline;\n\tgap: 10px;\n}\n\n.cn-features-board__group-title {\n\tmargin: 0;\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n.cn-features-board__groups--rows .cn-features-board__group-title {\n\tfont-size: 17px;\n}\n\n.cn-features-board__group-caption {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__cards {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n\tgap: 16px;\n}\n\n.cn-features-board__card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 10px;\n\tpadding: 18px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__card-head {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: flex-start;\n\tgap: 4px;\n}\n\n.cn-features-board__card-title {\n\tmargin: 0;\n\tfont-size: 17px;\n\tfont-weight: 700;\n\tline-height: 1.3;\n}\n\n.cn-features-board__card-summary {\n\tmargin: 0;\n\tfont-size: 14px;\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-board__card-footer {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\talign-items: center;\n\tmin-height: 34px;\n\tmargin-top: auto;\n\tpadding-top: 10px;\n\tborder-top: 1px solid var(--color-background-dark);\n}\n\n.cn-features-board__read-more {\n\tdisplay: inline-flex;\n\talign-items: center;\n\theight: 34px;\n\tpadding: 0 12px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: 8px;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n\tfont-weight: 600;\n\ttext-decoration: none;\n}\n\n.cn-features-board__rows {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-features-board__row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 6px 16px;\n\tpadding: 10px 0;\n\tborder-top: 1px solid var(--color-background-dark);\n}\n\n.cn-features-board__row-text {\n\tflex: 1 1 240px;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-features-board__row-title {\n\tfont-size: 15px;\n\tfont-weight: 500;\n}\n\n.cn-features-board__row-summary {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__next {\n\tflex: 1 1 280px;\n\tmin-width: 0;\n\tmax-width: 340px;\n}\n\n.cn-features-board__next-card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n\tpadding: 18px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-features-board__next-head {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-features-board__next-title {\n\tmargin: 0;\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n.cn-features-board__next-lead {\n\tmargin: 0;\n\tfont-size: 14px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__next-list {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 10px;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-features-board__next-item {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 12px 14px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 10px;\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__next-link {\n\tfont-size: 15px;\n\tfont-weight: 600;\n\tline-height: 1.3;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-features-board__next-meta {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__next-opened {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n}\n\n.cn-features-board__next-more {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-features-board__empty {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__notice {\n\tmargin: 0;\n}\n</style>\n"],"names":["CnBuildiqEditButton","CnRoadmapTab","CnStatusBadge","t","n"],"mappings":"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"}
|
|
@@ -86,6 +86,7 @@ const _hoisted_38 = {
|
|
|
86
86
|
"data-testid": "cn-features-board-next-empty"
|
|
87
87
|
};
|
|
88
88
|
const _hoisted_39 = ["href"];
|
|
89
|
+
const _hoisted_40 = ["href"];
|
|
89
90
|
|
|
90
91
|
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
91
92
|
const _component_BookOpenVariant = resolveComponent("BookOpenVariant");
|
|
@@ -348,7 +349,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
348
349
|
}, [
|
|
349
350
|
createElementVNode("span", _hoisted_26, [
|
|
350
351
|
createElementVNode("span", _hoisted_27, toDisplayString(feature.title), 1 /* TEXT */),
|
|
351
|
-
(feature.summary)
|
|
352
|
+
($props.summaries && feature.summary)
|
|
352
353
|
? (openBlock(), createElementBlock("span", _hoisted_28, toDisplayString(feature.summary), 1 /* TEXT */))
|
|
353
354
|
: createCommentVNode("v-if", true)
|
|
354
355
|
]),
|
|
@@ -448,7 +449,20 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
448
449
|
"data-testid": "cn-features-board-notice"
|
|
449
450
|
}, {
|
|
450
451
|
default: withCtx(() => [
|
|
451
|
-
|
|
452
|
+
($options.noticeParts.link)
|
|
453
|
+
? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
454
|
+
createTextVNode(toDisplayString($options.noticeParts.before), 1 /* TEXT */),
|
|
455
|
+
createElementVNode("a", {
|
|
456
|
+
href: $options.noticeParts.link.href,
|
|
457
|
+
target: "_blank",
|
|
458
|
+
rel: "noopener noreferrer",
|
|
459
|
+
"data-testid": "cn-features-board-notice-link"
|
|
460
|
+
}, toDisplayString($options.noticeParts.link.text), 9 /* TEXT, PROPS */, _hoisted_40),
|
|
461
|
+
createTextVNode(toDisplayString($options.noticeParts.after), 1 /* TEXT */)
|
|
462
|
+
], 64 /* STABLE_FRAGMENT */))
|
|
463
|
+
: (openBlock(), createElementBlock(Fragment, { key: 1 }, [
|
|
464
|
+
createTextVNode(toDisplayString($options.noticeParts.before), 1 /* TEXT */)
|
|
465
|
+
], 64 /* STABLE_FRAGMENT */))
|
|
452
466
|
]),
|
|
453
467
|
_: 1 /* STABLE */
|
|
454
468
|
}))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnFeaturesBoard.vue3.js","sources":["../../../../src/components/CnFeaturesBoard/CnFeaturesBoard.vue?vue&type=template&id=f306c9ee&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-features-board\" :class=\"`cn-features-board--${layout}`\" data-testid=\"cn-features-board\">\n\t\t<header class=\"cn-features-board__header\">\n\t\t\t<div class=\"cn-features-board__title-row\">\n\t\t\t\t<h1 class=\"cn-features-board__title\">\n\t\t\t\t\t{{ resolvedTitle }}\n\t\t\t\t</h1>\n\t\t\t\t<div class=\"cn-features-board__header-actions\">\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"documentationButton && documentationUrl\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t:href=\"documentationUrl\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-docs\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<BookOpenVariant :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Documentation') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<CnBuildiqEditButton />\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"suggestUrl && suggestButton\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:href=\"suggestUrl\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-suggest\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Suggest feature') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcActions\n\t\t\t\t\t\tv-if=\"!tabs\"\n\t\t\t\t\t\t:menuName=\"t('nextcloud-vue', 'Actions')\"\n\t\t\t\t\t\t:forceName=\"true\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-actions\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<DotsHorizontal :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<NcActionLink\n\t\t\t\t\t\t\tv-for=\"action in menuLinks\"\n\t\t\t\t\t\t\t:key=\"action.key\"\n\t\t\t\t\t\t\t:href=\"action.href\"\n\t\t\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t\t<NcActionButton @click=\"onSupport\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Support this app') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</NcActions>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<p v-if=\"resolvedSubtitle\" class=\"cn-features-board__subtitle\" data-testid=\"cn-features-board-subtitle\">\n\t\t\t\t{{ resolvedSubtitle }}\n\t\t\t</p>\n\t\t</header>\n\n\t\t<div v-if=\"tabs\" class=\"cn-features-board__tab-row\">\n\t\t\t<div\n\t\t\t\tclass=\"cn-features-board__tabs\"\n\t\t\t\trole=\"tablist\"\n\t\t\t\t:aria-label=\"resolvedTitle\"\n\t\t\t\t@keydown=\"onTabKeydown\">\n\t\t\t\t<button\n\t\t\t\t\tv-for=\"tab in tabList\"\n\t\t\t\t\t:id=\"`${uid}-tab-${tab.key}`\"\n\t\t\t\t\t:key=\"tab.key\"\n\t\t\t\t\tref=\"tabButtons\"\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\trole=\"tab\"\n\t\t\t\t\tclass=\"cn-features-board__tab\"\n\t\t\t\t\t:class=\"{ 'cn-features-board__tab--active': activeTab === tab.key }\"\n\t\t\t\t\t:aria-selected=\"activeTab === tab.key ? 'true' : 'false'\"\n\t\t\t\t\t:aria-controls=\"`${uid}-panel`\"\n\t\t\t\t\t:tabindex=\"activeTab === tab.key ? 0 : -1\"\n\t\t\t\t\t:data-testid=\"`cn-features-board-tab-${tab.key}`\"\n\t\t\t\t\t@click=\"activeTab = tab.key\">\n\t\t\t\t\t{{ tab.label }}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t<NcActions\n\t\t\t\t:menuName=\"t('nextcloud-vue', 'Actions')\"\n\t\t\t\t:forceName=\"true\"\n\t\t\t\tclass=\"cn-features-board__tab-actions\"\n\t\t\t\tdata-testid=\"cn-features-board-actions\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<DotsHorizontal :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t<NcActionLink\n\t\t\t\t\tv-for=\"action in menuLinks\"\n\t\t\t\t\t:key=\"action.key\"\n\t\t\t\t\t:href=\"action.href\"\n\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionLink>\n\t\t\t\t<NcActionButton @click=\"onSupport\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Support this app') }}\n\t\t\t\t</NcActionButton>\n\t\t\t</NcActions>\n\t\t</div>\n\n\t\t<div\n\t\t\t:id=\"`${uid}-panel`\"\n\t\t\tclass=\"cn-features-board__panel\"\n\t\t\t:class=\"{ 'cn-features-board__panel--tabbed': tabs }\"\n\t\t\t:role=\"tabs ? 'tabpanel' : null\"\n\t\t\t:aria-labelledby=\"tabs ? `${uid}-tab-${activeTab}` : null\">\n\t\t\t<CnRoadmapTab v-if=\"tabs && activeTab === 'roadmap'\" :repo=\"repo\" />\n\t\t\t<template v-else>\n\t\t\t\t<div\n\t\t\t\t\tclass=\"cn-features-board__filter\"\n\t\t\t\t\t:class=\"`cn-features-board__filter--${filter}`\"\n\t\t\t\t\trole=\"group\"\n\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Status')\"\n\t\t\t\t\tdata-testid=\"cn-features-board-filter\">\n\t\t\t\t\t<button\n\t\t\t\t\t\tv-for=\"option in filterOptions\"\n\t\t\t\t\t\t:key=\"option.key\"\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclass=\"cn-features-board__filter-option\"\n\t\t\t\t\t\t:class=\"{ 'cn-features-board__filter-option--active': status === option.key }\"\n\t\t\t\t\t\t:aria-pressed=\"status === option.key ? 'true' : 'false'\"\n\t\t\t\t\t\t:data-status=\"option.key\"\n\t\t\t\t\t\t@click=\"status = option.key\">\n\t\t\t\t\t\t{{ option.label }}<span\n\t\t\t\t\t\t\tv-if=\"filter === 'chips'\"\n\t\t\t\t\t\t\tclass=\"cn-features-board__filter-count\"\n\t\t\t\t\t\t\tdata-testid=\"cn-features-board-filter-count\">{{ ' ' + option.count }}</span>\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\n\t\t\t\t<div class=\"cn-features-board__body\">\n\t\t\t\t\t<div class=\"cn-features-board__groups\" :class=\"`cn-features-board__groups--${layout}`\">\n\t\t\t\t\t\t<p v-if=\"shownGroups.length === 0\" class=\"cn-features-board__empty\" data-testid=\"cn-features-board-empty\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'No features with this status') }}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<section\n\t\t\t\t\t\t\tv-for=\"group in shownGroups\"\n\t\t\t\t\t\t\t:key=\"group.key\"\n\t\t\t\t\t\t\tclass=\"cn-features-board__group\"\n\t\t\t\t\t\t\t:aria-labelledby=\"group.label ? `${uid}-group-${group.key}` : null\"\n\t\t\t\t\t\t\t:aria-label=\"group.label ? null : resolvedTitle\"\n\t\t\t\t\t\t\tdata-testid=\"cn-features-board-group\"\n\t\t\t\t\t\t\t:data-group=\"group.key\">\n\t\t\t\t\t\t\t<div v-if=\"group.label\" class=\"cn-features-board__group-head\">\n\t\t\t\t\t\t\t\t<h2 :id=\"`${uid}-group-${group.key}`\" class=\"cn-features-board__group-title\">\n\t\t\t\t\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"layout === 'cards'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__group-caption\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-group-caption\">{{ groupCaption(group) }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div v-if=\"layout === 'cards'\" class=\"cn-features-board__cards\">\n\t\t\t\t\t\t\t\t<article\n\t\t\t\t\t\t\t\t\tv-for=\"feature in group.shown\"\n\t\t\t\t\t\t\t\t\t:key=\"feature.slug\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__card\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-feature\">\n\t\t\t\t\t\t\t\t\t<div class=\"cn-features-board__card-head\">\n\t\t\t\t\t\t\t\t\t\t<h3 class=\"cn-features-board__card-title\">\n\t\t\t\t\t\t\t\t\t\t\t{{ feature.title }}\n\t\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\t\t\t:label=\"statusText(feature)\"\n\t\t\t\t\t\t\t\t\t\t\t:variant=\"variantOf(feature.status)\"\n\t\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-status\" />\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<p v-if=\"feature.summary\" class=\"cn-features-board__card-summary\">\n\t\t\t\t\t\t\t\t\t\t{{ feature.summary }}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t<div v-if=\"readMoreHref(feature)\" class=\"cn-features-board__card-footer\">\n\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__read-more\"\n\t\t\t\t\t\t\t\t\t\t\t:href=\"readMoreHref(feature)\"\n\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Learn more about {title}', { title: feature.title })\">\n\t\t\t\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Learn more') }}\n\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</article>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<ul v-else class=\"cn-features-board__rows\">\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tv-for=\"feature in group.shown\"\n\t\t\t\t\t\t\t\t\t:key=\"feature.slug\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__row\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-feature\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-features-board__row-text\">\n\t\t\t\t\t\t\t\t\t\t<span class=\"cn-features-board__row-title\">{{ feature.title }}</span>\n\t\t\t\t\t\t\t\t\t\t<span v-if=\"feature.summary\" class=\"cn-features-board__row-summary\">{{ feature.summary }}</span>\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\t\t:label=\"statusText(feature)\"\n\t\t\t\t\t\t\t\t\t\t:variant=\"variantOf(feature.status)\"\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-status\" />\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</section>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<aside\n\t\t\t\t\t\tv-if=\"next > 0 && repo\"\n\t\t\t\t\t\tclass=\"cn-features-board__next\"\n\t\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Next on the roadmap')\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-next\">\n\t\t\t\t\t\t<section class=\"cn-features-board__next-card\">\n\t\t\t\t\t\t\t<div class=\"cn-features-board__next-head\">\n\t\t\t\t\t\t\t\t<RoadVariant :size=\"20\" />\n\t\t\t\t\t\t\t\t<h2 class=\"cn-features-board__next-title\">\n\t\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Coming next') }}\n\t\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<p class=\"cn-features-board__next-lead\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'The most requested items from the issue tracker.') }}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<ol v-if=\"nextIssues.length > 0\" class=\"cn-features-board__next-list\">\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tv-for=\"issue in nextIssues\"\n\t\t\t\t\t\t\t\t\t:key=\"issue.number\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-item\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-next-item\">\n\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-link\"\n\t\t\t\t\t\t\t\t\t\t:href=\"safeHref(issue.html_url)\"\n\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\">{{ issue.title }}</a>\n\t\t\t\t\t\t\t\t\t<div class=\"cn-features-board__next-meta\">\n\t\t\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\t\t\tv-if=\"issue.status\"\n\t\t\t\t\t\t\t\t\t\t\t:label=\"statusLabel(issue.status)\"\n\t\t\t\t\t\t\t\t\t\t\t:variant=\"variantOf(issue.status)\" />\n\t\t\t\t\t\t\t\t\t\t<span v-if=\"issue.opened\" class=\"cn-features-board__next-opened\">\n\t\t\t\t\t\t\t\t\t\t\t<CalendarBlankOutline :size=\"14\" />{{ t('nextcloud-vue', 'Opened {date}', { date: issue.opened }) }}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t<span>#{{ issue.number }}</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t</ol>\n\t\t\t\t\t\t\t<p v-else-if=\"!issuesLoading\" class=\"cn-features-board__next-lead\" data-testid=\"cn-features-board-next-empty\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'No roadmap items yet') }}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\tv-if=\"tabs\"\n\t\t\t\t\t\t\t\thref=\"#\"\n\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-more\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-whole-roadmap\"\n\t\t\t\t\t\t\t\t@click.prevent=\"activeTab = 'roadmap'\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'See the whole roadmap') }}<ChevronRight :size=\"16\" />\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\tv-if=\"documentationUrl\"\n\t\t\t\t\t\t\t\t:href=\"documentationUrl\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-more\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Documentation') }}<ChevronRight :size=\"16\" />\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</section>\n\t\t\t\t\t</aside>\n\t\t\t\t</div>\n\n\t\t\t\t<NcNoteCard v-if=\"notice\"\n\t\t\t\t\ttype=\"info\"\n\t\t\t\t\tclass=\"cn-features-board__notice\"\n\t\t\t\t\tdata-testid=\"cn-features-board-notice\">\n\t\t\t\t\t{{ tr(notice) }}\n\t\t\t\t</NcNoteCard>\n\t\t\t</template>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { getLanguage, translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActionLink, NcActions, NcButton, NcNoteCard } from '@nextcloud/vue'\nimport BookOpenVariant from 'vue-material-design-icons/BookOpenVariant.vue'\nimport CalendarBlankOutline from 'vue-material-design-icons/CalendarBlankOutline.vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport DotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport RoadVariant from 'vue-material-design-icons/RoadVariant.vue'\nimport CnBuildiqEditButton from '../CnBuildiqEditButton/CnBuildiqEditButton.vue'\nimport CnRoadmapTab from '../CnRoadmapTab/CnRoadmapTab.vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { formatBoardDate } from '../../utils/boardDate.js'\nimport { countByStatus, FEATURE_STATUS_VARIANTS, FEATURE_STATUSES, groupFeatures } from '../../utils/featureCatalog.js'\nimport { fetchRoadmapIssues, issueStatus } from '../../utils/roadmapIssues.js'\nimport { safeHref } from '../../utils/safeHref.js'\n\nlet featuresBoardUid = 0\n\n/**\n * Read the reader's language, which @nextcloud/l10n throws on outside a\n * Nextcloud page.\n *\n * @return {string} The language tag, or ''.\n */\nfunction readerLanguage() {\n\ttry {\n\t\treturn getLanguage() || ''\n\t} catch {\n\t\treturn ''\n\t}\n}\n\n/**\n * CnFeaturesBoard - the Features and roadmap page in the board look.\n *\n * A title with a subtitle, optional folder tabs (Features, Roadmap), a status\n * filter (count chips or a segmented control), the features grouped by\n * category (as two-up cards or as rows inside one card per group), an\n * optional \"Next\" card with the most requested forge issues, and an optional\n * notice under the groups. The links the default look puts in its right-hand\n * sidebar (suggest, buildiq, AI skills, support) sit in an Actions menu.\n *\n * Rendered by CnFeaturesAndRoadmapView when the page is in the board look.\n *\n * @spec openspec/changes/screens-roadmap-board-look/specs/features-roadmap-board-look/spec.md\n *\n * @event support Emitted when the reader picks \"Support this app\" in the Actions menu.\n */\nexport default {\n\tname: 'CnFeaturesBoard',\n\n\tcomponents: {\n\t\tBookOpenVariant,\n\t\tCalendarBlankOutline,\n\t\tChevronRight,\n\t\tCnBuildiqEditButton,\n\t\tCnRoadmapTab,\n\t\tCnStatusBadge,\n\t\tDotsHorizontal,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcActions,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tPlus,\n\t\tRoadVariant,\n\t},\n\n\tinject: {\n\t\t/** The app's translate function, provided by CnAppRoot. Identity outside one. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The features: `{ slug, title, summary, status, category, expected, docsUrl, title_<lang>, summary_<lang> }`. */\n\t\tfeatures: { type: Array, default: () => [] },\n\t\t/** Category key to caption; sets the order of the groups. */\n\t\tcategories: { type: Object, default: () => ({}) },\n\t\t/** `cards`: a section per category with two-up feature cards. `rows`: one card per category with a row per feature. */\n\t\tlayout: {\n\t\t\ttype: String,\n\t\t\tdefault: 'cards',\n\t\t\tvalidator: (value) => ['cards', 'rows'].includes(value),\n\t\t},\n\n\t\t/** Show the Features and Roadmap folder tabs. */\n\t\ttabs: { type: Boolean, default: true },\n\t\t/** `chips`: pill buttons with a count. `segmented`: a segmented control without counts. */\n\t\tfilter: {\n\t\t\ttype: String,\n\t\t\tdefault: 'chips',\n\t\t\tvalidator: (value) => ['chips', 'segmented'].includes(value),\n\t\t},\n\n\t\t/** Overrides of the status labels, by key (`all`, `available`, `beta`, `in_progress`, `planned`). */\n\t\tstatusLabels: { type: Object, default: () => ({}) },\n\t\t/** How many issues the Next card lists; 0 hides the card. */\n\t\tnext: { type: Number, default: 3 },\n\t\t/** Forge label to issue status (`in_progress`, `planned`) for the badges in the Next card. */\n\t\tissueLabels: { type: Object, default: () => ({}) },\n\t\t/** `<owner>/<repo>` on the forge, for the Roadmap tab and the Next card. */\n\t\trepo: { type: String, default: '' },\n\t\t/** Page title. */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Subtitle template; `{count}` is the number of features and `{appVersion}` the app version. */\n\t\tsubtitle: { type: String, default: '' },\n\t\t/** The app version, for `{appVersion}` in the subtitle. */\n\t\tappVersion: { type: String, default: '' },\n\t\t/** An info notice under the groups. */\n\t\tnotice: { type: String, default: '' },\n\t\t/** The app's documentation site: the Next card's link and the target of \"Learn more\". */\n\t\tdocumentationUrl: { type: String, default: '' },\n\t\t/** Show a Documentation button in the header. */\n\t\tdocumentationButton: { type: Boolean, default: false },\n\t\t/** The feature-request form; the header's primary button. */\n\t\tsuggestUrl: { type: String, default: '' },\n\t\t/** Show the feature-request form as the header's primary button; without it, it is an entry in the Actions menu. */\n\t\tsuggestButton: { type: Boolean, default: true },\n\t\t/** Target of the OpenBuilt entry in the Actions menu. */\n\t\topenbuiltUrl: { type: String, default: '' },\n\t\t/** Target of the AI skills entry in the Actions menu. */\n\t\tllmSkillsUrl: { type: String, default: '' },\n\t},\n\n\temits: ['support'],\n\n\tdata() {\n\t\tfeaturesBoardUid += 1\n\t\treturn {\n\t\t\tuid: `cn-features-board-${featuresBoardUid}`,\n\t\t\tactiveTab: 'features',\n\t\t\tstatus: 'all',\n\t\t\tissues: [],\n\t\t\tissuesLoading: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tlanguage() {\n\t\t\treturn readerLanguage()\n\t\t},\n\n\t\tgroups() {\n\t\t\treturn groupFeatures(this.features, this.categories, this.language)\n\t\t},\n\n\t\tallFeatures() {\n\t\t\treturn this.groups.flatMap((group) => group.features)\n\t\t},\n\n\t\tcounts() {\n\t\t\treturn countByStatus(this.allFeatures)\n\t\t},\n\n\t\t/** The groups with the features the filter lets through; an emptied group is left out. */\n\t\tshownGroups() {\n\t\t\treturn this.groups\n\t\t\t\t.map((group) => ({\n\t\t\t\t\t...group,\n\t\t\t\t\tshown: this.status === 'all' ? group.features : group.features.filter((feature) => feature.status === this.status),\n\t\t\t\t}))\n\t\t\t\t.filter((group) => group.shown.length > 0)\n\t\t},\n\n\t\t/** All, then each status; `in_progress` only when a feature has it. */\n\t\tfilterOptions() {\n\t\t\tconst options = [{ key: 'all', label: this.statusLabel('all'), count: this.counts.all }]\n\t\t\tfor (const key of FEATURE_STATUSES) {\n\t\t\t\tif (key === 'in_progress' && this.counts.in_progress === 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\toptions.push({ key, label: this.statusLabel(key), count: this.counts[key] })\n\t\t\t}\n\t\t\treturn options\n\t\t},\n\n\t\ttabList() {\n\t\t\treturn [\n\t\t\t\t{ key: 'features', label: t('nextcloud-vue', 'Features') },\n\t\t\t\t{ key: 'roadmap', label: t('nextcloud-vue', 'Roadmap') },\n\t\t\t]\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\treturn this.title ? this.tr(this.title) : t('nextcloud-vue', 'Features and roadmap')\n\t\t},\n\n\t\tresolvedSubtitle() {\n\t\t\tif (!this.subtitle) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.tr(this.subtitle)\n\t\t\t\t.replace(/\\{count\\}/g, String(this.counts.all))\n\t\t\t\t.replace(/\\{appVersion\\}/g, this.appVersion)\n\t\t},\n\n\t\tmenuLinks() {\n\t\t\tconst links = []\n\t\t\tif (this.suggestUrl && !this.suggestButton) {\n\t\t\t\tlinks.push({ key: 'suggest', href: this.suggestUrl, label: t('nextcloud-vue', 'Suggest feature') })\n\t\t\t}\n\t\t\tif (this.openbuiltUrl) {\n\t\t\t\tlinks.push({ key: 'openbuilt', href: this.openbuiltUrl, label: t('nextcloud-vue', 'Build on this app') })\n\t\t\t}\n\t\t\tif (this.llmSkillsUrl) {\n\t\t\t\tlinks.push({ key: 'llm', href: this.llmSkillsUrl, label: t('nextcloud-vue', 'AI skills') })\n\t\t\t}\n\t\t\treturn links\n\t\t},\n\n\t\tnextIssues() {\n\t\t\treturn this.issues.slice(0, this.next).map((issue) => ({\n\t\t\t\tnumber: issue.number,\n\t\t\t\ttitle: issue.title,\n\t\t\t\thtml_url: issue.html_url,\n\t\t\t\tstatus: issueStatus(issue, this.issueLabels),\n\t\t\t\topened: issue.created_at ? formatBoardDate(issue.created_at) : '',\n\t\t\t}))\n\t\t},\n\t},\n\n\tmounted() {\n\t\tif (this.next > 0 && this.repo) {\n\t\t\tthis.loadIssues()\n\t\t}\n\t},\n\n\tmethods: {\n\t\tt,\n\t\tsafeHref,\n\n\t\t/**\n\t\t * Run an app-supplied string through the app's translate function.\n\t\t *\n\t\t * @param {string} key The string.\n\t\t * @return {string} The translated string.\n\t\t */\n\t\ttr(key) {\n\t\t\treturn this.cnTranslate(key)\n\t\t},\n\n\t\t/**\n\t\t * The label of a status, or of `all`: the app's override, else the library's.\n\t\t *\n\t\t * @param {string} key The status key.\n\t\t * @return {string} The label.\n\t\t */\n\t\tstatusLabel(key) {\n\t\t\tif (typeof this.statusLabels[key] === 'string' && this.statusLabels[key] !== '') {\n\t\t\t\treturn this.tr(this.statusLabels[key])\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tall: t('nextcloud-vue', 'All'),\n\t\t\t\tavailable: t('nextcloud-vue', 'Available'),\n\t\t\t\tbeta: t('nextcloud-vue', 'Beta'),\n\t\t\t\tin_progress: t('nextcloud-vue', 'In progress'),\n\t\t\t\tplanned: t('nextcloud-vue', 'Planned'),\n\t\t\t}[key] || key\n\t\t},\n\n\t\t/**\n\t\t * The badge text of a feature: its status, with the expected moment\n\t\t * when it has one (\"Planned, Q4 2026\").\n\t\t *\n\t\t * @param {{status: string, expected: string}} feature The feature.\n\t\t * @return {string} The badge text.\n\t\t */\n\t\tstatusText(feature) {\n\t\t\tconst label = this.statusLabel(feature.status)\n\t\t\treturn feature.expected ? t('nextcloud-vue', '{status}, {expected}', { status: label, expected: feature.expected }) : label\n\t\t},\n\n\t\t/**\n\t\t * The badge variant of a status.\n\t\t *\n\t\t * @param {string} status The status key.\n\t\t * @return {string} The CnStatusBadge variant.\n\t\t */\n\t\tvariantOf(status) {\n\t\t\treturn FEATURE_STATUS_VARIANTS[status] || 'default'\n\t\t},\n\n\t\t/**\n\t\t * \"14 features, 14 available\": the group's size, then each status it holds.\n\t\t *\n\t\t * @param {{features: Array<{status: string}>}} group The group.\n\t\t * @return {string} The caption.\n\t\t */\n\t\tgroupCaption(group) {\n\t\t\tconst counts = countByStatus(group.features)\n\t\t\tconst parts = [n('nextcloud-vue', '%n feature', '%n features', counts.all)]\n\t\t\tfor (const key of FEATURE_STATUSES) {\n\t\t\t\tif (counts[key] > 0) {\n\t\t\t\t\tparts.push(`${counts[key]} ${this.statusLabel(key).toLocaleLowerCase()}`)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn parts.join(', ')\n\t\t},\n\n\t\t/**\n\t\t * Where \"Learn more\" goes: the feature's own page when it is a web\n\t\t * address, else the documentation site, else nowhere.\n\t\t *\n\t\t * @param {{docsUrl: string}} feature The feature.\n\t\t * @return {string} The address, or ''.\n\t\t */\n\t\treadMoreHref(feature) {\n\t\t\tif (/^https?:\\/\\//i.test(feature.docsUrl)) {\n\t\t\t\treturn safeHref(feature.docsUrl)\n\t\t\t}\n\t\t\treturn this.documentationUrl ? safeHref(this.documentationUrl) : ''\n\t\t},\n\n\t\t/** Ask the host to open the support dialog. */\n\t\tonSupport() {\n\t\t\t/**\n\t\t\t * @event support Emitted when the reader picks \"Support this app\" in the Actions menu.\n\t\t\t */\n\t\t\tthis.$emit('support')\n\t\t},\n\n\t\t/** Fetch the forge issues for the Next card. */\n\t\tasync loadIssues() {\n\t\t\tthis.issuesLoading = true\n\t\t\tconst { items } = await fetchRoadmapIssues(this.repo)\n\t\t\tthis.issues = items\n\t\t\tthis.issuesLoading = false\n\t\t},\n\n\t\t/**\n\t\t * Arrow keys, Home and End move between the folder tabs.\n\t\t *\n\t\t * @param {KeyboardEvent} event The key press.\n\t\t */\n\t\tonTabKeydown(event) {\n\t\t\tconst keys = this.tabList.map((tab) => tab.key)\n\t\t\tconst index = keys.indexOf(this.activeTab)\n\t\t\tconst target = {\n\t\t\t\tArrowRight: (index + 1) % keys.length,\n\t\t\t\tArrowLeft: (index - 1 + keys.length) % keys.length,\n\t\t\t\tHome: 0,\n\t\t\t\tEnd: keys.length - 1,\n\t\t\t}[event.key]\n\t\t\tif (target === undefined) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tthis.activeTab = keys[target]\n\t\t\tthis.$nextTick(() => this.$refs.tabButtons?.[target]?.focus())\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-features-board {\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--cn-board-section-gap, 20px);\n\tpadding: var(--cn-board-content-padding, 24px 28px);\n\tmax-width: 1240px;\n\twidth: 100%;\n}\n\n.cn-features-board__header {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 4px 0 8px;\n}\n\n.cn-features-board__title-row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tjustify-content: space-between;\n\talign-items: flex-start;\n\tgap: 12px 24px;\n}\n\n.cn-features-board__title {\n\tflex: 1 1 320px;\n\tmin-width: 0;\n\tmargin: 0;\n\tfont-size: 28px;\n\tfont-weight: 700;\n\tline-height: 1.2;\n}\n\n.cn-features-board__header-actions {\n\tflex: none;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 10px;\n}\n\n.cn-features-board__subtitle {\n\tmargin: 0;\n\tfont-size: 15px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__tab-row {\n\tdisplay: flex;\n\talign-items: flex-end;\n\tgap: 8px;\n\tmargin-bottom: calc(-1 * var(--cn-board-section-gap, 20px));\n}\n\n.cn-features-board__tabs {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 4px;\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-features-board__tab {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tbox-sizing: border-box;\n\tmin-height: 34px;\n\tmargin: 0 0 -1px;\n\tpadding: 8px 12px;\n\tborder: 1px solid var(--cn-board-hairline, var(--color-border));\n\tborder-bottom: none;\n\tborder-radius: 8px 8px 0 0;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n\tfont-weight: 400;\n\tcursor: pointer;\n\twhite-space: nowrap;\n}\n\n.cn-features-board__tab--active {\n\tposition: relative;\n\tbackground: var(--color-main-background);\n\tborder-bottom: 1px solid var(--color-main-background);\n\tfont-weight: 700;\n}\n\n.cn-features-board__tab-actions {\n\tflex: none;\n\talign-self: flex-start;\n\tmargin: 0 8px 8px 0;\n}\n\n.cn-features-board__panel {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 22px;\n}\n\n.cn-features-board__panel--tabbed {\n\tpadding: 22px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 0 var(--cn-board-card-radius, 12px) var(--cn-board-card-radius, 12px) var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__filter {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-features-board__filter-option {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin: 0;\n\tfont-size: 14px;\n\tfont-weight: 600;\n\tcursor: pointer;\n}\n\n.cn-features-board__filter--chips .cn-features-board__filter-option {\n\theight: 36px;\n\tpadding: 0 14px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: 18px;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-board__filter--chips .cn-features-board__filter-option--active {\n\tborder-color: var(--color-main-text);\n\tbackground: var(--color-main-text);\n\tcolor: var(--color-main-background);\n}\n\n.cn-features-board__filter-count {\n\tfont-weight: 400;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__filter--chips .cn-features-board__filter-option--active .cn-features-board__filter-count {\n\tcolor: var(--color-border-dark);\n}\n\n.cn-features-board__filter--segmented {\n\tdisplay: inline-flex;\n\talign-self: flex-start;\n\tgap: 2px;\n\tpadding: 3px;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n}\n\n.cn-features-board__filter--segmented .cn-features-board__filter-option {\n\theight: 34px;\n\tpadding: 0 14px;\n\tborder: 0;\n\tborder-radius: 6px;\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-board__filter--segmented .cn-features-board__filter-option--active {\n\tbackground: var(--color-main-background);\n\tbox-shadow: 0 1px 2px rgba(27, 28, 29, 0.12);\n}\n\n.cn-features-board__body {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tgap: 24px;\n}\n\n.cn-features-board__groups {\n\tflex: 999 1 520px;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 26px;\n}\n\n.cn-features-board__groups--rows {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(340px, 1fr));\n\tgap: 20px;\n}\n\n.cn-features-board__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n\tmax-width: none;\n\tmargin: 0;\n}\n\n.cn-features-board__groups--rows .cn-features-board__group {\n\tpadding: var(--cn-board-card-padding, 20px 22px);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__group-head {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: baseline;\n\tgap: 10px;\n}\n\n.cn-features-board__group-title {\n\tmargin: 0;\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n.cn-features-board__groups--rows .cn-features-board__group-title {\n\tfont-size: 17px;\n}\n\n.cn-features-board__group-caption {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__cards {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n\tgap: 16px;\n}\n\n.cn-features-board__card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 10px;\n\tpadding: 18px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__card-head {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: flex-start;\n\tgap: 4px;\n}\n\n.cn-features-board__card-title {\n\tmargin: 0;\n\tfont-size: 17px;\n\tfont-weight: 700;\n\tline-height: 1.3;\n}\n\n.cn-features-board__card-summary {\n\tmargin: 0;\n\tfont-size: 14px;\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-board__card-footer {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\talign-items: center;\n\tmin-height: 34px;\n\tmargin-top: auto;\n\tpadding-top: 10px;\n\tborder-top: 1px solid var(--color-background-dark);\n}\n\n.cn-features-board__read-more {\n\tdisplay: inline-flex;\n\talign-items: center;\n\theight: 34px;\n\tpadding: 0 12px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: 8px;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n\tfont-weight: 600;\n\ttext-decoration: none;\n}\n\n.cn-features-board__rows {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-features-board__row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 6px 16px;\n\tpadding: 10px 0;\n\tborder-top: 1px solid var(--color-background-dark);\n}\n\n.cn-features-board__row-text {\n\tflex: 1 1 240px;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-features-board__row-title {\n\tfont-size: 15px;\n\tfont-weight: 500;\n}\n\n.cn-features-board__row-summary {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__next {\n\tflex: 1 1 280px;\n\tmin-width: 0;\n\tmax-width: 340px;\n}\n\n.cn-features-board__next-card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n\tpadding: 18px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-features-board__next-head {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-features-board__next-title {\n\tmargin: 0;\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n.cn-features-board__next-lead {\n\tmargin: 0;\n\tfont-size: 14px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__next-list {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 10px;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-features-board__next-item {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 12px 14px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 10px;\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__next-link {\n\tfont-size: 15px;\n\tfont-weight: 600;\n\tline-height: 1.3;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-features-board__next-meta {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__next-opened {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n}\n\n.cn-features-board__next-more {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-features-board__empty {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__notice {\n\tmargin: 0;\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode","_toDisplayString","_createBlock","_createVNode","_createTextVNode","_Fragment","_renderList","_openBlock"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnFeaturesBoard.vue3.js","sources":["../../../../src/components/CnFeaturesBoard/CnFeaturesBoard.vue?vue&type=template&id=ed367d64&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-features-board\" :class=\"`cn-features-board--${layout}`\" data-testid=\"cn-features-board\">\n\t\t<header class=\"cn-features-board__header\">\n\t\t\t<div class=\"cn-features-board__title-row\">\n\t\t\t\t<h1 class=\"cn-features-board__title\">\n\t\t\t\t\t{{ resolvedTitle }}\n\t\t\t\t</h1>\n\t\t\t\t<div class=\"cn-features-board__header-actions\">\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"documentationButton && documentationUrl\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t:href=\"documentationUrl\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-docs\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<BookOpenVariant :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Documentation') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<CnBuildiqEditButton />\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"suggestUrl && suggestButton\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:href=\"suggestUrl\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-suggest\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Suggest feature') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcActions\n\t\t\t\t\t\tv-if=\"!tabs\"\n\t\t\t\t\t\t:menuName=\"t('nextcloud-vue', 'Actions')\"\n\t\t\t\t\t\t:forceName=\"true\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-actions\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<DotsHorizontal :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<NcActionLink\n\t\t\t\t\t\t\tv-for=\"action in menuLinks\"\n\t\t\t\t\t\t\t:key=\"action.key\"\n\t\t\t\t\t\t\t:href=\"action.href\"\n\t\t\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t\t<NcActionButton @click=\"onSupport\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Support this app') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</NcActions>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<p v-if=\"resolvedSubtitle\" class=\"cn-features-board__subtitle\" data-testid=\"cn-features-board-subtitle\">\n\t\t\t\t{{ resolvedSubtitle }}\n\t\t\t</p>\n\t\t</header>\n\n\t\t<div v-if=\"tabs\" class=\"cn-features-board__tab-row\">\n\t\t\t<div\n\t\t\t\tclass=\"cn-features-board__tabs\"\n\t\t\t\trole=\"tablist\"\n\t\t\t\t:aria-label=\"resolvedTitle\"\n\t\t\t\t@keydown=\"onTabKeydown\">\n\t\t\t\t<button\n\t\t\t\t\tv-for=\"tab in tabList\"\n\t\t\t\t\t:id=\"`${uid}-tab-${tab.key}`\"\n\t\t\t\t\t:key=\"tab.key\"\n\t\t\t\t\tref=\"tabButtons\"\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\trole=\"tab\"\n\t\t\t\t\tclass=\"cn-features-board__tab\"\n\t\t\t\t\t:class=\"{ 'cn-features-board__tab--active': activeTab === tab.key }\"\n\t\t\t\t\t:aria-selected=\"activeTab === tab.key ? 'true' : 'false'\"\n\t\t\t\t\t:aria-controls=\"`${uid}-panel`\"\n\t\t\t\t\t:tabindex=\"activeTab === tab.key ? 0 : -1\"\n\t\t\t\t\t:data-testid=\"`cn-features-board-tab-${tab.key}`\"\n\t\t\t\t\t@click=\"activeTab = tab.key\">\n\t\t\t\t\t{{ tab.label }}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t<NcActions\n\t\t\t\t:menuName=\"t('nextcloud-vue', 'Actions')\"\n\t\t\t\t:forceName=\"true\"\n\t\t\t\tclass=\"cn-features-board__tab-actions\"\n\t\t\t\tdata-testid=\"cn-features-board-actions\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<DotsHorizontal :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t<NcActionLink\n\t\t\t\t\tv-for=\"action in menuLinks\"\n\t\t\t\t\t:key=\"action.key\"\n\t\t\t\t\t:href=\"action.href\"\n\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionLink>\n\t\t\t\t<NcActionButton @click=\"onSupport\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Support this app') }}\n\t\t\t\t</NcActionButton>\n\t\t\t</NcActions>\n\t\t</div>\n\n\t\t<div\n\t\t\t:id=\"`${uid}-panel`\"\n\t\t\tclass=\"cn-features-board__panel\"\n\t\t\t:class=\"{ 'cn-features-board__panel--tabbed': tabs }\"\n\t\t\t:role=\"tabs ? 'tabpanel' : null\"\n\t\t\t:aria-labelledby=\"tabs ? `${uid}-tab-${activeTab}` : null\">\n\t\t\t<CnRoadmapTab v-if=\"tabs && activeTab === 'roadmap'\" :repo=\"repo\" />\n\t\t\t<template v-else>\n\t\t\t\t<div\n\t\t\t\t\tclass=\"cn-features-board__filter\"\n\t\t\t\t\t:class=\"`cn-features-board__filter--${filter}`\"\n\t\t\t\t\trole=\"group\"\n\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Status')\"\n\t\t\t\t\tdata-testid=\"cn-features-board-filter\">\n\t\t\t\t\t<button\n\t\t\t\t\t\tv-for=\"option in filterOptions\"\n\t\t\t\t\t\t:key=\"option.key\"\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclass=\"cn-features-board__filter-option\"\n\t\t\t\t\t\t:class=\"{ 'cn-features-board__filter-option--active': status === option.key }\"\n\t\t\t\t\t\t:aria-pressed=\"status === option.key ? 'true' : 'false'\"\n\t\t\t\t\t\t:data-status=\"option.key\"\n\t\t\t\t\t\t@click=\"status = option.key\">\n\t\t\t\t\t\t{{ option.label }}<span\n\t\t\t\t\t\t\tv-if=\"filter === 'chips'\"\n\t\t\t\t\t\t\tclass=\"cn-features-board__filter-count\"\n\t\t\t\t\t\t\tdata-testid=\"cn-features-board-filter-count\">{{ ' ' + option.count }}</span>\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\n\t\t\t\t<div class=\"cn-features-board__body\">\n\t\t\t\t\t<div class=\"cn-features-board__groups\" :class=\"`cn-features-board__groups--${layout}`\">\n\t\t\t\t\t\t<p v-if=\"shownGroups.length === 0\" class=\"cn-features-board__empty\" data-testid=\"cn-features-board-empty\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'No features with this status') }}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<section\n\t\t\t\t\t\t\tv-for=\"group in shownGroups\"\n\t\t\t\t\t\t\t:key=\"group.key\"\n\t\t\t\t\t\t\tclass=\"cn-features-board__group\"\n\t\t\t\t\t\t\t:aria-labelledby=\"group.label ? `${uid}-group-${group.key}` : null\"\n\t\t\t\t\t\t\t:aria-label=\"group.label ? null : resolvedTitle\"\n\t\t\t\t\t\t\tdata-testid=\"cn-features-board-group\"\n\t\t\t\t\t\t\t:data-group=\"group.key\">\n\t\t\t\t\t\t\t<div v-if=\"group.label\" class=\"cn-features-board__group-head\">\n\t\t\t\t\t\t\t\t<h2 :id=\"`${uid}-group-${group.key}`\" class=\"cn-features-board__group-title\">\n\t\t\t\t\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"layout === 'cards'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__group-caption\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-group-caption\">{{ groupCaption(group) }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div v-if=\"layout === 'cards'\" class=\"cn-features-board__cards\">\n\t\t\t\t\t\t\t\t<article\n\t\t\t\t\t\t\t\t\tv-for=\"feature in group.shown\"\n\t\t\t\t\t\t\t\t\t:key=\"feature.slug\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__card\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-feature\">\n\t\t\t\t\t\t\t\t\t<div class=\"cn-features-board__card-head\">\n\t\t\t\t\t\t\t\t\t\t<h3 class=\"cn-features-board__card-title\">\n\t\t\t\t\t\t\t\t\t\t\t{{ feature.title }}\n\t\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\t\t\t:label=\"statusText(feature)\"\n\t\t\t\t\t\t\t\t\t\t\t:variant=\"variantOf(feature.status)\"\n\t\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-status\" />\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<p v-if=\"feature.summary\" class=\"cn-features-board__card-summary\">\n\t\t\t\t\t\t\t\t\t\t{{ feature.summary }}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t<div v-if=\"readMoreHref(feature)\" class=\"cn-features-board__card-footer\">\n\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__read-more\"\n\t\t\t\t\t\t\t\t\t\t\t:href=\"readMoreHref(feature)\"\n\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Learn more about {title}', { title: feature.title })\">\n\t\t\t\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Learn more') }}\n\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</article>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<ul v-else class=\"cn-features-board__rows\">\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tv-for=\"feature in group.shown\"\n\t\t\t\t\t\t\t\t\t:key=\"feature.slug\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__row\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-feature\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-features-board__row-text\">\n\t\t\t\t\t\t\t\t\t\t<span class=\"cn-features-board__row-title\">{{ feature.title }}</span>\n\t\t\t\t\t\t\t\t\t\t<span v-if=\"summaries && feature.summary\" class=\"cn-features-board__row-summary\">{{ feature.summary }}</span>\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\t\t:label=\"statusText(feature)\"\n\t\t\t\t\t\t\t\t\t\t:variant=\"variantOf(feature.status)\"\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-status\" />\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</section>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<aside\n\t\t\t\t\t\tv-if=\"next > 0 && repo\"\n\t\t\t\t\t\tclass=\"cn-features-board__next\"\n\t\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Next on the roadmap')\"\n\t\t\t\t\t\tdata-testid=\"cn-features-board-next\">\n\t\t\t\t\t\t<section class=\"cn-features-board__next-card\">\n\t\t\t\t\t\t\t<div class=\"cn-features-board__next-head\">\n\t\t\t\t\t\t\t\t<RoadVariant :size=\"20\" />\n\t\t\t\t\t\t\t\t<h2 class=\"cn-features-board__next-title\">\n\t\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Coming next') }}\n\t\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<p class=\"cn-features-board__next-lead\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'The most requested items from the issue tracker.') }}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<ol v-if=\"nextIssues.length > 0\" class=\"cn-features-board__next-list\">\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tv-for=\"issue in nextIssues\"\n\t\t\t\t\t\t\t\t\t:key=\"issue.number\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-item\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-next-item\">\n\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-link\"\n\t\t\t\t\t\t\t\t\t\t:href=\"safeHref(issue.html_url)\"\n\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\">{{ issue.title }}</a>\n\t\t\t\t\t\t\t\t\t<div class=\"cn-features-board__next-meta\">\n\t\t\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\t\t\tv-if=\"issue.status\"\n\t\t\t\t\t\t\t\t\t\t\t:label=\"statusLabel(issue.status)\"\n\t\t\t\t\t\t\t\t\t\t\t:variant=\"variantOf(issue.status)\" />\n\t\t\t\t\t\t\t\t\t\t<span v-if=\"issue.opened\" class=\"cn-features-board__next-opened\">\n\t\t\t\t\t\t\t\t\t\t\t<CalendarBlankOutline :size=\"14\" />{{ t('nextcloud-vue', 'Opened {date}', { date: issue.opened }) }}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t<span>#{{ issue.number }}</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t</ol>\n\t\t\t\t\t\t\t<p v-else-if=\"!issuesLoading\" class=\"cn-features-board__next-lead\" data-testid=\"cn-features-board-next-empty\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'No roadmap items yet') }}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\tv-if=\"tabs\"\n\t\t\t\t\t\t\t\thref=\"#\"\n\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-more\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-features-board-whole-roadmap\"\n\t\t\t\t\t\t\t\t@click.prevent=\"activeTab = 'roadmap'\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'See the whole roadmap') }}<ChevronRight :size=\"16\" />\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\tv-if=\"documentationUrl\"\n\t\t\t\t\t\t\t\t:href=\"documentationUrl\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\tclass=\"cn-features-board__next-more\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Documentation') }}<ChevronRight :size=\"16\" />\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</section>\n\t\t\t\t\t</aside>\n\t\t\t\t</div>\n\n\t\t\t\t<NcNoteCard v-if=\"notice\"\n\t\t\t\t\ttype=\"info\"\n\t\t\t\t\tclass=\"cn-features-board__notice\"\n\t\t\t\t\tdata-testid=\"cn-features-board-notice\">\n\t\t\t\t\t<template v-if=\"noticeParts.link\">\n\t\t\t\t\t\t{{ noticeParts.before }}<a\n\t\t\t\t\t\t\t:href=\"noticeParts.link.href\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\tdata-testid=\"cn-features-board-notice-link\">{{ noticeParts.link.text }}</a>{{ noticeParts.after }}\n\t\t\t\t\t</template>\n\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t{{ noticeParts.before }}\n\t\t\t\t\t</template>\n\t\t\t\t</NcNoteCard>\n\t\t\t</template>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { getLanguage, translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActionLink, NcActions, NcButton, NcNoteCard } from '@nextcloud/vue'\nimport BookOpenVariant from 'vue-material-design-icons/BookOpenVariant.vue'\nimport CalendarBlankOutline from 'vue-material-design-icons/CalendarBlankOutline.vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport DotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport RoadVariant from 'vue-material-design-icons/RoadVariant.vue'\nimport CnBuildiqEditButton from '../CnBuildiqEditButton/CnBuildiqEditButton.vue'\nimport CnRoadmapTab from '../CnRoadmapTab/CnRoadmapTab.vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { formatBoardDate } from '../../utils/boardDate.js'\nimport { countByStatus, FEATURE_STATUS_VARIANTS, FEATURE_STATUSES, groupFeatures } from '../../utils/featureCatalog.js'\nimport { fetchRoadmapIssues, issueStatus } from '../../utils/roadmapIssues.js'\nimport { safeHref } from '../../utils/safeHref.js'\n\nlet featuresBoardUid = 0\n\n/**\n * Read the reader's language, which @nextcloud/l10n throws on outside a\n * Nextcloud page.\n *\n * @return {string} The language tag, or ''.\n */\nfunction readerLanguage() {\n\ttry {\n\t\treturn getLanguage() || ''\n\t} catch {\n\t\treturn ''\n\t}\n}\n\n/**\n * CnFeaturesBoard - the Features and roadmap page in the board look.\n *\n * A title with a subtitle, optional folder tabs (Features, Roadmap), a status\n * filter (count chips or a segmented control), the features grouped by\n * category (as two-up cards or as rows inside one card per group), an\n * optional \"Next\" card with the most requested forge issues, and an optional\n * notice under the groups. The links the default look puts in its right-hand\n * sidebar (suggest, buildiq, AI skills, support) sit in an Actions menu.\n *\n * Rendered by CnFeaturesAndRoadmapView when the page is in the board look.\n *\n * @spec openspec/changes/screens-roadmap-board-look/specs/features-roadmap-board-look/spec.md\n *\n * @event support Emitted when the reader picks \"Support this app\" in the Actions menu.\n */\nexport default {\n\tname: 'CnFeaturesBoard',\n\n\tcomponents: {\n\t\tBookOpenVariant,\n\t\tCalendarBlankOutline,\n\t\tChevronRight,\n\t\tCnBuildiqEditButton,\n\t\tCnRoadmapTab,\n\t\tCnStatusBadge,\n\t\tDotsHorizontal,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcActions,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tPlus,\n\t\tRoadVariant,\n\t},\n\n\tinject: {\n\t\t/** The app's translate function, provided by CnAppRoot. Identity outside one. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The features: `{ slug, title, summary, status, category, expected, docsUrl, title_<lang>, summary_<lang> }`. */\n\t\tfeatures: { type: Array, default: () => [] },\n\t\t/** Category key to caption; sets the order of the groups. */\n\t\tcategories: { type: Object, default: () => ({}) },\n\t\t/** `cards`: a section per category with two-up feature cards. `rows`: one card per category with a row per feature. */\n\t\tlayout: {\n\t\t\ttype: String,\n\t\t\tdefault: 'cards',\n\t\t\tvalidator: (value) => ['cards', 'rows'].includes(value),\n\t\t},\n\n\t\t/** Show the Features and Roadmap folder tabs. */\n\t\ttabs: { type: Boolean, default: true },\n\t\t/** `chips`: pill buttons with a count. `segmented`: a segmented control without counts. */\n\t\tfilter: {\n\t\t\ttype: String,\n\t\t\tdefault: 'chips',\n\t\t\tvalidator: (value) => ['chips', 'segmented'].includes(value),\n\t\t},\n\n\t\t/** Overrides of the status labels, by key (`all`, `available`, `beta`, `in_progress`, `planned`). */\n\t\tstatusLabels: { type: Object, default: () => ({}) },\n\t\t/** How many issues the Next card lists; 0 hides the card. */\n\t\tnext: { type: Number, default: 3 },\n\t\t/** Forge label to issue status (`in_progress`, `planned`) for the badges in the Next card. */\n\t\tissueLabels: { type: Object, default: () => ({}) },\n\t\t/** `<owner>/<repo>` on the forge, for the Roadmap tab and the Next card. */\n\t\trepo: { type: String, default: '' },\n\t\t/** Page title. */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Subtitle template; `{count}` is the number of features and `{appVersion}` the app version. */\n\t\tsubtitle: { type: String, default: '' },\n\t\t/** The app version, for `{appVersion}` in the subtitle. */\n\t\tappVersion: { type: String, default: '' },\n\t\t/** An info notice under the groups. */\n\t\tnotice: { type: String, default: '' },\n\t\t/** The app's documentation site: the Next card's link and the target of \"Learn more\". */\n\t\tdocumentationUrl: { type: String, default: '' },\n\t\t/** Show a Documentation button in the header. */\n\t\tdocumentationButton: { type: Boolean, default: false },\n\t\t/** The feature-request form; the header's primary button. */\n\t\tsuggestUrl: { type: String, default: '' },\n\t\t/** Show the feature-request form as the header's primary button; without it, it is an entry in the Actions menu. */\n\t\tsuggestButton: { type: Boolean, default: true },\n\t\t/** In the `rows` layout, draw each feature's summary under its title; false shows the title and status only. */\n\t\tsummaries: { type: Boolean, default: true },\n\t\t/** A link inside the notice: `{ text, href }`; the first occurrence of `text` in the translated notice opens `href` in a new tab. Only http(s) and site-relative hrefs link. */\n\t\tnoticeLink: { type: Object, default: null },\n\t\t/** Target of the OpenBuilt entry in the Actions menu. */\n\t\topenbuiltUrl: { type: String, default: '' },\n\t\t/** Target of the AI skills entry in the Actions menu. */\n\t\tllmSkillsUrl: { type: String, default: '' },\n\t},\n\n\temits: ['support'],\n\n\tdata() {\n\t\tfeaturesBoardUid += 1\n\t\treturn {\n\t\t\tuid: `cn-features-board-${featuresBoardUid}`,\n\t\t\tactiveTab: 'features',\n\t\t\tstatus: 'all',\n\t\t\tissues: [],\n\t\t\tissuesLoading: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tlanguage() {\n\t\t\treturn readerLanguage()\n\t\t},\n\n\t\t/** The groups, their captions through the app's translate function. */\n\t\tgroups() {\n\t\t\treturn groupFeatures(this.features, this.categories, this.language)\n\t\t\t\t.map((group) => ({ ...group, label: group.label ? this.tr(group.label) : '' }))\n\t\t},\n\n\t\tallFeatures() {\n\t\t\treturn this.groups.flatMap((group) => group.features)\n\t\t},\n\n\t\tcounts() {\n\t\t\treturn countByStatus(this.allFeatures)\n\t\t},\n\n\t\t/** The groups with the features the filter lets through; an emptied group is left out. */\n\t\tshownGroups() {\n\t\t\treturn this.groups\n\t\t\t\t.map((group) => ({\n\t\t\t\t\t...group,\n\t\t\t\t\tshown: this.status === 'all' ? group.features : group.features.filter((feature) => feature.status === this.status),\n\t\t\t\t}))\n\t\t\t\t.filter((group) => group.shown.length > 0)\n\t\t},\n\n\t\t/** All, then each status; `in_progress` only when a feature has it. */\n\t\tfilterOptions() {\n\t\t\tconst options = [{ key: 'all', label: this.statusLabel('all'), count: this.counts.all }]\n\t\t\tfor (const key of FEATURE_STATUSES) {\n\t\t\t\tif (key === 'in_progress' && this.counts.in_progress === 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\toptions.push({ key, label: this.statusLabel(key), count: this.counts[key] })\n\t\t\t}\n\t\t\treturn options\n\t\t},\n\n\t\ttabList() {\n\t\t\treturn [\n\t\t\t\t{ key: 'features', label: t('nextcloud-vue', 'Features') },\n\t\t\t\t{ key: 'roadmap', label: t('nextcloud-vue', 'Roadmap') },\n\t\t\t]\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\treturn this.title ? this.tr(this.title) : t('nextcloud-vue', 'Features and roadmap')\n\t\t},\n\n\t\t/**\n\t\t * The notice split around its link: the text before, the link and the\n\t\t * text after. Without a usable `noticeLink` (no match in the\n\t\t * translated notice, or an href that is not http(s) or site-relative)\n\t\t * `before` is the whole notice.\n\t\t *\n\t\t * @return {{before: string, link: ({text: string, href: string}|null), after: string}} The parts.\n\t\t */\n\t\tnoticeParts() {\n\t\t\tconst text = this.notice ? this.tr(this.notice) : ''\n\t\t\tconst link = this.noticeLink\n\t\t\tconst label = link && typeof link.text === 'string' ? this.tr(link.text) : ''\n\t\t\tconst href = link && typeof link.href === 'string' ? link.href.trim() : ''\n\t\t\tconst index = label ? text.indexOf(label) : -1\n\t\t\tconst allowed = (/^https?:\\/\\//i.test(href) || href.startsWith('/')) && safeHref(href) === href\n\t\t\tif (index === -1 || !allowed) {\n\t\t\t\treturn { before: text, link: null, after: '' }\n\t\t\t}\n\t\t\treturn { before: text.slice(0, index), link: { text: label, href }, after: text.slice(index + label.length) }\n\t\t},\n\n\t\tresolvedSubtitle() {\n\t\t\tif (!this.subtitle) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.tr(this.subtitle)\n\t\t\t\t.replace(/\\{count\\}/g, String(this.counts.all))\n\t\t\t\t.replace(/\\{appVersion\\}/g, this.appVersion)\n\t\t},\n\n\t\tmenuLinks() {\n\t\t\tconst links = []\n\t\t\tif (this.suggestUrl && !this.suggestButton) {\n\t\t\t\tlinks.push({ key: 'suggest', href: this.suggestUrl, label: t('nextcloud-vue', 'Suggest feature') })\n\t\t\t}\n\t\t\tif (this.openbuiltUrl) {\n\t\t\t\tlinks.push({ key: 'openbuilt', href: this.openbuiltUrl, label: t('nextcloud-vue', 'Build on this app') })\n\t\t\t}\n\t\t\tif (this.llmSkillsUrl) {\n\t\t\t\tlinks.push({ key: 'llm', href: this.llmSkillsUrl, label: t('nextcloud-vue', 'AI skills') })\n\t\t\t}\n\t\t\treturn links\n\t\t},\n\n\t\tnextIssues() {\n\t\t\treturn this.issues.slice(0, this.next).map((issue) => ({\n\t\t\t\tnumber: issue.number,\n\t\t\t\ttitle: issue.title,\n\t\t\t\thtml_url: issue.html_url,\n\t\t\t\tstatus: issueStatus(issue, this.issueLabels),\n\t\t\t\topened: issue.created_at ? formatBoardDate(issue.created_at) : '',\n\t\t\t}))\n\t\t},\n\t},\n\n\tmounted() {\n\t\tif (this.next > 0 && this.repo) {\n\t\t\tthis.loadIssues()\n\t\t}\n\t},\n\n\tmethods: {\n\t\tt,\n\t\tsafeHref,\n\n\t\t/**\n\t\t * Run an app-supplied string through the app's translate function.\n\t\t *\n\t\t * @param {string} key The string.\n\t\t * @return {string} The translated string.\n\t\t */\n\t\ttr(key) {\n\t\t\treturn this.cnTranslate(key)\n\t\t},\n\n\t\t/**\n\t\t * The label of a status, or of `all`: the app's override, else the library's.\n\t\t *\n\t\t * @param {string} key The status key.\n\t\t * @return {string} The label.\n\t\t */\n\t\tstatusLabel(key) {\n\t\t\tif (typeof this.statusLabels[key] === 'string' && this.statusLabels[key] !== '') {\n\t\t\t\treturn this.tr(this.statusLabels[key])\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tall: t('nextcloud-vue', 'All'),\n\t\t\t\tavailable: t('nextcloud-vue', 'Available'),\n\t\t\t\tbeta: t('nextcloud-vue', 'Beta'),\n\t\t\t\tin_progress: t('nextcloud-vue', 'In progress'),\n\t\t\t\tplanned: t('nextcloud-vue', 'Planned'),\n\t\t\t}[key] || key\n\t\t},\n\n\t\t/**\n\t\t * The badge text of a feature: its status, with the expected moment\n\t\t * when it has one (\"Planned, Q4 2026\").\n\t\t *\n\t\t * @param {{status: string, expected: string}} feature The feature.\n\t\t * @return {string} The badge text.\n\t\t */\n\t\tstatusText(feature) {\n\t\t\tconst label = this.statusLabel(feature.status)\n\t\t\treturn feature.expected ? t('nextcloud-vue', '{status}, {expected}', { status: label, expected: feature.expected }) : label\n\t\t},\n\n\t\t/**\n\t\t * The badge variant of a status.\n\t\t *\n\t\t * @param {string} status The status key.\n\t\t * @return {string} The CnStatusBadge variant.\n\t\t */\n\t\tvariantOf(status) {\n\t\t\treturn FEATURE_STATUS_VARIANTS[status] || 'default'\n\t\t},\n\n\t\t/**\n\t\t * \"14 features, 14 available\": the group's size, then each status it holds.\n\t\t *\n\t\t * @param {{features: Array<{status: string}>}} group The group.\n\t\t * @return {string} The caption.\n\t\t */\n\t\tgroupCaption(group) {\n\t\t\tconst counts = countByStatus(group.features)\n\t\t\tconst parts = [n('nextcloud-vue', '%n feature', '%n features', counts.all)]\n\t\t\tfor (const key of FEATURE_STATUSES) {\n\t\t\t\tif (counts[key] > 0) {\n\t\t\t\t\tparts.push(`${counts[key]} ${this.statusLabel(key).toLocaleLowerCase()}`)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn parts.join(', ')\n\t\t},\n\n\t\t/**\n\t\t * Where \"Learn more\" goes: the feature's own page when it is a web\n\t\t * address, else the documentation site, else nowhere.\n\t\t *\n\t\t * @param {{docsUrl: string}} feature The feature.\n\t\t * @return {string} The address, or ''.\n\t\t */\n\t\treadMoreHref(feature) {\n\t\t\tif (/^https?:\\/\\//i.test(feature.docsUrl)) {\n\t\t\t\treturn safeHref(feature.docsUrl)\n\t\t\t}\n\t\t\treturn this.documentationUrl ? safeHref(this.documentationUrl) : ''\n\t\t},\n\n\t\t/** Ask the host to open the support dialog. */\n\t\tonSupport() {\n\t\t\t/**\n\t\t\t * @event support Emitted when the reader picks \"Support this app\" in the Actions menu.\n\t\t\t */\n\t\t\tthis.$emit('support')\n\t\t},\n\n\t\t/** Fetch the forge issues for the Next card. */\n\t\tasync loadIssues() {\n\t\t\tthis.issuesLoading = true\n\t\t\tconst { items } = await fetchRoadmapIssues(this.repo)\n\t\t\tthis.issues = items\n\t\t\tthis.issuesLoading = false\n\t\t},\n\n\t\t/**\n\t\t * Arrow keys, Home and End move between the folder tabs.\n\t\t *\n\t\t * @param {KeyboardEvent} event The key press.\n\t\t */\n\t\tonTabKeydown(event) {\n\t\t\tconst keys = this.tabList.map((tab) => tab.key)\n\t\t\tconst index = keys.indexOf(this.activeTab)\n\t\t\tconst target = {\n\t\t\t\tArrowRight: (index + 1) % keys.length,\n\t\t\t\tArrowLeft: (index - 1 + keys.length) % keys.length,\n\t\t\t\tHome: 0,\n\t\t\t\tEnd: keys.length - 1,\n\t\t\t}[event.key]\n\t\t\tif (target === undefined) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tthis.activeTab = keys[target]\n\t\t\tthis.$nextTick(() => this.$refs.tabButtons?.[target]?.focus())\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-features-board {\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--cn-board-section-gap, 20px);\n\tpadding: var(--cn-board-content-padding, 24px 28px);\n\tmax-width: 1240px;\n\twidth: 100%;\n}\n\n.cn-features-board__header {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 4px 0 8px;\n}\n\n.cn-features-board__title-row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tjustify-content: space-between;\n\talign-items: flex-start;\n\tgap: 12px 24px;\n}\n\n.cn-features-board__title {\n\tflex: 1 1 320px;\n\tmin-width: 0;\n\tmargin: 0;\n\tfont-size: 28px;\n\tfont-weight: 700;\n\tline-height: 1.2;\n}\n\n.cn-features-board__header-actions {\n\tflex: none;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 10px;\n}\n\n.cn-features-board__subtitle {\n\tmargin: 0;\n\tfont-size: 15px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__tab-row {\n\tdisplay: flex;\n\talign-items: flex-end;\n\tgap: 8px;\n\tmargin-bottom: calc(-1 * var(--cn-board-section-gap, 20px));\n}\n\n.cn-features-board__tabs {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 4px;\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-features-board__tab {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tbox-sizing: border-box;\n\tmin-height: 34px;\n\tmargin: 0 0 -1px;\n\tpadding: 8px 12px;\n\tborder: 1px solid var(--cn-board-hairline, var(--color-border));\n\tborder-bottom: none;\n\tborder-radius: 8px 8px 0 0;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n\tfont-weight: 400;\n\tcursor: pointer;\n\twhite-space: nowrap;\n}\n\n.cn-features-board__tab--active {\n\tposition: relative;\n\tbackground: var(--color-main-background);\n\tborder-bottom: 1px solid var(--color-main-background);\n\tfont-weight: 700;\n}\n\n.cn-features-board__tab-actions {\n\tflex: none;\n\talign-self: flex-start;\n\tmargin: 0 8px 8px 0;\n}\n\n.cn-features-board__panel {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 22px;\n}\n\n.cn-features-board__panel--tabbed {\n\tpadding: 22px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 0 var(--cn-board-card-radius, 12px) var(--cn-board-card-radius, 12px) var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__filter {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-features-board__filter-option {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin: 0;\n\tfont-size: 14px;\n\tfont-weight: 600;\n\tcursor: pointer;\n}\n\n.cn-features-board__filter--chips .cn-features-board__filter-option {\n\theight: 36px;\n\tpadding: 0 14px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: 18px;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-board__filter--chips .cn-features-board__filter-option--active {\n\tborder-color: var(--color-main-text);\n\tbackground: var(--color-main-text);\n\tcolor: var(--color-main-background);\n}\n\n.cn-features-board__filter-count {\n\tfont-weight: 400;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__filter--chips .cn-features-board__filter-option--active .cn-features-board__filter-count {\n\tcolor: var(--color-border-dark);\n}\n\n.cn-features-board__filter--segmented {\n\tdisplay: inline-flex;\n\talign-self: flex-start;\n\tgap: 2px;\n\tpadding: 3px;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n}\n\n.cn-features-board__filter--segmented .cn-features-board__filter-option {\n\theight: 34px;\n\tpadding: 0 14px;\n\tborder: 0;\n\tborder-radius: 6px;\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-board__filter--segmented .cn-features-board__filter-option--active {\n\tbackground: var(--color-main-background);\n\tbox-shadow: 0 1px 2px rgba(27, 28, 29, 0.12);\n}\n\n.cn-features-board__body {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tgap: 24px;\n}\n\n.cn-features-board__groups {\n\tflex: 999 1 520px;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 26px;\n}\n\n.cn-features-board__groups--rows {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(340px, 1fr));\n\tgap: 20px;\n}\n\n.cn-features-board__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n\tmax-width: none;\n\tmargin: 0;\n}\n\n.cn-features-board__groups--rows .cn-features-board__group {\n\tpadding: var(--cn-board-card-padding, 20px 22px);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__group-head {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: baseline;\n\tgap: 10px;\n}\n\n.cn-features-board__group-title {\n\tmargin: 0;\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n.cn-features-board__groups--rows .cn-features-board__group-title {\n\tfont-size: 17px;\n}\n\n.cn-features-board__group-caption {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__cards {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n\tgap: 16px;\n}\n\n.cn-features-board__card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 10px;\n\tpadding: 18px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__card-head {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: flex-start;\n\tgap: 4px;\n}\n\n.cn-features-board__card-title {\n\tmargin: 0;\n\tfont-size: 17px;\n\tfont-weight: 700;\n\tline-height: 1.3;\n}\n\n.cn-features-board__card-summary {\n\tmargin: 0;\n\tfont-size: 14px;\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-board__card-footer {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\talign-items: center;\n\tmin-height: 34px;\n\tmargin-top: auto;\n\tpadding-top: 10px;\n\tborder-top: 1px solid var(--color-background-dark);\n}\n\n.cn-features-board__read-more {\n\tdisplay: inline-flex;\n\talign-items: center;\n\theight: 34px;\n\tpadding: 0 12px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: 8px;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n\tfont-weight: 600;\n\ttext-decoration: none;\n}\n\n.cn-features-board__rows {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-features-board__row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 6px 16px;\n\tpadding: 10px 0;\n\tborder-top: 1px solid var(--color-background-dark);\n}\n\n.cn-features-board__row-text {\n\tflex: 1 1 240px;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-features-board__row-title {\n\tfont-size: 15px;\n\tfont-weight: 500;\n}\n\n.cn-features-board__row-summary {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__next {\n\tflex: 1 1 280px;\n\tmin-width: 0;\n\tmax-width: 340px;\n}\n\n.cn-features-board__next-card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n\tpadding: 18px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--cn-board-card-radius, 12px);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-features-board__next-head {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-features-board__next-title {\n\tmargin: 0;\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n.cn-features-board__next-lead {\n\tmargin: 0;\n\tfont-size: 14px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__next-list {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 10px;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-features-board__next-item {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 12px 14px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 10px;\n\tbackground: var(--color-main-background);\n}\n\n.cn-features-board__next-link {\n\tfont-size: 15px;\n\tfont-weight: 600;\n\tline-height: 1.3;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-features-board__next-meta {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__next-opened {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n}\n\n.cn-features-board__next-more {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-features-board__empty {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-features-board__notice {\n\tmargin: 0;\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode","_toDisplayString","_createBlock","_createVNode","_createTextVNode","_Fragment","_renderList","_openBlock"],"mappings":"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"}
|
|
@@ -38,6 +38,7 @@ import { isModifiedClick, openRowTarget } from '../../utils/linkNavigation.js';
|
|
|
38
38
|
import { resolveClaimedTeams, splitViewsIntoTabs, viewAsTab, resolveClaimTokens, viewIdOf } from '../../utils/listLenses.js';
|
|
39
39
|
import { LIST_SHORTCUTS, listPaletteCommands, shortcutFor } from '../../utils/listShortcuts.js';
|
|
40
40
|
import { multiKeySort } from '../../utils/multiKeySort.js';
|
|
41
|
+
import { fetchGroups } from '../../utils/nextcloudDirectory.js';
|
|
41
42
|
import { OBJECTS_CHANGED_EVENT } from '../../utils/objectSignals.js';
|
|
42
43
|
import { objectUrlOf, patchObject } from '../../utils/objectWrite.js';
|
|
43
44
|
import { withPersonalLenses } from '../../utils/personalLenses.js';
|
|
@@ -2567,6 +2568,8 @@ var script = {
|
|
|
2567
2568
|
*/
|
|
2568
2569
|
folderSidebarAllValues: [],
|
|
2569
2570
|
folderRegisterList: [],
|
|
2571
|
+
/** The Nextcloud groups a `folderSidebar.source: 'groups'` pane lists. */
|
|
2572
|
+
folderGroupList: [],
|
|
2570
2573
|
// `x-index` blocks read off the schema rows a `source: 'register'`
|
|
2571
2574
|
// folder list was built from, keyed by folder id. The layout a case
|
|
2572
2575
|
// type carries travels with the record rather than being restated
|
|
@@ -3484,7 +3487,7 @@ var script = {
|
|
|
3484
3487
|
*/
|
|
3485
3488
|
folderSidebarSource() {
|
|
3486
3489
|
const s = this.folderSidebar && this.folderSidebar.source;
|
|
3487
|
-
return (s === 'register' || !s) ? 'custom' : s
|
|
3490
|
+
return (s === 'register' || s === 'groups' || !s) ? 'custom' : s
|
|
3488
3491
|
},
|
|
3489
3492
|
|
|
3490
3493
|
/**
|
|
@@ -3501,6 +3504,9 @@ var script = {
|
|
|
3501
3504
|
if (this.folderSidebar.source === 'register') {
|
|
3502
3505
|
return this.folderRegisterList
|
|
3503
3506
|
}
|
|
3507
|
+
if (this.folderSidebar.source === 'groups') {
|
|
3508
|
+
return this.folderGroupList
|
|
3509
|
+
}
|
|
3504
3510
|
return this.folderSidebar.folders || []
|
|
3505
3511
|
},
|
|
3506
3512
|
|
|
@@ -4266,7 +4272,7 @@ var script = {
|
|
|
4266
4272
|
* @return {string} The id field key.
|
|
4267
4273
|
*/
|
|
4268
4274
|
folderPassthroughIdField() {
|
|
4269
|
-
if (this.folderSidebar && this.folderSidebar.source === 'register') {
|
|
4275
|
+
if (this.folderSidebar && (this.folderSidebar.source === 'register' || this.folderSidebar.source === 'groups')) {
|
|
4270
4276
|
return 'id'
|
|
4271
4277
|
}
|
|
4272
4278
|
return (this.folderSidebar && this.folderSidebar.idField) || 'id'
|
|
@@ -4276,7 +4282,7 @@ var script = {
|
|
|
4276
4282
|
* @return {string} The name field key for CnFolderSidebar's custom list.
|
|
4277
4283
|
*/
|
|
4278
4284
|
folderPassthroughNameField() {
|
|
4279
|
-
if (this.folderSidebar && this.folderSidebar.source === 'register') {
|
|
4285
|
+
if (this.folderSidebar && (this.folderSidebar.source === 'register' || this.folderSidebar.source === 'groups')) {
|
|
4280
4286
|
return 'name'
|
|
4281
4287
|
}
|
|
4282
4288
|
return (this.folderSidebar && this.folderSidebar.nameField) || 'name'
|
|
@@ -5931,6 +5937,7 @@ var script = {
|
|
|
5931
5937
|
this.maybeOpenCreateFromQuery();
|
|
5932
5938
|
if (this.folderSidebar) {
|
|
5933
5939
|
this.loadFolderRegister();
|
|
5940
|
+
this.loadFolderGroups();
|
|
5934
5941
|
// Reflect a deep-link filter (e.g. ?caseType=<id>) as the active folder.
|
|
5935
5942
|
const key = this.folderSidebar.filterField || this.folderSidebar.field;
|
|
5936
5943
|
const active = key && this.effectiveActiveFilters[key];
|
|
@@ -7803,6 +7810,39 @@ var script = {
|
|
|
7803
7810
|
}
|
|
7804
7811
|
},
|
|
7805
7812
|
|
|
7813
|
+
/**
|
|
7814
|
+
* Load the Nextcloud groups for `folderSidebar.source: 'groups'`, each
|
|
7815
|
+
* with its member count. `folderSidebar.groups` (`[{ id, parent? }]`)
|
|
7816
|
+
* picks and orders them and nests a group under its `parent`; without
|
|
7817
|
+
* it every group shows, by name. A refused request lists none.
|
|
7818
|
+
*
|
|
7819
|
+
* @return {Promise<void>}
|
|
7820
|
+
* @spec openspec/changes/index-groups-users-source/specs/index-page/spec.md#requirement-an-index-page-can-list-nextcloud-groups-and-their-members
|
|
7821
|
+
*/
|
|
7822
|
+
async loadFolderGroups() {
|
|
7823
|
+
const cfg = this.folderSidebar;
|
|
7824
|
+
if (!cfg || cfg.source !== 'groups') {
|
|
7825
|
+
return
|
|
7826
|
+
}
|
|
7827
|
+
try {
|
|
7828
|
+
const { default: axios } = await import('@nextcloud/axios');
|
|
7829
|
+
const groups = await fetchGroups(axios, { search: cfg.search || '', limit: cfg.limit || 200 });
|
|
7830
|
+
const declared = Array.isArray(cfg.groups) ? cfg.groups.filter((entry) => entry && entry.id) : null;
|
|
7831
|
+
if (!declared) {
|
|
7832
|
+
this.folderGroupList = [...groups].sort((a, b) => a.name.localeCompare(b.name));
|
|
7833
|
+
return
|
|
7834
|
+
}
|
|
7835
|
+
const byId = new Map(groups.map((group) => [group.id, group]));
|
|
7836
|
+
this.folderGroupList = declared
|
|
7837
|
+
.filter((entry) => byId.has(String(entry.id)))
|
|
7838
|
+
.map((entry) => ({ ...byId.get(String(entry.id)), parentId: entry.parent ? String(entry.parent) : null }));
|
|
7839
|
+
} catch (e) {
|
|
7840
|
+
// eslint-disable-next-line no-console
|
|
7841
|
+
console.error('[CnIndexPage] failed to load the Nextcloud groups', e);
|
|
7842
|
+
this.folderGroupList = [];
|
|
7843
|
+
}
|
|
7844
|
+
},
|
|
7845
|
+
|
|
7806
7846
|
/**
|
|
7807
7847
|
* Load the folder list from an OpenRegister register/schema for a
|
|
7808
7848
|
* `folderSidebar.source === 'register'` config. Maps each object to
|