@conduction/nextcloud-vue 2.56.0-beta.11 → 2.56.0-beta.13

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.
Files changed (138) hide show
  1. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +253 -10
  2. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +139 -46
  4. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  5. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +87 -0
  6. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  7. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +26 -24
  8. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  9. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +61 -16
  10. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
  11. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
  12. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js +20 -0
  13. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js.map +1 -1
  14. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js +3 -2
  15. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js.map +1 -1
  16. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js +13 -0
  17. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js.map +1 -1
  18. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js +2 -1
  19. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js.map +1 -1
  20. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +33 -2
  21. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  22. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  23. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +154 -4
  24. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  25. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +555 -480
  26. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  27. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js +9 -3
  28. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js.map +1 -1
  29. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js +1 -1
  30. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js.map +1 -1
  31. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +37 -1
  32. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  33. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +12 -4
  34. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  35. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +187 -19
  36. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  37. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +215 -135
  38. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  39. package/dist/esm/components/CnFkResolveCell/CnFkResolveCell.vue2.js +3 -1
  40. package/dist/esm/components/CnFkResolveCell/CnFkResolveCell.vue2.js.map +1 -1
  41. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +144 -4
  42. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  43. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  44. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +157 -7
  45. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
  46. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +23 -5
  47. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
  48. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +30 -0
  49. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  50. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +2 -2
  51. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  52. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +5 -1
  53. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  54. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -2
  55. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  56. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js +11 -0
  57. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
  58. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js +2 -1
  59. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -1
  60. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +63 -2
  61. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
  62. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js +96 -37
  63. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
  64. package/dist/esm/components/CnStagesWidget/stagesModel.js +5 -2
  65. package/dist/esm/components/CnStagesWidget/stagesModel.js.map +1 -1
  66. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +74 -5
  67. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  68. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +15 -8
  69. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  70. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +14 -2
  71. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  72. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +2 -1
  73. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  74. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js +73 -1
  75. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js.map +1 -1
  76. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js +14 -2
  77. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js.map +1 -1
  78. package/dist/esm/l10n/en.json.js +5 -1
  79. package/dist/esm/l10n/en.json.js.map +1 -1
  80. package/dist/esm/l10n/nl.json.js +5 -1
  81. package/dist/esm/l10n/nl.json.js.map +1 -1
  82. package/dist/esm/mixins/pageViews.js +400 -0
  83. package/dist/esm/mixins/pageViews.js.map +1 -0
  84. package/dist/esm/nextcloud-vue.css +513 -81
  85. package/dist/esm/store/useObjectStore.js +118 -33
  86. package/dist/esm/store/useObjectStore.js.map +1 -1
  87. package/dist/esm/utils/actionsDispatcher.js +25 -2
  88. package/dist/esm/utils/actionsDispatcher.js.map +1 -1
  89. package/dist/esm/utils/columnFilters.js +27 -3
  90. package/dist/esm/utils/columnFilters.js.map +1 -1
  91. package/dist/esm/utils/schema.js +40 -2
  92. package/dist/esm/utils/schema.js.map +1 -1
  93. package/dist/esm/utils/schemaRefSlug.js +19 -3
  94. package/dist/esm/utils/schemaRefSlug.js.map +1 -1
  95. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  96. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  97. package/dist/nextcloud-vue.cjs.js +8858 -6566
  98. package/dist/nextcloud-vue.cjs.js.map +2 -2
  99. package/dist/nextcloud-vue.css +513 -81
  100. package/l10n/en.json +5 -0
  101. package/l10n/nl.json +5 -0
  102. package/package.json +1 -1
  103. package/src/components/CnAppNav/CnAppNav.vue +441 -14
  104. package/src/components/CnAppRoot/CnAppRoot.md +4 -0
  105. package/src/components/CnAppRoot/CnAppRoot.vue +94 -1
  106. package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +61 -16
  107. package/src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue +22 -1
  108. package/src/components/CnBreadcrumbs/CnBreadcrumbs.vue +14 -0
  109. package/src/components/CnCellRenderer/CnCellRenderer.vue +33 -2
  110. package/src/components/CnDashboardPage/CnDashboardPage.vue +237 -12
  111. package/src/components/CnDataTable/CnColumnFilterPopover.vue +11 -5
  112. package/src/components/CnDataTable/CnDataTable.vue +45 -1
  113. package/src/components/CnDetailPage/CnDetailPage.md +8 -0
  114. package/src/components/CnDetailPage/CnDetailPage.vue +263 -7
  115. package/src/components/CnFkResolveCell/CnFkResolveCell.vue +3 -1
  116. package/src/components/CnFormDialog/CnFormDialog.vue +144 -4
  117. package/src/components/CnHeaderWidget/CnHeaderWidget.vue +206 -9
  118. package/src/components/CnIndexPage/CnIndexPage.vue +32 -2
  119. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +7 -1
  120. package/src/components/CnSegmentedControl/CnSegmentedControl.md +1 -1
  121. package/src/components/CnSegmentedControl/CnSegmentedControl.vue +12 -0
  122. package/src/components/CnStagesWidget/CnStagesWidget.vue +189 -2
  123. package/src/components/CnStagesWidget/stagesModel.js +5 -2
  124. package/src/components/CnStatWidget/CnStatWidget.vue +89 -7
  125. package/src/components/CnTabsWidget/CnTabsWidget.vue +15 -2
  126. package/src/components/CnWidgetWrapper/CnWidgetWrapper.md +4 -0
  127. package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +107 -1
  128. package/src/css/detail-page.css +37 -4
  129. package/src/css/kpi-card.css +71 -1
  130. package/src/css/table.css +18 -6
  131. package/src/mixins/pageViews.js +396 -0
  132. package/src/schemas/app-manifest-v2.schema.json +162 -3
  133. package/src/store/useObjectStore.js +118 -33
  134. package/src/utils/actionsDispatcher.js +25 -2
  135. package/src/utils/columnFilters.js +29 -2
  136. package/src/utils/schema.js +40 -2
  137. package/src/utils/schemaRefSlug.js +19 -3
  138. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -13,25 +13,28 @@ import InformationOutline from 'vue-material-design-icons/InformationOutline.vue
