@conduction/nextcloud-vue 2.56.0-beta.47 → 2.56.0-beta.49

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 (162) hide show
  1. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js +1 -1
  2. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +2 -1
  4. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
  5. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
  6. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js +28 -8
  7. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  8. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +37 -2
  9. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  10. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +46 -28
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +17 -10
  13. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  14. package/dist/esm/components/CnBrandBar/CnBrandBar.vue2.js.map +1 -1
  15. package/dist/esm/components/CnBrandBar/CnBrandBar.vue3.js.map +1 -1
  16. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue.js +1 -1
  17. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js +139 -3
  18. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js.map +1 -1
  19. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue3.js +60 -12
  20. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue3.js.map +1 -1
  21. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  22. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +91 -36
  23. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  24. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +115 -72
  25. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  26. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +32 -3
  27. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  28. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +7 -7
  29. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  30. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +56 -16
  31. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  32. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  33. package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue.js +9 -0
  34. package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue.js.map +1 -0
  35. package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue2.js +240 -0
  36. package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue2.js.map +1 -0
  37. package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue3.js +65 -0
  38. package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue3.js.map +1 -0
  39. package/dist/esm/components/CnDeviceFramesWidget/index.js +30 -0
  40. package/dist/esm/components/CnDeviceFramesWidget/index.js.map +1 -0
  41. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
  42. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +17 -1
  43. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  44. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +30 -31
  45. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  46. package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue.js +6 -0
  47. package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue.js.map +1 -0
  48. package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue2.js +90 -0
  49. package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue2.js.map +1 -0
  50. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +141 -7
  51. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  52. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +5 -2
  53. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  54. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue.js +8 -0
  55. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue.js.map +1 -0
  56. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue2.js +114 -0
  57. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue2.js.map +1 -0
  58. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue3.js +45 -0
  59. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue3.js.map +1 -0
  60. package/dist/esm/components/CnPageHeader/CnPageHeader.vue2.js +38 -2
  61. package/dist/esm/components/CnPageHeader/CnPageHeader.vue2.js.map +1 -1
  62. package/dist/esm/components/CnPageHeader/CnPageHeader.vue3.js +12 -2
  63. package/dist/esm/components/CnPageHeader/CnPageHeader.vue3.js.map +1 -1
  64. package/dist/esm/components/CnPagination/CnPagination.vue2.js.map +1 -1
  65. package/dist/esm/components/CnPagination/CnPagination.vue3.js +56 -51
  66. package/dist/esm/components/CnPagination/CnPagination.vue3.js.map +1 -1
  67. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  68. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +51 -4
  69. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  70. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +20 -2
  71. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  72. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue.js +1 -1
  73. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js +230 -25
  74. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js.map +1 -1
  75. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js +447 -221
  76. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js.map +1 -1
  77. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue.js +1 -1
  78. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js +24 -0
  79. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js.map +1 -1
  80. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js +3 -1
  81. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js.map +1 -1
  82. package/dist/esm/components/CnTabs/CnTabs.vue.js +1 -1
  83. package/dist/esm/components/CnTabs/CnTabs.vue2.js +39 -2
  84. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  85. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  86. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
  87. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +85 -8
  88. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  89. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +37 -9
  90. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  91. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue.js +1 -1
  92. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js.map +1 -1
  93. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js +4 -3
  94. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js.map +1 -1
  95. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  96. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  97. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue.js +1 -1
  98. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js +20 -0
  99. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js.map +1 -1
  100. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js +31 -20
  101. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js.map +1 -1
  102. package/dist/esm/composables/indexSources.js +9 -0
  103. package/dist/esm/composables/indexSources.js.map +1 -1
  104. package/dist/esm/index.js +1 -0
  105. package/dist/esm/index.js.map +1 -1
  106. package/dist/esm/l10n/en.json.js +18 -1
  107. package/dist/esm/l10n/en.json.js.map +1 -1
  108. package/dist/esm/l10n/nl.json.js +18 -1
  109. package/dist/esm/l10n/nl.json.js.map +1 -1
  110. package/dist/esm/library-style.js +1 -1
  111. package/dist/esm/nextcloud-vue.css +712 -330
  112. package/dist/esm/utils/dueText.js +49 -8
  113. package/dist/esm/utils/dueText.js.map +1 -1
  114. package/dist/esm/utils/libraryWidgetKeys.js +1 -0
  115. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  116. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  117. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  118. package/dist/nextcloud-vue.cjs.js +8275 -6302
  119. package/dist/nextcloud-vue.cjs.js.map +2 -2
  120. package/dist/nextcloud-vue.css +712 -330
  121. package/l10n/en.json +18 -1
  122. package/l10n/nl.json +18 -1
  123. package/package.json +1 -1
  124. package/src/components/CnActionsMenu/CnActionsMenu.vue +3 -1
  125. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +39 -1
  126. package/src/components/CnAppRoot/CnAppRoot.vue +20 -0
  127. package/src/components/CnBrandBar/CnBrandBar.vue +5 -4
  128. package/src/components/CnCountdownWidget/CnCountdownWidget.vue +222 -6
  129. package/src/components/CnDashboardPage/CnDashboardPage.vue +96 -2
  130. package/src/components/CnDataTable/CnDataTable.md +1 -0
  131. package/src/components/CnDataTable/CnDataTable.vue +39 -10
  132. package/src/components/CnDetailPage/CnDetailPage.vue +55 -15
  133. package/src/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue +360 -0
  134. package/src/components/CnDeviceFramesWidget/index.js +29 -0
  135. package/src/components/CnFactsWidget/CnFactsWidget.vue +46 -2
  136. package/src/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue +107 -0
  137. package/src/components/CnIndexPage/CnIndexPage.vue +144 -7
  138. package/src/components/CnPageHeader/CnHeaderTrail.vue +140 -0
  139. package/src/components/CnPageHeader/CnPageHeader.md +11 -0
  140. package/src/components/CnPageHeader/CnPageHeader.vue +42 -0
  141. package/src/components/CnPagination/CnPagination.vue +10 -2
  142. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +84 -4
  143. package/src/components/CnRegisterMapping/CnRegisterMapping.md +10 -0
  144. package/src/components/CnRegisterMapping/CnRegisterMapping.vue +511 -28
  145. package/src/components/CnSettingsPage/CnSettingsPage.vue +26 -0
  146. package/src/components/CnTabs/CnTabs.vue +39 -2
  147. package/src/components/CnTabsWidget/CnTabsWidget.vue +106 -13
  148. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +4 -3
  149. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  150. package/src/components/CnWidgetWrapper/CnWidgetWrapper.md +8 -0
  151. package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +31 -2
  152. package/src/components/index.js +1 -0
  153. package/src/composables/indexSources.js +9 -0
  154. package/src/css/dialog.css +14 -0
  155. package/src/css/look-board-index.css +27 -1
  156. package/src/css/look-board.css +27 -0
  157. package/src/css/page-header.css +16 -0
  158. package/src/index.js +1 -0
  159. package/src/schemas/app-manifest-v2.schema.json +27 -15
  160. package/src/utils/dueText.js +49 -8
  161. package/src/utils/libraryWidgetKeys.js +1 -0
  162. package/src/utils/validateManifestV2.compiled.js +3 -3
