@conduction/nextcloud-vue 2.77.0 → 2.79.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.
Files changed (159) hide show
  1. package/dist/bundled-packages.json +0 -2
  2. package/dist/esm/bundled-packages.json +0 -2
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +113 -104
  5. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  6. package/dist/esm/components/CnAiCompanion/CnAiCompanion.vue2.js +23 -0
  7. package/dist/esm/components/CnAiCompanion/CnAiCompanion.vue2.js.map +1 -1
  8. package/dist/esm/components/CnAiCompanion/CnAiCompanion.vue3.js.map +1 -1
  9. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +12 -2
  10. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  12. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +2 -1
  13. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  14. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  15. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +117 -0
  16. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  17. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +122 -97
  18. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  19. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +37 -0
  20. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  21. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js +2 -2
  22. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  23. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +168 -5
  24. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  25. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +140 -92
  26. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  27. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +84 -2
  28. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  29. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +4 -2
  30. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  31. package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue.js +1 -1
  32. package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue2.js +45 -2
  33. package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue2.js.map +1 -1
  34. package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue3.js.map +1 -1
  35. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +61 -1
  36. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  37. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  38. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +173 -5
  39. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  40. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +181 -99
  41. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  42. package/dist/esm/components/CnFilesBrowser/filesBrowser.js +29 -11
  43. package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
  44. package/dist/esm/components/CnFilesBrowser/openRegisterSource.js +252 -0
  45. package/dist/esm/components/CnFilesBrowser/openRegisterSource.js.map +1 -0
  46. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +8 -2
  47. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  48. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  49. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +4 -1
  50. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
  51. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
  52. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +31 -2
  53. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  54. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +3 -2
  55. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  56. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +100 -3
  57. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  58. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +4 -2
  59. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  60. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +3 -2
  61. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
  62. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
  63. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
  64. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js +1 -1
  65. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
  66. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +36 -0
  67. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  68. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +35 -23
  69. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  70. package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue2.js +6 -2
  71. package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue2.js.map +1 -1
  72. package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue3.js.map +1 -1
  73. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +30 -1
  74. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  75. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  76. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -0
  77. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  78. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  79. package/dist/esm/composables/useAppManifest.js +8 -3
  80. package/dist/esm/composables/useAppManifest.js.map +1 -1
  81. package/dist/esm/composables/useFormDraft.js +11 -1
  82. package/dist/esm/composables/useFormDraft.js.map +1 -1
  83. package/dist/esm/composables/useObjectPresence.js +15 -4
  84. package/dist/esm/composables/useObjectPresence.js.map +1 -1
  85. package/dist/esm/index.js +1 -0
  86. package/dist/esm/index.js.map +1 -1
  87. package/dist/esm/l10n/en.json.js +68 -1
  88. package/dist/esm/l10n/en.json.js.map +1 -1
  89. package/dist/esm/l10n/nl.json.js +113 -2
  90. package/dist/esm/l10n/nl.json.js.map +1 -1
  91. package/dist/esm/nextcloud-vue.css +526 -16
  92. package/dist/esm/utils/badgeVariants.js +32 -0
  93. package/dist/esm/utils/badgeVariants.js.map +1 -0
  94. package/dist/esm/utils/boardDate.js +193 -0
  95. package/dist/esm/utils/boardDate.js.map +1 -0
  96. package/dist/esm/utils/dynamicProperties.js +5 -1
  97. package/dist/esm/utils/dynamicProperties.js.map +1 -1
  98. package/dist/esm/utils/validateManifestV2.compiled.js +57 -6
  99. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  100. package/dist/nextcloud-vue.cjs.js +2569 -811
  101. package/dist/nextcloud-vue.cjs.js.map +2 -2
  102. package/dist/nextcloud-vue.css +526 -16
  103. package/l10n/en.json +68 -1
  104. package/l10n/nl.json +113 -2
  105. package/package.json +1 -1
  106. package/scripts/build-validators.js +26 -0
  107. package/src/components/CnActionsBar/CnActionsBar.vue +48 -39
  108. package/src/components/CnAiCompanion/CnAiCompanion.vue +23 -0
  109. package/src/components/CnAppRoot/CnAppRoot.vue +12 -2
  110. package/src/components/CnBoardView/CnBoardView.vue +2 -1
  111. package/src/components/CnCellRenderer/CnCellRenderer.md +1 -0
  112. package/src/components/CnCellRenderer/CnCellRenderer.vue +169 -10
  113. package/src/components/CnChartWidget/CnChartWidget.vue +39 -2
  114. package/src/components/CnDashboardPage/CnDashboardPage.vue +248 -8
  115. package/src/components/CnDataTable/CnDataTable.md +2 -0
  116. package/src/components/CnDataTable/CnDataTable.vue +88 -4
  117. package/src/components/CnDateRangePicker/CnDateRangePicker.vue +44 -1
  118. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +61 -1
  119. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +232 -11
  120. package/src/components/CnFilesBrowser/filesBrowser.js +43 -2
  121. package/src/components/CnFilesBrowser/index.js +1 -0
  122. package/src/components/CnFilesBrowser/openRegisterSource.js +248 -0
  123. package/src/components/CnFormDialog/CnFormDialog.vue +8 -2
  124. package/src/components/CnFormPage/CnFormPage.vue +4 -1
  125. package/src/components/CnIndexPage/CnIndexPage.vue +33 -3
  126. package/src/components/CnObjectSidebar/CnFilesTab.vue +103 -4
  127. package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +3 -2
  128. package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +1 -1
  129. package/src/components/CnStatWidget/CnStatWidget.vue +52 -2
  130. package/src/components/CnStatusBadge/CnStatusBadge.vue +6 -2
  131. package/src/components/CnTabsWidget/CnTabsWidget.vue +30 -1
  132. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -0
  133. package/src/components/index.js +1 -1
  134. package/src/composables/__tests__/useObjectPresence.spec.js +24 -0
  135. package/src/composables/useAppManifest.js +8 -3
  136. package/src/composables/useFormDraft.js +11 -1
  137. package/src/composables/useObjectPresence.js +15 -4
  138. package/src/css/badge.css +31 -0
  139. package/src/css/kpi-card.css +51 -0
  140. package/src/css/look-board-detail.css +10 -0
  141. package/src/css/look-board-index.css +128 -14
  142. package/src/css/look-board.css +85 -0
  143. package/src/css/table.css +37 -0
  144. package/src/index.js +4 -0
  145. package/src/schemas/app-manifest-v2.schema.json +50 -5
  146. package/src/utils/badgeVariants.js +29 -0
  147. package/src/utils/boardDate.js +189 -0
  148. package/src/utils/dynamicProperties.js +5 -1
  149. package/src/utils/validateManifestV2.compiled.js +57 -2
  150. package/dist/esm/_virtual/equal.js +0 -4
  151. package/dist/esm/_virtual/equal.js.map +0 -1
  152. package/dist/esm/_virtual/ucs2length.js +0 -4
  153. package/dist/esm/_virtual/ucs2length.js.map +0 -1
  154. package/dist/esm/node_modules/ajv/dist/runtime/equal.js +0 -9
  155. package/dist/esm/node_modules/ajv/dist/runtime/equal.js.map +0 -1
  156. package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js +0 -25
  157. package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js.map +0 -1
  158. package/dist/esm/node_modules/fast-deep-equal/index.js +0 -47
  159. package/dist/esm/node_modules/fast-deep-equal/index.js.map +0 -1