13
13
  import Pencil from 'vue-material-design-icons/Pencil.vue';
14
14
  import Plus from 'vue-material-design-icons/Plus.vue';
15
15
  import Refresh from 'vue-material-design-icons/Refresh.vue';
16
- import __sfc_main$g from '../../dialogs/CnRelationLinkModal.vue.js';
17
- import __sfc_main$f from '../../dialogs/CnWidgetStyleEditorModal.vue.js';
16
+ import ViewDashboardOutline from 'vue-material-design-icons/ViewDashboardOutline.vue';
17
+ import __sfc_main$i from '../../dialogs/CnRelationLinkModal.vue.js';
18
+ import __sfc_main$h from '../../dialogs/CnWidgetStyleEditorModal.vue.js';
18
19
  import __sfc_main$1 from '../CnActionsMenu/CnActionsMenu.vue.js';
19
- import __sfc_main$d from '../CnBodySections/CnBodySections.vue.js';
20
+ import __sfc_main$f from '../CnBodySections/CnBodySections.vue.js';
20
21
  import __sfc_main$2 from '../CnBuildiqEditButton/CnBuildiqEditButton.vue.js';
21
22
  import __sfc_main$7 from '../CnDashboardGrid/CnDashboardGrid.vue.js';
22
23
  import __sfc_main$5 from '../CnDetailWidgetHost/CnDetailWidgetHost.vue.js';
23
24
  import __sfc_main$6 from '../CnFormDialog/CnFormDialog.vue.js';
