@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
|
@@ -21,7 +21,11 @@
|
|
|
21
21
|
- Content sections via slots
|
|
22
22
|
-->
|
|
23
23
|
<template>
|
|
24
|
-
<div
|
|
24
|
+
<div
|
|
25
|
+
class="cn-detail-page"
|
|
26
|
+
:class="{ 'cn-detail-page--with-side': showsSideColumn }"
|
|
27
|
+
data-testid="cn-detail-page"
|
|
28
|
+
:style="{ maxWidth: maxWidth }">
|
|
25
29
|
<!-- Skip link to the page's primary action. Off screen until it takes
|
|
26
30
|
focus, so it costs a sighted mouse user nothing and saves a
|
|
27
31
|
keyboard user every widget on the page. Targets the action the
|
|
@@ -29,7 +33,7 @@
|
|
|
29
33
|
that lands somewhere arbitrary is worse than none, because it is
|
|
30
34
|
the one link a keyboard user is told to trust. -->
|
|
31
35
|
<a
|
|
32
|
-
v-if="
|
|
36
|
+
v-if="effectivePrimaryLabel && !objectNotFound"
|
|
33
37
|
:href="`#${primaryActionAnchorId}`"
|
|
34
38
|
class="cn-detail-page__skip-link"
|
|
35
39
|
data-testid="cn-detail-page-skip-link"
|
|
@@ -72,6 +76,23 @@
|
|
|
72
76
|
class="cn-detail-page__icon" />
|
|
73
77
|
</slot>
|
|
74
78
|
<div class="cn-detail-page__header-text">
|
|
79
|
+
<!-- Type and status pills (manifest `config.typePill` /
|
|
80
|
+
`config.statusPill`). What kind of record this is and
|
|
81
|
+
where it stands, readable before the title. Nothing
|
|
82
|
+
renders for a page that declares neither. -->
|
|
83
|
+
<div
|
|
84
|
+
v-if="headerPills.length > 0"
|
|
85
|
+
class="cn-detail-page__pills"
|
|
86
|
+
data-testid="cn-detail-page-pills">
|
|
87
|
+
<CnStatusBadge
|
|
88
|
+
v-for="pill in headerPills"
|
|
89
|
+
:key="pill.key"
|
|
90
|
+
:label="pill.label"
|
|
91
|
+
:colorKey="pill.colorKey"
|
|
92
|
+
:colorMap="pill.colorMap"
|
|
93
|
+
:variant="pill.variant"
|
|
94
|
+
:data-testid="`cn-detail-page-pill-${pill.key}`" />
|
|
95
|
+
</div>
|
|
75
96
|
<!-- Type name as a small eyebrow above the record name, so
|
|
76
97
|
the header reads "the record" not "the kind of record"
|
|
77
98
|
(ADR-062). Only shown once the object resolves to a
|
|
@@ -82,7 +103,10 @@
|
|
|
82
103
|
data-testid="cn-detail-page-type-eyebrow">
|
|
83
104
|
{{ typeEyebrow }}
|
|
84
105
|
</p>
|
|
85
|
-
<h2
|
|
106
|
+
<h2
|
|
107
|
+
v-if="displayTitle"
|
|
108
|
+
class="cn-detail-page__title"
|
|
109
|
+
:title="displayTitle">
|
|
86
110
|
{{ displayTitle }}
|
|
87
111
|
</h2>
|
|
88
112
|
<!--
|
|
@@ -150,14 +174,38 @@
|
|
|
150
174
|
</div>
|
|
151
175
|
<!-- The action the page declares as its primary one, and the
|
|
152
176
|
target the skip link above lands on. -->
|
|
177
|
+
<!-- With a "what now" card on the page the button lives in that
|
|
178
|
+
card, next to the checklist that explains it, and not here
|
|
179
|
+
as well: one primary button per page. -->
|
|
153
180
|
<NcButton
|
|
154
|
-
v-if="
|
|
181
|
+
v-if="effectivePrimaryLabel && !showsNextStepCard"
|
|
155
182
|
:id="primaryActionAnchorId"
|
|
156
183
|
ref="primaryActionButton"
|
|
157
184
|
variant="primary"
|
|
185
|
+
:href="primaryActionHref || undefined"
|
|
186
|
+
:target="primaryActionHref ? primaryActionTarget : undefined"
|
|
187
|
+
:disabled="primaryActionDisabled"
|
|
158
188
|
data-testid="cn-detail-page-primary-action"
|
|
159
|
-
@click="
|
|
160
|
-
{{
|
|
189
|
+
@click="onPrimaryActionClick">
|
|
190
|
+
{{ effectivePrimaryLabel }}
|
|
191
|
+
</NcButton>
|
|
192
|
+
<!-- Quick actions (manifest `config.quickActions`): the few
|
|
193
|
+
actions a handler reaches for on every record, always
|
|
194
|
+
visible. At most three, so the row cannot grow back into
|
|
195
|
+
the button wall the Actions menu replaced. -->
|
|
196
|
+
<NcButton
|
|
197
|
+
v-for="entry in quickActionEntries"
|
|
198
|
+
:key="`quick-${entry.id}`"
|
|
199
|
+
variant="secondary"
|
|
200
|
+
:href="entry.href || undefined"
|
|
201
|
+
:target="entry.href ? (entry.linkTarget || undefined) : undefined"
|
|
202
|
+
:disabled="entry.disabled"
|
|
203
|
+
:data-testid="`cn-detail-page-quick-${entry.id}`"
|
|
204
|
+
@click="onPinnedActionClick(entry)">
|
|
205
|
+
<template v-if="entry.iconName" #icon>
|
|
206
|
+
<CnIcon :name="entry.iconName" :size="20" />
|
|
207
|
+
</template>
|
|
208
|
+
{{ entry.label }}
|
|
161
209
|
</NcButton>
|
|
162
210
|
<!-- Declarative lifecycle transitions (manifest
|
|
163
211
|
`config.lifecycleActions`). Status-gated; driven by the
|
|
@@ -245,11 +293,13 @@
|
|
|
245
293
|
<!-- In-app edit button (ADR-041): icon-only, self-wires from CnAppRoot. -->
|
|
246
294
|
<CnBuildiqEditButton />
|
|
247
295
|
<CnActionsMenu
|
|
248
|
-
:showRefresh="
|
|
296
|
+
:showRefresh="menuShowsRefresh"
|
|
249
297
|
:refreshing="effectiveRefreshing"
|
|
250
|
-
:showRequestFeature="showRequestFeature"
|
|
251
|
-
:showReportBug="showReportBug"
|
|
252
|
-
:showDocumentation="showDocumentation"
|
|
298
|
+
:showRequestFeature="showRequestFeature && menuShowsHelpLinks"
|
|
299
|
+
:showReportBug="showReportBug && menuShowsHelpLinks"
|
|
300
|
+
:showDocumentation="showDocumentation && menuShowsHelpLinks"
|
|
301
|
+
:actionsMenuLabel="actionsMenuName"
|
|
302
|
+
:hasPrimaryItems="headerMenuGroups.length > 0"
|
|
253
303
|
:documentationUrl="documentationUrl"
|
|
254
304
|
:docsAnchor="resolvedPageId"
|
|
255
305
|
:documentationLabel="documentationLabel || undefined"
|
|
@@ -260,40 +310,56 @@
|
|
|
260
310
|
testidBase="cn-detail-page"
|
|
261
311
|
@refresh="onHeaderRefresh"
|
|
262
312
|
@requestFeature="onHeaderRequestFeature">
|
|
263
|
-
<template v-if="
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
:
|
|
274
|
-
:
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
</
|
|
293
|
-
|
|
294
|
-
|
|
313
|
+
<template v-if="headerMenuGroups.length" #primary-items>
|
|
314
|
+
<!-- One block per group. A page that groups nothing has
|
|
315
|
+
a single captionless group, and renders exactly the
|
|
316
|
+
flat list it always did. -->
|
|
317
|
+
<template v-for="(group, groupIndex) in headerMenuGroups">
|
|
318
|
+
<NcActionSeparator
|
|
319
|
+
v-if="groupIndex > 0"
|
|
320
|
+
:key="`${group.key}-separator`" />
|
|
321
|
+
<NcActionCaption
|
|
322
|
+
v-if="group.label"
|
|
323
|
+
:key="`${group.key}-caption`"
|
|
324
|
+
:name="group.label"
|
|
325
|
+
:data-testid="`cn-detail-page-action-group-${group.key}`" />
|
|
326
|
+
<template v-for="entry in group.entries">
|
|
327
|
+
<!-- An action that goes to a URL is a LINK. The browser
|
|
328
|
+
then supplies middle-click, "open in new tab" and the
|
|
329
|
+
semantics assistive tech announces, none of which a
|
|
330
|
+
click handler can. -->
|
|
331
|
+
<NcActionLink
|
|
332
|
+
v-if="entry.href"
|
|
333
|
+
:key="`${entry.id}-link`"
|
|
334
|
+
:href="entry.href"
|
|
335
|
+
:target="entry.linkTarget || undefined"
|
|
336
|
+
:data-testid="entry.testid">
|
|
337
|
+
<template v-if="entry.iconName || entry.iconClass" #icon>
|
|
338
|
+
<CnIcon v-if="entry.iconName" :name="entry.iconName" :size="20" />
|
|
339
|
+
<span v-else :class="entry.iconClass" />
|
|
340
|
+
</template>
|
|
341
|
+
{{ entry.label }}
|
|
342
|
+
</NcActionLink>
|
|
343
|
+
<NcActionButton
|
|
344
|
+
v-else
|
|
345
|
+
:key="entry.id"
|
|
346
|
+
:data-testid="entry.testid"
|
|
347
|
+
:disabled="entry.disabled"
|
|
348
|
+
:aria-pressed="entry.pressed === null ? null : String(entry.pressed)"
|
|
349
|
+
:closeAfterClick="true"
|
|
350
|
+
@click="entry.run()">
|
|
351
|
+
<template v-if="entry.iconName || entry.iconClass" #icon>
|
|
352
|
+
<CnIcon v-if="entry.iconName" :name="entry.iconName" :size="20" />
|
|
353
|
+
<span v-else :class="entry.iconClass" />
|
|
354
|
+
</template>
|
|
355
|
+
{{ entry.label }}
|
|
356
|
+
</NcActionButton>
|
|
357
|
+
</template>
|
|
295
358
|
</template>
|
|
296
|
-
|
|
359
|
+
<!-- Divides the page's own actions from the help links
|
|
360
|
+
below. With the help links switched off there is
|
|
361
|
+
nothing below to divide from. -->
|
|
362
|
+
<NcActionSeparator v-if="menuShowsHelpLinks" />
|
|
297
363
|
</template>
|
|
298
364
|
</CnActionsMenu>
|
|
299
365
|
</div>
|
|
@@ -326,6 +392,33 @@
|
|
|
326
392
|
{{ editLockRefusal }}
|
|
327
393
|
</p>
|
|
328
394
|
|
|
395
|
+
<!-- The "what now" card (manifest `config.nextStep`): what this stage
|
|
396
|
+
still needs, next to the button that takes the step. Above the
|
|
397
|
+
body and the side column, at the full width of both. Hidden for a
|
|
398
|
+
stage that declares no checklist. -->
|
|
399
|
+
<CnNextStepCard
|
|
400
|
+
v-if="showsNextStepCard"
|
|
401
|
+
class="cn-detail-page__next-step"
|
|
402
|
+
:title="nextStepCard.title"
|
|
403
|
+
titleTag="h3"
|
|
404
|
+
:items="nextStepCard.items"
|
|
405
|
+
:after="nextStepCard.after"
|
|
406
|
+
data-testid="cn-detail-page-next-step">
|
|
407
|
+
<template v-if="effectivePrimaryLabel" #action>
|
|
408
|
+
<NcButton
|
|
409
|
+
:id="primaryActionAnchorId"
|
|
410
|
+
ref="primaryActionButton"
|
|
411
|
+
variant="primary"
|
|
412
|
+
:href="primaryActionHref || undefined"
|
|
413
|
+
:target="primaryActionHref ? primaryActionTarget : undefined"
|
|
414
|
+
:disabled="primaryActionDisabled"
|
|
415
|
+
data-testid="cn-detail-page-primary-action"
|
|
416
|
+
@click="onPrimaryActionClick">
|
|
417
|
+
{{ effectivePrimaryLabel }}
|
|
418
|
+
</NcButton>
|
|
419
|
+
</template>
|
|
420
|
+
</CnNextStepCard>
|
|
421
|
+
|
|
329
422
|
<!-- Not-found state: the record behind the address does not exist. -->
|
|
330
423
|
<div v-if="objectNotFound" class="cn-detail-page__not-found" data-testid="cn-detail-page-not-found">
|
|
331
424
|
<!--
|
|
@@ -709,6 +802,68 @@
|
|
|
709
802
|
</div>
|
|
710
803
|
</div>
|
|
711
804
|
|
|
805
|
+
<!-- Side column (manifest `config.sideColumn`): the record's facts as
|
|
806
|
+
a column of cards beside the body. A sibling of the body, after it
|
|
807
|
+
in the document, so it reads after the body and stacks under it
|
|
808
|
+
when the page is too narrow for both. -->
|
|
809
|
+
<aside
|
|
810
|
+
v-if="showsSideColumn"
|
|
811
|
+
class="cn-detail-page__side"
|
|
812
|
+
:aria-label="sideColumnLabel"
|
|
813
|
+
data-testid="cn-detail-page-side">
|
|
814
|
+
<div
|
|
815
|
+
v-for="widget in sideColumnWidgets"
|
|
816
|
+
:key="`side-${widget.id}`"
|
|
817
|
+
class="cn-detail-page__side-item"
|
|
818
|
+
:data-testid="`cn-detail-page-side-${widget.id}`">
|
|
819
|
+
<!--
|
|
820
|
+
@slot `widget-${widget.id}`
|
|
821
|
+
@description The same per-widget slot the body grid offers,
|
|
822
|
+
for a widget placed in the side column. Same name and same
|
|
823
|
+
bindings, so a custom widget placed here needs nothing it
|
|
824
|
+
would not need in the grid. Default content renders the
|
|
825
|
+
widget through `CnDetailWidgetHost` as a card.
|
|
826
|
+
@binding {object} item `{ id, widgetId }`, for parity with the grid slot.
|
|
827
|
+
@binding {object} widget The widget definition.
|
|
828
|
+
@binding {string|number} objectId This record's id.
|
|
829
|
+
@binding {object} object The loaded record, or null while it is still being fetched.
|
|
830
|
+
@binding {object} objectData Alias of `object`.
|
|
831
|
+
@binding {string} objectType Resolved object type slug.
|
|
832
|
+
@binding {string} register Register slug of this page.
|
|
833
|
+
@binding {string} schema Schema slug of this page.
|
|
834
|
+
-->
|
|
835
|
+
<slot
|
|
836
|
+
:name="`widget-${widget.id}`"
|
|
837
|
+
:item="{ id: widget.id, widgetId: widget.id }"
|
|
838
|
+
:widget="widget"
|
|
839
|
+
:objectId="objectId"
|
|
840
|
+
:object="resolvedObject"
|
|
841
|
+
:objectData="resolvedObject"
|
|
842
|
+
:objectType="resolvedObjectType"
|
|
843
|
+
:register="register"
|
|
844
|
+
:schema="schema">
|
|
845
|
+
<CnDetailWidgetHost
|
|
846
|
+
:widget="widget"
|
|
847
|
+
chrome="card"
|
|
848
|
+
:showCardTitle="true"
|
|
849
|
+
:objectId="objectId"
|
|
850
|
+
:object="currentObject"
|
|
851
|
+
:objectType="resolvedObjectType"
|
|
852
|
+
:schemaObject="currentSchema"
|
|
853
|
+
:register="register"
|
|
854
|
+
:schema="schema"
|
|
855
|
+
:store="effectiveObjectStore"
|
|
856
|
+
:surface="surface"
|
|
857
|
+
:integrationContext="effectiveIntegrationContext"
|
|
858
|
+
:hideEmpty="hideEmpty"
|
|
859
|
+
:cnRegistry="cnRegistry"
|
|
860
|
+
:availableWidgets="widgets"
|
|
861
|
+
@geoSaved="onGeoSaved"
|
|
862
|
+
@openIntegration="onAutoBodyOpenIntegration" />
|
|
863
|
+
</slot>
|
|
864
|
+
</div>
|
|
865
|
+
</aside>
|
|
866
|
+
|
|
712
867
|
<!-- Per-widget style/config editor (ADR-041). Opened by the per-widget
|
|
713
868
|
configure cog while in Buildiq edit mode; the modal's own Delete
|
|
714
869
|
button removes the widget from the grid. -->
|
|
@@ -810,9 +965,10 @@
|
|
|
810
965
|
</template>
|
|
811
966
|
|
|
812
967
|
<script>
|
|
968
|
+
import { getCurrentUser } from '@nextcloud/auth'
|
|
813
969
|
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
|
|
814
970
|
import { translate as t } from '@nextcloud/l10n'
|
|
815
|
-
import { NcActionButton, NcActionLink, NcActionSeparator, NcButton, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'
|
|
971
|
+
import { NcActionButton, NcActionCaption, NcActionLink, NcActionSeparator, NcButton, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'
|
|
816
972
|
import { provide, ref, watch } from 'vue'
|
|
817
973
|
import AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'
|
|
818
974
|
import ArrowLeft from 'vue-material-design-icons/ArrowLeft.vue'
|
|
@@ -834,6 +990,7 @@ import CnDetailWidgetHost from '../CnDetailWidgetHost/CnDetailWidgetHost.vue'
|
|
|
834
990
|
import CnFormDialog from '../CnFormDialog/CnFormDialog.vue'
|
|
835
991
|
import CnLifecycleActions from '../CnLifecycleActions/CnLifecycleActions.vue'
|
|
836
992
|
import CnLockedBanner from '../CnLockedBanner/CnLockedBanner.vue'
|
|
993
|
+
import CnNextStepCard from '../CnNextStepCard/CnNextStepCard.vue'
|
|
837
994
|
import CnRelatedCollections from '../CnRelatedCollections/CnRelatedCollections.vue'
|
|
838
995
|
import CnSummaryAggregates from '../CnSummaryAggregates/CnSummaryAggregates.vue'
|
|
839
996
|
import CnTranslatedBadge from '../CnTranslatedBadge/CnTranslatedBadge.vue'
|
|
@@ -846,6 +1003,15 @@ import { useObjectStore } from '../../store/index.js'
|
|
|
846
1003
|
import { isAppInstalled } from '../../utils/appInstalled.js'
|
|
847
1004
|
import { compactLayoutRows } from '../../utils/dashboardPlacement.js'
|
|
848
1005
|
import { defaultDetailGrid } from '../../utils/defaultDetailGrid.js'
|
|
1006
|
+
import {
|
|
1007
|
+
groupMenuEntries,
|
|
1008
|
+
MAX_QUICK_ACTIONS,
|
|
1009
|
+
normalisePinnedAction,
|
|
1010
|
+
resolveNextStep,
|
|
1011
|
+
resolvePill,
|
|
1012
|
+
stageEntry,
|
|
1013
|
+
stageOf,
|
|
1014
|
+
} from '../../utils/detailActionModel.js'
|
|
849
1015
|
import { cnGridCellStyle, hasGridRow } from '../../utils/grid.js'
|
|
850
1016
|
import { slotRenders } from '../../utils/slotContent.js'
|
|
851
1017
|
import {
|
|
@@ -860,6 +1026,7 @@ import {
|
|
|
860
1026
|
} from '../../utils/widgetDispatch.js'
|
|
861
1027
|
import { CnActionButtons } from '../CnActionButtons/index.js'
|
|
862
1028
|
import { CnIcon } from '../CnIcon/index.js'
|
|
1029
|
+
import { CnStatusBadge } from '../CnStatusBadge/index.js'
|
|
863
1030
|
import { getWidgetTypeEntry } from '../CnWidgetGrid/dashboardWidgetRegistry.js'
|
|
864
1031
|
|
|
865
1032
|
import '../CnWidgetGrid/registerDashboardWidgets.js'
|
|
@@ -984,6 +1151,7 @@ export default {
|
|
|
984
1151
|
ChevronLeft,
|
|
985
1152
|
ChevronRight,
|
|
986
1153
|
NcActionButton,
|
|
1154
|
+
NcActionCaption,
|
|
987
1155
|
NcActionLink,
|
|
988
1156
|
NcActionSeparator,
|
|
989
1157
|
NcButton,
|
|
@@ -998,11 +1166,13 @@ export default {
|
|
|
998
1166
|
CnActionsMenu,
|
|
999
1167
|
CnBuildiqEditButton,
|
|
1000
1168
|
CnLockedBanner,
|
|
1169
|
+
CnNextStepCard,
|
|
1001
1170
|
CnDetailWidgetHost,
|
|
1002
1171
|
CnFormDialog,
|
|
1003
1172
|
CnDashboardGrid,
|
|
1004
1173
|
CnLifecycleActions,
|
|
1005
1174
|
CnActionButtons,
|
|
1175
|
+
CnStatusBadge,
|
|
1006
1176
|
CnSummaryAggregates,
|
|
1007
1177
|
CnRelatedCollections,
|
|
1008
1178
|
CnBodySections,
|
|
@@ -1811,6 +1981,130 @@ export default {
|
|
|
1811
1981
|
type: Boolean,
|
|
1812
1982
|
default: false,
|
|
1813
1983
|
},
|
|
1984
|
+
|
|
1985
|
+
// ── The action model (detail-action-model-and-case-surfaces) ────
|
|
1986
|
+
|
|
1987
|
+
/**
|
|
1988
|
+
* The primary action per stage (manifest `config.primaryActionByStage`):
|
|
1989
|
+
* a map from a stage value to an action. When the record's stage has
|
|
1990
|
+
* an entry, that action is the page's primary button, so the button
|
|
1991
|
+
* names the next step: "Take on" on a new case, "Publish" on a
|
|
1992
|
+
* decided one. An entry is an action object in the `headerActions`
|
|
1993
|
+
* shape, or the id of a declared header action. A stage without an
|
|
1994
|
+
* entry falls back to `primaryAction`.
|
|
1995
|
+
*
|
|
1996
|
+
* @type {Record<string, string|object>|null}
|
|
1997
|
+
*/
|
|
1998
|
+
primaryActionByStage: {
|
|
1999
|
+
type: Object,
|
|
2000
|
+
default: null,
|
|
2001
|
+
},
|
|
2002
|
+
|
|
2003
|
+
/**
|
|
2004
|
+
* Dot-path to the field that holds the record's stage. Read by
|
|
2005
|
+
* `primaryActionByStage` and `nextStep`.
|
|
2006
|
+
*
|
|
2007
|
+
* @type {string}
|
|
2008
|
+
*/
|
|
2009
|
+
stageField: {
|
|
2010
|
+
type: String,
|
|
2011
|
+
default: 'status',
|
|
2012
|
+
},
|
|
2013
|
+
|
|
2014
|
+
/**
|
|
2015
|
+
* Always visible buttons in the header (manifest `config.quickActions`)
|
|
2016
|
+
* for the few actions a handler uses on every record. At most three
|
|
2017
|
+
* render; later entries are dropped. An entry is an action object in
|
|
2018
|
+
* the `headerActions` shape, or the id of a declared header action,
|
|
2019
|
+
* which then leaves the Actions menu.
|
|
2020
|
+
*
|
|
2021
|
+
* @type {Array<string|object>}
|
|
2022
|
+
*/
|
|
2023
|
+
quickActions: {
|
|
2024
|
+
type: Array,
|
|
2025
|
+
default: () => [],
|
|
2026
|
+
},
|
|
2027
|
+
|
|
2028
|
+
/**
|
|
2029
|
+
* What the Actions menu itself shows (manifest `config.actionsMenu`):
|
|
2030
|
+
* `{ showRefresh?, showHelpLinks?, label? }`. `showRefresh: false`
|
|
2031
|
+
* removes Refresh. `showHelpLinks: false` removes Request a feature,
|
|
2032
|
+
* Report a bug and Documentation, for an app that offers them in its
|
|
2033
|
+
* own help menu. `label` names the menu, for example "More". `null`
|
|
2034
|
+
* (the default) changes nothing.
|
|
2035
|
+
*
|
|
2036
|
+
* @type {{showRefresh?: boolean, showHelpLinks?: boolean, label?: string}|null}
|
|
2037
|
+
*/
|
|
2038
|
+
actionsMenu: {
|
|
2039
|
+
type: Object,
|
|
2040
|
+
default: null,
|
|
2041
|
+
},
|
|
2042
|
+
|
|
2043
|
+
/**
|
|
2044
|
+
* The "what now" card (manifest `config.nextStep`):
|
|
2045
|
+
* `{ field?, stages: { <stage>: { title?, checklist, after? } } }`.
|
|
2046
|
+
* Each checklist item is `{ label, doneField?, doneWhen?, hint? }`.
|
|
2047
|
+
* Rendered above the body for a stage that declares a checklist, with
|
|
2048
|
+
* the primary button inside it. See `CnNextStepCard`.
|
|
2049
|
+
*
|
|
2050
|
+
* @type {object|null}
|
|
2051
|
+
*/
|
|
2052
|
+
nextStep: {
|
|
2053
|
+
type: Object,
|
|
2054
|
+
default: null,
|
|
2055
|
+
},
|
|
2056
|
+
|
|
2057
|
+
/**
|
|
2058
|
+
* A pill above the title saying what kind of record this is
|
|
2059
|
+
* (manifest `config.typePill`): `{ field, colorMap?, labels?, variant? }`.
|
|
2060
|
+
* Rendered through `CnStatusBadge`; `colorMap` is keyed on the field's
|
|
2061
|
+
* raw value, `labels` maps a raw value to the text to show.
|
|
2062
|
+
*
|
|
2063
|
+
* @type {{field: string, colorMap?: object, labels?: object, variant?: string}|null}
|
|
2064
|
+
*/
|
|
2065
|
+
typePill: {
|
|
2066
|
+
type: Object,
|
|
2067
|
+
default: null,
|
|
2068
|
+
},
|
|
2069
|
+
|
|
2070
|
+
/**
|
|
2071
|
+
* A pill above the title saying where the record stands (manifest
|
|
2072
|
+
* `config.statusPill`). Same shape as `typePill`.
|
|
2073
|
+
*
|
|
2074
|
+
* @type {{field: string, colorMap?: object, labels?: object, variant?: string}|null}
|
|
2075
|
+
*/
|
|
2076
|
+
statusPill: {
|
|
2077
|
+
type: Object,
|
|
2078
|
+
default: null,
|
|
2079
|
+
},
|
|
2080
|
+
|
|
2081
|
+
/**
|
|
2082
|
+
* A column of cards beside the body (manifest `config.sideColumn`),
|
|
2083
|
+
* for the record's facts: deadline, requester, handler, links. Each
|
|
2084
|
+
* entry is a widget definition (`{ id?, type, title?, content? }`) or
|
|
2085
|
+
* the id of a widget declared in `widgets`. The column stacks under
|
|
2086
|
+
* the body on a narrow page. Empty (the default) leaves the layout as
|
|
2087
|
+
* it is.
|
|
2088
|
+
*
|
|
2089
|
+
* @type {Array<string|object>}
|
|
2090
|
+
*/
|
|
2091
|
+
sideColumn: {
|
|
2092
|
+
type: Array,
|
|
2093
|
+
default: () => [],
|
|
2094
|
+
},
|
|
2095
|
+
|
|
2096
|
+
/**
|
|
2097
|
+
* Whether the current user administers this instance. Shows the
|
|
2098
|
+
* `adminOnly` header actions. `null` (the default) reads it from
|
|
2099
|
+
* `getCurrentUser()`. Visibility only, never an authorization
|
|
2100
|
+
* decision: the endpoint behind an action decides who may call it.
|
|
2101
|
+
*
|
|
2102
|
+
* @type {boolean|null}
|
|
2103
|
+
*/
|
|
2104
|
+
isAdmin: {
|
|
2105
|
+
type: Boolean,
|
|
2106
|
+
default: null,
|
|
2107
|
+
},
|
|
1814
2108
|
},
|
|
1815
2109
|
|
|
1816
2110
|
emits: [
|
|
@@ -2078,7 +2372,7 @@ export default {
|
|
|
2078
2372
|
* @spec openspec/changes/case-page-and-list-as-a-place/specs/index-page/spec.md
|
|
2079
2373
|
*/
|
|
2080
2374
|
primaryActionAnchorId() {
|
|
2081
|
-
const scope = this.primaryAction?.id || this.pageId || this.objectId || 'record'
|
|
2375
|
+
const scope = this.stagePrimaryEntry?.id || this.primaryAction?.id || this.pageId || this.objectId || 'record'
|
|
2082
2376
|
return `cn-primary-action-${String(scope).replace(/[^A-Za-z0-9_-]/g, '-')}`
|
|
2083
2377
|
},
|
|
2084
2378
|
|
|
@@ -2093,7 +2387,291 @@ export default {
|
|
|
2093
2387
|
* @spec openspec/changes/case-page-and-list-as-a-place/specs/index-page/spec.md
|
|
2094
2388
|
*/
|
|
2095
2389
|
skipLinkLabel() {
|
|
2096
|
-
return `${this.effectiveTranslate('Skip to')} ${this.
|
|
2390
|
+
return `${this.effectiveTranslate('Skip to')} ${this.effectivePrimaryLabel}`
|
|
2391
|
+
},
|
|
2392
|
+
|
|
2393
|
+
// ── The action model (detail-action-model-and-case-surfaces) ────
|
|
2394
|
+
|
|
2395
|
+
/**
|
|
2396
|
+
* The record's stage, as `primaryActionByStage` and `nextStep` key it.
|
|
2397
|
+
*
|
|
2398
|
+
* @return {string} The stage, or '' while the record has none.
|
|
2399
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage
|
|
2400
|
+
*/
|
|
2401
|
+
recordStage() {
|
|
2402
|
+
return stageOf(this.currentObject, this.stageField)
|
|
2403
|
+
},
|
|
2404
|
+
|
|
2405
|
+
/**
|
|
2406
|
+
* The stage's primary action as an id, plus its definition when the
|
|
2407
|
+
* map carries one inline.
|
|
2408
|
+
*
|
|
2409
|
+
* @return {{id: string, inline: object|null}|null} The pinned action, or null when this stage declares none.
|
|
2410
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage
|
|
2411
|
+
*/
|
|
2412
|
+
stagePrimary() {
|
|
2413
|
+
return normalisePinnedAction(stageEntry(this.primaryActionByStage, this.recordStage), 'cn-stage-primary')
|
|
2414
|
+
},
|
|
2415
|
+
|
|
2416
|
+
/**
|
|
2417
|
+
* The quick actions as ids, plus definitions for the inline ones.
|
|
2418
|
+
* Capped at three, and never the action that is already the primary
|
|
2419
|
+
* button.
|
|
2420
|
+
*
|
|
2421
|
+
* @return {Array<{id: string, inline: object|null}>} The pinned actions.
|
|
2422
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-quick-actions
|
|
2423
|
+
*/
|
|
2424
|
+
pinnedQuickActions() {
|
|
2425
|
+
const primaryId = this.stagePrimary?.id
|
|
2426
|
+
return (Array.isArray(this.quickActions) ? this.quickActions : [])
|
|
2427
|
+
.map((entry, index) => normalisePinnedAction(entry, `cn-quick-${index}`))
|
|
2428
|
+
.filter((pinned) => pinned && pinned.id !== primaryId)
|
|
2429
|
+
.slice(0, MAX_QUICK_ACTIONS)
|
|
2430
|
+
},
|
|
2431
|
+
|
|
2432
|
+
/**
|
|
2433
|
+
* The stage's primary action in dispatchable form: the menu entry
|
|
2434
|
+
* CnActionButtons built for it, with its `run()`. Null while the
|
|
2435
|
+
* action is hidden by its own `visibleWhen`, in which case the page
|
|
2436
|
+
* falls back to `primaryAction`.
|
|
2437
|
+
*
|
|
2438
|
+
* @return {object|null} The entry, or null.
|
|
2439
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage
|
|
2440
|
+
*/
|
|
2441
|
+
stagePrimaryEntry() {
|
|
2442
|
+
const id = this.stagePrimary?.id
|
|
2443
|
+
return (id && this.menuHeaderActions.find((entry) => entry.id === id)) || null
|
|
2444
|
+
},
|
|
2445
|
+
|
|
2446
|
+
/**
|
|
2447
|
+
* The quick actions in dispatchable form, in declaration order.
|
|
2448
|
+
*
|
|
2449
|
+
* @return {Array<object>} The entries that are visible.
|
|
2450
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-quick-actions
|
|
2451
|
+
*/
|
|
2452
|
+
quickActionEntries() {
|
|
2453
|
+
return this.pinnedQuickActions
|
|
2454
|
+
.map((pinned) => this.menuHeaderActions.find((entry) => entry.id === pinned.id))
|
|
2455
|
+
.filter(Boolean)
|
|
2456
|
+
},
|
|
2457
|
+
|
|
2458
|
+
/**
|
|
2459
|
+
* What the primary button says: the stage's action when the stage
|
|
2460
|
+
* declares one, the page's `primaryAction` otherwise. Empty gates
|
|
2461
|
+
* both the button and the skip link.
|
|
2462
|
+
*
|
|
2463
|
+
* @return {string} The label, or an empty string.
|
|
2464
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage
|
|
2465
|
+
*/
|
|
2466
|
+
effectivePrimaryLabel() {
|
|
2467
|
+
return this.stagePrimaryEntry ? this.stagePrimaryEntry.label : this.primaryActionLabel
|
|
2468
|
+
},
|
|
2469
|
+
|
|
2470
|
+
/**
|
|
2471
|
+
* The URL the primary button links to, when its action is a link.
|
|
2472
|
+
*
|
|
2473
|
+
* @return {string} The href, or '' for an action that is dispatched.
|
|
2474
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage
|
|
2475
|
+
*/
|
|
2476
|
+
primaryActionHref() {
|
|
2477
|
+
return this.stagePrimaryEntry?.href || ''
|
|
2478
|
+
},
|
|
2479
|
+
|
|
2480
|
+
/**
|
|
2481
|
+
* The link target that goes with `primaryActionHref`.
|
|
2482
|
+
*
|
|
2483
|
+
* @return {string|undefined} The target, or undefined.
|
|
2484
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage
|
|
2485
|
+
*/
|
|
2486
|
+
primaryActionTarget() {
|
|
2487
|
+
return this.stagePrimaryEntry?.linkTarget || undefined
|
|
2488
|
+
},
|
|
2489
|
+
|
|
2490
|
+
/**
|
|
2491
|
+
* Whether the primary button waits for its own request.
|
|
2492
|
+
*
|
|
2493
|
+
* @return {boolean} True while the stage's action is in flight.
|
|
2494
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage
|
|
2495
|
+
*/
|
|
2496
|
+
primaryActionDisabled() {
|
|
2497
|
+
return Boolean(this.stagePrimaryEntry?.disabled)
|
|
2498
|
+
},
|
|
2499
|
+
|
|
2500
|
+
/**
|
|
2501
|
+
* Whether the viewer administers this instance, for `adminOnly`
|
|
2502
|
+
* actions. The prop wins; otherwise Nextcloud says.
|
|
2503
|
+
*
|
|
2504
|
+
* @return {boolean} True for an instance admin.
|
|
2505
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-grouped-and-admin-only-menu-actions
|
|
2506
|
+
*/
|
|
2507
|
+
viewerIsAdmin() {
|
|
2508
|
+
if (this.isAdmin !== null) {
|
|
2509
|
+
return this.isAdmin
|
|
2510
|
+
}
|
|
2511
|
+
try {
|
|
2512
|
+
return getCurrentUser()?.isAdmin === true
|
|
2513
|
+
} catch {
|
|
2514
|
+
return false
|
|
2515
|
+
}
|
|
2516
|
+
},
|
|
2517
|
+
|
|
2518
|
+
/**
|
|
2519
|
+
* The declared actions by id, for the `group` and `adminOnly` a menu
|
|
2520
|
+
* entry does not carry itself.
|
|
2521
|
+
*
|
|
2522
|
+
* @return {Record<string, object>} The lookup.
|
|
2523
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-grouped-and-admin-only-menu-actions
|
|
2524
|
+
*/
|
|
2525
|
+
headerActionsById() {
|
|
2526
|
+
return Object.fromEntries(this.effectiveHeaderActions
|
|
2527
|
+
.filter((action) => action && action.id)
|
|
2528
|
+
.map((action) => [action.id, action]))
|
|
2529
|
+
},
|
|
2530
|
+
|
|
2531
|
+
/**
|
|
2532
|
+
* The menu entries in the groups the menu draws: ungrouped first,
|
|
2533
|
+
* named groups in first appearance order, admin actions last and for
|
|
2534
|
+
* admins only. A page that groups nothing gets one captionless group.
|
|
2535
|
+
*
|
|
2536
|
+
* @return {Array<{key: string, label: string, entries: Array<object>}>} The groups.
|
|
2537
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-grouped-and-admin-only-menu-actions
|
|
2538
|
+
*/
|
|
2539
|
+
headerMenuGroups() {
|
|
2540
|
+
const groups = groupMenuEntries(this.headerMenuEntries, this.headerActionsById, {
|
|
2541
|
+
isAdmin: this.viewerIsAdmin,
|
|
2542
|
+
adminLabel: t('nextcloud-vue', 'Administration'),
|
|
2543
|
+
})
|
|
2544
|
+
return groups.map((group) => ({ ...group, label: group.label ? this.effectiveTranslate(group.label) : '' }))
|
|
2545
|
+
},
|
|
2546
|
+
|
|
2547
|
+
/**
|
|
2548
|
+
* Whether Refresh renders in the Actions menu.
|
|
2549
|
+
*
|
|
2550
|
+
* @return {boolean} The existing rule, unless `actionsMenu.showRefresh` is false.
|
|
2551
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-actions-menu-built-ins-are-optional
|
|
2552
|
+
*/
|
|
2553
|
+
menuShowsRefresh() {
|
|
2554
|
+
return this.actionsMenu?.showRefresh === false ? false : this.effectiveHeaderShowRefresh
|
|
2555
|
+
},
|
|
2556
|
+
|
|
2557
|
+
/**
|
|
2558
|
+
* Whether the help links render in the Actions menu.
|
|
2559
|
+
*
|
|
2560
|
+
* @return {boolean} True unless `actionsMenu.showHelpLinks` is false.
|
|
2561
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-actions-menu-built-ins-are-optional
|
|
2562
|
+
*/
|
|
2563
|
+
menuShowsHelpLinks() {
|
|
2564
|
+
return this.actionsMenu?.showHelpLinks !== false
|
|
2565
|
+
},
|
|
2566
|
+
|
|
2567
|
+
/**
|
|
2568
|
+
* The Actions menu's name.
|
|
2569
|
+
*
|
|
2570
|
+
* @return {string|undefined} `actionsMenu.label` translated, or undefined for the default "Actions".
|
|
2571
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-actions-menu-built-ins-are-optional
|
|
2572
|
+
*/
|
|
2573
|
+
actionsMenuName() {
|
|
2574
|
+
const label = this.actionsMenu?.label
|
|
2575
|
+
return typeof label === 'string' && label !== '' ? this.effectiveTranslate(label) : undefined
|
|
2576
|
+
},
|
|
2577
|
+
|
|
2578
|
+
/**
|
|
2579
|
+
* Whether the page is showing its body: not the not-found, loading,
|
|
2580
|
+
* error or empty state. The "what now" card and the side column are
|
|
2581
|
+
* siblings of the body and follow it.
|
|
2582
|
+
*
|
|
2583
|
+
* @return {boolean} True when the body renders.
|
|
2584
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-next-step-card
|
|
2585
|
+
*/
|
|
2586
|
+
showsBody() {
|
|
2587
|
+
return !this.objectNotFound && !this.showLoadingState && !this.error && !this.empty
|
|
2588
|
+
},
|
|
2589
|
+
|
|
2590
|
+
/**
|
|
2591
|
+
* The "what now" card for the record's stage, labels translated.
|
|
2592
|
+
*
|
|
2593
|
+
* @return {{stage: string, title: string, items: Array<object>, after: string}|null} The card, or null when the stage declares no checklist.
|
|
2594
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-next-step-card
|
|
2595
|
+
*/
|
|
2596
|
+
nextStepCard() {
|
|
2597
|
+
const card = resolveNextStep(this.nextStep, this.currentObject, this.stageField)
|
|
2598
|
+
if (!card) {
|
|
2599
|
+
return null
|
|
2600
|
+
}
|
|
2601
|
+
const tr = (text) => (text ? this.effectiveTranslate(text) : '')
|
|
2602
|
+
return {
|
|
2603
|
+
...card,
|
|
2604
|
+
title: tr(card.title),
|
|
2605
|
+
after: tr(card.after),
|
|
2606
|
+
items: card.items.map((item) => ({ ...item, label: tr(item.label), hint: tr(item.hint) })),
|
|
2607
|
+
}
|
|
2608
|
+
},
|
|
2609
|
+
|
|
2610
|
+
/**
|
|
2611
|
+
* Whether the "what now" card renders.
|
|
2612
|
+
*
|
|
2613
|
+
* @return {boolean} True when the stage has a checklist and the body is showing.
|
|
2614
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-next-step-card
|
|
2615
|
+
*/
|
|
2616
|
+
showsNextStepCard() {
|
|
2617
|
+
return Boolean(this.nextStepCard) && this.showsBody
|
|
2618
|
+
},
|
|
2619
|
+
|
|
2620
|
+
/**
|
|
2621
|
+
* The pills above the title: type first, then status.
|
|
2622
|
+
*
|
|
2623
|
+
* @return {Array<{key: string, label: string, colorKey: string, colorMap: object|null, variant: string}>} The pills whose field has a value.
|
|
2624
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-header-pills
|
|
2625
|
+
*/
|
|
2626
|
+
headerPills() {
|
|
2627
|
+
return [['type', this.typePill], ['status', this.statusPill]]
|
|
2628
|
+
.map(([key, config]) => {
|
|
2629
|
+
const pill = resolvePill(config, this.resolvedObject)
|
|
2630
|
+
return pill ? { ...pill, key, label: this.effectiveTranslate(pill.label) } : null
|
|
2631
|
+
})
|
|
2632
|
+
.filter(Boolean)
|
|
2633
|
+
},
|
|
2634
|
+
|
|
2635
|
+
/**
|
|
2636
|
+
* The side column's widget definitions. A string names a widget
|
|
2637
|
+
* declared in `widgets`; an object is a definition of its own.
|
|
2638
|
+
*
|
|
2639
|
+
* @return {Array<object>} The definitions that resolve, each with an `id`.
|
|
2640
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-side-column
|
|
2641
|
+
*/
|
|
2642
|
+
sideColumnWidgets() {
|
|
2643
|
+
const declared = Array.isArray(this.widgets) ? this.widgets : []
|
|
2644
|
+
return (Array.isArray(this.sideColumn) ? this.sideColumn : [])
|
|
2645
|
+
.map((entry, index) => {
|
|
2646
|
+
if (typeof entry === 'string') {
|
|
2647
|
+
return declared.find((widget) => widget && widget.id === entry) || null
|
|
2648
|
+
}
|
|
2649
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
2650
|
+
return null
|
|
2651
|
+
}
|
|
2652
|
+
return entry.id ? entry : { ...entry, id: `cn-side-${index}` }
|
|
2653
|
+
})
|
|
2654
|
+
.filter(Boolean)
|
|
2655
|
+
},
|
|
2656
|
+
|
|
2657
|
+
/**
|
|
2658
|
+
* Whether the side column renders.
|
|
2659
|
+
*
|
|
2660
|
+
* @return {boolean} True when it has widgets and the body is showing.
|
|
2661
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-side-column
|
|
2662
|
+
*/
|
|
2663
|
+
showsSideColumn() {
|
|
2664
|
+
return this.sideColumnWidgets.length > 0 && this.showsBody
|
|
2665
|
+
},
|
|
2666
|
+
|
|
2667
|
+
/**
|
|
2668
|
+
* Accessible name of the side column.
|
|
2669
|
+
*
|
|
2670
|
+
* @return {string} The label.
|
|
2671
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-side-column
|
|
2672
|
+
*/
|
|
2673
|
+
sideColumnLabel() {
|
|
2674
|
+
return t('nextcloud-vue', 'Details')
|
|
2097
2675
|
},
|
|
2098
2676
|
|
|
2099
2677
|
/**
|
|
@@ -2297,7 +2875,16 @@ export default {
|
|
|
2297
2875
|
*/
|
|
2298
2876
|
headerMenuEntries() {
|
|
2299
2877
|
const lifecycle = this.showsLifecycleActions ? this.lifecycleMenuEntries : []
|
|
2300
|
-
|
|
2878
|
+
// An action drawn as a button, the stage's primary one or a quick
|
|
2879
|
+
// action, is not listed a second time in the menu.
|
|
2880
|
+
const pinned = new Set([
|
|
2881
|
+
...(this.stagePrimaryEntry ? [this.stagePrimaryEntry.id] : []),
|
|
2882
|
+
...this.quickActionEntries.map((entry) => entry.id),
|
|
2883
|
+
])
|
|
2884
|
+
const inMenu = pinned.size > 0
|
|
2885
|
+
? this.menuHeaderActions.filter((entry) => !pinned.has(entry.id))
|
|
2886
|
+
: this.menuHeaderActions
|
|
2887
|
+
return [...lifecycle, ...inMenu]
|
|
2301
2888
|
},
|
|
2302
2889
|
|
|
2303
2890
|
/**
|
|
@@ -2319,7 +2906,17 @@ export default {
|
|
|
2319
2906
|
* @return {Array<object>} The action descriptors.
|
|
2320
2907
|
*/
|
|
2321
2908
|
effectiveHeaderActions() {
|
|
2322
|
-
const
|
|
2909
|
+
const authored = Array.isArray(this.headerActions) ? this.headerActions : []
|
|
2910
|
+
// Inline pinned actions (a stage's primary action, a quick action
|
|
2911
|
+
// written out in place) join the list CnActionButtons dispatches,
|
|
2912
|
+
// so they get the same dialogs, toasts and `visibleWhen` gating as
|
|
2913
|
+
// any header action. An id that is already declared is not added
|
|
2914
|
+
// twice. With nothing pinned inline this is `headerActions` itself.
|
|
2915
|
+
const known = new Set(authored.map((action) => action && action.id))
|
|
2916
|
+
const inline = [this.stagePrimary, ...this.pinnedQuickActions]
|
|
2917
|
+
.filter((pinned) => pinned && pinned.inline && !known.has(pinned.id))
|
|
2918
|
+
.map((pinned) => pinned.inline)
|
|
2919
|
+
const declared = inline.length > 0 ? [...authored, ...inline] : authored
|
|
2323
2920
|
if (!(this.foldsEditIntoActions && this.canEditRecord)) {
|
|
2324
2921
|
return declared
|
|
2325
2922
|
}
|
|
@@ -3132,7 +3729,11 @@ export default {
|
|
|
3132
3729
|
* @spec openspec/changes/case-page-and-list-as-a-place/specs/index-page/spec.md
|
|
3133
3730
|
*/
|
|
3134
3731
|
onSkipToPrimaryAction(event) {
|
|
3135
|
-
|
|
3732
|
+
// The ref covers the header button; the id also finds the button
|
|
3733
|
+
// when it sits inside the "what now" card.
|
|
3734
|
+
const target = this.$refs.primaryActionButton?.$el
|
|
3735
|
+
?? this.$refs.primaryActionButton
|
|
3736
|
+
?? this.$el?.querySelector?.(`[id="${this.primaryActionAnchorId}"]`)
|
|
3136
3737
|
const focusable = target?.matches?.('button, a') ? target : target?.querySelector?.('button, a')
|
|
3137
3738
|
if (focusable?.focus) {
|
|
3138
3739
|
event.preventDefault()
|
|
@@ -3140,6 +3741,43 @@ export default {
|
|
|
3140
3741
|
}
|
|
3141
3742
|
},
|
|
3142
3743
|
|
|
3744
|
+
/**
|
|
3745
|
+
* The primary button was pressed.
|
|
3746
|
+
*
|
|
3747
|
+
* A stage's action is dispatched the way a header action is, through
|
|
3748
|
+
* the `run()` CnActionButtons bound for it, and announced to the host
|
|
3749
|
+
* with its declaration. A link action is left to the browser. Without
|
|
3750
|
+
* a stage action this is the page's `primaryAction`, announced as
|
|
3751
|
+
* before.
|
|
3752
|
+
*
|
|
3753
|
+
* @return {void}
|
|
3754
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage
|
|
3755
|
+
*/
|
|
3756
|
+
onPrimaryActionClick() {
|
|
3757
|
+
const entry = this.stagePrimaryEntry
|
|
3758
|
+
if (!entry) {
|
|
3759
|
+
this.$emit('primary-action', this.primaryAction)
|
|
3760
|
+
return
|
|
3761
|
+
}
|
|
3762
|
+
this.$emit('primary-action', this.headerActionsById[entry.id] || { id: entry.id, label: entry.label })
|
|
3763
|
+
this.onPinnedActionClick(entry)
|
|
3764
|
+
},
|
|
3765
|
+
|
|
3766
|
+
/**
|
|
3767
|
+
* A pinned action's button was pressed: run it, unless it is a link
|
|
3768
|
+
* the browser is already following.
|
|
3769
|
+
*
|
|
3770
|
+
* @param {object} entry The action's menu entry.
|
|
3771
|
+
* @return {void}
|
|
3772
|
+
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-quick-actions
|
|
3773
|
+
*/
|
|
3774
|
+
onPinnedActionClick(entry) {
|
|
3775
|
+
if (!entry || entry.href || typeof entry.run !== 'function') {
|
|
3776
|
+
return
|
|
3777
|
+
}
|
|
3778
|
+
entry.run()
|
|
3779
|
+
},
|
|
3780
|
+
|
|
3143
3781
|
/**
|
|
3144
3782
|
* The active tab changed: tell the host, and put it in the address
|
|
3145
3783
|
* when the page declares that its tab is part of its address.
|