@@ -24,12 +24,15 @@
24
24
  </span>
25
25
  </template>
26
26
 
27
- <!-- Built-in "badge" widget — renders the (possibly formatter-shaped) value as a status pill -->
27
+ <!-- Built-in "badge" widget: renders the value as a status pill. An enum
28
+ value shows its label (x-enum-labels, translated) unless a formatter
29
+ shapes it; the colour stays keyed on the raw value. -->
28
30
  <template v-else-if="widget === 'badge'">
29
31
  <CnStatusBadge v-if="hasValue"
30
- :label="String(formattedValue)"
32
+ :label="labelledValue"
31
33
  :variant="badgeVariant"
32
- :colorMap="badgeColorMap" />
34
+ :colorMap="badgeColorMap"
35
+ :colorKey="badgeColorKey" />
33
36
  <span v-else class="cn-cell-renderer__dash">—</span>
34
37
  </template>
35
38
 
@@ -78,7 +81,7 @@
78
81
  v-if="linkRoute"
79
82
  :to="linkRoute"
80
83
  class="cn-cell-renderer__link">
81
- {{ formattedValue }}
84
+ {{ labelledValue }}
82
85
  </router-link>
83
86
  <a
84
87
  v-else-if="linkHref"
@@ -86,9 +89,9 @@
86
89
  target="_blank"