24
- import __sfc_main$8 from '../CnLifecycleActions/CnLifecycleActions.vue.js';
25
+ import __sfc_main$9 from '../CnLifecycleActions/CnLifecycleActions.vue.js';
25
26
  import __sfc_main$3 from '../CnLockedBanner/CnLockedBanner.vue.js';
26
27
  import __sfc_main$4 from '../CnNextStepCard/CnNextStepCard.vue.js';
27
- import __sfc_main$c from '../CnRelatedCollections/CnRelatedCollections.vue.js';
28
- import __sfc_main$b from '../CnSummaryAggregates/CnSummaryAggregates.vue.js';
29
- import __sfc_main$e from '../CnTranslatedBadge/CnTranslatedBadge.vue.js';
28
+ import __sfc_main$e from '../CnRelatedCollections/CnRelatedCollections.vue.js';
29
+ import __sfc_main$8 from '../CnSegmentedControl/CnSegmentedControl.vue.js';
30
+ import __sfc_main$d from '../CnSummaryAggregates/CnSummaryAggregates.vue.js';
31
+ import __sfc_main$g from '../CnTranslatedBadge/CnTranslatedBadge.vue.js';
30
32
  import { useIntegrationRegistry } from '../../composables/useIntegrationRegistry.js';
31
33
  import { useObjectLock } from '../../composables/useObjectLock.js';
32
34
  import { useObjectPresence } from '../../composables/useObjectPresence.js';
33
35
  import { useObjectSubscription } from '../../composables/useObjectSubscription.js';
34
36
  import { gridLayout } from '../../mixins/gridLayout.js';
37
+ import { pageViews } from '../../mixins/pageViews.js';
35
38
  import { isAppInstalled } from '../../utils/appInstalled.js';
36
39
  import { compactLayoutRows } from '../../utils/dashboardPlacement.js';
37
40
  import { defaultDetailGrid } from '../../utils/defaultDetailGrid.js';