@@ -34,7 +34,29 @@
34
34
  <!-- @slot title-meta Content on the title's row, right of the title (e.g. a page-wide picker). Fill it from a manifest with `page.slots: { "title-meta": "<RegistryName>" }`. -->
35
35
  <slot name="title-meta" />
36
36
  </div>
37
- <p v-if="subtitleText" class="cn-dashboard-page__description" data-testid="cn-dashboard-page-subtitle">
37
+ <CnHeaderTrail
38
+ :breadcrumb="breadcrumb"
39
+ :current="title ? resolvedTitle : ''"
40
+ :meta="headerMeta ? effectiveTranslate(headerMeta) : ''" />
41
+ <!-- `dateRange.placement: "description"`: the segmented period
42
+ group ends the description line ("Week 41 · alle teams ·
43
+ [Deze week | Dit kwartaal | Dit jaar]"). -->
44
+ <div
45
+ v-if="datePeriodInDescription"
46
+ class="cn-dashboard-page__description cn-dashboard-page__description--with-period"
47
+ data-testid="cn-dashboard-page-subtitle">
48
+ <span v-if="subtitleText">{{ subtitleText }}</span>
49
+ <span v-if="subtitleText" aria-hidden="true">&middot;</span>
50
+ <CnSegmentedControl
51
+ size="compact"
52
+ mode="toggle"
53
+ :options="segmentedOptions"
54
+ :modelValue="segmentedValue"
55
+ :ariaLabel="datePeriodLabel"
56
+ data-testid="cn-dashboard-page-date-segmented"
57
+ @update:modelValue="onSegmentPick" />
58
+ </div>
59
+ <p v-else-if="subtitleText" class="cn-dashboard-page__description" data-testid="cn-dashboard-page-subtitle">
38
60
  {{ subtitleText }}
39
61
  </p>
40
62
  </div>
@@ -185,7 +207,7 @@
185
207
  handling, persistence, and event emission lives in
186
208
  `onDateRangeChange`. -->
187
209
  <div
188
- v-if="showHeaderDateRange"
210
+ v-if="showHeaderDateRange && !datePeriodInDescription"
189
211
  class="cn-dashboard-page__date-range"
190
212
  :class="{
191
213
  'cn-dashboard-page__date-range--pills': dateRangeControl === 'pills',
@@ -436,6 +458,7 @@
436
458
  v-else
437
459
  :title="getWidgetTitle(item)"
438
460
  :eyebrow="getWidgetEyebrow(item)"
461
+ :subtitle="getWidgetSubtitle(item)"
439
462
  :showTitle="false"
440
463
  :showRefresh="false">
441
464
  <div class="cn-dashboard-page__unknown">
@@ -498,6 +521,7 @@
498
521
  v-if="missingRequiredApp(item)"
499
522
  :title="getWidgetTitle(item)"
500
523
  :eyebrow="getWidgetEyebrow(item)"
524
+ :subtitle="getWidgetSubtitle(item)"
501
525
  :showTitle="widgetShowTitle(item)">
502
526
  <!-- Compact on purpose: a two-row tile is about 150px, less
503
527
  a title row. NcEmptyContent's 64px icon, 20px name,
@@ -537,6 +561,7 @@
537
561
  :widgetId="item.widgetId"
538
562
  :title="getWidgetTitle(item)"
539
563
  :eyebrow="getWidgetEyebrow(item)"
564
+ :subtitle="getWidgetSubtitle(item)"
540
565
  :iconUrl="getWidgetIconUrl(item)"
541
566
  :iconClass="getWidgetIconClass(item)"
542
567
  :showTitle="widgetShowTitle(item)"
@@ -619,6 +644,7 @@
619
644
  :class="{ 'cn-dashboard-page__chart-fit': isChartFitted(item) }"
620
645
  :title="getWidgetTitle(item)"
621
646
  :eyebrow="getWidgetEyebrow(item)"
647
+ :subtitle="getWidgetSubtitle(item)"
622
648
  :iconUrl="getWidgetIconUrl(item)"
623
649
  :iconClass="getWidgetIconClass(item)"
624
650
  :showTitle="widgetShowTitle(item)"
@@ -698,6 +724,7 @@
698
724
  :widgetId="item.widgetId"
699
725
  :title="getWidgetTitle(item)"
700
726
  :eyebrow="getWidgetEyebrow(item)"
727
+ :subtitle="getWidgetSubtitle(item)"
701
728
  :iconUrl="getWidgetIconUrl(item)"
702
729
  :iconClass="getWidgetIconClass(item)"
703
730
  :showTitle="widgetShowTitle(item)"
@@ -726,6 +753,7 @@
726
753
  :widgetId="item.widgetId"
727
754
  :title="getWidgetTitle(item)"
728
755
  :eyebrow="getWidgetEyebrow(item)"
756
+ :subtitle="getWidgetSubtitle(item)"
729
757
  :iconUrl="getWidgetIconUrl(item)"
730
758
  :iconClass="getWidgetIconClass(item)"
731
759
  :showTitle="widgetShowTitle(item)"
@@ -766,6 +794,7 @@
766
794
  :widgetId="item.widgetId"
767
795
  :title="getWidgetTitle(item)"
768
796
  :eyebrow="getWidgetEyebrow(item)"
797
+ :subtitle="getWidgetSubtitle(item)"
769
798
  :iconUrl="getWidgetIconUrl(item)"
770
799
  :iconClass="getWidgetIconClass(item)"
771
800
  :showTitle="widgetShowTitle(item)"
@@ -792,6 +821,7 @@
792
821
  :widgetId="item.widgetId"
793
822
  :title="getWidgetTitle(item)"
794
823
  :eyebrow="getWidgetEyebrow(item)"
824
+ :subtitle="getWidgetSubtitle(item)"
795
825
  :showTitle="widgetShowTitle(item)"
796
826
  :showActions="widgetShowActions(item)"
797
827
  :showRefresh="getWidgetShowRefresh(item)"
@@ -893,6 +923,7 @@
893
923
  v-else
894
924
  :title="getWidgetTitle(item)"
895
925
  :eyebrow="getWidgetEyebrow(item)"
926
+ :subtitle="getWidgetSubtitle(item)"
896
927
  :showTitle="widgetShowTitle(item)"
897
928
  :showRefresh="false">
898
929
  <div class="cn-dashboard-page__unknown">
@@ -972,6 +1003,7 @@ import CnDashboardGrid from '../CnDashboardGrid/CnDashboardGrid.vue'
972
1003
  import CnDateRangePicker, { DEFAULT_DATE_RANGE_PRESETS, resolvePresetWindow, translatePresetLabel } from '../CnDateRangePicker/CnDateRangePicker.vue'
973
1004
  import CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'
974
1005
  import CnKpiGrid from '../CnKpiGrid/CnKpiGrid.vue'
1006
+ import CnHeaderTrail from '../CnPageHeader/CnHeaderTrail.vue'
975
1007
  import CnSegmentedControl from '../CnSegmentedControl/CnSegmentedControl.vue'
976
1008
  import CnStatsBlockWidget from '../CnStatsBlockWidget/CnStatsBlockWidget.vue'
977
1009
  import CnTileWidget from '../CnTileWidget/CnTileWidget.vue'
@@ -1115,6 +1147,7 @@ export default {
1115
1147
  name: 'CnDashboardPage',
1116
1148
 
1117
1149
  components: {
1150
+ CnHeaderTrail,
1118
1151
  NcActions,
1119
1152
  NcActionButton,
1120
1153
  NcActionInput,
@@ -1214,6 +1247,30 @@ export default {
1214
1247
  default: '',
1215
1248
  },
1216
1249
 
1250
+ /**
1251
+ * A breadcrumb under the title (manifest `config.breadcrumb`): one
1252
+ * crumb `{ label, route?, params?, href?, icon?, separator? }` or a
1253
+ * list; the title follows as the current crumb. Drawn as the detail
1254
+ * header's breadcrumb row. Null (the default) draws none.
1255
+ *
1256
+ * @type {object|Array<object>|null}
1257
+ * @spec openspec/changes/page-header-breadcrumb/specs/page-header/spec.md#requirement-a-list-dashboard-or-settings-page-can-show-a-breadcrumb-and-a-meta-note
1258
+ */
1259
+ breadcrumb: {
1260
+ type: [Object, Array],
1261
+ default: null,
1262
+ },
1263
+
1264
+ /**
1265
+ * A note after the breadcrumb and a middle dot (manifest `config.headerMeta`).
1266
+ *
1267
+ * @spec openspec/changes/page-header-breadcrumb/specs/page-header/spec.md#requirement-a-list-dashboard-or-settings-page-can-show-a-breadcrumb-and-a-meta-note
1268
+ */
1269
+ headerMeta: {
1270
+ type: String,
1271
+ default: '',
1272
+ },
1273
+
1217
1274
  /** Page description (shown below title) */
1218
1275
  description: {
1219
1276
  type: String,
@@ -2261,6 +2318,20 @@ export default {
2261
2318
  return this.dateRangeEnabled && this.dateRange.showHeaderPicker !== false
2262
2319
  },
2263
2320
 
2321
+ /**
2322
+ * Whether the segmented period group sits at the end of the
2323
+ * description line (`dateRange.placement: "description"`) instead of
2324
+ * in a row of its own. Only for the segmented control without a
2325
+ * custom range, whose from/to popover keeps the row of its own.
2326
+ *
2327
+ * @return {boolean}
2328
+ * @spec openspec/changes/page-header-breadcrumb/specs/page-header/spec.md#requirement-a-dashboard-can-show-its-period-on-the-description-line
2329
+ */
2330
+ datePeriodInDescription() {
2331
+ return this.showHeaderDateRange && this.dateRangeControl === 'segmented'
2332
+ && this.dateRange.placement === 'description' && !this.hasCustomPreset
2333
+ },
2334
+
2264
2335
  /**
2265
2336
  * The preset list passed to the picker. Falls back to the
2266
2337
  * `DEFAULT_DATE_RANGE_PRESETS` constant when the consumer
@@ -2972,6 +3043,7 @@ export default {
2972
3043
  widgetId: item.widgetId,
2973
3044
  title: this.getWidgetTitle(item),
2974
3045
  eyebrow: this.getWidgetEyebrow(item),
3046
+ subtitle: this.getWidgetSubtitle(item),
2975
3047
  showTitle: this.widgetShowTitle(item),
2976
3048
  showActions: this.widgetShowActions(item),
2977
3049
  showRefresh: this.getWidgetShowRefresh(item),
@@ -4090,6 +4162,20 @@ export default {
4090
4162
  this.showWidgetConfig = false
4091
4163
  },
4092
4164
 
4165
+ /**
4166
+ * The line under a widget's title (`subtitle` on the widget
4167
+ * definition), translated; empty when it carries none.
4168
+ *
4169
+ * @param {object} item Layout item.
4170
+ * @return {string}
4171
+ * @spec openspec/changes/page-header-breadcrumb/specs/page-header/spec.md#requirement-a-dashboard-widget-card-can-carry-a-line-under-its-title
4172
+ */
4173
+ getWidgetSubtitle(item) {
4174
+ const def = this.getWidgetDef(item.widgetId)
4175
+ const subtitle = def && typeof def.subtitle === 'string' ? def.subtitle : ''
4176
+ return subtitle ? this.effectiveTranslate(subtitle) : ''
4177
+ },
4178
+
4093
4179
  /**
4094
4180
  * The line above a widget's title (`eyebrow` on the widget
4095
4181
  * definition: "Saldo per week" above "Prognose 13 weken"), through the
@@ -4903,6 +4989,14 @@ export default {
4903
4989
  margin-bottom: 16px;
4904
4990
  }
4905
4991
 
4992
+ /* The period group at the end of the description line. */
4993
+ .cn-dashboard-page__description--with-period {
4994
+ align-items: center;
4995
+ display: flex;
4996
+ flex-wrap: wrap;
4997
+ gap: 8px;
4998
+ }
4999
+
4906
5000
  .cn-dashboard-page__date-segmented {
4907
5001
  display: flex;
4908
5002
  align-items: center;
@@ -198,6 +198,7 @@ export default {
198
198
  | `rowIndicatorCap` | Number | `3` | How many declared indicators render on the row before the rest move into the row menu, reachable as `indicatorsFor(row).overflow`. |
199
199
  | `selectAllLabel` | String | `'Select all rows'` | Accessible name (`aria-label`) for the header select-all checkbox, so screen readers announce a named control (WCAG 4.1.2) |
200
200
  | `selectRowLabel` | String | `'Select row'` | Accessible name (`aria-label`) for each per-row select checkbox, so screen readers announce a named control (WCAG 4.1.2) |
201
+ | `showUnread` | Boolean | `true` | Draw an unread record (`@self.unread`) in bold with the unread dot. Off, every row reads alike. CnIndexPage turns it off under the board look unless the page offers the Unread lens. |
201
202
  | `rowTitle` | String | `'link'` | Board look only: how the title column draws its title. `link` underlines the title (not its secondary line). `plain` draws it as 15px bold text without an underline over a 13px muted secondary line, as the PqTickets board does; the row stays clickable. The Nextcloud look ignores it. |
202
203
  | `hideHeader` | Boolean | `false` | Hide the column-header row (`<thead>`) — for compact dashboard list widgets that want a plain bordered-row list without column labels |
203
204
  | `pinnedCount` | Number | `0` | How many data columns are pinned to the start of the table. They, and the selection and icon columns, stay in view when the table scrolls sideways |
@@ -130,7 +130,7 @@
130
130
  col.class || '',
131
131
  pinClass(leadingCount + colIndex),
132
132
  isGrowColumn(col) ? 'cn-table-col--grow' : '',
133
- colIndex === 0 ? 'cn-table-head--title' : '',
133
+ colIndex === titleColumnIndex ? 'cn-table-head--title' : '',
134
134
  ]"
135
135
  :style="{ ...(col.width ? { width: col.width } : {}), ...pinStyle(leadingCount + colIndex) }"
136
136
  :aria-sort="ariaSortFor(col)"
@@ -259,7 +259,7 @@
259
259
  <td
260
260
  v-for="(col, colIndex) in effectiveColumns"
261
261
  :key="col.key"
262
- :class="[col.class || '', col.cellClass || '', cellClass ? cellClass(row, col) : '', pinClass(leadingCount + colIndex), colIndex === 0 ? 'cn-table-col--title' : '', isGrowColumn(col) ? 'cn-table-col--grow' : '']"
262
+ :class="[col.class || '', col.cellClass || '', cellClass ? cellClass(row, col) : '', pinClass(leadingCount + colIndex), colIndex === titleColumnIndex ? 'cn-table-col--title' : '', isGrowColumn(col) ? 'cn-table-col--grow' : '']"
263
263
  :style="{ ...(col.width ? { maxWidth: col.width } : {}), ...pinStyle(leadingCount + colIndex) }"
264
264
  @mouseenter="titleWhenClipped">
265
265
  <!-- A row with a `rowClickRoute` is a real link: this anchor
@@ -267,7 +267,7 @@
267
267
  the URL, a middle or ctrl click opens a new tab natively,
268
268
  and the row is reachable with Tab. The row's own click
269
269
  handlers treat it as a nested control and leave it be. -->
270
- <a v-if="colIndex === 0 && rowLinks[String(row[rowKey])]"
270
+ <a v-if="colIndex === titleColumnIndex && rowLinks[String(row[rowKey])]"
271
271
  class="cn-table-row__link"
272
272
  :href="rowLinks[String(row[rowKey])].href"
273
273
  :aria-label="rowLinkLabel(row, col)"
@@ -281,9 +281,9 @@
281
281
  the record is locked. It renders nothing when unlocked,
282
282
  so an unlocked table is byte-for-byte what it was. -->
283
283
  <CnUnreadMarker
284
- v-if="colIndex === 0 && isUnreadRow(row)" />
284
+ v-if="colIndex === titleColumnIndex && isUnreadRow(row)" />
285
285
  <CnLockIndicator
286
- v-if="colIndex === 0"
286
+ v-if="colIndex === titleColumnIndex"
287
287
  :object="row"
288
288
  :size="16" />
289
289
  <!-- Declared state indicators ride the first data cell
@@ -293,7 +293,7 @@
293
293
  however the record is shaped. Past the cap the rest move
294
294
  into the row menu, because a row of nine icons is not a
295
295
  row anyone can triage at a glance. -->
296
- <span v-if="colIndex === 0 && indicatorsFor(row).shown.length > 0"
296
+ <span v-if="colIndex === titleColumnIndex && indicatorsFor(row).shown.length > 0"
297
297
  class="cn-table-row-indicators"
298
298
  data-testid="cn-row-indicators">
299
299
  <span v-for="indicator in indicatorsFor(row).shown"
@@ -338,7 +338,7 @@
338
338
  file names it (OpenRegister `@self.matchedFile`): plain text,
339
339
  file name only, under the first cell. -->
340
340
  <span
341
- v-if="colIndex === 0 && matchedFileOf(row)"
341
+ v-if="colIndex === titleColumnIndex && matchedFileOf(row)"
342
342
  class="cn-table-cell__secondary cn-table-cell__matched-file"
343
343
  data-testid="cn-row-matched-file">{{ matchedFileLabel(row) }}</span>
344
344
  </td>
@@ -532,7 +532,10 @@ export default {
532
532
  * formats through `fillTemplate`), or a function of the row. Empty fields leave their placeholder blank; a line that
533
533
  * resolves to nothing is not drawn.
534
534
  *
535
- * @type {Array<{key: string, label: string, description: string, sortable: boolean, width: string, class: string, cellClass: string, secondary: (string|((row: object) => string))}|string>}
535
+ * `link: true` makes that column the one that names the row: it carries
536
+ * the row link and the title look. Without it the first column does.
537
+ *
538
+ * @type {Array<{key: string, label: string, description: string, sortable: boolean, width: string, class: string, cellClass: string, link: boolean, secondary: (string|((row: object) => string))}|string>}
536
539
  */
537
540
  columns: {
538
541
  type: Array,
@@ -742,6 +745,17 @@ export default {
742
745
  default: '',
743
746
  },
744
747
 
748
+ /**
749
+ * Draw an unread record (`@self.unread`) in bold with the unread dot.
750
+ * Off, every row reads alike whatever its read state.
751
+ *
752
+ * @spec openspec/changes/screens-index-board-gaps/specs/index-list-board-look/spec.md#requirement-unread-rows-are-marked-only-where-the-reader-asked-for-them
753
+ */
754
+ showUnread: {
755
+ type: Boolean,
756
+ default: true,
757
+ },
758
+
745
759
  /**
746
760
  * How the board look draws the title column: `link` (the default) is
747
761
  * the 15px weight 600 underlined title of the DqZaken board; `plain` is
@@ -1045,6 +1059,20 @@ export default {
1045
1059
  * @return {string|null} The column key, or null when not fitting.
1046
1060
  * @spec openspec/changes/narrow-object-table-widgets/specs/narrow-object-table-widgets/spec.md#requirement-a-narrow-object-table-keeps-its-trailing-columns-in-view
1047
1061
  */
1062
+ /**
1063
+ * The column that names the row: it carries the row link, the title
1064
+ * look and the unread, lock and indicator marks. The first column with
1065
+ * `link: true`, else the first column (DqTeamwachtrij puts the waiting
1066
+ * time first and links the case title in the second).
1067
+ *
1068
+ * @return {number} The index in `effectiveColumns`.
1069
+ * @spec openspec/changes/screens-index-board-gaps/specs/index-list-board-look/spec.md#requirement-a-column-can-carry-the-row-link
1070
+ */
1071
+ titleColumnIndex() {
1072
+ const index = this.effectiveColumns.findIndex((c) => c && c.link === true)
1073
+ return index === -1 ? 0 : index
1074
+ },
1075
+
1048
1076
  growColumnKey() {
1049
1077
  if (!this.fitWidth || this.effectiveColumns.length === 0) {
1050
1078
  return null
@@ -1365,13 +1393,14 @@ export default {
1365
1393
 
1366
1394
  /**
1367
1395
  * Whether a row's record changed since the user last looked
1368
- * (`@self.unread`). A row without the marker renders as it always did.
1396
+ * (`@self.unread`), when `showUnread` is on. A row without the marker
1397
+ * renders as it always did.
1369
1398
  *
1370
1399
  * @param {object} row The row.
1371
1400
  * @return {boolean} True when the row is unread.
1372
1401
  */
1373
1402
  isUnreadRow(row) {
1374
- return !!(row && row['@self'] && row['@self'].unread === true)
1403
+ return this.showUnread && !!(row && row['@self'] && row['@self'].unread === true)
1375
1404
  },
1376
1405
 
1377
1406
  /**
@@ -2101,7 +2101,7 @@ export default {
2101
2101
  default: false,
2102
2102
  },
2103
2103
 
2104
- /** Label for the header Edit button. Defaults to a translated "Edit". */
2104
+ /** Label for the header Edit button, run through the host translate. Defaults to a translated "Edit". */
2105
2105
  editLabel: {
2106
2106
  type: String,
2107
2107
  default: '',
@@ -3416,14 +3416,40 @@ export default {
3416
3416
 
3417
3417
  /**
3418
3418
  * The side column's widget definitions. A string names a widget
3419
- * declared in `widgets`; an object is a definition of its own.
3419
+ * declared in `widgets`; an object is a definition of its own. A
3420
+ * definition whose `visibleWhen` does not hold for the record (or the
3421
+ * records `visibilityContext` names) is left out, exactly as in the
3422
+ * body grid; edit mode shows them all.
3420
3423
  *
3421
3424
  * @return {Array<object>} The definitions that resolve, each with an `id`.
3422
3425
  * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-side-column
3426
+ * @spec openspec/changes/detail-side-visible-when/specs/detail-page/spec.md#requirement-a-side-column-widget-shows-only-while-its-visiblewhen-holds
3423
3427
  */
3424
3428
  sideColumnWidgets() {
3429
+ const resolved = this.declaredSideWidgets.filter((widget) => this.sideWidgetVisible(widget))
3430
+ if (!this.isBoard) {
3431
+ return resolved
3432
+ }
3433
+ // The board look: the notice first, the History card last.
3434
+ const rank = (widget) => (widget.type === 'banner' ? 0 : isActivityWidget(widget) ? 2 : 1)
3435
+ return resolved
3436
+ .map((widget, index) => ({ widget, index }))
3437
+ .sort((a, b) => (rank(a.widget) - rank(b.widget)) || (a.index - b.index))
3438
+ .map(({ widget }) => widget)
3439
+ },
3440
+
3441
+ /**
3442
+ * The side column's entries resolved to definitions, before any
3443
+ * `visibleWhen` is read: a string names a widget declared in
3444
+ * `widgets`, an object is a definition of its own (given an id when it
3445
+ * has none).
3446
+ *
3447
+ * @return {Array<object>}
3448
+ * @spec openspec/changes/detail-side-visible-when/specs/detail-page/spec.md#requirement-a-side-column-widget-shows-only-while-its-visiblewhen-holds
3449
+ */
3450
+ declaredSideWidgets() {
3425
3451
  const declared = Array.isArray(this.widgets) ? this.widgets : []
3426
- const resolved = (Array.isArray(this.sideColumn) ? this.sideColumn : [])
3452
+ return (Array.isArray(this.sideColumn) ? this.sideColumn : [])
3427
3453
  .map((entry, index) => {
3428
3454
  if (typeof entry === 'string') {
3429
3455
  return declared.find((widget) => widget && widget.id === entry) || null
@@ -3434,15 +3460,6 @@ export default {
3434
3460
  return entry.id ? entry : { ...entry, id: `cn-side-${index}` }
3435
3461
  })
3436
3462
  .filter(Boolean)
3437
- if (!this.isBoard) {
3438
- return resolved
3439
- }
3440
- // The board look: the notice first, the History card last.
3441
- const rank = (widget) => (widget.type === 'banner' ? 0 : isActivityWidget(widget) ? 2 : 1)
3442
- return resolved
3443
- .map((widget, index) => ({ widget, index }))
3444
- .sort((a, b) => (rank(a.widget) - rank(b.widget)) || (a.index - b.index))
3445
- .map(({ widget }) => widget)
3446
3463
  },
3447
3464
 
3448
3465
  /**
@@ -3763,12 +3780,14 @@ export default {
3763
3780
  },
3764
3781
 
3765
3782
  /**
3766
- * Label for the header Edit button.
3783
+ * Label for the header Edit button: the manifest's `editLabel` through
3784
+ * the host translate, else the library's translated "Edit".
3767
3785
  *
3768
3786
  * @return {string}
3787
+ * @spec openspec/changes/detail-edit-label-translate/specs/detail-page/spec.md#requirement-the-edit-button-label-follows-the-reader-s-language
3769
3788
  */
3770
3789
  editActionLabel() {
3771
- return this.editLabel || t('nextcloud-vue', 'Edit')
3790
+ return this.editLabel ? this.effectiveTranslate(this.editLabel) : t('nextcloud-vue', 'Edit')
3772
3791
  },
3773
3792
 
3774
3793
  /**
@@ -4022,7 +4041,7 @@ export default {
4022
4041
  if (!this.widgetVisibilityData || this.visibilityRowsPending) {
4023
4042
  return ''
4024
4043
  }
4025
- const defs = [...(this.bodyGridWidgets || []), ...((this.activeView && this.activeView.widgets) || [])]
4044
+ const defs = [...(this.bodyGridWidgets || []), ...((this.activeView && this.activeView.widgets) || []), ...this.declaredSideWidgets]
4026
4045
  const remote = defs.filter((def) => def && def.visibleWhen && typeof def.visibleWhen === 'object'
4027
4046
  && !isLocallyDecidableVisibleWhen(def.visibleWhen))
4028
4047
  if (remote.length === 0) {
@@ -5897,6 +5916,27 @@ export default {
5897
5916
  return compactLayoutRows(drawable)
5898
5917
  },
5899
5918
 
5919
+ /**
5920
+ * Whether a side column widget shows: always in edit mode or without a
5921
+ * `visibleWhen`, else while it holds for the record and the records
5922
+ * `visibilityContext` names, read by the same `detailWidgetVisible` the
5923
+ * body grid uses (local field conditions, `@context.<name>.*`, and the
5924
+ * answers of endpoint and source conditions).
5925
+ *
5926
+ * @param {object} widget A side column widget definition.
5927
+ * @return {boolean}
5928
+ * @spec openspec/changes/detail-side-visible-when/specs/detail-page/spec.md#requirement-a-side-column-widget-shows-only-while-its-visiblewhen-holds
5929
+ */
5930
+ sideWidgetVisible(widget) {
5931
+ if (this.editingBody || widget.visibleWhen === undefined || widget.visibleWhen === null) {
5932
+ return true
5933
+ }
5934
+ return detailWidgetVisible(widget, this.widgetVisibilityData, {
5935
+ pending: this.visibilityRowsPending,
5936
+ answers: this.widgetConditionAnswers,
5937
+ })
5938
+ },
5939
+
5900
5940
  /**
5901
5941
  * Whether a layout item has anything to draw, so a grid cell is worth
5902
5942
  * spending on it.