87
90
  rel="noopener"
88
91
  class="cn-cell-renderer__link">
89
- {{ formattedValue }}
92
+ {{ labelledValue }}
90
93
  </a>
91
- <span v-else :title="rawTitle">{{ formattedValue }}</span>
94
+ <span v-else :title="rawTitle">{{ labelledValue }}</span>
92
95
  </template>
93
96
 
94
97
  <!-- Built-in "avatar" widget: a person as an avatar with the name beside
@@ -115,6 +118,19 @@
115
118
  <span v-else class="cn-cell-renderer__dash">—</span>
116
119
  </template>
117
120
 
121
+ <!-- Built-in "age" widget: how long something has waited ("4 uur",
122
+ "3 dagen"), coloured by widgetProps.variantWhen rules on the days
123
+ the cell shows. The full date is the tooltip. -->
124
+ <template v-else-if="widget === 'age'">
125
+ <time
126
+ v-if="dateTimestamp"
127
+ class="cn-cell-renderer__age"
128
+ :class="ageVariant ? 'cn-cell-renderer__age--' + ageVariant : null"
129
+ :datetime="dateTimestamp.toISOString()"
130
+ :title="absoluteDateLabel">{{ ageLabel }}</time>
131
+ <span v-else class="cn-cell-renderer__dash">—</span>
132
+ </template>
133
+
118
134
  <!-- Built-in "date" widget: a date whose colour follows
119
135
  widgetProps.variantWhen, rules on the number of days until it
120
136
  (0 = today, negative = overdue). -->
@@ -141,7 +157,7 @@
141
157
  class="cn-cell-renderer__swatch-dot"
142
158
  :style="{ backgroundColor: swatchColor }"
143
159
  aria-hidden="true" />
144
- <span v-if="hasValue" :title="rawTitle">{{ formattedValue }}</span>
160
+ <span v-if="hasValue" :title="rawTitle">{{ labelledValue }}</span>
145
161
  <span v-else class="cn-cell-renderer__dash">—</span>
146
162
  </span>
147
163
  </template>
@@ -153,7 +169,12 @@
153
169
 
154
170
  <!-- Date / date-time: dynamic NcDateTime (relative time, absolute on hover) -->
155
171
  <template v-else-if="isDate">
156
- <NcDateTime v-if="dateTimestamp" :timestamp="dateTimestamp" />
172
+ <time
173
+ v-if="dateTimestamp && isBoardLook"
174
+ class="cn-cell-renderer__date"
175
+ :datetime="dateTimestamp.toISOString()"
176
+ :title="absoluteDateLabel">{{ boardDateLabel }}</time>
177
+ <NcDateTime v-else-if="dateTimestamp" :timestamp="dateTimestamp" />
157
178
  <span v-else class="cn-cell-renderer__dash">—</span>
158
179
  </template>
159
180
 
@@ -207,6 +228,8 @@ import { NcAvatar, NcDateTime } from '@nextcloud/vue'
207
228
  import CheckBold from 'vue-material-design-icons/CheckBold.vue'
208
229
  import CnFkResolveCell from '../CnFkResolveCell/CnFkResolveCell.vue'
209
230
  import CnGroupNameCell from './CnGroupNameCell.vue'
231
+ import { normalizeLook } from '../../composables/useLook.js'
232
+ import { formatAge, formatBoardDate, resolveAgeVariant } from '../../utils/boardDate.js'
210
233
  import { parseDateValue, resolveDateVariant } from '../../utils/dateVariant.js'
211
234
  import { safeCurrencyCode } from '../../utils/formatMetric.js'
212
235
  import { objectFieldValue } from '../../utils/objectName.js'
