@conduction/nextcloud-vue 2.59.0 → 2.61.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 +14 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +80 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +46 -4
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +232 -10
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +79 -25
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue2.js +42 -3
- package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue3.js +39 -6
- package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +46 -0
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +11 -4
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue.js +10 -0
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue.js.map +1 -0
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue2.js +39 -0
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue2.js.map +1 -0
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue3.js +16 -0
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue3.js.map +1 -0
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +117 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +147 -89
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js +10 -0
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js.map +1 -0
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js +259 -0
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -0
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js +131 -0
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js +10 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js.map +1 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +116 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js +24 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +10 -2
- 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/CnDetailPage/CnDetailPage.vue2.js +503 -28
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +303 -144
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue.js +10 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue.js.map +1 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue2.js +237 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue2.js.map +1 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue3.js +120 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue3.js.map +1 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue.js +10 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue.js.map +1 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +77 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js +27 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +171 -12
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +15 -7
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue2.js +37 -3
- package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue2.js.map +1 -1
- package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue3.js +44 -16
- package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +132 -3
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +6 -3
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue.js +10 -0
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue.js.map +1 -0
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js +316 -0
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js.map +1 -0
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js +105 -0
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js.map +1 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue.js +10 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue.js.map +1 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue2.js +150 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue2.js.map +1 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue3.js +106 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue3.js.map +1 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue.js +9 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue.js.map +1 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +47 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue3.js +17 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +49 -0
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue2.js +6 -0
- package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue2.js +46 -0
- package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue3.js +15 -3
- package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue3.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/pageTypes.js +1 -0
- package/dist/esm/components/CnPageRenderer/pageTypes.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +37 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js +29 -13
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js +50 -3
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue.js +10 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue.js.map +1 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js +186 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js +46 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -0
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue.js +11 -0
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue.js.map +1 -0
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js +298 -0
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js +77 -0
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue.js +10 -0
- package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue.js.map +1 -0
- package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue2.js +169 -0
- package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue2.js.map +1 -0
- package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue3.js +48 -0
- package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue3.js.map +1 -0
- package/dist/esm/components/CnTabs/CnTab.vue2.js +35 -0
- package/dist/esm/components/CnTabs/CnTab.vue2.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTab.vue3.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue2.js +120 -7
- package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue3.js +57 -5
- package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +59 -4
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +6 -2
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js +10 -0
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js.map +1 -0
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js +484 -0
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js +115 -0
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue.js +10 -0
- package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue.js.map +1 -0
- package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue2.js +189 -0
- package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue2.js.map +1 -0
- package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue3.js +74 -0
- package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue3.js.map +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +82 -8
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/index.js +16 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +55 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +55 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +1036 -19
- package/dist/esm/utils/dateVariant.js +124 -0
- package/dist/esm/utils/dateVariant.js.map +1 -0
- package/dist/esm/utils/detailActionModel.js +260 -0
- package/dist/esm/utils/detailActionModel.js.map +1 -0
- package/dist/esm/utils/dueRule.js +84 -0
- package/dist/esm/utils/dueRule.js.map +1 -0
- package/dist/esm/utils/fetchAggregate.js +37 -1
- package/dist/esm/utils/fetchAggregate.js.map +1 -1
- package/dist/esm/utils/fetchFilterCounts.js +186 -0
- package/dist/esm/utils/fetchFilterCounts.js.map +1 -0
- package/dist/esm/utils/headers.js +28 -2
- package/dist/esm/utils/headers.js.map +1 -1
- package/dist/esm/utils/libraryWidgetKeys.js +5 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/readPath.js +30 -0
- package/dist/esm/utils/readPath.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +16019 -9528
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1036 -19
- package/l10n/en.json +55 -1
- package/l10n/nl.json +55 -1
- package/package.json +1 -1
- package/scripts/.jsdoc-baselines.json +4 -0
- package/src/components/CnActionsMenu/CnActionsMenu.vue +14 -1
- package/src/components/CnAppNav/CnAppNav.md +26 -0
- package/src/components/CnAppNav/CnAppNav.vue +145 -2
- package/src/components/CnBannerWidget/CnBannerWidget.vue +373 -11
- package/src/components/CnBannerWidgetForm/CnBannerWidgetForm.vue +65 -3
- package/src/components/CnBoardView/CnBoardView.vue +77 -0
- package/src/components/CnBrandStripe/CnBrandStripe.md +22 -0
- package/src/components/CnBrandStripe/CnBrandStripe.vue +100 -0
- package/src/components/CnBrandStripe/index.js +4 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +207 -1
- package/src/components/CnConversationThread/CnConversationThread.md +51 -0
- package/src/components/CnConversationThread/CnConversationThread.vue +457 -0
- package/src/components/CnConversationThread/CnConversationWidget.vue +144 -0
- package/src/components/CnConversationThread/index.js +4 -0
- package/src/components/CnDashboardPage/CnDashboardPage.vue +10 -2
- package/src/components/CnDetailPage/CnDetailPage.md +16 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +687 -49
- package/src/components/CnDocumentReviewList/CnDocumentReviewList.md +55 -0
- package/src/components/CnDocumentReviewList/CnDocumentReviewList.vue +384 -0
- package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +108 -0
- package/src/components/CnDocumentReviewList/index.js +4 -0
- package/src/components/CnHeaderWidget/CnHeaderWidget.vue +191 -14
- package/src/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue +53 -3
- package/src/components/CnIndexPage/CnIndexPage.vue +135 -3
- package/src/components/CnLinkCardsPage/CnLinkCardsPage.vue +484 -0
- package/src/components/CnLinkCardsPage/index.js +4 -0
- package/src/components/CnNextStepCard/CnNextStepCard.md +40 -0
- package/src/components/CnNextStepCard/CnNextStepCard.vue +319 -0
- package/src/components/CnNextStepCard/CnNextStepWidget.vue +59 -0
- package/src/components/CnNextStepCard/index.js +4 -0
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +49 -0
- package/src/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue +6 -0
- package/src/components/CnObjectKanban/CnObjectKanban.vue +86 -2
- package/src/components/CnPageRenderer/pageTypes.js +1 -0
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.md +3 -0
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +60 -1
- package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +50 -3
- package/src/components/CnSegmentedControl/CnSegmentedControl.md +46 -0
- package/src/components/CnSegmentedControl/CnSegmentedControl.vue +291 -0
- package/src/components/CnSegmentedControl/index.js +4 -0
- package/src/components/CnStackedBarWidget/CnStackedBarWidget.md +15 -0
- package/src/components/CnStackedBarWidget/CnStackedBarWidget.vue +414 -0
- package/src/components/CnStackedBarWidget/index.js +14 -0
- package/src/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue +224 -0
- package/src/components/CnStackedBarWidgetForm/index.js +4 -0
- package/src/components/CnTabs/CnTab.vue +35 -0
- package/src/components/CnTabs/CnTabs.vue +244 -10
- package/src/components/CnTabsWidget/CnTabsWidget.vue +63 -4
- package/src/components/CnWeekStripWidget/CnWeekStripWidget.md +31 -0
- package/src/components/CnWeekStripWidget/CnWeekStripWidget.vue +668 -0
- package/src/components/CnWeekStripWidget/index.js +14 -0
- package/src/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue +265 -0
- package/src/components/CnWeekStripWidgetForm/index.js +4 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +74 -0
- package/src/components/index.js +12 -0
- package/src/css/detail-page.css +57 -2
- package/src/index.js +16 -0
- package/src/schemas/app-manifest-v2.schema.json +433 -3
- package/src/schemas/app-manifest.schema.json +2 -1
- package/src/types/manifest.d.ts +1 -1
- package/src/utils/dateVariant.js +120 -0
- package/src/utils/detailActionModel.js +256 -0
- package/src/utils/dueRule.js +94 -0
- package/src/utils/fetchAggregate.js +36 -0
- package/src/utils/fetchFilterCounts.js +182 -0
- package/src/utils/headers.js +28 -2
- package/src/utils/index.js +1 -0
- package/src/utils/libraryWidgetKeys.js +5 -0
- package/src/utils/readPath.js +27 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnBoardView.vue3.js","sources":["../../../../src/components/CnBoardView/CnBoardView.vue?vue&type=template&id=997a7a5e&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-board-view\" data-testid=\"cn-board-view\">\n\t\t<p v-if=\"!usable\" class=\"cn-board-view__unusable\" data-testid=\"cn-board-unusable\">\n\t\t\t{{ unusableText }}\n\t\t</p>\n\n\t\t<div\n\t\t\tv-for=\"lane in lanes\"\n\t\t\tv-else\n\t\t\t:key=\"`lane-${lane.key}`\"\n\t\t\tclass=\"cn-board-view__lane\"\n\t\t\tdata-testid=\"cn-board-lane\"\n\t\t\t:data-lane=\"lane.key\">\n\t\t\t<button\n\t\t\t\tv-if=\"lane.key !== ''\"\n\t\t\t\tclass=\"cn-board-view__lane-header\"\n\t\t\t\tdata-testid=\"cn-board-lane-header\"\n\t\t\t\t:aria-expanded=\"String(!isCollapsed(lane.key))\"\n\t\t\t\t@click=\"toggleLane(lane.key)\">\n\t\t\t\t{{ lane.label }} ({{ lane.count }})\n\t\t\t</button>\n\n\t\t\t<div\n\t\t\t\tv-show=\"!isCollapsed(lane.key)\"\n\t\t\t\tclass=\"cn-board-view__columns\"\n\t\t\t\trole=\"list\"\n\t\t\t\t:aria-label=\"lane.key === '' ? boardLabel : lane.label\">\n\t\t\t\t<section\n\t\t\t\t\tv-for=\"column in lane.columns\"\n\t\t\t\t\t:key=\"`col-${lane.key}-${column.key}`\"\n\t\t\t\t\tclass=\"cn-board-view__column\"\n\t\t\t\t\trole=\"listitem\"\n\t\t\t\t\tdata-testid=\"cn-board-column\"\n\t\t\t\t\t:data-column=\"column.key\"\n\t\t\t\t\t@dragover.prevent\n\t\t\t\t\t@drop=\"onDrop(column, $event)\">\n\t\t\t\t\t<h3 class=\"cn-board-view__column-header\">\n\t\t\t\t\t\t{{ column.label }}\n\t\t\t\t\t\t<span class=\"cn-board-view__count\" data-testid=\"cn-board-count\">\n\t\t\t\t\t\t\t{{ countLabel(column) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</h3>\n\n\t\t\t\t\t<!--\n\t\t\t\t\t\tA list of its own, so a reader is told how many cards\n\t\t\t\t\t\tthis column holds. The column is a listitem of the\n\t\t\t\t\t\tboard's list of columns; the cards are a list inside\n\t\t\t\t\t\tit, not more items of that same list.\n\t\t\t\t\t-->\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-if=\"column.cards.length > 0\"\n\t\t\t\t\t\tclass=\"cn-board-view__cards\"\n\t\t\t\t\t\trole=\"list\"\n\t\t\t\t\t\t:aria-label=\"column.label\">\n\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\tThe card is a CONTAINER, and every action on it is\n\t\t\t\t\t\t\tits own control. It carries the drag and nothing\n\t\t\t\t\t\t\telse: no tabindex, no click, no key handler, and\n\t\t\t\t\t\t\tnever role=\"button\".\n\n\t\t\t\t\t\t\t🔴 role=\"button\" HERE WOULD BREAK THE MOVE. An\n\t\t\t\t\t\t\telement with that role has presentational children,\n\t\t\t\t\t\t\tso the <select> below would be an interactive\n\t\t\t\t\t\t\tcontrol inside a button. That is invalid, and it\n\t\t\t\t\t\t\ttakes away the only way a keyboard user can move a\n\t\t\t\t\t\t\tcard. It would also collapse every field on the\n\t\t\t\t\t\t\tcard into one button label. See\n\t\t\t\t\t\t\topenspec/changes/board-card-role-and-keyboard.\n\n\t\t\t\t\t\t\tMeasured rather than argued: putting role=\"button\"\n\t\t\t\t\t\t\there makes axe report nested-interactive (serious,\n\t\t\t\t\t\t\t\"Interactive controls must not be nested\") and\n\t\t\t\t\t\t\taria-required-children (critical), and four tests\n\t\t\t\t\t\t\tin tests/a11y/CnBoardView.a11y.spec.js go red.\n\t\t\t\t\t\t\tGate 32 suggests exactly this change. The gate is a\n\t\t\t\t\t\t\tmechanical floor, not the standard.\n\t\t\t\t\t\t-->\n\t\t\t\t\t\t<article\n\t\t\t\t\t\t\tv-for=\"card in column.cards\"\n\t\t\t\t\t\t\t:key=\"`card-${cardKey(card)}`\"\n\t\t\t\t\t\t\tclass=\"cn-board-view__card\"\n\t\t\t\t\t\t\trole=\"listitem\"\n\t\t\t\t\t\t\tdata-testid=\"cn-board-card\"\n\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t:draggable=\"canMove\"\n\t\t\t\t\t\t\t@dragstart=\"onDragStart(card, column, $event)\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"field in cardFields\"\n\t\t\t\t\t\t\t\t:key=\"`f-${cardKey(card)}-${field}`\"\n\t\t\t\t\t\t\t\tclass=\"cn-board-view__card-field\">\n\t\t\t\t\t\t\t\t{{ card[field] }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\t\tOpening the card. A native button, so Enter and\n\t\t\t\t\t\t\t\tSpace both work: the old markup handled Enter\n\t\t\t\t\t\t\t\tonly, and Space is what most people try on\n\t\t\t\t\t\t\t\tsomething that looks like a button.\n\t\t\t\t\t\t\t-->\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"cn-board-view__card-open\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-board-card-open\"\n\t\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t\t:aria-label=\"cardLabel(card, column)\"\n\t\t\t\t\t\t\t\t@click=\"openCard(card, $event)\"\n\t\t\t\t\t\t\t\t@mousedown=\"preventMiddleClickAutoscroll\"\n\t\t\t\t\t\t\t\t@auxclick=\"onCardAuxClick(card, $event)\">\n\t\t\t\t\t\t\t\t{{ openLabel }}\n\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\t\tThe keyboard's way to do what a drag does. A board\n\t\t\t\t\t\t\t\twhose only move is a drag is a board a keyboard\n\t\t\t\t\t\t\t\tuser cannot use at all, and \"drag the card\" is not\n\t\t\t\t\t\t\t\tan instruction you can follow with a keyboard.\n\t\t\t\t\t\t\t-->\n\t\t\t\t\t\t\t<label v-if=\"canMove\" class=\"cn-board-view__move\">\n\t\t\t\t\t\t\t\t<span class=\"cn-board-view__move-label\">{{ moveLabel }}</span>\n\t\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-board-move\"\n\t\t\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t\t\t:value=\"column.key\"\n\t\t\t\t\t\t\t\t\t@change=\"onMoveTo(card, column, $event)\">\n\t\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\t\tv-for=\"target in lane.columns\"\n\t\t\t\t\t\t\t\t\t\t:key=\"`t-${cardKey(card)}-${target.key}`\"\n\t\t\t\t\t\t\t\t\t\t:value=\"target.key\">\n\t\t\t\t\t\t\t\t\t\t{{ target.label }}\n\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</article>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<p\n\t\t\t\t\t\tv-else\n\t\t\t\t\t\tclass=\"cn-board-view__empty\"\n\t\t\t\t\t\tdata-testid=\"cn-board-column-empty\">\n\t\t\t\t\t\t{{ emptyColumnText }}\n\t\t\t\t\t</p>\n\t\t\t\t</section>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<p\n\t\t\tv-if=\"refusal\"\n\t\t\tclass=\"cn-board-view__refusal\"\n\t\t\tdata-testid=\"cn-board-refusal\"\n\t\t\trole=\"alert\">\n\t\t\t{{ refusal }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { buildBoardColumns } from '../../utils/boardColumns.js'\nimport { buildSwimlanes } from '../../utils/boardSwimlanes.js'\nimport { DROP_OUTCOMES, runBoardDrop } from '../../utils/boardTransition.js'\nimport { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'\n\n/**\n * CnBoardView — the index page's rows as a board, one column per stage.\n *\n * THE COLUMNS ARE THE SCHEMA'S, NOT THE DATA'S. `buildBoardColumns` decides\n * them and is tested on its own; this renders what it returns. A board that\n * built its columns from the values present would lose the stage nothing is\n * in, which is the one you drag a card into.\n *\n * 🔴 A MOVE GOES THROUGH THE HOST'S TRANSITION AND NEVER WRITES THE STATUS\n * FIELD. Writing it here would skip every guard, side effect and audit entry\n * the transition carries, and the board would become a way to move a case past\n * a rule the case page enforces. `runBoardDrop` owns that contract; this\n * component owns the gestures that reach it.\n *\n * 🔴 THERE ARE TWO GESTURES, ALWAYS. A board whose only move is a drag is a\n * board a keyboard user cannot use, and \"drag the card\" is not an instruction\n * anybody can follow with a keyboard. Move to on the card runs the identical\n * call.\n *\n * @event {object} card-click — A card was opened. Payload: the row.\n * @event {object} moved — A card moved. Payload: `{ card, toKey }`.\n * @event {object} refused — A move was refused. Payload: `{ card, message }`.\n * @event {object} stale — The card had already moved. Payload: `{ card }`.\n */\nexport default {\n\tname: 'CnBoardView',\n\n\tprops: {\n\t\t/** The rows the list holds. */\n\t\trows: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The status field's schema: its enum or its lifecycle states. */\n\t\tstatusFieldSchema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The property a row's status lives on. */\n\t\tstatusField: {\n\t\t\ttype: String,\n\t\t\tdefault: 'status',\n\t\t},\n\n\t\t/** The fields a card shows. */\n\t\tcardFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** A second field to group the board into rows by. */\n\t\tswimlaneField: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The row key, so a card is identifiable. */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/**\n\t\t * The host's transition. Absent, the board is read-only: no drag, no\n\t\t * Move to, because a gesture that cannot do anything is worse than no\n\t\t * gesture at all.\n\t\t */\n\t\trunTransition: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Re-read one card, to check it has not moved under the dragger. */\n\t\treread: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Whether the counts are of one page. A count of a page shown as\n\t\t * though it were the total is a number somebody quotes in a meeting.\n\t\t */\n\t\tpaged: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: ['card-click', 'card-aux-click', 'moved', 'refused', 'stale'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Lanes the reader has collapsed, for as long as they are here. */\n\t\t\tcollapsed: [],\n\t\t\t/** The guard's own words, when it refused the last move. */\n\t\t\trefusal: '',\n\t\t\t/** The card being dragged, and where from. */\n\t\t\tdragging: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {boolean} Whether the status field can back a board at all. */\n\t\tusable() {\n\t\t\treturn this.board.usable\n\t\t},\n\n\t\t/** @return {object} The columns, from the schema. */\n\t\tboard() {\n\t\t\treturn buildBoardColumns({\n\t\t\t\tfield: this.statusFieldSchema,\n\t\t\t\trows: this.rows,\n\t\t\t\tstatusField: this.statusField,\n\t\t\t\toffBoardLabel: t('nextcloud-vue', 'Elsewhere'),\n\t\t\t})\n\t\t},\n\n\t\t/** @return {Array<object>} The board as rows of columns. */\n\t\tlanes() {\n\t\t\treturn buildSwimlanes({\n\t\t\t\tcolumns: this.board.columns,\n\t\t\t\tswimlaneField: this.swimlaneField,\n\t\t\t\tunassignedLabel: t('nextcloud-vue', 'Unassigned'),\n\t\t\t})\n\t\t},\n\n\t\t/** @return {boolean} Whether a move is possible at all. */\n\t\tcanMove() {\n\t\t\treturn typeof this.runTransition === 'function'\n\t\t},\n\n\t\t/** @return {string} What to say when the field cannot back a board. */\n\t\tunusableText() {\n\t\t\treturn t('nextcloud-vue', 'This list has no status field with stages, so it cannot be shown as a board.')\n\t\t},\n\n\t\t/** @return {string} The board's accessible name. */\n\t\tboardLabel() {\n\t\t\treturn t('nextcloud-vue', 'Board')\n\t\t},\n\n\t\t/** @return {string} The Move to control's label. */\n\t\tmoveLabel() {\n\t\t\treturn t('nextcloud-vue', 'Move to')\n\t\t},\n\n\t\t/**\n\t\t * The visible text on a card's opening control.\n\t\t *\n\t\t * Short, because it repeats on every card. Which card it opens is\n\t\t * said by the control's `aria-label`, so a reader hearing a list of\n\t\t * buttons hears the card's own name rather than \"Open\" nine times.\n\t\t *\n\t\t * @return {string} The label.\n\t\t */\n\t\topenLabel() {\n\t\t\treturn t('nextcloud-vue', 'Open')\n\t\t},\n\n\t\t/** @return {string} What an empty column says. */\n\t\temptyColumnText() {\n\t\t\treturn t('nextcloud-vue', 'Nothing here')\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\tpreventMiddleClickAutoscroll,\n\n\t\t/**\n\t\t * Open one card. The pointer and the keyboard both come through here,\n\t\t * so a reader on Enter reaches the same record as a reader on click.\n\t\t *\n\t\t * @param {object} card The card the reader picked.\n\t\t * @param {MouseEvent|KeyboardEvent} [event] The originating event.\n\t\t * @return {void} Nothing.\n\t\t */\n\t\topenCard(card, event) {\n\t\t\t/**\n\t\t\t * @event card-click Emitted when a reader opens a card, by click or Enter. Payload: `(card, event)` — the card row and the native event, so the host can open it in a new tab on a ctrl/cmd/shift click. A middle click emits `card-aux-click` instead.\n\t\t\t */\n\t\t\tthis.$emit('card-click', card, event)\n\t\t},\n\n\t\t/**\n\t\t * Middle click on a card's open button: emits `card-aux-click`, not\n\t\t * `card-click`, so an existing listener that navigates never moves the\n\t\t * current tab away.\n\t\t *\n\t\t * @param {object} card The card the reader picked.\n\t\t * @param {MouseEvent} event The auxclick event.\n\t\t * @return {void} Nothing.\n\t\t */\n\t\tonCardAuxClick(card, event) {\n\t\t\tif (isRowMiddleClick(event)) {\n\t\t\t\t/**\n\t\t\t\t * @event card-aux-click Emitted when a reader middle-clicks a card, for opening it in a new tab (see `openRowTarget`). Payload: `(card, event)` — the card row and the native auxclick event.\n\t\t\t\t */\n\t\t\t\tthis.$emit('card-aux-click', card, event)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A card's identity.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @return {string} Its key.\n\t\t */\n\t\tcardKey(card) {\n\t\t\treturn String(card?.[this.rowKey] ?? '')\n\t\t},\n\n\t\t/**\n\t\t * A column's count, said as a page count when that is what it is.\n\t\t *\n\t\t * @param {object} column The column.\n\t\t * @return {string} The count.\n\t\t */\n\t\tcountLabel(column) {\n\t\t\treturn this.paged\n\t\t\t\t? t('nextcloud-vue', '{count} on this page', { count: column.count })\n\t\t\t\t: String(column.count)\n\t\t},\n\n\t\t/**\n\t\t * What a card is called to somebody who cannot see which column it is in.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcardLabel(card, column) {\n\t\t\tconst name = this.cardFields.map((field) => card?.[field]).filter(Boolean).join(', ')\n\t\t\treturn t('nextcloud-vue', '{name}, in {column}', { name: name || this.cardKey(card), column: column.label })\n\t\t},\n\n\t\t/**\n\t\t * Whether a lane is collapsed.\n\t\t *\n\t\t * @param {string} key The lane.\n\t\t * @return {boolean} True when collapsed.\n\t\t */\n\t\tisCollapsed(key) {\n\t\t\treturn this.collapsed.includes(key)\n\t\t},\n\n\t\t/**\n\t\t * Collapse a lane, or open it again.\n\t\t *\n\t\t * Held in the component for as long as the reader is on the view, and\n\t\t * deliberately not persisted: a collapsed lane somebody forgot they\n\t\t * collapsed is work that has gone missing for them.\n\t\t *\n\t\t * @param {string} key The lane.\n\t\t */\n\t\ttoggleLane(key) {\n\t\t\tthis.collapsed = this.isCollapsed(key)\n\t\t\t\t? this.collapsed.filter((entry) => entry !== key)\n\t\t\t\t: [...this.collapsed, key]\n\t\t},\n\n\t\t/**\n\t\t * Remember which card is being dragged and where from.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @param {DragEvent} event The event.\n\t\t */\n\t\tonDragStart(card, column, event) {\n\t\t\tthis.dragging = { card, fromKey: column.key }\n\t\t\t// Firefox drops a drag that carries no data.\n\t\t\tevent?.dataTransfer?.setData?.('text/plain', this.cardKey(card))\n\t\t},\n\n\t\t/**\n\t\t * A drop on a column.\n\t\t *\n\t\t * @param {object} column The column dropped on.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync onDrop(column) {\n\t\t\tif (!this.dragging) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst { card, fromKey } = this.dragging\n\t\t\tthis.dragging = null\n\t\t\tawait this.move(card, fromKey, column.key)\n\t\t},\n\n\t\t/**\n\t\t * Move to, from the card's own menu.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @param {Event} event The change event.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync onMoveTo(card, column, event) {\n\t\t\tawait this.move(card, column.key, event?.target?.value ?? '')\n\t\t},\n\n\t\t/**\n\t\t * The one move, whichever gesture asked for it.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {string} fromKey Where it was.\n\t\t * @param {string} toKey Where it is going.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync move(card, fromKey, toKey) {\n\t\t\tthis.refusal = ''\n\n\t\t\tconst result = await runBoardDrop({\n\t\t\t\tcard,\n\t\t\t\tfromKey,\n\t\t\t\ttoKey,\n\t\t\t\tstatusField: this.statusField,\n\t\t\t\trunTransition: this.runTransition,\n\t\t\t\treread: this.reread,\n\t\t\t})\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.MOVED) {\n\t\t\t\t/**\n\t\t\t\t * @event moved Emitted when the host's transition accepted the move and the card now sits in the new lane. Payload: `{ card, toKey }`.\n\t\t\t\t */\n\t\t\t\tthis.$emit('moved', { card: result.card, toKey })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.REFUSED) {\n\t\t\t\t// The guard's own sentence when it gave one. Our own only when\n\t\t\t\t// it did not, so the two are never confused.\n\t\t\t\tthis.refusal = result.message || t('nextcloud-vue', 'That move was refused.')\n\t\t\t\t/**\n\t\t\t\t * @event refused Emitted when the guard turned the move down. Payload: `{ card, message }`, the message being the guard's own sentence when it gave one.\n\t\t\t\t */\n\t\t\t\tthis.$emit('refused', { card: result.card, message: this.refusal })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.STALE) {\n\t\t\t\tthis.refusal = t('nextcloud-vue', 'Somebody else moved this card. It is shown where it is now.')\n\t\t\t\t/**\n\t\t\t\t * @event stale Emitted when somebody else moved the card first, so the board shows it where it is now. Payload: `{ card }`.\n\t\t\t\t */\n\t\t\t\tthis.$emit('stale', { card: result.card })\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped lang=\"scss\">\n.cn-board-view__columns {\n\tdisplay: flex;\n\tgap: 12px;\n\toverflow-x: auto;\n\talign-items: flex-start;\n}\n\n.cn-board-view__column {\n\tflex: 0 0 260px;\n\tbackground-color: var(--color-background-hover);\n\tborder-radius: var(--border-radius-large);\n\tpadding: 8px;\n}\n\n.cn-board-view__column-header {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 1rem;\n\tmargin: 0 0 8px;\n}\n\n.cn-board-view__count,\n.cn-board-view__empty,\n.cn-board-view__unusable {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-board-view__card {\n\tdisplay: block;\n\tbackground-color: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 8px;\n\tmargin-bottom: 8px;\n}\n\n.cn-board-view__card-field {\n\tdisplay: block;\n}\n\n/*\n * The card's opening control. The cursor and the focus ring live here now,\n * on the thing that actually does something, rather than on the box.\n */\n.cn-board-view__card-open {\n\tbackground: none;\n\tborder: none;\n\tpadding: 0;\n\tcursor: pointer;\n\tcolor: var(--color-primary-element);\n\tfont-size: 0.8rem;\n}\n\n.cn-board-view__card-open:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-board-view__lane-header {\n\tbackground: none;\n\tborder: none;\n\tfont-weight: bold;\n\tpadding: 8px 0;\n\tcursor: pointer;\n\tcolor: var(--color-main-text);\n}\n\n.cn-board-view__move-label {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.8rem;\n}\n\n.cn-board-view__refusal {\n\tcolor: var(--color-error);\n}\n</style>\n"],"names":["_createElementBlock","_toDisplayString","_Fragment","_renderList","_openBlock","_createElementVNode"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnBoardView.vue3.js","sources":["../../../../src/components/CnBoardView/CnBoardView.vue?vue&type=template&id=997a7a5e&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-board-view\" data-testid=\"cn-board-view\">\n\t\t<p v-if=\"!usable\" class=\"cn-board-view__unusable\" data-testid=\"cn-board-unusable\">\n\t\t\t{{ unusableText }}\n\t\t</p>\n\n\t\t<div\n\t\t\tv-for=\"lane in lanes\"\n\t\t\tv-else\n\t\t\t:key=\"`lane-${lane.key}`\"\n\t\t\tclass=\"cn-board-view__lane\"\n\t\t\tdata-testid=\"cn-board-lane\"\n\t\t\t:data-lane=\"lane.key\">\n\t\t\t<button\n\t\t\t\tv-if=\"lane.key !== ''\"\n\t\t\t\tclass=\"cn-board-view__lane-header\"\n\t\t\t\tdata-testid=\"cn-board-lane-header\"\n\t\t\t\t:aria-expanded=\"String(!isCollapsed(lane.key))\"\n\t\t\t\t@click=\"toggleLane(lane.key)\">\n\t\t\t\t{{ lane.label }} ({{ lane.count }})\n\t\t\t</button>\n\n\t\t\t<div\n\t\t\t\tv-show=\"!isCollapsed(lane.key)\"\n\t\t\t\tclass=\"cn-board-view__columns\"\n\t\t\t\trole=\"list\"\n\t\t\t\t:aria-label=\"lane.key === '' ? boardLabel : lane.label\">\n\t\t\t\t<section\n\t\t\t\t\tv-for=\"column in lane.columns\"\n\t\t\t\t\t:key=\"`col-${lane.key}-${column.key}`\"\n\t\t\t\t\tclass=\"cn-board-view__column\"\n\t\t\t\t\trole=\"listitem\"\n\t\t\t\t\tdata-testid=\"cn-board-column\"\n\t\t\t\t\t:data-column=\"column.key\"\n\t\t\t\t\t@dragover.prevent\n\t\t\t\t\t@drop=\"onDrop(column, $event)\">\n\t\t\t\t\t<h3 class=\"cn-board-view__column-header\">\n\t\t\t\t\t\t{{ column.label }}\n\t\t\t\t\t\t<span class=\"cn-board-view__count\" data-testid=\"cn-board-count\">\n\t\t\t\t\t\t\t{{ countLabel(column) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</h3>\n\n\t\t\t\t\t<!--\n\t\t\t\t\t\tA list of its own, so a reader is told how many cards\n\t\t\t\t\t\tthis column holds. The column is a listitem of the\n\t\t\t\t\t\tboard's list of columns; the cards are a list inside\n\t\t\t\t\t\tit, not more items of that same list.\n\t\t\t\t\t-->\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-if=\"column.cards.length > 0\"\n\t\t\t\t\t\tclass=\"cn-board-view__cards\"\n\t\t\t\t\t\trole=\"list\"\n\t\t\t\t\t\t:aria-label=\"column.label\">\n\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\tThe card is a CONTAINER, and every action on it is\n\t\t\t\t\t\t\tits own control. It carries the drag and nothing\n\t\t\t\t\t\t\telse: no tabindex, no click, no key handler, and\n\t\t\t\t\t\t\tnever role=\"button\".\n\n\t\t\t\t\t\t\t🔴 role=\"button\" HERE WOULD BREAK THE MOVE. An\n\t\t\t\t\t\t\telement with that role has presentational children,\n\t\t\t\t\t\t\tso the <select> below would be an interactive\n\t\t\t\t\t\t\tcontrol inside a button. That is invalid, and it\n\t\t\t\t\t\t\ttakes away the only way a keyboard user can move a\n\t\t\t\t\t\t\tcard. It would also collapse every field on the\n\t\t\t\t\t\t\tcard into one button label. See\n\t\t\t\t\t\t\topenspec/changes/board-card-role-and-keyboard.\n\n\t\t\t\t\t\t\tMeasured rather than argued: putting role=\"button\"\n\t\t\t\t\t\t\there makes axe report nested-interactive (serious,\n\t\t\t\t\t\t\t\"Interactive controls must not be nested\") and\n\t\t\t\t\t\t\taria-required-children (critical), and four tests\n\t\t\t\t\t\t\tin tests/a11y/CnBoardView.a11y.spec.js go red.\n\t\t\t\t\t\t\tGate 32 suggests exactly this change. The gate is a\n\t\t\t\t\t\t\tmechanical floor, not the standard.\n\t\t\t\t\t\t-->\n\t\t\t\t\t\t<article\n\t\t\t\t\t\t\tv-for=\"card in column.cards\"\n\t\t\t\t\t\t\t:key=\"`card-${cardKey(card)}`\"\n\t\t\t\t\t\t\tclass=\"cn-board-view__card\"\n\t\t\t\t\t\t\t:class=\"dueState(card) ? `cn-board-view__card--${dueState(card)}` : null\"\n\t\t\t\t\t\t\trole=\"listitem\"\n\t\t\t\t\t\t\tdata-testid=\"cn-board-card\"\n\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t:draggable=\"canMove\"\n\t\t\t\t\t\t\t@dragstart=\"onDragStart(card, column, $event)\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"dueLabel(card)\"\n\t\t\t\t\t\t\t\tclass=\"cn-board-view__due\"\n\t\t\t\t\t\t\t\t:class=\"`cn-board-view__due--${dueState(card)}`\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-board-due\">\n\t\t\t\t\t\t\t\t{{ dueLabel(card) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"field in cardFields\"\n\t\t\t\t\t\t\t\t:key=\"`f-${cardKey(card)}-${field}`\"\n\t\t\t\t\t\t\t\tclass=\"cn-board-view__card-field\">\n\t\t\t\t\t\t\t\t{{ card[field] }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\t\tOpening the card. A native button, so Enter and\n\t\t\t\t\t\t\t\tSpace both work: the old markup handled Enter\n\t\t\t\t\t\t\t\tonly, and Space is what most people try on\n\t\t\t\t\t\t\t\tsomething that looks like a button.\n\t\t\t\t\t\t\t-->\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"cn-board-view__card-open\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-board-card-open\"\n\t\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t\t:aria-label=\"cardLabel(card, column)\"\n\t\t\t\t\t\t\t\t@click=\"openCard(card, $event)\"\n\t\t\t\t\t\t\t\t@mousedown=\"preventMiddleClickAutoscroll\"\n\t\t\t\t\t\t\t\t@auxclick=\"onCardAuxClick(card, $event)\">\n\t\t\t\t\t\t\t\t{{ openLabel }}\n\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\t\tThe keyboard's way to do what a drag does. A board\n\t\t\t\t\t\t\t\twhose only move is a drag is a board a keyboard\n\t\t\t\t\t\t\t\tuser cannot use at all, and \"drag the card\" is not\n\t\t\t\t\t\t\t\tan instruction you can follow with a keyboard.\n\t\t\t\t\t\t\t-->\n\t\t\t\t\t\t\t<label v-if=\"canMove\" class=\"cn-board-view__move\">\n\t\t\t\t\t\t\t\t<span class=\"cn-board-view__move-label\">{{ moveLabel }}</span>\n\t\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-board-move\"\n\t\t\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t\t\t:value=\"column.key\"\n\t\t\t\t\t\t\t\t\t@change=\"onMoveTo(card, column, $event)\">\n\t\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\t\tv-for=\"target in lane.columns\"\n\t\t\t\t\t\t\t\t\t\t:key=\"`t-${cardKey(card)}-${target.key}`\"\n\t\t\t\t\t\t\t\t\t\t:value=\"target.key\">\n\t\t\t\t\t\t\t\t\t\t{{ target.label }}\n\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</article>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<p\n\t\t\t\t\t\tv-else\n\t\t\t\t\t\tclass=\"cn-board-view__empty\"\n\t\t\t\t\t\tdata-testid=\"cn-board-column-empty\">\n\t\t\t\t\t\t{{ emptyColumnText }}\n\t\t\t\t\t</p>\n\t\t\t\t</section>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<p\n\t\t\tv-if=\"refusal\"\n\t\t\tclass=\"cn-board-view__refusal\"\n\t\t\tdata-testid=\"cn-board-refusal\"\n\t\t\trole=\"alert\">\n\t\t\t{{ refusal }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { buildBoardColumns } from '../../utils/boardColumns.js'\nimport { buildSwimlanes } from '../../utils/boardSwimlanes.js'\nimport { DROP_OUTCOMES, runBoardDrop } from '../../utils/boardTransition.js'\nimport { dueStateForRow } from '../../utils/dueRule.js'\nimport { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'\n\n/**\n * CnBoardView — the index page's rows as a board, one column per stage.\n *\n * THE COLUMNS ARE THE SCHEMA'S, NOT THE DATA'S. `buildBoardColumns` decides\n * them and is tested on its own; this renders what it returns. A board that\n * built its columns from the values present would lose the stage nothing is\n * in, which is the one you drag a card into.\n *\n * 🔴 A MOVE GOES THROUGH THE HOST'S TRANSITION AND NEVER WRITES THE STATUS\n * FIELD. Writing it here would skip every guard, side effect and audit entry\n * the transition carries, and the board would become a way to move a case past\n * a rule the case page enforces. `runBoardDrop` owns that contract; this\n * component owns the gestures that reach it.\n *\n * 🔴 THERE ARE TWO GESTURES, ALWAYS. A board whose only move is a drag is a\n * board a keyboard user cannot use, and \"drag the card\" is not an instruction\n * anybody can follow with a keyboard. Move to on the card runs the identical\n * call.\n *\n * @event {object} card-click — A card was opened. Payload: the row.\n * @event {object} moved — A card moved. Payload: `{ card, toKey }`.\n * @event {object} refused — A move was refused. Payload: `{ card, message }`.\n * @event {object} stale — The card had already moved. Payload: `{ card }`.\n */\nexport default {\n\tname: 'CnBoardView',\n\n\tprops: {\n\t\t/** The rows the list holds. */\n\t\trows: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The status field's schema: its enum or its lifecycle states. */\n\t\tstatusFieldSchema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The property a row's status lives on. */\n\t\tstatusField: {\n\t\t\ttype: String,\n\t\t\tdefault: 'status',\n\t\t},\n\n\t\t/** The fields a card shows. */\n\t\tcardFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** A second field to group the board into rows by. */\n\t\tswimlaneField: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The row key, so a card is identifiable. */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/**\n\t\t * The host's transition. Absent, the board is read-only: no drag, no\n\t\t * Move to, because a gesture that cannot do anything is worse than no\n\t\t * gesture at all.\n\t\t */\n\t\trunTransition: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Re-read one card, to check it has not moved under the dragger. */\n\t\treread: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Whether the counts are of one page. A count of a page shown as\n\t\t * though it were the total is a number somebody quotes in a meeting.\n\t\t */\n\t\tpaged: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Marks late cards: `{ field, soonDays? }`. `field` is the dot-path to\n\t\t * a card's due date. A date before today gets an error edge and the\n\t\t * label \"Overdue\"; a date within `soonDays` (default 3) gets \"Due\n\t\t * soon\" in the warning colour. `null` (the default) marks nothing.\n\t\t *\n\t\t * The same rule shape the table's date cell uses, so a list and its\n\t\t * board agree on what late means.\n\t\t *\n\t\t * @type {{field: string, soonDays?: number}|null}\n\t\t */\n\t\tdueRule: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['card-click', 'card-aux-click', 'moved', 'refused', 'stale'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Lanes the reader has collapsed, for as long as they are here. */\n\t\t\tcollapsed: [],\n\t\t\t/** The guard's own words, when it refused the last move. */\n\t\t\trefusal: '',\n\t\t\t/** The card being dragged, and where from. */\n\t\t\tdragging: null,\n\t\t\t/** The day late marking measures from. */\n\t\t\ttoday: new Date(),\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {boolean} Whether the status field can back a board at all. */\n\t\tusable() {\n\t\t\treturn this.board.usable\n\t\t},\n\n\t\t/** @return {object} The columns, from the schema. */\n\t\tboard() {\n\t\t\treturn buildBoardColumns({\n\t\t\t\tfield: this.statusFieldSchema,\n\t\t\t\trows: this.rows,\n\t\t\t\tstatusField: this.statusField,\n\t\t\t\toffBoardLabel: t('nextcloud-vue', 'Elsewhere'),\n\t\t\t})\n\t\t},\n\n\t\t/** @return {Array<object>} The board as rows of columns. */\n\t\tlanes() {\n\t\t\treturn buildSwimlanes({\n\t\t\t\tcolumns: this.board.columns,\n\t\t\t\tswimlaneField: this.swimlaneField,\n\t\t\t\tunassignedLabel: t('nextcloud-vue', 'Unassigned'),\n\t\t\t})\n\t\t},\n\n\t\t/** @return {boolean} Whether a move is possible at all. */\n\t\tcanMove() {\n\t\t\treturn typeof this.runTransition === 'function'\n\t\t},\n\n\t\t/** @return {string} What to say when the field cannot back a board. */\n\t\tunusableText() {\n\t\t\treturn t('nextcloud-vue', 'This list has no status field with stages, so it cannot be shown as a board.')\n\t\t},\n\n\t\t/** @return {string} The board's accessible name. */\n\t\tboardLabel() {\n\t\t\treturn t('nextcloud-vue', 'Board')\n\t\t},\n\n\t\t/** @return {string} The Move to control's label. */\n\t\tmoveLabel() {\n\t\t\treturn t('nextcloud-vue', 'Move to')\n\t\t},\n\n\t\t/**\n\t\t * The visible text on a card's opening control.\n\t\t *\n\t\t * Short, because it repeats on every card. Which card it opens is\n\t\t * said by the control's `aria-label`, so a reader hearing a list of\n\t\t * buttons hears the card's own name rather than \"Open\" nine times.\n\t\t *\n\t\t * @return {string} The label.\n\t\t */\n\t\topenLabel() {\n\t\t\treturn t('nextcloud-vue', 'Open')\n\t\t},\n\n\t\t/** @return {string} What an empty column says. */\n\t\temptyColumnText() {\n\t\t\treturn t('nextcloud-vue', 'Nothing here')\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\tpreventMiddleClickAutoscroll,\n\n\t\t/**\n\t\t * A card's due state under `dueRule`.\n\t\t *\n\t\t * @param {object} card The card's row.\n\t\t * @return {'overdue'|'soon'|'ok'|null} The state, or null when no rule applies.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-late-marking-on-board-cards\n\t\t */\n\t\tdueState(card) {\n\t\t\treturn dueStateForRow(card, this.dueRule, this.today)\n\t\t},\n\n\t\t/**\n\t\t * The words a late card carries. Colour alone would not reach a reader\n\t\t * who cannot see it, or cannot tell red from amber.\n\t\t *\n\t\t * @param {object} card The card's row.\n\t\t * @return {string} \"Overdue\", \"Due soon\", or '' for a card that is fine.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-late-marking-on-board-cards\n\t\t */\n\t\tdueLabel(card) {\n\t\t\tconst state = this.dueState(card)\n\t\t\tif (state === 'overdue') {\n\t\t\t\treturn t('nextcloud-vue', 'Overdue')\n\t\t\t}\n\t\t\treturn state === 'soon' ? t('nextcloud-vue', 'Due soon') : ''\n\t\t},\n\n\t\t/**\n\t\t * Open one card. The pointer and the keyboard both come through here,\n\t\t * so a reader on Enter reaches the same record as a reader on click.\n\t\t *\n\t\t * @param {object} card The card the reader picked.\n\t\t * @param {MouseEvent|KeyboardEvent} [event] The originating event.\n\t\t * @return {void} Nothing.\n\t\t */\n\t\topenCard(card, event) {\n\t\t\t/**\n\t\t\t * @event card-click Emitted when a reader opens a card, by click or Enter. Payload: `(card, event)` — the card row and the native event, so the host can open it in a new tab on a ctrl/cmd/shift click. A middle click emits `card-aux-click` instead.\n\t\t\t */\n\t\t\tthis.$emit('card-click', card, event)\n\t\t},\n\n\t\t/**\n\t\t * Middle click on a card's open button: emits `card-aux-click`, not\n\t\t * `card-click`, so an existing listener that navigates never moves the\n\t\t * current tab away.\n\t\t *\n\t\t * @param {object} card The card the reader picked.\n\t\t * @param {MouseEvent} event The auxclick event.\n\t\t * @return {void} Nothing.\n\t\t */\n\t\tonCardAuxClick(card, event) {\n\t\t\tif (isRowMiddleClick(event)) {\n\t\t\t\t/**\n\t\t\t\t * @event card-aux-click Emitted when a reader middle-clicks a card, for opening it in a new tab (see `openRowTarget`). Payload: `(card, event)` — the card row and the native auxclick event.\n\t\t\t\t */\n\t\t\t\tthis.$emit('card-aux-click', card, event)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A card's identity.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @return {string} Its key.\n\t\t */\n\t\tcardKey(card) {\n\t\t\treturn String(card?.[this.rowKey] ?? '')\n\t\t},\n\n\t\t/**\n\t\t * A column's count, said as a page count when that is what it is.\n\t\t *\n\t\t * @param {object} column The column.\n\t\t * @return {string} The count.\n\t\t */\n\t\tcountLabel(column) {\n\t\t\treturn this.paged\n\t\t\t\t? t('nextcloud-vue', '{count} on this page', { count: column.count })\n\t\t\t\t: String(column.count)\n\t\t},\n\n\t\t/**\n\t\t * What a card is called to somebody who cannot see which column it is in.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcardLabel(card, column) {\n\t\t\tconst name = this.cardFields.map((field) => card?.[field]).filter(Boolean).join(', ')\n\t\t\treturn t('nextcloud-vue', '{name}, in {column}', { name: name || this.cardKey(card), column: column.label })\n\t\t},\n\n\t\t/**\n\t\t * Whether a lane is collapsed.\n\t\t *\n\t\t * @param {string} key The lane.\n\t\t * @return {boolean} True when collapsed.\n\t\t */\n\t\tisCollapsed(key) {\n\t\t\treturn this.collapsed.includes(key)\n\t\t},\n\n\t\t/**\n\t\t * Collapse a lane, or open it again.\n\t\t *\n\t\t * Held in the component for as long as the reader is on the view, and\n\t\t * deliberately not persisted: a collapsed lane somebody forgot they\n\t\t * collapsed is work that has gone missing for them.\n\t\t *\n\t\t * @param {string} key The lane.\n\t\t */\n\t\ttoggleLane(key) {\n\t\t\tthis.collapsed = this.isCollapsed(key)\n\t\t\t\t? this.collapsed.filter((entry) => entry !== key)\n\t\t\t\t: [...this.collapsed, key]\n\t\t},\n\n\t\t/**\n\t\t * Remember which card is being dragged and where from.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @param {DragEvent} event The event.\n\t\t */\n\t\tonDragStart(card, column, event) {\n\t\t\tthis.dragging = { card, fromKey: column.key }\n\t\t\t// Firefox drops a drag that carries no data.\n\t\t\tevent?.dataTransfer?.setData?.('text/plain', this.cardKey(card))\n\t\t},\n\n\t\t/**\n\t\t * A drop on a column.\n\t\t *\n\t\t * @param {object} column The column dropped on.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync onDrop(column) {\n\t\t\tif (!this.dragging) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst { card, fromKey } = this.dragging\n\t\t\tthis.dragging = null\n\t\t\tawait this.move(card, fromKey, column.key)\n\t\t},\n\n\t\t/**\n\t\t * Move to, from the card's own menu.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @param {Event} event The change event.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync onMoveTo(card, column, event) {\n\t\t\tawait this.move(card, column.key, event?.target?.value ?? '')\n\t\t},\n\n\t\t/**\n\t\t * The one move, whichever gesture asked for it.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {string} fromKey Where it was.\n\t\t * @param {string} toKey Where it is going.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync move(card, fromKey, toKey) {\n\t\t\tthis.refusal = ''\n\n\t\t\tconst result = await runBoardDrop({\n\t\t\t\tcard,\n\t\t\t\tfromKey,\n\t\t\t\ttoKey,\n\t\t\t\tstatusField: this.statusField,\n\t\t\t\trunTransition: this.runTransition,\n\t\t\t\treread: this.reread,\n\t\t\t})\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.MOVED) {\n\t\t\t\t/**\n\t\t\t\t * @event moved Emitted when the host's transition accepted the move and the card now sits in the new lane. Payload: `{ card, toKey }`.\n\t\t\t\t */\n\t\t\t\tthis.$emit('moved', { card: result.card, toKey })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.REFUSED) {\n\t\t\t\t// The guard's own sentence when it gave one. Our own only when\n\t\t\t\t// it did not, so the two are never confused.\n\t\t\t\tthis.refusal = result.message || t('nextcloud-vue', 'That move was refused.')\n\t\t\t\t/**\n\t\t\t\t * @event refused Emitted when the guard turned the move down. Payload: `{ card, message }`, the message being the guard's own sentence when it gave one.\n\t\t\t\t */\n\t\t\t\tthis.$emit('refused', { card: result.card, message: this.refusal })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.STALE) {\n\t\t\t\tthis.refusal = t('nextcloud-vue', 'Somebody else moved this card. It is shown where it is now.')\n\t\t\t\t/**\n\t\t\t\t * @event stale Emitted when somebody else moved the card first, so the board shows it where it is now. Payload: `{ card }`.\n\t\t\t\t */\n\t\t\t\tthis.$emit('stale', { card: result.card })\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped lang=\"scss\">\n.cn-board-view__columns {\n\tdisplay: flex;\n\tgap: 12px;\n\toverflow-x: auto;\n\talign-items: flex-start;\n}\n\n.cn-board-view__column {\n\tflex: 0 0 260px;\n\tbackground-color: var(--color-background-hover);\n\tborder-radius: var(--border-radius-large);\n\tpadding: 8px;\n}\n\n.cn-board-view__column-header {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 1rem;\n\tmargin: 0 0 8px;\n}\n\n.cn-board-view__count,\n.cn-board-view__empty,\n.cn-board-view__unusable {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-board-view__card {\n\tdisplay: block;\n\tbackground-color: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 8px;\n\tmargin-bottom: 8px;\n}\n\n.cn-board-view__card-field {\n\tdisplay: block;\n}\n\n/* A late card: an edge in the error colour, drawn as an inset shadow so the\n card keeps its size and its rounded corners. The label below carries the\n meaning; the edge only helps a sighted reader find it. */\n.cn-board-view__card--overdue {\n\tborder-color: var(--color-error);\n\tbox-shadow: inset 3px 0 0 var(--color-error);\n}\n\n.cn-board-view__due {\n\tdisplay: block;\n\tfont-size: 0.85em;\n\tfont-weight: 700;\n}\n\n.cn-board-view__due--overdue {\n\tcolor: var(--color-error-text, var(--color-error));\n}\n\n.cn-board-view__due--soon {\n\tcolor: var(--color-warning-text, var(--color-main-text));\n\tfont-weight: 600;\n}\n\n/*\n * The card's opening control. The cursor and the focus ring live here now,\n * on the thing that actually does something, rather than on the box.\n */\n.cn-board-view__card-open {\n\tbackground: none;\n\tborder: none;\n\tpadding: 0;\n\tcursor: pointer;\n\tcolor: var(--color-primary-element);\n\tfont-size: 0.8rem;\n}\n\n.cn-board-view__card-open:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-board-view__lane-header {\n\tbackground: none;\n\tborder: none;\n\tfont-weight: bold;\n\tpadding: 8px 0;\n\tcursor: pointer;\n\tcolor: var(--color-main-text);\n}\n\n.cn-board-view__move-label {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.8rem;\n}\n\n.cn-board-view__refusal {\n\tcolor: var(--color-error);\n}\n</style>\n"],"names":["_createElementBlock","_toDisplayString","_Fragment","_renderList","_openBlock","_createElementVNode","_normalizeClass"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import script from './CnBrandStripe.vue2.js';
|
|
2
|
+
import { render } from './CnBrandStripe.vue3.js';
|
|
3
|
+
|
|
4
|
+
script.render = render;
|
|
5
|
+
script.__scopeId = "data-v-6fdfa763";
|
|
6
|
+
script.__file = "src/components/CnBrandStripe/CnBrandStripe.vue";
|
|
7
|
+
const __sfc_main = script;
|
|
8
|
+
|
|
9
|
+
export { __sfc_main as default };
|
|
10
|
+
//# sourceMappingURL=CnBrandStripe.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnBrandStripe.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CnBrandStripe draws the organisation's brand stripe: up to three coloured
|
|
3
|
+
* bands next to each other, for the top of a page, a card or a sidebar.
|
|
4
|
+
*
|
|
5
|
+
* The theme decides what it looks like, through the library's own custom
|
|
6
|
+
* properties: `--cn-brand-stripe-color-1/2/3` (the colours, start to end),
|
|
7
|
+
* `--cn-brand-stripe-ratio-1/2/3` (unitless shares, such as 6, 3 and 1)
|
|
8
|
+
* and `--cn-brand-stripe-height` (such as 5px). A theming app maps its own
|
|
9
|
+
* tokens onto these; the library never reads a theme's tokens. Without them the
|
|
10
|
+
* stripe is one band in the Nextcloud primary colour, 4px high.
|
|
11
|
+
*
|
|
12
|
+
* The stripe is decoration: it carries no meaning and is hidden from
|
|
13
|
+
* assistive technology.
|
|
14
|
+
*
|
|
15
|
+
* ```vue
|
|
16
|
+
* <CnBrandStripe />
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
var script = {
|
|
20
|
+
name: 'CnBrandStripe',
|
|
21
|
+
|
|
22
|
+
props: {
|
|
23
|
+
/**
|
|
24
|
+
* The direction the stripe runs in: `horizontal` (the default, a bar
|
|
25
|
+
* across the top) or `vertical` (a bar down the side; the height
|
|
26
|
+
* token then sets its width).
|
|
27
|
+
*
|
|
28
|
+
* @type {'horizontal'|'vertical'}
|
|
29
|
+
*/
|
|
30
|
+
orientation: {
|
|
31
|
+
type: String,
|
|
32
|
+
default: 'horizontal',
|
|
33
|
+
validator: (v) => ['horizontal', 'vertical'].includes(v),
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export { script as default };
|
|
39
|
+
//# sourceMappingURL=CnBrandStripe.vue2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnBrandStripe.vue2.js","sources":["../../../../src/components/CnBrandStripe/CnBrandStripe.vue"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div\n\t\tclass=\"cn-brand-stripe\"\n\t\t:class=\"{ 'cn-brand-stripe--vertical': orientation === 'vertical' }\"\n\t\taria-hidden=\"true\"\n\t\tdata-testid=\"cn-brand-stripe\">\n\t\t<span class=\"cn-brand-stripe__band cn-brand-stripe__band--1\" />\n\t\t<span class=\"cn-brand-stripe__band cn-brand-stripe__band--2\" />\n\t\t<span class=\"cn-brand-stripe__band cn-brand-stripe__band--3\" />\n\t</div>\n</template>\n\n<script>\n/**\n * CnBrandStripe draws the organisation's brand stripe: up to three coloured\n * bands next to each other, for the top of a page, a card or a sidebar.\n *\n * The theme decides what it looks like, through the library's own custom\n * properties: `--cn-brand-stripe-color-1/2/3` (the colours, start to end),\n * `--cn-brand-stripe-ratio-1/2/3` (unitless shares, such as 6, 3 and 1)\n * and `--cn-brand-stripe-height` (such as 5px). A theming app maps its own\n * tokens onto these; the library never reads a theme's tokens. Without them the\n * stripe is one band in the Nextcloud primary colour, 4px high.\n *\n * The stripe is decoration: it carries no meaning and is hidden from\n * assistive technology.\n *\n * ```vue\n * <CnBrandStripe />\n * ```\n */\nexport default {\n\tname: 'CnBrandStripe',\n\n\tprops: {\n\t\t/**\n\t\t * The direction the stripe runs in: `horizontal` (the default, a bar\n\t\t * across the top) or `vertical` (a bar down the side; the height\n\t\t * token then sets its width).\n\t\t *\n\t\t * @type {'horizontal'|'vertical'}\n\t\t */\n\t\torientation: {\n\t\t\ttype: String,\n\t\t\tdefault: 'horizontal',\n\t\t\tvalidator: (v) => ['horizontal', 'vertical'].includes(v),\n\t\t},\n\t},\n}\n</script>\n\n<!--\n The stripe reads the library's own `--cn-brand-stripe-*` properties, each\n with a Nextcloud fallback. A theming app sets them from its own tokens.\n-->\n<style scoped>\n.cn-brand-stripe {\n\tdisplay: flex;\n\tflex: none;\n\twidth: 100%;\n\theight: var(--cn-brand-stripe-height, 4px);\n}\n\n.cn-brand-stripe--vertical {\n\tflex-direction: column;\n\twidth: var(--cn-brand-stripe-height, 4px);\n\theight: 100%;\n}\n\n.cn-brand-stripe__band {\n\tflex-basis: 0;\n\tflex-shrink: 1;\n}\n\n.cn-brand-stripe__band--1 {\n\tflex-grow: var(--cn-brand-stripe-ratio-1, 1);\n\tbackground-color: var(--cn-brand-stripe-color-1, var(--color-primary-element));\n}\n\n.cn-brand-stripe__band--2 {\n\tflex-grow: var(--cn-brand-stripe-ratio-2, 1);\n\tbackground-color: var(--cn-brand-stripe-color-2, var(--color-primary-element));\n}\n\n.cn-brand-stripe__band--3 {\n\tflex-grow: var(--cn-brand-stripe-ratio-3, 1);\n\tbackground-color: var(--cn-brand-stripe-color-3, var(--color-primary-element));\n}\n\n@media print {\n\t.cn-brand-stripe {\n\t\tprint-color-adjust: exact;\n\t\t-webkit-print-color-adjust: exact;\n\t}\n}\n</style>\n"],"names":[],"mappings":"AAiBA;AACC;;AAEA;;AAEA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAED,aAAe;AACd,CAAA,IAAI,EAAE,eAAe;;AAErB,CAAA,KAAK,EAAE;AACN;;;;AAIC;AACA;;EAED,WAAW,EAAE;GACZ,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,YAAY;GACrB,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;GACxD;EACD;AACF;;;;"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { openBlock, createElementBlock, normalizeClass, createElementVNode } from 'vue';
|
|
2
|
+
|
|
3
|
+
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
4
|
+
return (openBlock(), createElementBlock("div", {
|
|
5
|
+
class: normalizeClass(["cn-brand-stripe", { 'cn-brand-stripe--vertical': $props.orientation === 'vertical' }]),
|
|
6
|
+
"aria-hidden": "true",
|
|
7
|
+
"data-testid": "cn-brand-stripe"
|
|
8
|
+
}, [...(_cache[0] || (_cache[0] = [
|
|
9
|
+
createElementVNode("span", { class: "cn-brand-stripe__band cn-brand-stripe__band--1" }, null, -1 /* CACHED */),
|
|
10
|
+
createElementVNode("span", { class: "cn-brand-stripe__band cn-brand-stripe__band--2" }, null, -1 /* CACHED */),
|
|
11
|
+
createElementVNode("span", { class: "cn-brand-stripe__band cn-brand-stripe__band--3" }, null, -1 /* CACHED */)
|
|
12
|
+
]))], 2 /* CLASS */))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { render };
|
|
16
|
+
//# sourceMappingURL=CnBrandStripe.vue3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnBrandStripe.vue3.js","sources":["../../../../src/components/CnBrandStripe/CnBrandStripe.vue?vue&type=template&id=6fdfa763&lang.js"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div\n\t\tclass=\"cn-brand-stripe\"\n\t\t:class=\"{ 'cn-brand-stripe--vertical': orientation === 'vertical' }\"\n\t\taria-hidden=\"true\"\n\t\tdata-testid=\"cn-brand-stripe\">\n\t\t<span class=\"cn-brand-stripe__band cn-brand-stripe__band--1\" />\n\t\t<span class=\"cn-brand-stripe__band cn-brand-stripe__band--2\" />\n\t\t<span class=\"cn-brand-stripe__band cn-brand-stripe__band--3\" />\n\t</div>\n</template>\n\n<script>\n/**\n * CnBrandStripe draws the organisation's brand stripe: up to three coloured\n * bands next to each other, for the top of a page, a card or a sidebar.\n *\n * The theme decides what it looks like, through the library's own custom\n * properties: `--cn-brand-stripe-color-1/2/3` (the colours, start to end),\n * `--cn-brand-stripe-ratio-1/2/3` (unitless shares, such as 6, 3 and 1)\n * and `--cn-brand-stripe-height` (such as 5px). A theming app maps its own\n * tokens onto these; the library never reads a theme's tokens. Without them the\n * stripe is one band in the Nextcloud primary colour, 4px high.\n *\n * The stripe is decoration: it carries no meaning and is hidden from\n * assistive technology.\n *\n * ```vue\n * <CnBrandStripe />\n * ```\n */\nexport default {\n\tname: 'CnBrandStripe',\n\n\tprops: {\n\t\t/**\n\t\t * The direction the stripe runs in: `horizontal` (the default, a bar\n\t\t * across the top) or `vertical` (a bar down the side; the height\n\t\t * token then sets its width).\n\t\t *\n\t\t * @type {'horizontal'|'vertical'}\n\t\t */\n\t\torientation: {\n\t\t\ttype: String,\n\t\t\tdefault: 'horizontal',\n\t\t\tvalidator: (v) => ['horizontal', 'vertical'].includes(v),\n\t\t},\n\t},\n}\n</script>\n\n<!--\n The stripe reads the library's own `--cn-brand-stripe-*` properties, each\n with a Nextcloud fallback. A theming app sets them from its own tokens.\n-->\n<style scoped>\n.cn-brand-stripe {\n\tdisplay: flex;\n\tflex: none;\n\twidth: 100%;\n\theight: var(--cn-brand-stripe-height, 4px);\n}\n\n.cn-brand-stripe--vertical {\n\tflex-direction: column;\n\twidth: var(--cn-brand-stripe-height, 4px);\n\theight: 100%;\n}\n\n.cn-brand-stripe__band {\n\tflex-basis: 0;\n\tflex-shrink: 1;\n}\n\n.cn-brand-stripe__band--1 {\n\tflex-grow: var(--cn-brand-stripe-ratio-1, 1);\n\tbackground-color: var(--cn-brand-stripe-color-1, var(--color-primary-element));\n}\n\n.cn-brand-stripe__band--2 {\n\tflex-grow: var(--cn-brand-stripe-ratio-2, 1);\n\tbackground-color: var(--cn-brand-stripe-color-2, var(--color-primary-element));\n}\n\n.cn-brand-stripe__band--3 {\n\tflex-grow: var(--cn-brand-stripe-ratio-3, 1);\n\tbackground-color: var(--cn-brand-stripe-color-3, var(--color-primary-element));\n}\n\n@media print {\n\t.cn-brand-stripe {\n\t\tprint-color-adjust: exact;\n\t\t-webkit-print-color-adjust: exact;\n\t}\n}\n</style>\n"],"names":["_normalizeClass"],"mappings":";;;;IAUE,KAAM,EAAAA,cAAA,CAAA,CAAA,iBAAiB,EAAA,EAAA,2BAAA,EACgB,MAAW,CAAA,WAAA,KAAA,UAAA,EAAA,CAAA,CAAA;AAClD,IAAA,aAAW,EAAC,MAAM;AAClB,IAAA,aAAI,EAAA,iBAAA;;;;;;;;;;"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getCanonicalLocale } from '@nextcloud/l10n';
|
|
2
|
+
import { NcAvatar, NcDateTime } from '@nextcloud/vue';
|
|
2
3
|
import CheckBold from 'vue-material-design-icons/CheckBold.vue';
|
|
3
4
|
import __sfc_main$1 from '../CnFkResolveCell/CnFkResolveCell.vue.js';
|
|
5
|
+
import { resolveDateVariant, parseDateValue } from '../../utils/dateVariant.js';
|
|
4
6
|
import { safeCurrencyCode } from '../../utils/formatMetric.js';
|
|
7
|
+
import { objectFieldValue } from '../../utils/objectName.js';
|
|
5
8
|
import { safeHref } from '../../utils/safeHref.js';
|
|
6
9
|
import { formatValue } from '../../utils/schema.js';
|
|
7
10
|
import __sfc_main from '../CnStatusBadge/CnStatusBadge.vue.js';
|
|
@@ -31,6 +34,7 @@ var script = {
|
|
|
31
34
|
CnStatusBadge: __sfc_main,
|
|
32
35
|
CnFkResolveCell: __sfc_main$1,
|
|
33
36
|
CheckBold,
|
|
37
|
+
NcAvatar,
|
|
34
38
|
NcDateTime,
|
|
35
39
|
},
|
|
36
40
|
|
|
@@ -101,6 +105,12 @@ var script = {
|
|
|
101
105
|
* the built-in id `"badge"` renders `CnStatusBadge` and the built-in
|
|
102
106
|
* id `"fkResolve"` renders `CnFkResolveCell` (uuid → related object
|
|
103
107
|
* label, config via `widgetProps { register, schema, labelField }`).
|
|
108
|
+
* The built-in id `"avatar"` renders a person as an avatar with the
|
|
109
|
+
* name beside it (`widgetProps { userField?, user?, nameField?, size? }`;
|
|
110
|
+
* a free name gets its initials), and the built-in id `"date"` renders
|
|
111
|
+
* a date whose colour follows `widgetProps.variantWhen`, rules on the
|
|
112
|
+
* number of days until the date (`[{ op: "lt", value: 0, variant:
|
|
113
|
+
* "error" }, { op: "lte", value: 5, variant: "warning" }]`).
|
|
104
114
|
* Takes precedence over `formatter`/the type-aware rendering, but the
|
|
105
115
|
* value handed to the widget is the formatter-shaped `formatted` when
|
|
106
116
|
* `formatter` is also set.
|
|
@@ -219,6 +229,112 @@ var script = {
|
|
|
219
229
|
return Number.isNaN(date.getTime()) ? null : date
|
|
220
230
|
},
|
|
221
231
|
|
|
232
|
+
/**
|
|
233
|
+
* `avatar` widget: the Nextcloud user id whose avatar to show, or ''
|
|
234
|
+
* for a free name. Read from the row field `widgetProps.userField`,
|
|
235
|
+
* or from the cell value itself when `widgetProps.user` is true.
|
|
236
|
+
*
|
|
237
|
+
* @return {string}
|
|
238
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells
|
|
239
|
+
*/
|
|
240
|
+
avatarUserId() {
|
|
241
|
+
const props = this.widgetProps || {};
|
|
242
|
+
let id = '';
|
|
243
|
+
if (typeof props.userField === 'string' && props.userField !== '') {
|
|
244
|
+
id = objectFieldValue(this.row, props.userField);
|
|
245
|
+
} else if (props.user === true) {
|
|
246
|
+
id = this.value;
|
|
247
|
+
}
|
|
248
|
+
return (typeof id === 'string' || typeof id === 'number') ? String(id) : ''
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* `avatar` widget: the name shown beside the picture. Read from the
|
|
253
|
+
* row field `widgetProps.nameField` when set, else the cell value.
|
|
254
|
+
*
|
|
255
|
+
* @return {string}
|
|
256
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells
|
|
257
|
+
*/
|
|
258
|
+
avatarName() {
|
|
259
|
+
const props = this.widgetProps || {};
|
|
260
|
+
if (typeof props.nameField === 'string' && props.nameField !== '') {
|
|
261
|
+
const name = objectFieldValue(this.row, props.nameField);
|
|
262
|
+
if (typeof name === 'string' && name !== '') {
|
|
263
|
+
return name
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (!this.hasValue || typeof this.value === 'object') {
|
|
267
|
+
return ''
|
|
268
|
+
}
|
|
269
|
+
return String(this.hasFormatter ? this.formattedValue : this.value)
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* `avatar` widget: the initials of a free name, at most two letters
|
|
274
|
+
* (first and last word).
|
|
275
|
+
*
|
|
276
|
+
* @return {string}
|
|
277
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells
|
|
278
|
+
*/
|
|
279
|
+
avatarInitials() {
|
|
280
|
+
const words = this.avatarName.trim().split(/\s+/).filter((word) => word !== '');
|
|
281
|
+
if (words.length === 0) {
|
|
282
|
+
return ''
|
|
283
|
+
}
|
|
284
|
+
const first = Array.from(words[0])[0] || '';
|
|
285
|
+
const last = words.length > 1 ? (Array.from(words[words.length - 1])[0] || '') : '';
|
|
286
|
+
return (first + last).toLocaleUpperCase()
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* `avatar` widget: the picture size in pixels (`widgetProps.size`, default 24).
|
|
291
|
+
*
|
|
292
|
+
* @return {number}
|
|
293
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells
|
|
294
|
+
*/
|
|
295
|
+
avatarSize() {
|
|
296
|
+
const size = Number(this.widgetProps && this.widgetProps.size);
|
|
297
|
+
return Number.isFinite(size) && size > 0 ? size : 24
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* `date` widget: the variant of the first `widgetProps.variantWhen`
|
|
302
|
+
* rule the date matches, or '' for none.
|
|
303
|
+
*
|
|
304
|
+
* @return {string}
|
|
305
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells
|
|
306
|
+
*/
|
|
307
|
+
dateVariant() {
|
|
308
|
+
const rules = this.widgetProps && this.widgetProps.variantWhen;
|
|
309
|
+
const variant = resolveDateVariant(this.value, rules);
|
|
310
|
+
return variant === 'default' ? '' : variant
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* `date` widget: the date written out in the user's locale.
|
|
315
|
+
*
|
|
316
|
+
* @return {string}
|
|
317
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells
|
|
318
|
+
*/
|
|
319
|
+
dateLabel() {
|
|
320
|
+
const date = parseDateValue(this.value);
|
|
321
|
+
if (date === null) {
|
|
322
|
+
return ''
|
|
323
|
+
}
|
|
324
|
+
const options = { day: 'numeric', month: 'short', year: 'numeric' };
|
|
325
|
+
let locale;
|
|
326
|
+
try {
|
|
327
|
+
locale = getCanonicalLocale();
|
|
328
|
+
} catch {
|
|
329
|
+
locale = undefined;
|
|
330
|
+
}
|
|
331
|
+
try {
|
|
332
|
+
return new Intl.DateTimeFormat(locale || undefined, options).format(date)
|
|
333
|
+
} catch {
|
|
334
|
+
return new Intl.DateTimeFormat(undefined, options).format(date)
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
|
|
222
338
|
/** True when the property is a URI / URL (rendered as an external link). */
|
|
223
339
|
isUri() {
|
|
224
340
|
return this.property?.format === 'uri' || this.property?.format === 'url'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnCellRenderer.vue2.js","sources":["../../../../src/components/CnCellRenderer/CnCellRenderer.vue"],"sourcesContent":["<template>\n\t<span class=\"cn-cell-renderer\" :class=\"cellClass\">\n\t\t<!-- Consumer-registered cell widget (cnCellWidgets[column.widget]) -->\n\t\t<component\n\t\t\t:is=\"widgetComponent\"\n\t\t\tv-if=\"widgetComponent\"\n\t\t\t:value=\"value\"\n\t\t\t:row=\"row\"\n\t\t\t:property=\"property\"\n\t\t\t:formatted=\"formattedValue\"\n\t\t\tv-bind=\"widgetProps\" />\n\n\t\t<!-- Built-in \"badge\" widget — renders the (possibly formatter-shaped) value as a status pill -->\n\t\t<template v-else-if=\"widget === 'badge'\">\n\t\t\t<CnStatusBadge v-if=\"hasValue\"\n\t\t\t\t:label=\"String(formattedValue)\"\n\t\t\t\t:variant=\"badgeVariant\"\n\t\t\t\t:colorMap=\"badgeColorMap\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Built-in \"fkResolve\" widget — resolves a reference uuid to the related\n\t\t object's display label via the object store (per-schema caching).\n\t\t Config: widgetProps { register, schema, labelField }. -->\n\t\t<template v-else-if=\"widget === 'fkResolve'\">\n\t\t\t<CnFkResolveCell\n\t\t\t\tv-if=\"hasValue\"\n\t\t\t\t:value=\"value\"\n\t\t\t\t:register=\"(widgetProps && widgetProps.register) || ''\"\n\t\t\t\t:schema=\"(widgetProps && widgetProps.schema) || ''\"\n\t\t\t\t:labelField=\"(widgetProps && widgetProps.labelField) || 'name'\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Built-in \"link\" widget — renders the (possibly formatter-shaped) value\n\t\t as a router-link (when widgetProps.route is a manifest page id, or\n\t\t widgetProps.routeMap picks one per row by widgetProps.routeField) or\n\t\t an external anchor (when widgetProps.href is set). Falls back to\n\t\t plain text + a once-per-session console.warn when neither resolves. -->\n\t\t<template v-else-if=\"widget === 'link'\">\n\t\t\t<router-link\n\t\t\t\tv-if=\"linkRoute\"\n\t\t\t\t:to=\"linkRoute\"\n\t\t\t\tclass=\"cn-cell-renderer__link\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</router-link>\n\t\t\t<a\n\t\t\t\tv-else-if=\"linkHref\"\n\t\t\t\t:href=\"linkHref\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\"\n\t\t\t\tclass=\"cn-cell-renderer__link\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</a>\n\t\t\t<span v-else :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t</template>\n\n\t\t<!-- Explicit column formatter — overrides the type-aware paths below -->\n\t\t<template v-else-if=\"hasFormatter\">\n\t\t\t<span :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t</template>\n\n\t\t<!-- Declarative swatch — a colour dot (from a sibling row field) + text -->\n\t\t<template v-else-if=\"isSwatch\">\n\t\t\t<span class=\"cn-cell-renderer__swatch-wrap\">\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"swatchColor\"\n\t\t\t\t\tclass=\"cn-cell-renderer__swatch-dot\"\n\t\t\t\t\t:style=\"{ backgroundColor: swatchColor }\"\n\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t<span v-if=\"hasValue\" :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t\t</span>\n\t\t</template>\n\n\t\t<!-- Declarative built-in format (currency / duration / number / percent) -->\n\t\t<template v-else-if=\"hasBuiltinFormat\">\n\t\t\t<span :title=\"rawTitle\" class=\"cn-cell-renderer--number\">{{ formattedValue }}</span>\n\t\t</template>\n\n\t\t<!-- Date / date-time: dynamic NcDateTime (relative time, absolute on hover) -->\n\t\t<template v-else-if=\"isDate\">\n\t\t\t<NcDateTime v-if=\"dateTimestamp\" :timestamp=\"dateTimestamp\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- URI / URL: external link -->\n\t\t<template v-else-if=\"isUri\">\n\t\t\t<a\n\t\t\t\tv-if=\"uriHref\"\n\t\t\t\t:href=\"uriHref\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\"\n\t\t\t\tclass=\"cn-cell-renderer__link\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</a>\n\t\t\t<span v-else-if=\"hasValue\" :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Boolean: icon -->\n\t\t<template v-else-if=\"propertyType === 'boolean'\">\n\t\t\t<CheckBold v-if=\"value\" :size=\"16\" class=\"cn-cell-renderer__icon cn-cell-renderer__icon--success\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Enum: status badge (auto-coloured from the property's optional colorMap) -->\n\t\t<template v-else-if=\"isEnum\">\n\t\t\t<CnStatusBadge v-if=\"value\"\n\t\t\t\t:label=\"enumLabel\"\n\t\t\t\t:colorKey=\"String(value)\"\n\t\t\t\t:colorMap=\"enumColorMap\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Array: comma-joined or count -->\n\t\t<template v-else-if=\"propertyType === 'array'\">\n\t\t\t<span v-if=\"!value || !value.length\" class=\"cn-cell-renderer__dash\">—</span>\n\t\t\t<span v-else :title=\"Array.isArray(value) ? value.join(', ') : ''\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</span>\n\t\t</template>\n\n\t\t<!-- Default: formatted text -->\n\t\t<template v-else>\n\t\t\t<span :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t</template>\n\t</span>\n</template>\n\n<script>\nimport { NcDateTime } from '@nextcloud/vue'\nimport CheckBold from 'vue-material-design-icons/CheckBold.vue'\nimport CnFkResolveCell from '../CnFkResolveCell/CnFkResolveCell.vue'\nimport { safeCurrencyCode } from '../../utils/formatMetric.js'\nimport { safeHref } from '../../utils/safeHref.js'\nimport { formatValue } from '../../utils/schema.js'\nimport { CnStatusBadge } from '../CnStatusBadge/index.js'\n\n/**\n * Module-level set of column keys already warned about for a\n * `widget:\"link\"` declaration with no resolvable target — guarantees\n * one warning per (page, column-key) rather than per row × render.\n */\nconst WARNED_LINK_KEYS = new Set()\n\n/**\n * CnCellRenderer — Type-aware cell renderer for schema-driven tables.\n *\n * Renders a single cell value based on its schema property definition.\n * Booleans render as icons, enums as status badges, dates as formatted strings,\n * and everything else as truncated text via `formatValue()`.\n *\n * ```vue\n * <CnCellRenderer :value=\"row.status\" :property=\"schema.properties.status\" />\n * ```\n */\nexport default {\n\tname: 'CnCellRenderer',\n\n\tcomponents: {\n\t\tCnStatusBadge,\n\t\tCnFkResolveCell,\n\t\tCheckBold,\n\t\tNcDateTime,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Cell-formatter registry, provided by CnAppRoot (`cnFormatters`).\n\t\t * Map of formatter-id → `(value, row, property) => string|number`.\n\t\t * Defaults to an empty object so standalone use (no CnAppRoot\n\t\t * ancestor) is unaffected.\n\t\t */\n\t\tcnFormatters: { default: () => ({}) },\n\t\t/**\n\t\t * Cell-widget registry, provided by CnAppRoot (`cnCellWidgets`).\n\t\t * Map of widget-id → Vue component. Resolves a column's `widget` id;\n\t\t * the built-in `\"badge\"` is handled inline (no registry entry needed).\n\t\t * Defaults to an empty object.\n\t\t */\n\t\tcnCellWidgets: { default: () => ({}) },\n\t\t/**\n\t\t * Display-layer translation function, provided by CnAppRoot\n\t\t * (`cnTranslate: this.translate`, bound to the host app's id). Applied\n\t\t * to the enum badge label only — the underlying value is untouched.\n\t\t * Defaults to identity so standalone use is unaffected.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The raw cell value */\n\t\tvalue: {\n\t\t\ttype: [Boolean, String, Number, Array, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Schema property definition: { type, format, enum, items, title } */\n\t\tproperty: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Optional cell-formatter id (e.g. `currency`, `automationTrigger`).\n\t\t * When set and resolvable in the injected `cnFormatters` registry,\n\t\t * the cell renders `cnFormatters[formatter](value, row, property,\n\t\t * formatterOptions)` as text — an explicit override of the type-aware\n\t\t * rendering below.\n\t\t */\n\t\tformatter: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Declarative options map passed as the formatter's fourth argument\n\t\t * (e.g. `{ currency: 'USD' }` for the built-in `currency` formatter,\n\t\t * or `{ negative, zero, positive }` phrases for `conditionalPhrase`).\n\t\t * Undefined-safe: three-argument formatters simply ignore it.\n\t\t */\n\t\tformatterOptions: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Optional cell-widget id (e.g. `badge`, or a consumer-registered\n\t\t * name). When it resolves in `cnCellWidgets` the cell renders that\n\t\t * component with `{ value, row, property, formatted, ...widgetProps }`;\n\t\t * the built-in id `\"badge\"` renders `CnStatusBadge` and the built-in\n\t\t * id `\"fkResolve\"` renders `CnFkResolveCell` (uuid → related object\n\t\t * label, config via `widgetProps { register, schema, labelField }`).\n\t\t * Takes precedence over `formatter`/the type-aware rendering, but the\n\t\t * value handed to the widget is the formatter-shaped `formatted` when\n\t\t * `formatter` is also set.\n\t\t */\n\t\twidget: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Extra props spread onto the resolved cell-widget component. The\n\t\t * built-in `\"link\"` reads its target from here: `route` (a manifest\n\t\t * page id), `params` (route param to row field), `href` (URL with\n\t\t * `{field}` placeholders), and `routeField` + `routeMap` to pick the\n\t\t * page per row from a sibling field (`{ person: 'ContactDetail' }`),\n\t\t * with `route` as the fallback for a value the map does not hold.\n\t\t */\n\t\twidgetProps: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Optional declarative cell-format spec — a no-code alternative to a\n\t\t * registry `formatter`. Recognised `style` values:\n\t\t *\n\t\t * - `'currency'` → `Intl.NumberFormat` currency (e.g. `€ 1.234,56`),\n\t\t * honouring `currency` (ISO code, default `'EUR'`) and `decimals`\n\t\t * (default 2).\n\t\t * - `'number'` / `'percent'` → localized number (percent appends `%`),\n\t\t * honouring `decimals` (default 0).\n\t\t * - `'duration'` → a seconds value rendered compact (`1u 23m`, `45m 10s`,\n\t\t * `12s`); pass `unit: 'milliseconds'` / `'minutes'` / `'hours'` when the\n\t\t * raw value is not in seconds. Sub-second millisecond values render as\n\t\t * `245ms` rather than rounding down to `0s`.\n\t\t * - `'swatch'` → a colour dot read from a sibling row field named by\n\t\t * `colorField` (the value renders as the cell text beside it).\n\t\t *\n\t\t * `prefix` / `suffix` are prepended/appended to the numeric styles.\n\t\t * Resolved AFTER `formatter` / `widget` (those win), but BEFORE the\n\t\t * type-aware rendering, so a manifest column can opt into currency or a\n\t\t * colour swatch without registering a function.\n\t\t *\n\t\t * @type {{style?: 'currency'|'number'|'percent'|'duration'|'swatch', currency?: string, decimals?: number, unit?: 'milliseconds'|'seconds'|'minutes'|'hours', prefix?: string, suffix?: string, colorField?: string}}\n\t\t */\n\t\tformat: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The full row object — passed so a formatter can be a function of\n\t\t * the whole record (e.g. \"days since `@self.updated`\"), not just\n\t\t * this one cell value.\n\t\t */\n\t\trow: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Maximum string length before truncation */\n\t\ttruncate: {\n\t\t\ttype: Number,\n\t\t\tdefault: 100,\n\t\t},\n\n\t\t/**\n\t\t * Row identifier field — used by the built-in `widget:\"link\"` when\n\t\t * the manifest doesn't specify an explicit `widgetProps.params`\n\t\t * map. Defaults to `'id'` so router-link param resolution works\n\t\t * with the manifest convention (`/x/:id`).\n\t\t */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\t},\n\n\tcomputed: {\n\t\tpropertyType() {\n\t\t\treturn this.property?.type || (typeof this.value)\n\t\t},\n\n\t\tisEnum() {\n\t\t\treturn !!(this.property?.enum && this.property.enum.length > 0)\n\t\t},\n\n\t\t/**\n\t\t * Badge text for an enum cell. Schema enum values are English source\n\t\t * codes, so a Dutch session otherwise reads `submitted` in a table whose\n\t\t * headers are Dutch. Only the label is translated — the cell's value,\n\t\t * sorting and filtering keep using the raw code.\n\t\t */\n\t\tenumLabel() {\n\t\t\tconst raw = String(this.value)\n\t\t\tconst labels = (this.property && (this.property.enumLabels || this.property['x-enum-labels'])) || {}\n\t\t\treturn this.cnTranslate(labels[raw] || raw)\n\t\t},\n\n\t\t/** True when the property is a date / date-time (rendered via NcDateTime). */\n\t\tisDate() {\n\t\t\treturn this.property?.format === 'date-time' || this.property?.format === 'date'\n\t\t},\n\n\t\t/**\n\t\t * The cell value as a Date for NcDateTime, or `null` when absent or\n\t\t * unparseable (the cell then falls back to a dash / plain text).\n\t\t *\n\t\t * @return {Date|null}\n\t\t */\n\t\tdateTimestamp() {\n\t\t\tif (!this.hasValue) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst date = new Date(this.value)\n\t\t\treturn Number.isNaN(date.getTime()) ? null : date\n\t\t},\n\n\t\t/** True when the property is a URI / URL (rendered as an external link). */\n\t\tisUri() {\n\t\t\treturn this.property?.format === 'uri' || this.property?.format === 'url'\n\t\t},\n\n\t\t/**\n\t\t * Safe external href for a URI/URL cell, or `null` when the value is\n\t\t * absent or fails `safeHref` validation (e.g. an unsafe scheme).\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\turiHref() {\n\t\t\tif (!this.hasValue) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn safeHref(String(this.value))\n\t\t},\n\n\t\t/** True when the cell has a renderable value (not null/undefined/empty string). */\n\t\thasValue() {\n\t\t\treturn this.value !== null && this.value !== undefined && this.value !== ''\n\t\t},\n\n\t\t/**\n\t\t * Resolved cell-widget component for this column, or `null`. A column's\n\t\t * `widget` id resolves against the injected `cnCellWidgets` registry;\n\t\t * the built-in `\"badge\"` is NOT resolved here (handled inline in the\n\t\t * template) so apps can still override `\"badge\"` via the registry.\n\t\t *\n\t\t * @return {import('vue').Component|null}\n\t\t */\n\t\twidgetComponent() {\n\t\t\tif (!this.widget) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst c = this.cnCellWidgets && this.cnCellWidgets[this.widget]\n\t\t\treturn c || null\n\t\t},\n\n\t\t/** Variant for the built-in `badge` widget — `widgetProps.variant` or `'default'`. */\n\t\tbadgeVariant() {\n\t\t\treturn (this.widgetProps && this.widgetProps.variant) || 'default'\n\t\t},\n\n\t\t/**\n\t\t * Color map for the built-in `badge` widget — `widgetProps.colorMap`,\n\t\t * a `{ value: variant }` map (e.g. `{ placed: 'primary', delivered: 'success' }`)\n\t\t * that CnStatusBadge resolves per label. Null when unset (falls back to\n\t\t * `badgeVariant`). Lets a manifest colour a status column without code.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tbadgeColorMap() {\n\t\t\treturn (this.widgetProps && this.widgetProps.colorMap) || null\n\t\t},\n\n\t\t/**\n\t\t * Color map for the auto-rendered enum badge — read from the schema\n\t\t * property's optional `colorMap` (or `x-color-map`). Null when unset, so\n\t\t * enum cells stay the default grey unless the schema opts into colours.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tenumColorMap() {\n\t\t\treturn (this.property && (this.property.colorMap || this.property['x-color-map'])) || null\n\t\t},\n\n\t\t/**\n\t\t * The manifest page id the built-in `widget:\"link\"` routes this row\n\t\t * to, or null.\n\t\t *\n\t\t * `widgetProps.routeField` names a sibling field on the row, and\n\t\t * `widgetProps.routeMap` maps that field's values to page ids, so one\n\t\t * column can link a person to `ContactDetail` and an organisation to\n\t\t * `OrganisationDetail` off `initiatorType`. A row whose value has no\n\t\t * entry in the map falls back to the fixed `widgetProps.route`, and\n\t\t * without one the cell renders as text. The map is the whole\n\t\t * vocabulary: a row value is only ever used as a KEY into it, never\n\t\t * as a route name itself, so row data cannot send a link to a page\n\t\t * the manifest did not name.\n\t\t *\n\t\t * @return {string|null} The page id, or null when none resolves.\n\t\t */\n\t\tlinkRouteName() {\n\t\t\tconst wp = this.widgetProps || {}\n\t\t\tconst map = wp.routeMap\n\t\t\tif (typeof wp.routeField === 'string' && wp.routeField !== '' && map && typeof map === 'object') {\n\t\t\t\tconst key = this.row ? this.row[wp.routeField] : undefined\n\t\t\t\tif (key !== undefined && key !== null && Object.hasOwn(map, String(key))) {\n\t\t\t\t\tconst mapped = map[String(key)]\n\t\t\t\t\tif (typeof mapped === 'string' && mapped !== '') {\n\t\t\t\t\t\treturn mapped\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn (typeof wp.route === 'string' && wp.route !== '') ? wp.route : null\n\t\t},\n\n\t\t/**\n\t\t * Resolved router-link target for the built-in `widget:\"link\"`. When\n\t\t * a page id resolves (`widgetProps.route`, or the row's entry in\n\t\t * `widgetProps.routeMap`, see `linkRouteName`), returns\n\t\t * `{ name: route, params }`. Param map is `widgetProps.params`\n\t\t * (a map of route-param-name → row-field-name); when omitted,\n\t\t * defaults to `{ id: row[rowKey] }`. Returns null when no page id\n\t\t * resolves (the template falls through to `linkHref` or plain\n\t\t * text).\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tlinkRoute() {\n\t\t\tif (this.widget !== 'link') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst route = this.linkRouteName\n\t\t\tif (!route) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst paramMap = (this.widgetProps && this.widgetProps.params)\n\t\t\t\t|| { id: this.rowKey || 'id' }\n\t\t\tconst params = {}\n\t\t\tfor (const [routeParam, rowField] of Object.entries(paramMap)) {\n\t\t\t\tparams[routeParam] = this.row && this.row[rowField]\n\t\t\t}\n\t\t\treturn { name: route, params }\n\t\t},\n\n\t\t/**\n\t\t * Resolved external href for the built-in `widget:\"link\"` when\n\t\t * `widgetProps.href` is set. `{key}` placeholders in the href\n\t\t * are substituted from the row (`\"/x/{id}\"` + `row.id === \"42\"`\n\t\t * → `\"/x/42\"`). The final computed value is validated with\n\t\t * `safeHref` so row-injected values cannot introduce unsafe\n\t\t * schemes: a `javascript:` or `data:` result renders as plain text\n\t\t * instead of a link. Returns null when `href` isn't set.\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\tlinkHref() {\n\t\t\tif (this.widget !== 'link') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst href = this.widgetProps && this.widgetProps.href\n\t\t\tif (!href) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst resolved = String(href).replace(/\\{(\\w+)\\}/g, (_, key) => this.row && this.row[key] !== null && this.row[key] !== undefined ? String(this.row[key]) : '')\n\t\t\t// The safeHref retrofit (18700fd94) put this check here and a later\n\t\t\t// lint pass (e5ea00d51) dropped it, leaving the docblock above\n\t\t\t// promising a check the code no longer made: a template of `{url}`\n\t\t\t// put a row's `javascript:` value straight into `:href`. safeHref\n\t\t\t// answers '#' for anything unsafe; that is not a link worth\n\t\t\t// rendering, so fall through to plain text.\n\t\t\tconst safe = safeHref(resolved)\n\t\t\treturn safe === '#' ? null : safe\n\t\t},\n\n\t\t/**\n\t\t * Resolved formatter function for this cell, or `null`. A column's\n\t\t * `formatter` id resolves against the injected `cnFormatters` registry.\n\t\t *\n\t\t * @return {((value: unknown, row: object, property: object, options: object) => unknown)|null}\n\t\t */\n\t\tformatterFn() {\n\t\t\tconst fn = this.formatter && this.cnFormatters && this.cnFormatters[this.formatter]\n\t\t\treturn typeof fn === 'function' ? fn : null\n\t\t},\n\n\t\t/** True when an explicit formatter is in play for this cell. */\n\t\thasFormatter() {\n\t\t\treturn this.formatterFn !== null\n\t\t},\n\n\t\t/**\n\t\t * The declarative format `style`, or null when no (recognised) `format`\n\t\t * spec is set. Drives the built-in currency / duration / number / percent\n\t\t * / swatch rendering paths.\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\tformatStyle() {\n\t\t\tconst s = this.format && this.format.style\n\t\t\treturn typeof s === 'string' ? s : null\n\t\t},\n\n\t\t/** True when a built-in NUMERIC format (currency/number/percent/duration) applies. */\n\t\thasBuiltinFormat() {\n\t\t\treturn ['currency', 'number', 'percent', 'duration'].includes(this.formatStyle)\n\t\t},\n\n\t\t/** True when this cell renders as a colour swatch (`format.style:\"swatch\"`). */\n\t\tisSwatch() {\n\t\t\treturn this.formatStyle === 'swatch'\n\t\t},\n\n\t\t/**\n\t\t * The swatch colour — read from the sibling row field named by\n\t\t * `format.colorField` (default `'color'`). Returns null when no usable\n\t\t * colour string is present (the dot is then omitted).\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\tswatchColor() {\n\t\t\tif (!this.isSwatch) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst field = (this.format && this.format.colorField) || 'color'\n\t\t\tconst c = this.row && this.row[field]\n\t\t\treturn (typeof c === 'string' && c.trim() !== '') ? c : null\n\t\t},\n\n\t\tformattedValue() {\n\t\t\tif (this.formatterFn) {\n\t\t\t\ttry {\n\t\t\t\t\treturn this.formatterFn(this.value, this.row, this.property, this.formatterOptions || undefined)\n\t\t\t\t} catch (e) {\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.warn(`[CnCellRenderer] formatter \"${this.formatter}\" threw; falling back`, e)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.hasBuiltinFormat) {\n\t\t\t\treturn this.applyBuiltinFormat()\n\t\t\t}\n\t\t\treturn formatValue(this.value, this.property, { truncate: this.truncate })\n\t\t},\n\n\t\trawTitle() {\n\t\t\t// Show full value on hover if it was truncated\n\t\t\tconst raw = this.value\n\t\t\tif (typeof raw === 'string' && raw.length > this.truncate) {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t\treturn undefined\n\t\t},\n\n\t\tcellClass() {\n\t\t\tconst classes = []\n\t\t\tif (this.propertyType === 'boolean') {\n\t\t\t\tclasses.push('cn-cell-renderer--boolean')\n\t\t\t}\n\t\t\tif (this.isEnum) {\n\t\t\t\tclasses.push('cn-cell-renderer--enum')\n\t\t\t}\n\t\t\tif (this.property?.format === 'date-time' || this.property?.format === 'date') {\n\t\t\t\tclasses.push('cn-cell-renderer--date')\n\t\t\t}\n\t\t\tif (this.property?.format === 'uuid') {\n\t\t\t\tclasses.push('cn-cell-renderer--uuid')\n\t\t\t}\n\t\t\tif (this.propertyType === 'integer' || this.propertyType === 'number') {\n\t\t\t\tclasses.push('cn-cell-renderer--number')\n\t\t\t}\n\t\t\treturn classes\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// Warn once per column-property-name when `widget:\"link\"` resolves\n\t\t// to neither a `route` nor an `href` — silent fallback hides\n\t\t// manifest mistakes, an every-row warn is too noisy.\n\t\t// A column with a `routeMap` has declared its targets; a row whose\n\t\t// value is not in the map (an empty requester, say) is data, not a\n\t\t// manifest mistake, so it renders as text without the warning. The\n\t\t// same holds for a declared `href` whose resolved value safeHref\n\t\t// refused: the manifest is fine, the row value is not.\n\t\tif (\n\t\t\tthis.widget === 'link'\n\t\t\t&& !this.linkRoute\n\t\t\t&& !this.widgetProps?.href\n\t\t\t&& this.widgetProps?.fallback !== 'silent'\n\t\t\t&& !(this.widgetProps?.routeMap && typeof this.widgetProps.routeMap === 'object')\n\t\t) {\n\t\t\tconst key = this.property?.title || String(this.value).slice(0, 20)\n\t\t\tif (!WARNED_LINK_KEYS.has(key)) {\n\t\t\t\tWARNED_LINK_KEYS.add(key)\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[CnCellRenderer] widget:\"link\" on \"${key}\" has no resolvable target — set widgetProps.route (page id) or widgetProps.href (URL with optional {field} placeholders); cell falls back to plain text.`)\n\t\t\t}\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Render the cell value per the declarative `format` spec (currency /\n\t\t * number / percent / duration). Mirrors CnStatWidget's formatting so a\n\t\t * KPI tile and a table column read identically. Non-numeric values fall\n\t\t * back to `formatValue`; an empty value renders an em-dash.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tapplyBuiltinFormat() {\n\t\t\tif (!this.hasValue) {\n\t\t\t\treturn '—'\n\t\t\t}\n\t\t\tconst fmt = this.format || {}\n\t\t\tif (fmt.style === 'duration') {\n\t\t\t\treturn this.formatDuration()\n\t\t\t}\n\t\t\tconst num = Number(this.value)\n\t\t\tif (!Number.isFinite(num)) {\n\t\t\t\treturn formatValue(this.value, this.property, { truncate: this.truncate })\n\t\t\t}\n\t\t\tconst decimals = Number.isFinite(fmt.decimals)\n\t\t\t\t? fmt.decimals\n\t\t\t\t: (fmt.style === 'currency' ? 2 : 0)\n\t\t\tlet body\n\t\t\tif (fmt.style === 'currency') {\n\t\t\t\tbody = new Intl.NumberFormat(undefined, {\n\t\t\t\t\tstyle: 'currency',\n\t\t\t\t\tcurrency: safeCurrencyCode(fmt.currency),\n\t\t\t\t\tminimumFractionDigits: decimals,\n\t\t\t\t\tmaximumFractionDigits: decimals,\n\t\t\t\t}).format(num)\n\t\t\t} else if (fmt.style === 'percent') {\n\t\t\t\t// Values are stored as the literal percent (83.3), not a 0–1 ratio.\n\t\t\t\tbody = new Intl.NumberFormat(undefined, {\n\t\t\t\t\tminimumFractionDigits: decimals,\n\t\t\t\t\tmaximumFractionDigits: decimals,\n\t\t\t\t}).format(num) + '%'\n\t\t\t} else {\n\t\t\t\tbody = new Intl.NumberFormat(undefined, {\n\t\t\t\t\tminimumFractionDigits: decimals,\n\t\t\t\t\tmaximumFractionDigits: decimals,\n\t\t\t\t}).format(num)\n\t\t\t}\n\t\t\treturn `${fmt.prefix || ''}${body}${fmt.suffix || ''}`\n\t\t},\n\n\t\t/**\n\t\t * Render a numeric duration compactly (`1u 23m`, `45m 10s`, `12s`). The\n\t\t * raw value is seconds unless `format.unit` is `'milliseconds'` /\n\t\t * `'minutes'` / `'hours'`. The hour suffix is `u` (uur) to read naturally\n\t\t * under NL theming while staying digit-led for other locales.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tformatDuration() {\n\t\t\tconst fmt = this.format || {}\n\t\t\tlet secs = Number(this.value)\n\t\t\tif (!Number.isFinite(secs)) {\n\t\t\t\treturn formatValue(this.value, this.property, { truncate: this.truncate })\n\t\t\t}\n\t\t\tif (fmt.unit === 'milliseconds') {\n\t\t\t\t// Sub-second durations are the common case for a millisecond\n\t\t\t\t// field (request/step timings), and they would all collapse to\n\t\t\t\t// the `0s` floor below — so render them in ms directly.\n\t\t\t\tconst ms = Math.round(secs)\n\t\t\t\tif (Math.abs(ms) < 1000) {\n\t\t\t\t\treturn `${fmt.prefix || ''}${ms}ms${fmt.suffix || ''}`\n\t\t\t\t}\n\t\t\t\tsecs = ms / 1000\n\t\t\t} else if (fmt.unit === 'minutes') {\n\t\t\t\tsecs *= 60\n\t\t\t} else if (fmt.unit === 'hours') {\n\t\t\t\tsecs *= 3600\n\t\t\t}\n\t\t\tsecs = Math.round(secs)\n\t\t\tconst sign = secs < 0 ? '-' : ''\n\t\t\tsecs = Math.abs(secs)\n\t\t\tconst h = Math.floor(secs / 3600)\n\t\t\tconst m = Math.floor((secs % 3600) / 60)\n\t\t\tconst s = secs % 60\n\t\t\tconst parts = []\n\t\t\tif (h > 0) {\n\t\t\t\tparts.push(`${h}u`)\n\t\t\t}\n\t\t\tif (m > 0) {\n\t\t\t\tparts.push(`${m}m`)\n\t\t\t}\n\t\t\tif (s > 0 && h === 0) {\n\t\t\t\tparts.push(`${s}s`)\n\t\t\t}\n\t\t\tif (parts.length === 0) {\n\t\t\t\tparts.push('0s')\n\t\t\t}\n\t\t\treturn `${fmt.prefix || ''}${sign}${parts.join(' ')}${fmt.suffix || ''}`\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-cell-renderer--uuid {\n\tfont-family: monospace;\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cell-renderer--number {\n\tfont-variant-numeric: tabular-nums;\n}\n\n.cn-cell-renderer--date {\n\twhite-space: nowrap;\n}\n\n.cn-cell-renderer__dash {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cell-renderer__icon--success {\n\tcolor: var(--color-success);\n}\n\n.cn-cell-renderer__swatch-wrap {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-cell-renderer__swatch-dot {\n\tdisplay: inline-block;\n\twidth: 12px;\n\theight: 12px;\n\tborder-radius: 50%;\n\tflex-shrink: 0;\n\tborder: 1px solid var(--color-border-dark);\n}\n</style>\n"],"names":["CnStatusBadge","CnFkResolveCell"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnCellRenderer.vue2.js","sources":["../../../../src/components/CnCellRenderer/CnCellRenderer.vue"],"sourcesContent":["<template>\n\t<span class=\"cn-cell-renderer\" :class=\"cellClass\">\n\t\t<!-- Consumer-registered cell widget (cnCellWidgets[column.widget]) -->\n\t\t<component\n\t\t\t:is=\"widgetComponent\"\n\t\t\tv-if=\"widgetComponent\"\n\t\t\t:value=\"value\"\n\t\t\t:row=\"row\"\n\t\t\t:property=\"property\"\n\t\t\t:formatted=\"formattedValue\"\n\t\t\tv-bind=\"widgetProps\" />\n\n\t\t<!-- Built-in \"badge\" widget — renders the (possibly formatter-shaped) value as a status pill -->\n\t\t<template v-else-if=\"widget === 'badge'\">\n\t\t\t<CnStatusBadge v-if=\"hasValue\"\n\t\t\t\t:label=\"String(formattedValue)\"\n\t\t\t\t:variant=\"badgeVariant\"\n\t\t\t\t:colorMap=\"badgeColorMap\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Built-in \"fkResolve\" widget — resolves a reference uuid to the related\n\t\t object's display label via the object store (per-schema caching).\n\t\t Config: widgetProps { register, schema, labelField }. -->\n\t\t<template v-else-if=\"widget === 'fkResolve'\">\n\t\t\t<CnFkResolveCell\n\t\t\t\tv-if=\"hasValue\"\n\t\t\t\t:value=\"value\"\n\t\t\t\t:register=\"(widgetProps && widgetProps.register) || ''\"\n\t\t\t\t:schema=\"(widgetProps && widgetProps.schema) || ''\"\n\t\t\t\t:labelField=\"(widgetProps && widgetProps.labelField) || 'name'\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Built-in \"link\" widget — renders the (possibly formatter-shaped) value\n\t\t as a router-link (when widgetProps.route is a manifest page id, or\n\t\t widgetProps.routeMap picks one per row by widgetProps.routeField) or\n\t\t an external anchor (when widgetProps.href is set). Falls back to\n\t\t plain text + a once-per-session console.warn when neither resolves. -->\n\t\t<template v-else-if=\"widget === 'link'\">\n\t\t\t<router-link\n\t\t\t\tv-if=\"linkRoute\"\n\t\t\t\t:to=\"linkRoute\"\n\t\t\t\tclass=\"cn-cell-renderer__link\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</router-link>\n\t\t\t<a\n\t\t\t\tv-else-if=\"linkHref\"\n\t\t\t\t:href=\"linkHref\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\"\n\t\t\t\tclass=\"cn-cell-renderer__link\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</a>\n\t\t\t<span v-else :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t</template>\n\n\t\t<!-- Built-in \"avatar\" widget: a person as an avatar with the name beside\n\t\t it. A user id (widgetProps.userField, or widgetProps.user for a cell\n\t\t that holds the id itself) gets the Nextcloud avatar; a free name\n\t\t gets its initials. The picture is decoration: the name is the text. -->\n\t\t<template v-else-if=\"widget === 'avatar'\">\n\t\t\t<span v-if=\"avatarName || avatarUserId\" class=\"cn-cell-renderer__avatar\">\n\t\t\t\t<span v-if=\"avatarUserId\" class=\"cn-cell-renderer__avatar-picture\" aria-hidden=\"true\">\n\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t:user=\"avatarUserId\"\n\t\t\t\t\t\t:displayName=\"avatarName || avatarUserId\"\n\t\t\t\t\t\t:size=\"avatarSize\"\n\t\t\t\t\t\t:disableMenu=\"true\"\n\t\t\t\t\t\t:disableTooltip=\"true\" />\n\t\t\t\t</span>\n\t\t\t\t<span\n\t\t\t\t\tv-else\n\t\t\t\t\tclass=\"cn-cell-renderer__avatar-initials\"\n\t\t\t\t\t:style=\"{ width: avatarSize + 'px', height: avatarSize + 'px' }\"\n\t\t\t\t\taria-hidden=\"true\">{{ avatarInitials }}</span>\n\t\t\t\t<span class=\"cn-cell-renderer__avatar-name\" :title=\"avatarName || avatarUserId\">{{ avatarName || avatarUserId }}</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Built-in \"date\" widget: a date whose colour follows\n\t\t widgetProps.variantWhen, rules on the number of days until it\n\t\t (0 = today, negative = overdue). -->\n\t\t<template v-else-if=\"widget === 'date'\">\n\t\t\t<time\n\t\t\t\tv-if=\"dateTimestamp\"\n\t\t\t\tclass=\"cn-cell-renderer__date\"\n\t\t\t\t:class=\"dateVariant ? 'cn-cell-renderer__date--' + dateVariant : null\"\n\t\t\t\t:datetime=\"dateTimestamp.toISOString()\"\n\t\t\t\t:title=\"rawTitle\">{{ dateLabel }}</time>\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Explicit column formatter — overrides the type-aware paths below -->\n\t\t<template v-else-if=\"hasFormatter\">\n\t\t\t<span :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t</template>\n\n\t\t<!-- Declarative swatch — a colour dot (from a sibling row field) + text -->\n\t\t<template v-else-if=\"isSwatch\">\n\t\t\t<span class=\"cn-cell-renderer__swatch-wrap\">\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"swatchColor\"\n\t\t\t\t\tclass=\"cn-cell-renderer__swatch-dot\"\n\t\t\t\t\t:style=\"{ backgroundColor: swatchColor }\"\n\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t<span v-if=\"hasValue\" :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t\t</span>\n\t\t</template>\n\n\t\t<!-- Declarative built-in format (currency / duration / number / percent) -->\n\t\t<template v-else-if=\"hasBuiltinFormat\">\n\t\t\t<span :title=\"rawTitle\" class=\"cn-cell-renderer--number\">{{ formattedValue }}</span>\n\t\t</template>\n\n\t\t<!-- Date / date-time: dynamic NcDateTime (relative time, absolute on hover) -->\n\t\t<template v-else-if=\"isDate\">\n\t\t\t<NcDateTime v-if=\"dateTimestamp\" :timestamp=\"dateTimestamp\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- URI / URL: external link -->\n\t\t<template v-else-if=\"isUri\">\n\t\t\t<a\n\t\t\t\tv-if=\"uriHref\"\n\t\t\t\t:href=\"uriHref\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\"\n\t\t\t\tclass=\"cn-cell-renderer__link\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</a>\n\t\t\t<span v-else-if=\"hasValue\" :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Boolean: icon -->\n\t\t<template v-else-if=\"propertyType === 'boolean'\">\n\t\t\t<CheckBold v-if=\"value\" :size=\"16\" class=\"cn-cell-renderer__icon cn-cell-renderer__icon--success\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Enum: status badge (auto-coloured from the property's optional colorMap) -->\n\t\t<template v-else-if=\"isEnum\">\n\t\t\t<CnStatusBadge v-if=\"value\"\n\t\t\t\t:label=\"enumLabel\"\n\t\t\t\t:colorKey=\"String(value)\"\n\t\t\t\t:colorMap=\"enumColorMap\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Array: comma-joined or count -->\n\t\t<template v-else-if=\"propertyType === 'array'\">\n\t\t\t<span v-if=\"!value || !value.length\" class=\"cn-cell-renderer__dash\">—</span>\n\t\t\t<span v-else :title=\"Array.isArray(value) ? value.join(', ') : ''\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</span>\n\t\t</template>\n\n\t\t<!-- Default: formatted text -->\n\t\t<template v-else>\n\t\t\t<span :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t</template>\n\t</span>\n</template>\n\n<script>\nimport { getCanonicalLocale } from '@nextcloud/l10n'\nimport { NcAvatar, NcDateTime } from '@nextcloud/vue'\nimport CheckBold from 'vue-material-design-icons/CheckBold.vue'\nimport CnFkResolveCell from '../CnFkResolveCell/CnFkResolveCell.vue'\nimport { parseDateValue, resolveDateVariant } from '../../utils/dateVariant.js'\nimport { safeCurrencyCode } from '../../utils/formatMetric.js'\nimport { objectFieldValue } from '../../utils/objectName.js'\nimport { safeHref } from '../../utils/safeHref.js'\nimport { formatValue } from '../../utils/schema.js'\nimport { CnStatusBadge } from '../CnStatusBadge/index.js'\n\n/**\n * Module-level set of column keys already warned about for a\n * `widget:\"link\"` declaration with no resolvable target — guarantees\n * one warning per (page, column-key) rather than per row × render.\n */\nconst WARNED_LINK_KEYS = new Set()\n\n/**\n * CnCellRenderer — Type-aware cell renderer for schema-driven tables.\n *\n * Renders a single cell value based on its schema property definition.\n * Booleans render as icons, enums as status badges, dates as formatted strings,\n * and everything else as truncated text via `formatValue()`.\n *\n * ```vue\n * <CnCellRenderer :value=\"row.status\" :property=\"schema.properties.status\" />\n * ```\n */\nexport default {\n\tname: 'CnCellRenderer',\n\n\tcomponents: {\n\t\tCnStatusBadge,\n\t\tCnFkResolveCell,\n\t\tCheckBold,\n\t\tNcAvatar,\n\t\tNcDateTime,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Cell-formatter registry, provided by CnAppRoot (`cnFormatters`).\n\t\t * Map of formatter-id → `(value, row, property) => string|number`.\n\t\t * Defaults to an empty object so standalone use (no CnAppRoot\n\t\t * ancestor) is unaffected.\n\t\t */\n\t\tcnFormatters: { default: () => ({}) },\n\t\t/**\n\t\t * Cell-widget registry, provided by CnAppRoot (`cnCellWidgets`).\n\t\t * Map of widget-id → Vue component. Resolves a column's `widget` id;\n\t\t * the built-in `\"badge\"` is handled inline (no registry entry needed).\n\t\t * Defaults to an empty object.\n\t\t */\n\t\tcnCellWidgets: { default: () => ({}) },\n\t\t/**\n\t\t * Display-layer translation function, provided by CnAppRoot\n\t\t * (`cnTranslate: this.translate`, bound to the host app's id). Applied\n\t\t * to the enum badge label only — the underlying value is untouched.\n\t\t * Defaults to identity so standalone use is unaffected.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The raw cell value */\n\t\tvalue: {\n\t\t\ttype: [Boolean, String, Number, Array, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Schema property definition: { type, format, enum, items, title } */\n\t\tproperty: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Optional cell-formatter id (e.g. `currency`, `automationTrigger`).\n\t\t * When set and resolvable in the injected `cnFormatters` registry,\n\t\t * the cell renders `cnFormatters[formatter](value, row, property,\n\t\t * formatterOptions)` as text — an explicit override of the type-aware\n\t\t * rendering below.\n\t\t */\n\t\tformatter: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Declarative options map passed as the formatter's fourth argument\n\t\t * (e.g. `{ currency: 'USD' }` for the built-in `currency` formatter,\n\t\t * or `{ negative, zero, positive }` phrases for `conditionalPhrase`).\n\t\t * Undefined-safe: three-argument formatters simply ignore it.\n\t\t */\n\t\tformatterOptions: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Optional cell-widget id (e.g. `badge`, or a consumer-registered\n\t\t * name). When it resolves in `cnCellWidgets` the cell renders that\n\t\t * component with `{ value, row, property, formatted, ...widgetProps }`;\n\t\t * the built-in id `\"badge\"` renders `CnStatusBadge` and the built-in\n\t\t * id `\"fkResolve\"` renders `CnFkResolveCell` (uuid → related object\n\t\t * label, config via `widgetProps { register, schema, labelField }`).\n\t\t * The built-in id `\"avatar\"` renders a person as an avatar with the\n\t\t * name beside it (`widgetProps { userField?, user?, nameField?, size? }`;\n\t\t * a free name gets its initials), and the built-in id `\"date\"` renders\n\t\t * a date whose colour follows `widgetProps.variantWhen`, rules on the\n\t\t * number of days until the date (`[{ op: \"lt\", value: 0, variant:\n\t\t * \"error\" }, { op: \"lte\", value: 5, variant: \"warning\" }]`).\n\t\t * Takes precedence over `formatter`/the type-aware rendering, but the\n\t\t * value handed to the widget is the formatter-shaped `formatted` when\n\t\t * `formatter` is also set.\n\t\t */\n\t\twidget: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Extra props spread onto the resolved cell-widget component. The\n\t\t * built-in `\"link\"` reads its target from here: `route` (a manifest\n\t\t * page id), `params` (route param to row field), `href` (URL with\n\t\t * `{field}` placeholders), and `routeField` + `routeMap` to pick the\n\t\t * page per row from a sibling field (`{ person: 'ContactDetail' }`),\n\t\t * with `route` as the fallback for a value the map does not hold.\n\t\t */\n\t\twidgetProps: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Optional declarative cell-format spec — a no-code alternative to a\n\t\t * registry `formatter`. Recognised `style` values:\n\t\t *\n\t\t * - `'currency'` → `Intl.NumberFormat` currency (e.g. `€ 1.234,56`),\n\t\t * honouring `currency` (ISO code, default `'EUR'`) and `decimals`\n\t\t * (default 2).\n\t\t * - `'number'` / `'percent'` → localized number (percent appends `%`),\n\t\t * honouring `decimals` (default 0).\n\t\t * - `'duration'` → a seconds value rendered compact (`1u 23m`, `45m 10s`,\n\t\t * `12s`); pass `unit: 'milliseconds'` / `'minutes'` / `'hours'` when the\n\t\t * raw value is not in seconds. Sub-second millisecond values render as\n\t\t * `245ms` rather than rounding down to `0s`.\n\t\t * - `'swatch'` → a colour dot read from a sibling row field named by\n\t\t * `colorField` (the value renders as the cell text beside it).\n\t\t *\n\t\t * `prefix` / `suffix` are prepended/appended to the numeric styles.\n\t\t * Resolved AFTER `formatter` / `widget` (those win), but BEFORE the\n\t\t * type-aware rendering, so a manifest column can opt into currency or a\n\t\t * colour swatch without registering a function.\n\t\t *\n\t\t * @type {{style?: 'currency'|'number'|'percent'|'duration'|'swatch', currency?: string, decimals?: number, unit?: 'milliseconds'|'seconds'|'minutes'|'hours', prefix?: string, suffix?: string, colorField?: string}}\n\t\t */\n\t\tformat: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The full row object — passed so a formatter can be a function of\n\t\t * the whole record (e.g. \"days since `@self.updated`\"), not just\n\t\t * this one cell value.\n\t\t */\n\t\trow: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Maximum string length before truncation */\n\t\ttruncate: {\n\t\t\ttype: Number,\n\t\t\tdefault: 100,\n\t\t},\n\n\t\t/**\n\t\t * Row identifier field — used by the built-in `widget:\"link\"` when\n\t\t * the manifest doesn't specify an explicit `widgetProps.params`\n\t\t * map. Defaults to `'id'` so router-link param resolution works\n\t\t * with the manifest convention (`/x/:id`).\n\t\t */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\t},\n\n\tcomputed: {\n\t\tpropertyType() {\n\t\t\treturn this.property?.type || (typeof this.value)\n\t\t},\n\n\t\tisEnum() {\n\t\t\treturn !!(this.property?.enum && this.property.enum.length > 0)\n\t\t},\n\n\t\t/**\n\t\t * Badge text for an enum cell. Schema enum values are English source\n\t\t * codes, so a Dutch session otherwise reads `submitted` in a table whose\n\t\t * headers are Dutch. Only the label is translated — the cell's value,\n\t\t * sorting and filtering keep using the raw code.\n\t\t */\n\t\tenumLabel() {\n\t\t\tconst raw = String(this.value)\n\t\t\tconst labels = (this.property && (this.property.enumLabels || this.property['x-enum-labels'])) || {}\n\t\t\treturn this.cnTranslate(labels[raw] || raw)\n\t\t},\n\n\t\t/** True when the property is a date / date-time (rendered via NcDateTime). */\n\t\tisDate() {\n\t\t\treturn this.property?.format === 'date-time' || this.property?.format === 'date'\n\t\t},\n\n\t\t/**\n\t\t * The cell value as a Date for NcDateTime, or `null` when absent or\n\t\t * unparseable (the cell then falls back to a dash / plain text).\n\t\t *\n\t\t * @return {Date|null}\n\t\t */\n\t\tdateTimestamp() {\n\t\t\tif (!this.hasValue) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst date = new Date(this.value)\n\t\t\treturn Number.isNaN(date.getTime()) ? null : date\n\t\t},\n\n\t\t/**\n\t\t * `avatar` widget: the Nextcloud user id whose avatar to show, or ''\n\t\t * for a free name. Read from the row field `widgetProps.userField`,\n\t\t * or from the cell value itself when `widgetProps.user` is true.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells\n\t\t */\n\t\tavatarUserId() {\n\t\t\tconst props = this.widgetProps || {}\n\t\t\tlet id = ''\n\t\t\tif (typeof props.userField === 'string' && props.userField !== '') {\n\t\t\t\tid = objectFieldValue(this.row, props.userField)\n\t\t\t} else if (props.user === true) {\n\t\t\t\tid = this.value\n\t\t\t}\n\t\t\treturn (typeof id === 'string' || typeof id === 'number') ? String(id) : ''\n\t\t},\n\n\t\t/**\n\t\t * `avatar` widget: the name shown beside the picture. Read from the\n\t\t * row field `widgetProps.nameField` when set, else the cell value.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells\n\t\t */\n\t\tavatarName() {\n\t\t\tconst props = this.widgetProps || {}\n\t\t\tif (typeof props.nameField === 'string' && props.nameField !== '') {\n\t\t\t\tconst name = objectFieldValue(this.row, props.nameField)\n\t\t\t\tif (typeof name === 'string' && name !== '') {\n\t\t\t\t\treturn name\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!this.hasValue || typeof this.value === 'object') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn String(this.hasFormatter ? this.formattedValue : this.value)\n\t\t},\n\n\t\t/**\n\t\t * `avatar` widget: the initials of a free name, at most two letters\n\t\t * (first and last word).\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells\n\t\t */\n\t\tavatarInitials() {\n\t\t\tconst words = this.avatarName.trim().split(/\\s+/).filter((word) => word !== '')\n\t\t\tif (words.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst first = Array.from(words[0])[0] || ''\n\t\t\tconst last = words.length > 1 ? (Array.from(words[words.length - 1])[0] || '') : ''\n\t\t\treturn (first + last).toLocaleUpperCase()\n\t\t},\n\n\t\t/**\n\t\t * `avatar` widget: the picture size in pixels (`widgetProps.size`, default 24).\n\t\t *\n\t\t * @return {number}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells\n\t\t */\n\t\tavatarSize() {\n\t\t\tconst size = Number(this.widgetProps && this.widgetProps.size)\n\t\t\treturn Number.isFinite(size) && size > 0 ? size : 24\n\t\t},\n\n\t\t/**\n\t\t * `date` widget: the variant of the first `widgetProps.variantWhen`\n\t\t * rule the date matches, or '' for none.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells\n\t\t */\n\t\tdateVariant() {\n\t\t\tconst rules = this.widgetProps && this.widgetProps.variantWhen\n\t\t\tconst variant = resolveDateVariant(this.value, rules)\n\t\t\treturn variant === 'default' ? '' : variant\n\t\t},\n\n\t\t/**\n\t\t * `date` widget: the date written out in the user's locale.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells\n\t\t */\n\t\tdateLabel() {\n\t\t\tconst date = parseDateValue(this.value)\n\t\t\tif (date === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst options = { day: 'numeric', month: 'short', year: 'numeric' }\n\t\t\tlet locale\n\t\t\ttry {\n\t\t\t\tlocale = getCanonicalLocale()\n\t\t\t} catch {\n\t\t\t\tlocale = undefined\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Intl.DateTimeFormat(locale || undefined, options).format(date)\n\t\t\t} catch {\n\t\t\t\treturn new Intl.DateTimeFormat(undefined, options).format(date)\n\t\t\t}\n\t\t},\n\n\t\t/** True when the property is a URI / URL (rendered as an external link). */\n\t\tisUri() {\n\t\t\treturn this.property?.format === 'uri' || this.property?.format === 'url'\n\t\t},\n\n\t\t/**\n\t\t * Safe external href for a URI/URL cell, or `null` when the value is\n\t\t * absent or fails `safeHref` validation (e.g. an unsafe scheme).\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\turiHref() {\n\t\t\tif (!this.hasValue) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn safeHref(String(this.value))\n\t\t},\n\n\t\t/** True when the cell has a renderable value (not null/undefined/empty string). */\n\t\thasValue() {\n\t\t\treturn this.value !== null && this.value !== undefined && this.value !== ''\n\t\t},\n\n\t\t/**\n\t\t * Resolved cell-widget component for this column, or `null`. A column's\n\t\t * `widget` id resolves against the injected `cnCellWidgets` registry;\n\t\t * the built-in `\"badge\"` is NOT resolved here (handled inline in the\n\t\t * template) so apps can still override `\"badge\"` via the registry.\n\t\t *\n\t\t * @return {import('vue').Component|null}\n\t\t */\n\t\twidgetComponent() {\n\t\t\tif (!this.widget) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst c = this.cnCellWidgets && this.cnCellWidgets[this.widget]\n\t\t\treturn c || null\n\t\t},\n\n\t\t/** Variant for the built-in `badge` widget — `widgetProps.variant` or `'default'`. */\n\t\tbadgeVariant() {\n\t\t\treturn (this.widgetProps && this.widgetProps.variant) || 'default'\n\t\t},\n\n\t\t/**\n\t\t * Color map for the built-in `badge` widget — `widgetProps.colorMap`,\n\t\t * a `{ value: variant }` map (e.g. `{ placed: 'primary', delivered: 'success' }`)\n\t\t * that CnStatusBadge resolves per label. Null when unset (falls back to\n\t\t * `badgeVariant`). Lets a manifest colour a status column without code.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tbadgeColorMap() {\n\t\t\treturn (this.widgetProps && this.widgetProps.colorMap) || null\n\t\t},\n\n\t\t/**\n\t\t * Color map for the auto-rendered enum badge — read from the schema\n\t\t * property's optional `colorMap` (or `x-color-map`). Null when unset, so\n\t\t * enum cells stay the default grey unless the schema opts into colours.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tenumColorMap() {\n\t\t\treturn (this.property && (this.property.colorMap || this.property['x-color-map'])) || null\n\t\t},\n\n\t\t/**\n\t\t * The manifest page id the built-in `widget:\"link\"` routes this row\n\t\t * to, or null.\n\t\t *\n\t\t * `widgetProps.routeField` names a sibling field on the row, and\n\t\t * `widgetProps.routeMap` maps that field's values to page ids, so one\n\t\t * column can link a person to `ContactDetail` and an organisation to\n\t\t * `OrganisationDetail` off `initiatorType`. A row whose value has no\n\t\t * entry in the map falls back to the fixed `widgetProps.route`, and\n\t\t * without one the cell renders as text. The map is the whole\n\t\t * vocabulary: a row value is only ever used as a KEY into it, never\n\t\t * as a route name itself, so row data cannot send a link to a page\n\t\t * the manifest did not name.\n\t\t *\n\t\t * @return {string|null} The page id, or null when none resolves.\n\t\t */\n\t\tlinkRouteName() {\n\t\t\tconst wp = this.widgetProps || {}\n\t\t\tconst map = wp.routeMap\n\t\t\tif (typeof wp.routeField === 'string' && wp.routeField !== '' && map && typeof map === 'object') {\n\t\t\t\tconst key = this.row ? this.row[wp.routeField] : undefined\n\t\t\t\tif (key !== undefined && key !== null && Object.hasOwn(map, String(key))) {\n\t\t\t\t\tconst mapped = map[String(key)]\n\t\t\t\t\tif (typeof mapped === 'string' && mapped !== '') {\n\t\t\t\t\t\treturn mapped\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn (typeof wp.route === 'string' && wp.route !== '') ? wp.route : null\n\t\t},\n\n\t\t/**\n\t\t * Resolved router-link target for the built-in `widget:\"link\"`. When\n\t\t * a page id resolves (`widgetProps.route`, or the row's entry in\n\t\t * `widgetProps.routeMap`, see `linkRouteName`), returns\n\t\t * `{ name: route, params }`. Param map is `widgetProps.params`\n\t\t * (a map of route-param-name → row-field-name); when omitted,\n\t\t * defaults to `{ id: row[rowKey] }`. Returns null when no page id\n\t\t * resolves (the template falls through to `linkHref` or plain\n\t\t * text).\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tlinkRoute() {\n\t\t\tif (this.widget !== 'link') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst route = this.linkRouteName\n\t\t\tif (!route) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst paramMap = (this.widgetProps && this.widgetProps.params)\n\t\t\t\t|| { id: this.rowKey || 'id' }\n\t\t\tconst params = {}\n\t\t\tfor (const [routeParam, rowField] of Object.entries(paramMap)) {\n\t\t\t\tparams[routeParam] = this.row && this.row[rowField]\n\t\t\t}\n\t\t\treturn { name: route, params }\n\t\t},\n\n\t\t/**\n\t\t * Resolved external href for the built-in `widget:\"link\"` when\n\t\t * `widgetProps.href` is set. `{key}` placeholders in the href\n\t\t * are substituted from the row (`\"/x/{id}\"` + `row.id === \"42\"`\n\t\t * → `\"/x/42\"`). The final computed value is validated with\n\t\t * `safeHref` so row-injected values cannot introduce unsafe\n\t\t * schemes: a `javascript:` or `data:` result renders as plain text\n\t\t * instead of a link. Returns null when `href` isn't set.\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\tlinkHref() {\n\t\t\tif (this.widget !== 'link') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst href = this.widgetProps && this.widgetProps.href\n\t\t\tif (!href) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst resolved = String(href).replace(/\\{(\\w+)\\}/g, (_, key) => this.row && this.row[key] !== null && this.row[key] !== undefined ? String(this.row[key]) : '')\n\t\t\t// The safeHref retrofit (18700fd94) put this check here and a later\n\t\t\t// lint pass (e5ea00d51) dropped it, leaving the docblock above\n\t\t\t// promising a check the code no longer made: a template of `{url}`\n\t\t\t// put a row's `javascript:` value straight into `:href`. safeHref\n\t\t\t// answers '#' for anything unsafe; that is not a link worth\n\t\t\t// rendering, so fall through to plain text.\n\t\t\tconst safe = safeHref(resolved)\n\t\t\treturn safe === '#' ? null : safe\n\t\t},\n\n\t\t/**\n\t\t * Resolved formatter function for this cell, or `null`. A column's\n\t\t * `formatter` id resolves against the injected `cnFormatters` registry.\n\t\t *\n\t\t * @return {((value: unknown, row: object, property: object, options: object) => unknown)|null}\n\t\t */\n\t\tformatterFn() {\n\t\t\tconst fn = this.formatter && this.cnFormatters && this.cnFormatters[this.formatter]\n\t\t\treturn typeof fn === 'function' ? fn : null\n\t\t},\n\n\t\t/** True when an explicit formatter is in play for this cell. */\n\t\thasFormatter() {\n\t\t\treturn this.formatterFn !== null\n\t\t},\n\n\t\t/**\n\t\t * The declarative format `style`, or null when no (recognised) `format`\n\t\t * spec is set. Drives the built-in currency / duration / number / percent\n\t\t * / swatch rendering paths.\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\tformatStyle() {\n\t\t\tconst s = this.format && this.format.style\n\t\t\treturn typeof s === 'string' ? s : null\n\t\t},\n\n\t\t/** True when a built-in NUMERIC format (currency/number/percent/duration) applies. */\n\t\thasBuiltinFormat() {\n\t\t\treturn ['currency', 'number', 'percent', 'duration'].includes(this.formatStyle)\n\t\t},\n\n\t\t/** True when this cell renders as a colour swatch (`format.style:\"swatch\"`). */\n\t\tisSwatch() {\n\t\t\treturn this.formatStyle === 'swatch'\n\t\t},\n\n\t\t/**\n\t\t * The swatch colour — read from the sibling row field named by\n\t\t * `format.colorField` (default `'color'`). Returns null when no usable\n\t\t * colour string is present (the dot is then omitted).\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\tswatchColor() {\n\t\t\tif (!this.isSwatch) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst field = (this.format && this.format.colorField) || 'color'\n\t\t\tconst c = this.row && this.row[field]\n\t\t\treturn (typeof c === 'string' && c.trim() !== '') ? c : null\n\t\t},\n\n\t\tformattedValue() {\n\t\t\tif (this.formatterFn) {\n\t\t\t\ttry {\n\t\t\t\t\treturn this.formatterFn(this.value, this.row, this.property, this.formatterOptions || undefined)\n\t\t\t\t} catch (e) {\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.warn(`[CnCellRenderer] formatter \"${this.formatter}\" threw; falling back`, e)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.hasBuiltinFormat) {\n\t\t\t\treturn this.applyBuiltinFormat()\n\t\t\t}\n\t\t\treturn formatValue(this.value, this.property, { truncate: this.truncate })\n\t\t},\n\n\t\trawTitle() {\n\t\t\t// Show full value on hover if it was truncated\n\t\t\tconst raw = this.value\n\t\t\tif (typeof raw === 'string' && raw.length > this.truncate) {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t\treturn undefined\n\t\t},\n\n\t\tcellClass() {\n\t\t\tconst classes = []\n\t\t\tif (this.propertyType === 'boolean') {\n\t\t\t\tclasses.push('cn-cell-renderer--boolean')\n\t\t\t}\n\t\t\tif (this.isEnum) {\n\t\t\t\tclasses.push('cn-cell-renderer--enum')\n\t\t\t}\n\t\t\tif (this.property?.format === 'date-time' || this.property?.format === 'date') {\n\t\t\t\tclasses.push('cn-cell-renderer--date')\n\t\t\t}\n\t\t\tif (this.property?.format === 'uuid') {\n\t\t\t\tclasses.push('cn-cell-renderer--uuid')\n\t\t\t}\n\t\t\tif (this.propertyType === 'integer' || this.propertyType === 'number') {\n\t\t\t\tclasses.push('cn-cell-renderer--number')\n\t\t\t}\n\t\t\treturn classes\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// Warn once per column-property-name when `widget:\"link\"` resolves\n\t\t// to neither a `route` nor an `href` — silent fallback hides\n\t\t// manifest mistakes, an every-row warn is too noisy.\n\t\t// A column with a `routeMap` has declared its targets; a row whose\n\t\t// value is not in the map (an empty requester, say) is data, not a\n\t\t// manifest mistake, so it renders as text without the warning. The\n\t\t// same holds for a declared `href` whose resolved value safeHref\n\t\t// refused: the manifest is fine, the row value is not.\n\t\tif (\n\t\t\tthis.widget === 'link'\n\t\t\t&& !this.linkRoute\n\t\t\t&& !this.widgetProps?.href\n\t\t\t&& this.widgetProps?.fallback !== 'silent'\n\t\t\t&& !(this.widgetProps?.routeMap && typeof this.widgetProps.routeMap === 'object')\n\t\t) {\n\t\t\tconst key = this.property?.title || String(this.value).slice(0, 20)\n\t\t\tif (!WARNED_LINK_KEYS.has(key)) {\n\t\t\t\tWARNED_LINK_KEYS.add(key)\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[CnCellRenderer] widget:\"link\" on \"${key}\" has no resolvable target — set widgetProps.route (page id) or widgetProps.href (URL with optional {field} placeholders); cell falls back to plain text.`)\n\t\t\t}\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Render the cell value per the declarative `format` spec (currency /\n\t\t * number / percent / duration). Mirrors CnStatWidget's formatting so a\n\t\t * KPI tile and a table column read identically. Non-numeric values fall\n\t\t * back to `formatValue`; an empty value renders an em-dash.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tapplyBuiltinFormat() {\n\t\t\tif (!this.hasValue) {\n\t\t\t\treturn '—'\n\t\t\t}\n\t\t\tconst fmt = this.format || {}\n\t\t\tif (fmt.style === 'duration') {\n\t\t\t\treturn this.formatDuration()\n\t\t\t}\n\t\t\tconst num = Number(this.value)\n\t\t\tif (!Number.isFinite(num)) {\n\t\t\t\treturn formatValue(this.value, this.property, { truncate: this.truncate })\n\t\t\t}\n\t\t\tconst decimals = Number.isFinite(fmt.decimals)\n\t\t\t\t? fmt.decimals\n\t\t\t\t: (fmt.style === 'currency' ? 2 : 0)\n\t\t\tlet body\n\t\t\tif (fmt.style === 'currency') {\n\t\t\t\tbody = new Intl.NumberFormat(undefined, {\n\t\t\t\t\tstyle: 'currency',\n\t\t\t\t\tcurrency: safeCurrencyCode(fmt.currency),\n\t\t\t\t\tminimumFractionDigits: decimals,\n\t\t\t\t\tmaximumFractionDigits: decimals,\n\t\t\t\t}).format(num)\n\t\t\t} else if (fmt.style === 'percent') {\n\t\t\t\t// Values are stored as the literal percent (83.3), not a 0–1 ratio.\n\t\t\t\tbody = new Intl.NumberFormat(undefined, {\n\t\t\t\t\tminimumFractionDigits: decimals,\n\t\t\t\t\tmaximumFractionDigits: decimals,\n\t\t\t\t}).format(num) + '%'\n\t\t\t} else {\n\t\t\t\tbody = new Intl.NumberFormat(undefined, {\n\t\t\t\t\tminimumFractionDigits: decimals,\n\t\t\t\t\tmaximumFractionDigits: decimals,\n\t\t\t\t}).format(num)\n\t\t\t}\n\t\t\treturn `${fmt.prefix || ''}${body}${fmt.suffix || ''}`\n\t\t},\n\n\t\t/**\n\t\t * Render a numeric duration compactly (`1u 23m`, `45m 10s`, `12s`). The\n\t\t * raw value is seconds unless `format.unit` is `'milliseconds'` /\n\t\t * `'minutes'` / `'hours'`. The hour suffix is `u` (uur) to read naturally\n\t\t * under NL theming while staying digit-led for other locales.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tformatDuration() {\n\t\t\tconst fmt = this.format || {}\n\t\t\tlet secs = Number(this.value)\n\t\t\tif (!Number.isFinite(secs)) {\n\t\t\t\treturn formatValue(this.value, this.property, { truncate: this.truncate })\n\t\t\t}\n\t\t\tif (fmt.unit === 'milliseconds') {\n\t\t\t\t// Sub-second durations are the common case for a millisecond\n\t\t\t\t// field (request/step timings), and they would all collapse to\n\t\t\t\t// the `0s` floor below — so render them in ms directly.\n\t\t\t\tconst ms = Math.round(secs)\n\t\t\t\tif (Math.abs(ms) < 1000) {\n\t\t\t\t\treturn `${fmt.prefix || ''}${ms}ms${fmt.suffix || ''}`\n\t\t\t\t}\n\t\t\t\tsecs = ms / 1000\n\t\t\t} else if (fmt.unit === 'minutes') {\n\t\t\t\tsecs *= 60\n\t\t\t} else if (fmt.unit === 'hours') {\n\t\t\t\tsecs *= 3600\n\t\t\t}\n\t\t\tsecs = Math.round(secs)\n\t\t\tconst sign = secs < 0 ? '-' : ''\n\t\t\tsecs = Math.abs(secs)\n\t\t\tconst h = Math.floor(secs / 3600)\n\t\t\tconst m = Math.floor((secs % 3600) / 60)\n\t\t\tconst s = secs % 60\n\t\t\tconst parts = []\n\t\t\tif (h > 0) {\n\t\t\t\tparts.push(`${h}u`)\n\t\t\t}\n\t\t\tif (m > 0) {\n\t\t\t\tparts.push(`${m}m`)\n\t\t\t}\n\t\t\tif (s > 0 && h === 0) {\n\t\t\t\tparts.push(`${s}s`)\n\t\t\t}\n\t\t\tif (parts.length === 0) {\n\t\t\t\tparts.push('0s')\n\t\t\t}\n\t\t\treturn `${fmt.prefix || ''}${sign}${parts.join(' ')}${fmt.suffix || ''}`\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-cell-renderer--uuid {\n\tfont-family: monospace;\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cell-renderer--number {\n\tfont-variant-numeric: tabular-nums;\n}\n\n.cn-cell-renderer--date {\n\twhite-space: nowrap;\n}\n\n.cn-cell-renderer__dash {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cell-renderer__avatar {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n\tmax-width: 100%;\n\tvertical-align: middle;\n}\n\n.cn-cell-renderer__avatar-picture {\n\tdisplay: inline-flex;\n\tflex: none;\n}\n\n.cn-cell-renderer__avatar-initials {\n\tdisplay: inline-flex;\n\tflex: none;\n\talign-items: center;\n\tjustify-content: center;\n\tborder-radius: 50%;\n\tbackground-color: var(--color-primary-element-light);\n\tcolor: var(--color-primary-element-light-text);\n\tfont-size: 0.75em;\n\tfont-weight: 700;\n\tline-height: 1;\n}\n\n.cn-cell-renderer__avatar-name {\n\tmin-width: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n/* A date that matched a rule is also set in a heavier weight, so the signal\n does not rest on colour alone. */\n.cn-cell-renderer__date--success,\n.cn-cell-renderer__date--warning,\n.cn-cell-renderer__date--error {\n\tfont-weight: 600;\n}\n\n.cn-cell-renderer__date--success {\n\tcolor: var(--color-text-success, var(--color-success-text));\n}\n\n.cn-cell-renderer__date--warning {\n\tcolor: var(--color-text-warning, var(--color-warning-text));\n}\n\n.cn-cell-renderer__date--error {\n\tcolor: var(--color-text-error, var(--color-error-text));\n}\n\n.cn-cell-renderer__icon--success {\n\tcolor: var(--color-success);\n}\n\n.cn-cell-renderer__swatch-wrap {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-cell-renderer__swatch-dot {\n\tdisplay: inline-block;\n\twidth: 12px;\n\theight: 12px;\n\tborder-radius: 50%;\n\tflex-shrink: 0;\n\tborder: 1px solid var(--color-border-dark);\n}\n</style>\n"],"names":["CnStatusBadge","CnFkResolveCell"],"mappings":";;;;;;;;;;;AAmLA;;;;;AAKA,MAAM,gBAAiB,GAAE,IAAI,GAAG,GAAC;;AAEjC;;AAEC;;AAEA;;AAEA;AACA;AACA;AACA;;AAED,aAAe;AACd,CAAA,IAAI,EAAE,gBAAgB;;CAEtB,UAAU,EAAE;iBACXA,UAAa;AACb,mBAAAC,YAAe;AACf,EAAA,SAAS;AACT,EAAA,QAAQ;EACR,UAAU;EACV;;AAED,CAAA,MAAM,EAAE;AACP;;;;;;AAMA,EAAA,YAAY,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG;AACrC;;;;;;AAMA,EAAA,aAAa,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG;AACtC;;;;;;AAMA,EAAA,WAAW,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,KAAK,KAAK;EAC5C;;AAED,CAAA,KAAK,EAAE;AACN;AACA,EAAA,KAAK,EAAE;AACN,GAAA,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC;GAC9C,OAAO,EAAE,IAAI;GACb;;;AAGD,EAAA,QAAQ,EAAE;GACT,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,OAAO,EAAE,CAAC;GACnB;;AAED;;;;;AAKC;;EAED,SAAS,EAAE;GACV,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,IAAI;GACb;;AAED;;;;;;AAMA,EAAA,gBAAgB,EAAE;GACjB,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,IAAI;GACb;;AAED;;;;;;;;AAQC;;;;;;;AAOA;;AAED,EAAA,MAAM,EAAE;GACP,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,IAAI;GACb;;AAED;;;;;;;;EAQA,WAAW,EAAE;GACZ,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,OAAO,EAAE,CAAC;GACnB;;AAED;;AAEC;AACA;;;;;AAKA;;;;AAIA;;;AAGA;;;;AAIA;AACA;;;AAGD,EAAA,MAAM,EAAE;GACP,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,IAAI;GACb;;AAED;;;AAGC;;EAED,GAAG,EAAE;GACJ,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,OAAO,EAAE,CAAC;GACnB;;AAED;AACA,EAAA,QAAQ,EAAE;GACT,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,GAAG;GACZ;;AAED;;;;AAIC;;AAED,EAAA,MAAM,EAAE;GACP,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,IAAI;GACb;EACD;;AAED,CAAA,QAAQ,EAAE;AACT,EAAA,YAAY,GAAG;AACd,GAAA,OAAO,IAAI,CAAC,QAAQ,EAAE,SAAS,OAAO,IAAI,CAAC,KAAK,CAAA;GAChD;;AAED,EAAA,MAAM,GAAG;GACR,OAAO,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAG,IAAK,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAO,GAAE,CAAC,CAAA;GAC9D;;AAED;;;;AAIC;;EAED,SAAS,GAAG;GACX,MAAM,GAAI,GAAE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAA;AAC7B,GAAA,MAAM,SAAS,CAAC,IAAI,CAAC,QAAS,KAAI,IAAI,CAAC,QAAQ,CAAC,UAAS,IAAK,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,KAAK,GAAC;AACnG,GAAA,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAA,IAAK,GAAG,CAAA;GAC1C;;AAED;AACA,EAAA,MAAM,GAAG;AACR,GAAA,OAAO,IAAI,CAAC,QAAQ,EAAE,MAAO,KAAI,WAAY,IAAG,IAAI,CAAC,QAAQ,EAAE,MAAO,KAAI,MAAK;GAC/E;;AAED;;;AAGC;AACA;;AAED,EAAA,aAAa,GAAG;AACf,GAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;AACnB,IAAA,OAAO,IAAG;AACX,IAAA;GACA,MAAM,IAAK,GAAE,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,EAAA;AAChC,GAAA,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAE,GAAE,OAAO,IAAG;GAChD;;AAED;;;;AAIC;AACA;AACA;;AAED,EAAA,YAAY,GAAG;AACd,GAAA,MAAM,KAAM,GAAE,IAAI,CAAC,WAAU,IAAK,GAAC;AACnC,GAAA,IAAI,EAAC,GAAI,GAAC;GACV,IAAI,OAAO,KAAK,CAAC,SAAU,KAAI,QAAS,IAAG,KAAK,CAAC,SAAU,KAAI,EAAE,EAAE;AAClE,IAAA,KAAK,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,SAAS,EAAA;IAC9C,MAAK,IAAI,KAAK,CAAC,IAAG,KAAM,IAAI,EAAE;IAC/B,EAAC,GAAI,IAAI,CAAC,MAAI;AACf,IAAA;GACA,OAAO,CAAC,OAAO,EAAG,KAAI,QAAO,IAAK,OAAO,EAAC,KAAM,QAAQ,IAAI,MAAM,CAAC,EAAE,CAAE,GAAE,EAAC;GAC1E;;AAED;;;AAGC;AACA;AACA;;EAED,UAAU,GAAG;AACZ,GAAA,MAAM,KAAM,GAAE,IAAI,CAAC,WAAU,IAAK,GAAC;GACnC,IAAI,OAAO,KAAK,CAAC,SAAU,KAAI,QAAS,IAAG,KAAK,CAAC,SAAU,KAAI,EAAE,EAAE;IAClE,MAAM,IAAK,GAAE,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,SAAS,EAAA;AACvD,IAAA,IAAI,OAAO,IAAG,KAAM,QAAO,IAAK,SAAS,EAAE,EAAE;AAC5C,KAAA,OAAO,IAAG;AACX,KAAA;AACD,IAAA;GACA,IAAI,CAAC,IAAI,CAAC,QAAO,IAAK,OAAO,IAAI,CAAC,KAAI,KAAM,QAAQ,EAAE;AACrD,IAAA,OAAO,EAAC;AACT,IAAA;GACA,OAAO,MAAM,CAAC,IAAI,CAAC,YAAW,GAAI,IAAI,CAAC,cAAa,GAAI,IAAI,CAAC,KAAK,CAAA;GAClE;;AAED;;AAEC;AACA;AACA;AACA;;AAED,EAAA,cAAc,GAAG;AAChB,GAAA,MAAM,KAAI,GAAI,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAK,KAAI,EAAE,EAAA;AAC9E,GAAA,IAAI,KAAK,CAAC,MAAK,KAAM,CAAC,EAAE;AACvB,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,MAAM,KAAI,GAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,IAAG,GAAC;AAC1C,GAAA,MAAM,IAAG,GAAI,KAAK,CAAC,MAAK,GAAI,CAAE,IAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAO,GAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,IAAG,EAAE,IAAI,GAAC;AAClF,GAAA,OAAO,CAAC,KAAM,GAAE,IAAI,EAAE,iBAAiB,EAAC;GACxC;;AAED;AACC;AACA;AACA;AACA;;EAED,UAAU,GAAG;GACZ,MAAM,IAAG,GAAI,MAAM,CAAC,IAAI,CAAC,WAAU,IAAK,IAAI,CAAC,WAAW,CAAC,IAAI,EAAA;AAC7D,GAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAK,GAAE,IAAI,IAAG,GAAI,EAAC;GACnD;;AAED;;AAEC;AACA;AACA;AACA;;EAED,WAAW,GAAG;GACb,MAAM,KAAM,GAAE,IAAI,CAAC,WAAY,IAAG,IAAI,CAAC,WAAW,CAAC,YAAU;GAC7D,MAAM,UAAU,kBAAkB,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAA;AACpD,GAAA,OAAO,OAAQ,KAAI,SAAU,GAAE,EAAC,GAAI,OAAM;GAC1C;;AAED;;AAEC;AACA;AACA;;EAED,SAAS,GAAG;AACX,GAAA,MAAM,IAAK,GAAE,cAAc,CAAC,IAAI,CAAC,KAAK,EAAA;AACtC,GAAA,IAAI,IAAG,KAAM,IAAI,EAAE;AAClB,IAAA,OAAO,EAAC;AACT,IAAA;GACA,MAAM,OAAQ,GAAE,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAU,GAAA;AAClE,GAAA,IAAI,OAAK;AACT,GAAA,IAAI;IACH,MAAK,GAAI,kBAAkB,GAAC;AAC7B,IAAA,CAAE,MAAM;AACP,IAAA,MAAO,GAAE,UAAQ;AAClB,IAAA;AACA,GAAA,IAAI;AACH,IAAA,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC,MAAK,IAAK,SAAS,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,IAAI,CAAA;AACzE,IAAA,CAAE,MAAM;IACP,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,IAAI,CAAA;AAC/D,IAAA;GACA;;;AAGD,EAAA,KAAK,GAAG;AACP,GAAA,OAAO,IAAI,CAAC,QAAQ,EAAE,MAAK,KAAM,KAAM,IAAG,IAAI,CAAC,QAAQ,EAAE,MAAO,KAAI,KAAI;GACxE;;AAED;;;AAGC;AACA;;AAED,EAAA,OAAO,GAAG;AACT,GAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;AACnB,IAAA,OAAO,IAAG;AACX,IAAA;AACA,GAAA,OAAO,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;GAClC;;AAED;EACA,QAAQ,GAAG;AACV,GAAA,OAAO,IAAI,CAAC,KAAM,KAAI,IAAG,IAAK,IAAI,CAAC,KAAI,KAAM,SAAQ,IAAK,IAAI,CAAC,KAAI,KAAM,EAAC;GAC1E;;AAED;;;;;AAKC;AACA;;AAED,EAAA,eAAe,GAAG;AACjB,GAAA,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;AACjB,IAAA,OAAO,IAAG;AACX,IAAA;GACA,MAAM,CAAA,GAAI,IAAI,CAAC,aAAc,IAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAA;GAC9D,OAAO,KAAK,IAAG;GACf;;AAED;AACA,EAAA,YAAY,GAAG;GACd,OAAO,CAAC,IAAI,CAAC,WAAY,IAAG,IAAI,CAAC,WAAW,CAAC,OAAO,KAAK,SAAQ;GACjE;;AAED;;AAEC;;;AAGA;AACA;;AAED,EAAA,aAAa,GAAG;GACf,OAAO,CAAC,IAAI,CAAC,WAAY,IAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,KAAK,IAAG;GAC7D;;AAED;;AAEC;;AAEA;AACA;;AAED,EAAA,YAAY,GAAG;AACd,GAAA,OAAO,CAAC,IAAI,CAAC,QAAO,KAAM,IAAI,CAAC,QAAQ,CAAC,QAAS,IAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,KAAK,IAAG;GACzF;;AAED;;;AAGC;;;;;;;;;;AAUA;;;AAGD,EAAA,aAAa,GAAG;GACf,MAAM,KAAK,IAAI,CAAC,WAAU,IAAK,GAAC;AAChC,GAAA,MAAM,GAAI,GAAE,EAAE,CAAC,SAAO;AACtB,GAAA,IAAI,OAAO,EAAE,CAAC,eAAe,QAAS,IAAG,EAAE,CAAC,eAAe,MAAM,GAAE,IAAK,OAAO,QAAQ,QAAQ,EAAE;IAChG,MAAM,GAAE,GAAI,IAAI,CAAC,GAAI,GAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAA,GAAI,UAAQ;AACzD,IAAA,IAAI,GAAI,KAAI,SAAU,IAAG,GAAE,KAAM,IAAK,IAAG,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE;KACzE,MAAM,MAAK,GAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EAAA;AAC9B,KAAA,IAAI,OAAO,WAAW,QAAS,IAAG,MAAO,KAAI,EAAE,EAAE;MAChD,OAAO,MAAK;AACb,MAAA;AACD,KAAA;AACD,IAAA;GACA,OAAO,CAAC,OAAO,EAAE,CAAC,KAAM,KAAI,QAAO,IAAK,EAAE,CAAC,KAAI,KAAM,EAAE,IAAI,EAAE,CAAC,KAAM,GAAE,IAAG;GACzE;;AAED;;;;;;;;AAQC;AACA;AACA;;EAED,SAAS,GAAG;GACX,IAAI,IAAI,CAAC,MAAO,KAAI,MAAM,EAAE;AAC3B,IAAA,OAAO,IAAG;AACX,IAAA;GACA,MAAM,KAAI,GAAI,IAAI,CAAC,cAAY;GAC/B,IAAI,CAAC,KAAK,EAAE;AACX,IAAA,OAAO,IAAG;AACX,IAAA;GACA,MAAM,QAAO,GAAI,CAAC,IAAI,CAAC,WAAU,IAAK,IAAI,CAAC,WAAW,CAAC,MAAM;OACzD,EAAE,EAAE,EAAE,IAAI,CAAC,MAAK,IAAK,IAAK,GAAA;GAC9B,MAAM,MAAO,GAAE,GAAC;GAChB,KAAK,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAE,IAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;IAC9D,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAA;AACnD,IAAA;GACA,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAO,EAAA;GAC7B;;AAED;;;;;;;AAOC;AACA;AACA;;EAED,QAAQ,GAAG;GACV,IAAI,IAAI,CAAC,MAAO,KAAI,MAAM,EAAE;AAC3B,IAAA,OAAO,IAAG;AACX,IAAA;GACA,MAAM,OAAO,IAAI,CAAC,WAAY,IAAG,IAAI,CAAC,WAAW,CAAC,KAAG;AACrD,GAAA,IAAI,CAAC,IAAI,EAAE;AACV,IAAA,OAAO,IAAG;AACX,IAAA;AACA,GAAA,MAAM,QAAO,GAAI,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC,GAAI,IAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAE,KAAI,IAAK,IAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,SAAU,GAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAE,GAAE,EAAE,EAAA;;;;;;AAM9J;GACA,MAAM,IAAG,GAAI,QAAQ,CAAC,QAAQ,EAAA;GAC9B,OAAO,IAAG,KAAM,GAAI,GAAE,IAAK,GAAE,IAAG;GAChC;;AAED;;AAEC;AACA;AACA;;EAED,WAAW,GAAG;AACb,GAAA,MAAM,EAAG,GAAE,IAAI,CAAC,SAAQ,IAAK,IAAI,CAAC,YAAW,IAAK,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAA;AAClF,GAAA,OAAO,OAAO,EAAC,KAAM,UAAW,GAAE,EAAC,GAAI,IAAG;GAC1C;;;AAGD,EAAA,YAAY,GAAG;GACd,OAAO,IAAI,CAAC,WAAY,KAAI,IAAG;GAC/B;;AAED;;;;AAIC;AACA;;EAED,WAAW,GAAG;AACb,GAAA,MAAM,IAAI,IAAI,CAAC,MAAK,IAAK,IAAI,CAAC,MAAM,CAAC,MAAI;AACzC,GAAA,OAAO,OAAO,CAAA,KAAM,QAAS,GAAE,CAAE,GAAE,IAAG;GACtC;;AAED;AACA,EAAA,gBAAgB,GAAG;AAClB,GAAA,OAAO,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAA;GAC9E;;AAED;EACA,QAAQ,GAAG;AACV,GAAA,OAAO,IAAI,CAAC,WAAU,KAAM,QAAO;GACnC;;AAED;;;AAGC;AACA;AACA;;EAED,WAAW,GAAG;AACb,GAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;AACnB,IAAA,OAAO,IAAG;AACX,IAAA;GACA,MAAM,KAAM,GAAE,CAAC,IAAI,CAAC,MAAO,IAAG,IAAI,CAAC,MAAM,CAAC,UAAU,KAAK,QAAM;AAC/D,GAAA,MAAM,CAAE,GAAE,IAAI,CAAC,GAAE,IAAK,IAAI,CAAC,GAAG,CAAC,KAAK,EAAA;GACpC,OAAO,CAAC,OAAO,CAAA,KAAM,QAAS,IAAG,CAAC,CAAC,IAAI,EAAC,KAAM,EAAE,IAAI,CAAE,GAAE,IAAG;GAC3D;;AAED,EAAA,cAAc,GAAG;AAChB,GAAA,IAAI,IAAI,CAAC,WAAW,EAAE;AACrB,IAAA,IAAI;AACH,KAAA,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,gBAAe,IAAK,SAAS,CAAA;AAChG,KAAA,CAAE,OAAO,CAAC,EAAE;AACX;AACA,KAAA,OAAO,CAAC,IAAI,CAAC,CAAC,4BAA4B,EAAE,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,EAAE,CAAC,EAAA;AACrF,KAAA;AACD,IAAA;GACA,IAAI,IAAI,CAAC,gBAAgB,EAAE;AAC1B,IAAA,OAAO,IAAI,CAAC,kBAAkB,EAAC;AAChC,IAAA;AACA,GAAA,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAA;GACzE;;EAED,QAAQ,GAAG;AACV;AACA,GAAA,MAAM,GAAI,GAAE,IAAI,CAAC,MAAI;GACrB,IAAI,OAAO,GAAE,KAAM,QAAO,IAAK,GAAG,CAAC,MAAO,GAAE,IAAI,CAAC,QAAQ,EAAE;AAC1D,IAAA,OAAO,GAAE;AACV,IAAA;AACA,GAAA,OAAO,SAAQ;GACf;;EAED,SAAS,GAAG;AACX,GAAA,MAAM,OAAQ,GAAE,GAAC;AACjB,GAAA,IAAI,IAAI,CAAC,YAAW,KAAM,SAAS,EAAE;AACpC,IAAA,OAAO,CAAC,IAAI,CAAC,2BAA2B,EAAA;AACzC,IAAA;AACA,GAAA,IAAI,IAAI,CAAC,MAAM,EAAE;AAChB,IAAA,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAA;AACtC,IAAA;AACA,GAAA,IAAI,IAAI,CAAC,QAAQ,EAAE,WAAW,WAAY,IAAG,IAAI,CAAC,QAAQ,EAAE,MAAO,KAAI,MAAM,EAAE;AAC9E,IAAA,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAA;AACtC,IAAA;AACA,GAAA,IAAI,IAAI,CAAC,QAAQ,EAAE,WAAW,MAAM,EAAE;AACrC,IAAA,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAA;AACtC,IAAA;AACA,GAAA,IAAI,IAAI,CAAC,YAAa,KAAI,SAAU,IAAG,IAAI,CAAC,YAAW,KAAM,QAAQ,EAAE;AACtE,IAAA,OAAO,CAAC,IAAI,CAAC,0BAA0B,EAAA;AACxC,IAAA;GACA,OAAO,OAAM;GACb;EACD;;AAED,CAAA,OAAO,GAAG;;;AAGT;;;;;AAKA;AACA,EAAA;AACC,GAAA,IAAI,CAAC,MAAK,KAAM,MAAK;AACrB,MAAG,CAAC,IAAI,CAAC,SAAQ;MACd,CAAC,IAAI,CAAC,WAAW,EAAE,IAAG;AACzB,MAAG,IAAI,CAAC,WAAW,EAAE,QAAS,KAAI,QAAO;AACzC,MAAG,EAAE,IAAI,CAAC,WAAW,EAAE,YAAY,OAAO,IAAI,CAAC,WAAW,CAAC,aAAa,QAAQ,CAAA;IAC/E;GACD,MAAM,GAAE,GAAI,IAAI,CAAC,QAAQ,EAAE,KAAM,IAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAA;AAClE,GAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;IAC/B,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAA;AACxB;AACA,IAAA,OAAO,CAAC,IAAI,CAAC,CAAC,mCAAmC,EAAE,GAAG,CAAC,yJAAyJ,CAAC,EAAA;AAClN,IAAA;AACD,GAAA;EACA;;AAED,CAAA,OAAO,EAAE;AACR;;;;;AAKC;AACA;;AAED,EAAA,kBAAkB,GAAG;AACpB,GAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;AACnB,IAAA,OAAO,GAAE;AACV,IAAA;GACA,MAAM,GAAE,GAAI,IAAI,CAAC,MAAK,IAAK,GAAC;GAC5B,IAAI,GAAG,CAAC,KAAM,KAAI,UAAU,EAAE;IAC7B,OAAO,IAAI,CAAC,cAAc,EAAC;AAC5B,IAAA;GACA,MAAM,GAAI,GAAE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAA;GAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;AAC1B,IAAA,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAA;AAC1E,IAAA;AACA,GAAA,MAAM,WAAW,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAA;MAC1C,GAAG,CAAC,QAAO;OACV,GAAG,CAAC,KAAM,KAAI,UAAW,GAAE,CAAE,GAAE,CAAC,EAAA;AACpC,GAAA,IAAI,KAAG;GACP,IAAI,GAAG,CAAC,KAAM,KAAI,UAAU,EAAE;AAC7B,IAAA,IAAK,GAAE,IAAI,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE;AACvC,KAAA,KAAK,EAAE,UAAU;AACjB,KAAA,QAAQ,EAAE,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC;AACxC,KAAA,qBAAqB,EAAE,QAAQ;AAC/B,KAAA,qBAAqB,EAAE,QAAQ;KAC/B,CAAC,CAAC,MAAM,CAAC,GAAG,EAAA;AACd,IAAA,MAAO,IAAI,GAAG,CAAC,KAAI,KAAM,SAAS,EAAE;;AAEnC,IAAA,IAAK,GAAE,IAAI,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE;AACvC,KAAA,qBAAqB,EAAE,QAAQ;AAC/B,KAAA,qBAAqB,EAAE,QAAQ;AAChC,KAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAE,GAAE,IAAE;IAClB,MAAK;AACN,IAAA,IAAK,GAAE,IAAI,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE;AACvC,KAAA,qBAAqB,EAAE,QAAQ;AAC/B,KAAA,qBAAqB,EAAE,QAAQ;KAC/B,CAAC,CAAC,MAAM,CAAC,GAAG,EAAA;AACd,IAAA;GACA,OAAO,CAAC,EAAE,GAAG,CAAC,MAAO,IAAG,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;GACrD;;AAED;;;;;AAKC;AACA;;AAED,EAAA,cAAc,GAAG;GAChB,MAAM,GAAE,GAAI,IAAI,CAAC,MAAK,IAAK,GAAC;GAC5B,IAAI,IAAK,GAAE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAA;GAC5B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;AAC3B,IAAA,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAA;AAC1E,IAAA;GACA,IAAI,GAAG,CAAC,IAAK,KAAI,cAAc,EAAE;;;AAGhC;IACA,MAAM,EAAG,GAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAA;AAC1B,IAAA,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAE,GAAE,IAAI,EAAE;KACxB,OAAO,CAAC,EAAE,GAAG,CAAC,MAAO,IAAG,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;AACtD,KAAA;IACA,IAAK,GAAE,EAAG,GAAE,KAAG;IACd,MAAK,IAAI,GAAG,CAAC,IAAG,KAAM,SAAS,EAAE;AAClC,IAAA,IAAK,IAAG,GAAC;IACR,MAAK,IAAI,GAAG,CAAC,IAAG,KAAM,OAAO,EAAE;AAChC,IAAA,IAAG,IAAK,KAAG;AACZ,IAAA;AACA,GAAA,IAAG,GAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAA;GACtB,MAAM,OAAO,IAAG,GAAI,CAAE,GAAE,GAAE,GAAI,GAAC;AAC/B,GAAA,IAAG,GAAI,IAAI,CAAC,GAAG,CAAC,IAAI,EAAA;GACpB,MAAM,CAAA,GAAI,IAAI,CAAC,KAAK,CAAC,IAAG,GAAI,IAAI,EAAA;AAChC,GAAA,MAAM,CAAE,GAAE,IAAI,CAAC,KAAK,CAAC,CAAC,IAAK,GAAE,IAAI,IAAI,EAAE,EAAA;GACvC,MAAM,CAAA,GAAI,IAAK,GAAE,GAAC;GAClB,MAAM,KAAM,GAAE,GAAC;AACf,GAAA,IAAI,CAAE,GAAE,CAAC,EAAE;AACV,IAAA,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAA;AACnB,IAAA;AACA,GAAA,IAAI,CAAE,GAAE,CAAC,EAAE;AACV,IAAA,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAA;AACnB,IAAA;AACA,GAAA,IAAI,CAAE,GAAE,CAAE,IAAG,CAAA,KAAM,CAAC,EAAE;AACrB,IAAA,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAA;AACnB,IAAA;AACA,GAAA,IAAI,KAAK,CAAC,MAAK,KAAM,CAAC,EAAE;AACvB,IAAA,KAAK,CAAC,IAAI,CAAC,IAAI,EAAA;AAChB,IAAA;AACA,GAAA,OAAO,CAAC,EAAE,GAAG,CAAC,MAAK,IAAK,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,MAAO,IAAG,EAAE,CAAC,CAAA;GACvE;EACD;AACF;;;;"}
|