@@ -39,9 +42,10 @@ import { stageOf, normalisePinnedAction, stageEntry, MAX_QUICK_ACTIONS, groupMen
39
42
  import { cnGridCellStyle, hasGridRow } from '../../utils/grid.js';
40
43
  import { slotRenders } from '../../utils/slotContent.js';
41
44
  import { isDataWidgetDef, isRelatedWidgetDef, isGeoWidgetDef, isContentOnlyWidgetDef, isCardWidgetDef, widgetContentOf, resolveRegistryRenderer, widgetTitleOf } from '../../utils/widgetDispatch.js';
42
- import __sfc_main$9 from '../CnActionButtons/CnActionButtons.vue.js';
45
+ import __sfc_main$a from '../CnActionButtons/CnActionButtons.vue.js';
46
+ import __sfc_main$b from '../CnBreadcrumbs/CnBreadcrumbs.vue.js';
43
47
  import __sfc_main from '../CnIcon/CnIcon.vue.js';
44
- import __sfc_main$a from '../CnStatusBadge/CnStatusBadge.vue.js';
48
+ import __sfc_main$c from '../CnStatusBadge/CnStatusBadge.vue.js';
45
49
  import { getWidgetTypeEntry } from '../CnWidgetGrid/dashboardWidgetRegistry.js';
46
50
  import '../CnWidgetGrid/registerDashboardWidgets.js';
47
51
  import { useObjectStore } from '../../store/useObjectStore.js';
@@ -185,21 +189,24 @@ var script = {
185
189
  CnDetailWidgetHost: __sfc_main$5,
186
190
  CnFormDialog: __sfc_main$6,
187
191
  CnDashboardGrid: __sfc_main$7,
188
- CnLifecycleActions: __sfc_main$8,
189
- CnActionButtons: __sfc_main$9,
190
- CnStatusBadge: __sfc_main$a,
191
- CnSummaryAggregates: __sfc_main$b,
192
- CnRelatedCollections: __sfc_main$c,
193
- CnBodySections: __sfc_main$d,
194
- CnTranslatedBadge: __sfc_main$e,
195
- CnWidgetStyleEditorModal: __sfc_main$f,
196
- CnRelationLinkModal: __sfc_main$g,
192
+ CnSegmentedControl: __sfc_main$8,
193
+ ViewDashboardOutline,
194
+ CnLifecycleActions: __sfc_main$9,
195
+ CnActionButtons: __sfc_main$a,
196
+ CnBreadcrumbs: __sfc_main$b,
197
+ CnStatusBadge: __sfc_main$c,
198
+ CnSummaryAggregates: __sfc_main$d,
199
+ CnRelatedCollections: __sfc_main$e,
200
+ CnBodySections: __sfc_main$f,
201
+ CnTranslatedBadge: __sfc_main$g,
202
+ CnWidgetStyleEditorModal: __sfc_main$h,
203
+ CnRelationLinkModal: __sfc_main$i,
197
204
  Cog,
198
205
  Plus,
199
206
  Pencil,
200
207
  },
201
208
 
202
- mixins: [gridLayout],
209
+ mixins: [gridLayout, pageViews],
203
210
 
204
211
  inject: {
205
212
  /**
@@ -1093,6 +1100,37 @@ var script = {
1093
1100
  default: null,
1094
1101
  },
1095
1102
 
1103
+ /**
1104
+ * Whether the type eyebrow (the type label above the record name)
1105
+ * renders once the record resolves. `false` (manifest
1106
+ * `config.showTypeEyebrow: false`) drops it for a header that says
1107
+ * the type in a pill instead.
1108
+ *
1109
+ * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-a-detail-page-drops-its-eyebrow-and-shows-a-breadcrumb
1110
+ */
1111
+ showTypeEyebrow: {
1112
+ type: Boolean,
1113
+ default: true,
1114
+ },
1115
+
1116
+ /**
1117
+ * A breadcrumb line above the header (manifest `config.breadcrumb`):
1118
+ * `{ label, route?, params?, href? }` names the list the record
1119
+ * belongs to; the record's display name follows as the current
1120
+ * crumb. `label` goes through the host translate function and shows
1121
+ * as text; `icon` (an MDI name, e.g. `Home`) draws that icon instead,
1122
+ * with the label as its accessible name. Null (the default) draws no
1123
+ * trail.
1124
+ *
1125
+ * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-a-detail-page-drops-its-eyebrow-and-shows-a-breadcrumb
1126
+ * @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-a-declared-breadcrumb-label-shows-as-text
1127
+ * @type {{label: string, route?: string, params?: object, href?: string, icon?: string}|null}
1128
+ */
1129
+ breadcrumb: {
1130
+ type: Object,
1131
+ default: null,
1132
+ },
1133
+
1096
1134
  /**
1097
1135
  * A column of cards beside the body (manifest `config.sideColumn`),
1098
1136
  * for the record's facts: deadline, requester, handler, links. Each
@@ -1108,6 +1146,33 @@ var script = {
1108
1146
  default: () => [],
1109
1147
  },
1110
1148
 
1149
+ /**
1150
+ * Draw the header as a card (manifest `config.headerCard: true`): the
1151
+ * pills, the title and the actions on the page's surface colour inside
1152
+ * a bordered, rounded box, as the board's case header. Off by default,
1153
+ * which keeps the header on the page ground.
1154
+ *
1155
+ * @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-a-detail-header-can-be-a-card-that-holds-a-widget
1156
+ */
1157
+ headerCard: {
1158
+ type: Boolean,
1159
+ default: false,
1160
+ },
1161
+
1162
+ /**
1163
+ * The id of a widget declared in `widgets` to render inside the header,
1164
+ * on its own row under the title and the actions (manifest
1165
+ * `config.headerWidget`), e.g. the stages bars. It renders without a
1166
+ * card of its own and leaves the body grid. Empty (the default), or an
1167
+ * id that names no widget, renders nothing extra.
1168
+ *
1169
+ * @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-a-detail-header-can-be-a-card-that-holds-a-widget
1170
+ */
1171
+ headerWidget: {
1172
+ type: String,
1173
+ default: '',
1174
+ },
1175
+
1111
1176
  /**
1112
1177
  * Whether the current user administers this instance. Shows the
1113
1178
  * `adminOnly` header actions. `null` (the default) reads it from
@@ -1647,6 +1712,21 @@ var script = {
1647
1712
  .filter(Boolean)
1648
1713
  },
1649
1714
 
1715
+ /**
1716
+ * The widget the header holds (`headerWidget`): the definition in
1717
+ * `widgets` with that id, or null without the key or a match.
1718
+ *
1719
+ * @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-a-detail-header-can-be-a-card-that-holds-a-widget
1720
+ * @return {object|null} The widget definition, or null.
1721
+ */
1722
+ headerWidgetDef() {
1723
+ if (!this.headerWidget) {
1724
+ return null
1725
+ }
1726
+ const declared = Array.isArray(this.widgets) ? this.widgets : [];
1727
+ return declared.find((widget) => widget && widget.id === this.headerWidget) || null
1728
+ },
1729
+
1650
1730
  /**
1651
1731
  * The side column's widget definitions. A string names a widget
1652
1732
  * declared in `widgets`; an object is a definition of its own.
@@ -2216,6 +2296,33 @@ var script = {
2216
2296
  return this.objectDisplayName || this.resolvedTitle
2217
2297
  },
2218
2298
 
2299
+ /**
2300
+ * The breadcrumb trail: the declared crumb (a router target from
2301
+ * `route` + `params`, or an `href`), then the record's display name
2302
+ * as the current crumb. Empty without a `breadcrumb` label. The first
2303
+ * crumb carries `icon` only when the manifest declares one.
2304
+ *
2305
+ * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-a-detail-page-drops-its-eyebrow-and-shows-a-breadcrumb
2306
+ * @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-a-declared-breadcrumb-label-shows-as-text
2307
+ * @return {Array<{label: string, to?: object, href?: string, icon?: string}>}
2308
+ */
2309
+ breadcrumbCrumbs() {
2310
+ const crumb = this.breadcrumb;
2311
+ if (!crumb || typeof crumb.label !== 'string' || crumb.label === '') {
2312
+ return []
2313
+ }
2314
+ const first = { label: this.effectiveTranslate(crumb.label) };
2315
+ if (typeof crumb.icon === 'string' && crumb.icon !== '') {
2316
+ first.icon = crumb.icon;
2317
+ }
2318
+ if (crumb.route) {
2319
+ first.to = { name: crumb.route, ...(crumb.params ? { params: crumb.params } : {}) };
2320
+ } else if (crumb.href) {
2321
+ first.href = crumb.href;
2322
+ }
2323
+ return [first, { label: this.displayTitle }]
2324
+ },
2325
+
2219
2326
  /**
2220
2327
  * Effective translate function: the injected `cnTranslate` (the host
2221
2328
  * app's bound `t()`), identity by default.
@@ -2319,6 +2426,7 @@ var script = {
2319
2426
  && this.currentObject
2320
2427
  && !this.hasDefaultSlotContent
2321
2428
  && !this.hasGridLayout
2429
+ && !this.hasViews
2322
2430
  },
2323
2431
 
2324
2432
  /**
@@ -2398,6 +2506,55 @@ var script = {
2398
2506
  return this.bodyGridLayout.length > 0
2399
2507
  },
2400
2508
 
2509
+ /**
2510
+ * The chosen view's layout as the grid draws it: items that draw
2511
+ * nothing leave and their rows close, as for the body grid.
2512
+ *
2513
+ * @return {Array<object>}
2514
+ * @spec openspec/changes/view-switch-containers/specs/view-switch-containers/spec.md#requirement-a-view-with-nothing-to-draw-says-so
2515
+ */
2516
+ viewDisplayLayout() {
2517
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- reading the flag IS the effect, as in bodyGridLayout
2518
+ this.editingBody;
2519
+ return this.activeView ? this.closeDroppedRows(this.activeView.layout) : []
2520
+ },
2521
+
2522
+ /**
2523
+ * Whether the chosen view has nothing to draw.
2524
+ *
2525
+ * @return {boolean}
2526
+ * @spec openspec/changes/view-switch-containers/specs/view-switch-containers/spec.md#requirement-a-view-with-nothing-to-draw-says-so
2527
+ */
2528
+ viewIsEmpty() {
2529
+ return this.hasViews && this.viewDisplayLayout.length === 0
2530
+ },
2531
+
2532
+ /**
2533
+ * The grids the body draws: the body grid when it has items, then the
2534
+ * chosen view's grid in the region the view switch controls (left out
2535
+ * when the view is empty; the empty state takes the region then).
2536
+ *
2537
+ * @return {Array<{key: string, view: boolean, layout: Array<object>, attrs: object}>}
2538
+ * @spec openspec/changes/view-switch-containers/specs/view-switch-containers/spec.md#requirement-a-page-declares-views-that-each-hold-a-widget-grid
2539
+ */
2540
+ bodyGridSections() {
2541
+ const sections = this.hasBodyGrid ? [{ key: 'body', view: false, layout: this.bodyGridLayout, attrs: {} }] : [];
2542
+ if (this.hasViews && !this.viewIsEmpty) {
2543
+ sections.push({
2544
+ key: `view-${this.activeViewId}`,
2545
+ view: true,
2546
+ layout: this.viewDisplayLayout,
2547
+ attrs: {
2548
+ id: this.viewRegionId,
2549
+ role: 'region',
2550
+ 'aria-label': this.activeView.label,
2551
+ 'data-testid': 'cn-detail-page-view-region',
2552
+ },
2553
+ });
2554
+ }
2555
+ return sections
2556
+ },
2557
+
2401
2558
  /**
2402
2559
  * Whether the sidebar is rendered externally (via objectSidebarState inject)
2403
2560
  * rather than inline. When external, CnDetailPage only manages state —
@@ -3619,6 +3776,8 @@ var script = {
3619
3776
  */
3620
3777
  findWidget(item) {
3621
3778
  return this.bodyGridWidgets.find((w) => w.id === item.widgetId)
3779
+ || this.findViewWidget(item.widgetId)
3780
+ || undefined
3622
3781
  },
3623
3782
 
3624
3783
  /**
@@ -3667,6 +3826,10 @@ var script = {
3667
3826
  * it — CnPageRenderer's split pane forwards no slots, which is exactly
3668
3827
  * that fault and wants fixing, not concealing.
3669
3828
  *
3829
+ * The widget the header holds (`headerWidget`) draws nothing here
3830
+ * either: it is already drawn in the header.
3831
+ *
3832
+ * @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-a-detail-header-can-be-a-card-that-holds-a-widget
3670
3833
  * @param {object} item A layout item.
3671
3834
  * @return {boolean} False only when the item provably draws nothing.
3672
3835
  */
@@ -3677,6 +3840,11 @@ var script = {
3677
3840
  if (this.editingBody) {
3678
3841
  return true
3679
3842
  }
3843
+ // The widget the header holds (`headerWidget`) is drawn there, so
3844
+ // its grid cell would be the same widget twice.
3845
+ if (this.headerWidgetDef !== null && item.widgetId === this.headerWidgetDef.id) {
3846
+ return false
3847
+ }
3680
3848
  const widget = this.findWidget(item);
3681
3849
  if (!widget) {
3682
3850
  return true