@@ -269,6 +292,12 @@ export default {
269
292
  * Defaults to identity so standalone use is unaffected.
270
293
  */
271
294
  cnTranslate: { default: () => (key) => key },
295
+ /**
296
+ * The look the app is drawn in, provided by CnAppRoot (or a page with
297
+ * `config.look`). Under `board` a date cell reads the board's short
298
+ * form ("5 okt"). Defaults to `nextcloud`, which renders as before.
299
+ */
300
+ cnLook: { default: 'nextcloud' },
272
301
  },
273
302
 
274
303
  props: {
@@ -323,6 +352,10 @@ export default {
323
352
  * a date whose colour follows `widgetProps.variantWhen`, rules on the
324
353
  * number of days until the date (`[{ op: "lt", value: 0, variant:
325
354
  * "error" }, { op: "lte", value: 5, variant: "warning" }]`).
355
+ * The built-in id `"age"` renders how long the row has waited since
356
+ * the date ("4 hours", "3 days"), coloured by `widgetProps.variantWhen`
357
+ * rules on the days it shows (`[{ op: "gte", value: 3, variant:
358
+ * "error" }]`).
326
359
  * The built-in id `"group"` shows a Nextcloud group id (or a list of
327
360
  * them) as the group's display name; a column whose property is
328
361
  * marked `referenceType: "nextcloud-group"` gets it without asking.
@@ -525,6 +558,69 @@ export default {
525
558
  return variant === 'default' ? '' : variant
526
559
  },
527
560
 
561
+ /**
562
+ * Whether the app takes the board look (`cnLook` is `board`).
563
+ *
564
+ * @return {boolean}
565
+ * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-a-board-date-cell-reads-day-and-short-month-in-the-user-language
566
+ */
567
+ isBoardLook() {
568
+ return normalizeLook(this.cnLook) === 'board'
569
+ },
570
+
571
+ /**
572
+ * The board's short date ("5 okt", "14 feb 2024" outside this year).
573
+ *
574
+ * @return {string}
575
+ * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-a-board-date-cell-reads-day-and-short-month-in-the-user-language
576
+ */
577
+ boardDateLabel() {
578
+ return formatBoardDate(this.value)
579
+ },
580
+
581
+ /**
582
+ * The full date for a tooltip: day, month and year in the user's
583
+ * locale, with the time for a date-time value.
584
+ *
585
+ * @return {string|undefined}
586
+ * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-says-how-long-something-has-waited
587
+ */
588
+ absoluteDateLabel() {
589
+ const date = parseDateValue(this.value)
590
+ if (date === null) {
591
+ return undefined
592
+ }
593
+ const withTime = this.property?.format === 'date-time' || this.widget === 'age'
594
+ const options = { day: 'numeric', month: 'long', year: 'numeric', ...(withTime ? { hour: '2-digit', minute: '2-digit' } : {}) }
595
+ try {
596
+ return new Intl.DateTimeFormat(getCanonicalLocale() || undefined, options).format(date)
597
+ } catch {
598
+ return new Intl.DateTimeFormat(undefined, options).format(date)
599
+ }
600
+ },
601
+
602
+ /**
603
+ * `age` widget: how long the row has waited ("4 uur", "3 dagen").
604
+ *
605
+ * @return {string}
606
+ * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-says-how-long-something-has-waited
607
+ */
608
+ ageLabel() {
609
+ return formatAge(this.value)
610
+ },
611
+
612
+ /**
613
+ * `age` widget: the variant of the first `widgetProps.variantWhen`
614
+ * rule the shown days match, or '' for none.
615
+ *
616
+ * @return {string}
617
+ * @spec openspec/changes/screens-cell-date-parity/specs/cell-date-age/spec.md#requirement-an-age-cell-turns-red-past-its-threshold
618
+ */
619
+ ageVariant() {
620
+ const variant = resolveAgeVariant(this.value, this.widgetProps && this.widgetProps.variantWhen)
621
+ return variant === 'default' ? '' : variant
622
+ },
623
+
528
624
  /**
529
625
  * `date` widget: the date written out in the user's locale.
530
626
  *
@@ -541,6 +637,11 @@ export default {
541
637
  // 08:30"); `widgetProps.timeOnly: true` shows just the time
542
638
  // ("08:30"), for a timetable row. Default: the date alone.
543
639
  const props = this.widgetProps || {}
640
+ // The board look writes a plain date in its short form ("5 okt");
641
+ // a time of day keeps the full form below.
642
+ if (this.isBoardLook && props.timeOnly !== true && props.showTime !== true) {
643
+ return formatBoardDate(date)
644
+ }
544
645
  const options = props.timeOnly === true
545
646
  ? { hour: '2-digit', minute: '2-digit' }
546
647
  : { day: 'numeric', month: 'short', year: 'numeric', ...(props.showTime === true ? { hour: '2-digit', minute: '2-digit' } : {}) }
@@ -632,6 +733,43 @@ export default {
632
733
  .map((gid) => String(gid))
633
734
  },
634
735
 
736
+ /**
737
+ * The text a built-in widget (`badge`, `link`) or a swatch shows. An
738
+ * enum value reads its label the way the plain enum cell does
739
+ * (`enumLabel`: `x-enum-labels`, translated), so a list says "Active"
740
+ * where the detail page does, not the stored "active". A column
741
+ * formatter still wins: an app that shapes the value itself gets
742
+ * exactly what its formatter returns.
743
+ *
744
+ * @return {string}
745
+ * @spec openspec/changes/cell-labels-and-draft-indicator/specs/cell-labels-and-draft-indicator/spec.md#requirement-a-built-in-cell-widget-shows-an-enum-value-by-its-label
746
+ */
747
+ labelledValue() {
748
+ if (!this.hasFormatter && this.isEnum && this.hasValue && typeof this.value !== 'object') {
749
+ return this.enumLabel
750
+ }
751
+ return String(this.formattedValue)
752
+ },
753
+
754
+ /**
755
+ * The key the built-in `badge` looks its colour up by: the raw value
756
+ * when `widgetProps.colorMap` has an entry for it (case-insensitive),
757
+ * so a translated label keeps the colour the manifest keyed on the
758
+ * stored code. Otherwise '' and the badge looks up its label, as before,
759
+ * which keeps a map keyed on a formatter's output working.
760
+ *
761
+ * @return {string}
762
+ * @spec openspec/changes/cell-labels-and-draft-indicator/specs/cell-labels-and-draft-indicator/spec.md#requirement-a-built-in-cell-widget-shows-an-enum-value-by-its-label
763
+ */
764
+ badgeColorKey() {
765
+ const map = this.badgeColorMap
766
+ if (!map || !this.hasValue || typeof this.value === 'object') {
767
+ return ''
768
+ }
769
+ const raw = String(this.value).toLowerCase()
770
+ return Object.keys(map).some((key) => key.toLowerCase() === raw) ? String(this.value) : ''
771
+ },
772
+
635
773
  /** Variant for the built-in `badge` widget — `widgetProps.variant` or `'default'`. */
636
774
  badgeVariant() {
637
775
  return (this.widgetProps && this.widgetProps.variant) || 'default'
@@ -1087,10 +1225,29 @@ export default {
1087
1225
  does not rest on colour alone. */
1088
1226
  .cn-cell-renderer__date--success,
1089
1227
  .cn-cell-renderer__date--warning,
1090
- .cn-cell-renderer__date--error {
1228
+ .cn-cell-renderer__date--error,
1229
+ .cn-cell-renderer__age--success,
1230
+ .cn-cell-renderer__age--warning,
1231
+ .cn-cell-renderer__age--error {
1091
1232
  font-weight: 600;
1092
1233
  }
1093
1234
 
1235
+ .cn-cell-renderer__age {
1236
+ white-space: nowrap;
1237
+ }
1238
+
1239
+ .cn-cell-renderer__age--success {
1240
+ color: var(--color-text-success, var(--color-success-text));
1241
+ }
1242
+
1243
+ .cn-cell-renderer__age--warning {
1244
+ color: var(--color-text-warning, var(--color-warning-text));
1245
+ }
1246
+
1247
+ .cn-cell-renderer__age--error {
1248
+ color: var(--color-text-error, var(--color-error-text));
1249
+ }
1250
+
1094
1251
  .cn-cell-renderer__date--success {
1095
1252
  color: var(--color-text-success, var(--color-success-text));
1096
1253
  }
@@ -1103,8 +1260,10 @@ export default {
1103
1260
  color: var(--color-text-error, var(--color-error-text));
1104
1261
  }
1105
1262
 
1263
+ /* The text variant, not the fill: from Nextcloud 32 `--color-success` is a
1264
+ background colour and too pale for a 16px glyph on a light theme. */
1106
1265
  .cn-cell-renderer__icon--success {
1107
- color: var(--color-success);
1266
+ color: var(--color-text-success, var(--color-success-text, var(--color-success)));
1108
1267
  }
1109
1268
 
1110
1269
  .cn-cell-renderer__swatch-wrap {
@@ -29,7 +29,7 @@
29
29
  :aria-pressed="String(Boolean(activeView && activeView.key === view.key))"
30
30
  :data-testid="`cn-chart-widget-view-${view.key}`"
31
31
  @click="activeViewKey = view.key">
32
- {{ view.label || view.key }}
32
+ {{ view.label ? hostTranslate(view.label) : view.key }}
33
33
  </button>
34
34
  </div>
35
35
  <div v-if="showEmptyState" class="cn-chart-widget__empty" data-testid="cn-chart-widget-empty">
@@ -53,7 +53,7 @@
53
53
  :height="computedHeight"
54
54
  :width="computedWidth"
55
55
  :options="mergedOptions"
56
- :series="displayedSeries" />
56
+ :series="plottedSeries" />
57
57
  </div>
58
58
  <div v-else class="cn-chart-widget__fallback">
59
59
  <!-- @slot Rendered when the ApexCharts peer dependency is not available (defaults to the unavailableLabel text). -->
@@ -213,6 +213,11 @@ export default {
213
213
  * always a ref to inject, even outside a dashboard.
214
214
  */
215
215
  cnDashboardDateRange: { default: () => ref(null) },
216
+ /**
217
+ * Host translate function provided by CnAppRoot: manifest-authored
218
+ * view labels and series names are i18n keys (screens-dashboard-i18n).
219
+ */
220
+ cnTranslate: { default: () => (key) => key },
216
221
  },
217
222
 
218
223
  props: {
@@ -918,6 +923,25 @@ export default {
918
923
  *
919
924
  * @return {Array} The displayed series.
920
925
  */
926
+ /**
927
+ * The displayed series with each series name through the host
928
+ * translate function, so a manifest series named "Revenue" reads in
929
+ * the user's language in the legend and the tooltip. Views still
930
+ * filter on the name as written (`displayedSeries`).
931
+ *
932
+ * @spec openspec/changes/screens-dashboard-i18n/specs/dashboard-page/spec.md#requirement-chart-view-labels-and-series-names-are-translated
933
+ * @return {Array} The series handed to ApexCharts.
934
+ */
935
+ plottedSeries() {
936
+ const series = this.displayedSeries
937
+ if (!Array.isArray(series)) {
938
+ return series
939
+ }
940
+ return series.map((entry) => (entry && typeof entry === 'object' && typeof entry.name === 'string' && entry.name !== '')
941
+ ? { ...entry, name: this.hostTranslate(entry.name) }
942
+ : entry)
943
+ },
944
+
921
945
  displayedSeries() {
922
946
  const series = this.resolvedSeries
923
947
  const view = this.activeView
@@ -1417,6 +1441,19 @@ export default {
1417
1441
  },
1418
1442
 
1419
1443
  methods: {
1444
+ /**
1445
+ * A manifest-authored label through the host translate function.
1446
+ *
1447
+ * @spec openspec/changes/screens-dashboard-i18n/specs/dashboard-page/spec.md#requirement-chart-view-labels-and-series-names-are-translated
1448
+ * @param {string} key The label.
1449
+ * @return {string} The translated label.
1450
+ */
1451
+ hostTranslate(key) {
1452
+ const translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : null
1453
+ const out = translate ? translate(key) : key
1454
+ return (typeof out === 'string' && out !== '') ? out : key
1455
+ },
1456
+
1420
1457
  /**
1421
1458
  * Re-query the chart's dataSource / endpointSource. Exposed as a
1422
1459
  * ref-callable method (B3 canonical refresh mode) AND invoked by the