@conduction/nextcloud-vue 2.143.0 → 2.145.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 (165) hide show
  1. package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
  2. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +7 -2
  3. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +1 -0
  5. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
  6. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
  7. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +55 -2
  8. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
  10. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue.js +1 -1
  11. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js +16 -0
  12. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js.map +1 -1
  13. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js +3 -1
  14. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js.map +1 -1
  15. package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js +1 -1
  16. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -1
  17. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js +1 -1
  18. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -1
  19. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js +1 -1
  20. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js +1 -1
  21. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js.map +1 -1
  22. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js +3 -3
  23. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js.map +1 -1
  24. package/dist/esm/components/CnEventsCard/CnEventsCard.vue.js +1 -1
  25. package/dist/esm/components/CnEventsCard/CnEventsCard.vue2.js +9 -0
  26. package/dist/esm/components/CnEventsCard/CnEventsCard.vue2.js.map +1 -1
  27. package/dist/esm/components/CnEventsCard/CnEventsCard.vue3.js +24 -13
  28. package/dist/esm/components/CnEventsCard/CnEventsCard.vue3.js.map +1 -1
  29. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +1 -1
  30. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +2 -2
  31. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
  32. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
  33. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  34. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +2 -1
  35. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  36. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  37. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +9 -4
  38. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  39. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  40. package/dist/esm/components/CnMailTab/CnMailTab.vue.js +1 -1
  41. package/dist/esm/components/CnMailTab/CnMailTab.vue2.js.map +1 -1
  42. package/dist/esm/components/CnMailTab/CnMailTab.vue3.js +2 -2
  43. package/dist/esm/components/CnMailTab/CnMailTab.vue3.js.map +1 -1
  44. package/dist/esm/components/CnNotesCard/CnNotesCard.vue.js +1 -1
  45. package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js +3 -0
  46. package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js.map +1 -1
  47. package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js +19 -14
  48. package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js.map +1 -1
  49. package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue.js +1 -1
  50. package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue2.js.map +1 -1
  51. package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue3.js.map +1 -1
  52. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue.js +1 -1
  53. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue2.js +30 -0
  54. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue2.js.map +1 -1
  55. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue3.js +158 -102
  56. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue3.js.map +1 -1
  57. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  58. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +73 -12
  59. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  60. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +165 -126
  61. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  62. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +1 -1
  63. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -1
  64. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js +12 -0
  65. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -1
  66. package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +1 -1
  67. package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js +14 -0
  68. package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -1
  69. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +2 -1
  70. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -1
  71. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue.js +1 -1
  72. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +110 -2
  73. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  74. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +146 -80
  75. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  76. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
  77. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -8
  78. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  79. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  80. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue.js +1 -1
  81. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +10 -2
  82. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
  83. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
  84. package/dist/esm/composables/indexSources.js +35 -9
  85. package/dist/esm/composables/indexSources.js.map +1 -1
  86. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue.js +1 -1
  87. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js +45 -0
  88. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js.map +1 -1
  89. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue3.js +67 -34
  90. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue3.js.map +1 -1
  91. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue.js +1 -1
  92. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue2.js +3 -22
  93. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue2.js.map +1 -1
  94. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue3.js.map +1 -1
  95. package/dist/esm/integrations/builtin/calendar/eventStatus.js +67 -0
  96. package/dist/esm/integrations/builtin/calendar/eventStatus.js.map +1 -0
  97. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue.js +1 -1
  98. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +39 -0
  99. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
  100. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js +81 -32
  101. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
  102. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue.js +1 -1
  103. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +5 -6
  104. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
  105. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
  106. package/dist/esm/integrations/builtin/deck/deckCardParts.js +59 -0
  107. package/dist/esm/integrations/builtin/deck/deckCardParts.js.map +1 -0
  108. package/dist/esm/l10n/en.json.js +6 -0
  109. package/dist/esm/l10n/en.json.js.map +1 -1
  110. package/dist/esm/l10n/nl.json.js +8 -0
  111. package/dist/esm/l10n/nl.json.js.map +1 -1
  112. package/dist/esm/library-style.js +1 -1
  113. package/dist/esm/nextcloud-vue.css +1505 -637
  114. package/dist/esm/utils/builtInFormatters.js +18 -1
  115. package/dist/esm/utils/builtInFormatters.js.map +1 -1
  116. package/dist/esm/utils/fetchFilterCounts.js +5 -2
  117. package/dist/esm/utils/fetchFilterCounts.js.map +1 -1
  118. package/dist/esm/utils/localHeaderRows.js +95 -0
  119. package/dist/esm/utils/localHeaderRows.js.map +1 -0
  120. package/dist/esm/utils/nextcloudDirectory.js +19 -6
  121. package/dist/esm/utils/nextcloudDirectory.js.map +1 -1
  122. package/dist/nextcloud-vue.cjs.js +1848 -924
  123. package/dist/nextcloud-vue.cjs.js.map +2 -2
  124. package/dist/nextcloud-vue.css +1505 -637
  125. package/l10n/en.json +6 -0
  126. package/l10n/nl.json +8 -0
  127. package/package.json +1 -1
  128. package/src/components/CnActionDialog/CnActionDialog.vue +36 -4
  129. package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +55 -2
  130. package/src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue +18 -0
  131. package/src/components/CnConversationThread/CnConversationThread.vue +5 -2
  132. package/src/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue +2 -1
  133. package/src/components/CnEventsCard/CnEventsCard.vue +27 -5
  134. package/src/components/CnFieldListWidget/CnFieldListWidget.vue +2 -2
  135. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +15 -1
  136. package/src/components/CnIndexPage/CnIndexPage.vue +9 -4
  137. package/src/components/CnMailTab/CnMailTab.vue +6 -4
  138. package/src/components/CnNotesCard/CnNotesCard.vue +8 -0
  139. package/src/components/CnNotificationMatrix/CnNotificationMatrix.vue +4 -1
  140. package/src/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue +114 -46
  141. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +131 -20
  142. package/src/components/CnRetentionWidget/CnRetentionWidget.vue +26 -1
  143. package/src/components/CnSettingsField/CnSettingsField.vue +15 -0
  144. package/src/components/CnTasksWidget/CnTasksWidget.vue +170 -13
  145. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -8
  146. package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +10 -2
  147. package/src/composables/indexSources.js +35 -9
  148. package/src/css/dialog.css +25 -10
  149. package/src/css/kpi-card.css +3 -0
  150. package/src/css/look-board-detail.css +513 -0
  151. package/src/css/look-board-index.css +52 -4
  152. package/src/css/look-board.css +163 -8
  153. package/src/css/table.css +8 -1
  154. package/src/integrations/builtin/calendar/CnCalendarCard.vue +76 -11
  155. package/src/integrations/builtin/calendar/CnCalendarTab.vue +3 -22
  156. package/src/integrations/builtin/calendar/__tests__/CnCalendarCardBoardRows.spec.js +52 -0
  157. package/src/integrations/builtin/calendar/eventStatus.js +63 -0
  158. package/src/integrations/builtin/deck/CnDeckCard.vue +66 -0
  159. package/src/integrations/builtin/deck/CnDeckTab.vue +5 -6
  160. package/src/integrations/builtin/deck/__tests__/CnDeckCardBoardRows.spec.js +49 -0
  161. package/src/integrations/builtin/deck/deckCardParts.js +56 -0
  162. package/src/utils/builtInFormatters.js +17 -0
  163. package/src/utils/fetchFilterCounts.js +5 -2
  164. package/src/utils/localHeaderRows.js +91 -0
  165. package/src/utils/nextcloudDirectory.js +19 -6
package/l10n/en.json CHANGED
@@ -1706,6 +1706,12 @@
1706
1706
  "expires {date}": "expires {date}",
1707
1707
  "Loading …": "Loading …",
1708
1708
  "Reorder {item}: drag, or press the up and down arrow keys": "Reorder {item}: drag, or press the up and down arrow keys",
1709
+ "Board {name}": "Board {name}",
1710
+ "list {name}": "list {name}",
1711
+ "Tentative": "Tentative",
1712
+ "Confirmed": "Confirmed",
1713
+ "Cancelled": "Cancelled",
1714
+ "{start} until {end}": "{start} until {end}",
1709
1715
  "No contacts linked": "No contacts linked",
1710
1716
  "No relations yet": "No relations yet",
1711
1717
  "Period": "Period",
package/l10n/nl.json CHANGED
@@ -1919,6 +1919,12 @@
1919
1919
  "Made {date}": "Gemaakt {date}",
1920
1920
  "expires {date}": "verloopt {date}",
1921
1921
  "Reorder {item}: drag, or press the up and down arrow keys": "{item} verplaatsen: slepen, of de pijltoetsen omhoog en omlaag",
1922
+ "Board {name}": "Bord {name}",
1923
+ "list {name}": "lijst {name}",
1924
+ "Tentative": "Voorlopig",
1925
+ "Confirmed": "Bevestigd",
1926
+ "Cancelled": "Geannuleerd",
1927
+ "{start} until {end}": "{start} tot {end}",
1922
1928
  "No contacts linked": "Geen contacten gekoppeld",
1923
1929
  "No relations yet": "Nog geen relaties",
1924
1930
  "Period": "Periode",
@@ -1940,6 +1946,8 @@
1940
1946
  "Absent today": "Vandaag afwezig",
1941
1947
  "{message} until {time}": "{message} tot {time}",
1942
1948
  "Away": "Afwezig",
1949
+ "Present": "Aanwezig",
1950
+ "Absent until {date}": "Afwezig tot {date}",
1943
1951
  "Busy": "Bezet",
1944
1952
  "No one in the team": "Niemand in het team",
1945
1953
  "Also replace the criteria with the current filters": "Vervang ook de criteria door de huidige filters",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/nextcloud-vue",
3
- "version": "2.143.0",
3
+ "version": "2.145.0",
4
4
  "description": "Shared Vue component library for Conduction Nextcloud apps — complements @nextcloud/vue with higher-level components, OpenRegister integration, and NL Design System support",
5
5
  "homepage": "https://github.com/ConductionNL/nextcloud-vue#readme",
6
6
  "bugs": {
@@ -145,7 +145,7 @@
145
145
  :heading="text(config.note.title)"
146
146
  data-testid="cn-action-dialog-note">
147
147
  <template v-if="config.note.icon" #icon>
148
- <CnIcon :name="config.note.icon" :size="20" />
148
+ <CnIcon class="cn-action-dialog__note-icon" :name="config.note.icon" :size="20" />
149
149
  </template>
150
150
  {{ text(config.note.text) }}
151
151
  </NcNoteCard>
@@ -191,6 +191,7 @@ import CircleOutline from 'vue-material-design-icons/CircleOutline.vue'
191
191
  import HelpCircleOutline from 'vue-material-design-icons/HelpCircleOutline.vue'
192
192
  import CnFormField from '../CnFormField/CnFormField.vue'
193
193
  import { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'
194
+ import { BUILT_IN_FORMATTERS } from '../../utils/builtInFormatters.js'
194
195
  import { fillTemplate } from '../../utils/fillTemplate.js'
195
196
  import { readPath } from '../../utils/readPath.js'
196
197
  import { CnDialog } from '../CnDialog/index.js'
@@ -219,7 +220,8 @@ let reasonSeq = 0
219
220
  * `eyebrow` a template filled from the record ("Close off · {identifier}").
220
221
  * - `summary`: `{ source?, title, meta?, badge?, icon? }`: templates filled from
221
222
  * the record (`source` an endpointSource to read it from, else `object`).
222
- * - `facts`: `[{ title?, items: [{ label, value }] }]`, `value` a template.
223
+ * - `facts`: `[{ title?, items: [{ label, value }] }]`, `value` a template; a
224
+ * `{path|<formatter>}` token reads through the built-in and app formatters.
223
225
  * - `checklist`: `{ title?, source, itemsPath?, labels?, required? }`: items
224
226
  * `{ id, met, label?, detail? }` read from `source`; `labels[id]` gives an
225
227
  * item's `{ label, detail?, unmetDetail? }`; the ids in `required` must be
@@ -262,6 +264,8 @@ export default {
262
264
  cnTranslate: { default: () => (key) => key },
263
265
  /** The page's pre-bound action dispatcher (CnPageRenderer), for `confirm.action`. */
264
266
  cnDispatchAction: { default: null },
267
+ /** The app's named formatters (CnAppRoot), for `{path|<formatter>}` in a template. */
268
+ cnFormatters: { default: () => ({}) },
265
269
  },
266
270
 
267
271
  props: {
@@ -594,7 +598,8 @@ export default {
594
598
  if (typeof template !== 'string' || template === '') {
595
599
  return ''
596
600
  }
597
- return fillTemplate(this.cnTranslate(template), row || this.record || {}).trim()
601
+ const formatters = { ...BUILT_IN_FORMATTERS, ...(this.cnFormatters || {}) }
602
+ return fillTemplate(this.cnTranslate(template), row || this.record || {}, new Date(), { formatters }).trim()
598
603
  },
599
604
 
600
605
  /**
@@ -836,6 +841,7 @@ export default {
836
841
  .cn-action-dialog__summary-text strong {
837
842
  font-size: 15px;
838
843
  font-weight: 600;
844
+ line-height: 1.2;
839
845
  }
840
846
 
841
847
  .cn-action-dialog__summary-text .cn-action-dialog__muted {
@@ -945,11 +951,36 @@ export default {
945
951
 
946
952
  /* A checkbox field is a tinted box with a 2px edge, its label bold and its
947
953
  hint under it (DqArchiveren). */
954
+ /* A note's own icon stands on the heading's line, as the variant's icon
955
+ does (NcNoteCard centres a slotted icon on the whole note). */
956
+ .cn-action-dialog__note-icon {
957
+ align-self: flex-start;
958
+ margin-top: 1px;
959
+ }
960
+
948
961
  .cn-action-dialog__checkbox {
949
962
  padding: 12px 14px;
950
963
  border: 2px solid var(--color-primary-element);
951
964
  border-radius: var(--border-radius-element, 8px);
952
- background: var(--color-primary-element-light);
965
+ background: var(--color-main-background);
966
+ }
967
+
968
+ /* The box sits at the card's inset, 12px before the label, the label on
969
+ its first line (DqArchiveren "Sanne de Vries laten weten ..."):
970
+ NcCheckboxRadioSwitch pads its row and centres a 34px target. */
971
+ .cn-action-dialog__checkbox :deep(.checkbox-content) {
972
+ padding: 0;
973
+ gap: 12px;
974
+ align-items: flex-start;
975
+ min-height: 0;
976
+ }
977
+
978
+ .cn-action-dialog__checkbox :deep(.checkbox-content__icon) {
979
+ margin: 0;
980
+ }
981
+
982
+ .cn-action-dialog__checkbox :deep(.checkbox-radio-switch__input) {
983
+ min-height: 0;
953
984
  }
954
985
 
955
986
  .cn-action-dialog__checkbox-text {
@@ -961,6 +992,7 @@ export default {
961
992
  .cn-action-dialog__checkbox-label {
962
993
  font-size: 15px;
963
994
  font-weight: 600;
995
+ line-height: 1.2;
964
996
  }
965
997
 
966
998
  .cn-action-dialog__checkbox .cn-action-dialog__muted {
@@ -69,9 +69,21 @@ import CnDetailCard from '../CnDetailCard/CnDetailCard.vue'
69
69
  import { formatBoardDate, formatTimeOfDay } from '../../utils/boardDate.js'
70
70
  import { buildHeaders, prefixUrl } from '../../utils/index.js'
71
71
  import { leafSourceUrl } from '../../utils/leafSourceUrl.js'
72
+ import { readPath } from '../../utils/readPath.js'
72
73
  import { userDisplayName } from '../../utils/userDisplayName.js'
73
74
  import { userLocale } from '../../utils/userLocale.js'
74
75
 
76
+ /**
77
+ * A changed value as an entry records it: OpenRegister writes `{ old, new }`,
78
+ * other sources the value itself.
79
+ *
80
+ * @param {unknown} value The recorded change.
81
+ * @return {unknown} The value it changed to.
82
+ */
83
+ function newValueOf(value) {
84
+ return value !== null && typeof value === 'object' && Object.hasOwn(value, 'new') ? value.new : value
85
+ }
86
+
75
87
  /**
76
88
  * @param {object} entry An audit entry.
77
89
  * @return {number} When it happened, in ms; 0 when unreadable.
@@ -202,6 +214,18 @@ export default {
202
214
  * @type {{[key: string]: {[value: string]: string}}|null}
203
215
  */
204
216
  valueLabels: { type: Object, default: null },
217
+ /**
218
+ * What happened, in words, by the field an entry changed: rules
219
+ * `[{ action?, field, to?, label }]`, the first whose `field` the entry
220
+ * changed (to `to`, when given) wins over `actionLabels`. A label fills
221
+ * the same tokens, plus `{object.<path>}` from `object`
222
+ * ("Version {object.version} published" for `isDraft` set to false).
223
+ *
224
+ * @type {Array<{action?: string, field: string, to?: unknown, label: string}>}
225
+ */
226
+ changeLabels: { type: Array, default: () => [] },
227
+ /** The audited record, for `{object.<path>}` in a label; null without one. */
228
+ object: { type: Object, default: null },
205
229
  /**
206
230
  * Records beside the object whose events belong in its history:
207
231
  * `[{ as, register, schema, filter, limit? }]`. The card lists the
@@ -394,12 +418,16 @@ export default {
394
418
  * @return {string} The line.
395
419
  */
396
420
  formatAction(entry) {
397
- const label = this.labelFor(entry)
421
+ const label = this.changeLabelFor(entry) ?? this.labelFor(entry)
398
422
  if (label !== null) {
399
423
  return label
400
424
  .replace('{actor}', this.actorName(entry))
401
425
  .replace('{state}', entry.stateAfter || '')
402
426
  .replace(/\{changed\.([\w-]+)\}/g, (match, key) => this.changedValue(entry, key))
427
+ .replace(/\{object\.([\w.-]+)\}/g, (match, path) => {
428
+ const value = this.object ? readPath(this.object, path) : undefined
429
+ return value === null || value === undefined || typeof value === 'object' ? '' : String(value)
430
+ })
403
431
  }
404
432
  const action = entry.action || entry.event || this.actionLabel
405
433
  return entry.stateAfter ? `${action} (${entry.stateAfter})` : action
@@ -416,7 +444,7 @@ export default {
416
444
  */
417
445
  changedValue(entry, key) {
418
446
  const changed = entry && typeof entry.changed === 'object' && entry.changed !== null ? entry.changed : {}
419
- const value = changed[key]
447
+ const value = newValueOf(changed[key])
420
448
  if (value === null || value === undefined || typeof value === 'object') {
421
449
  return ''
422
450
  }
@@ -424,6 +452,31 @@ export default {
424
452
  return words && typeof words[value] === 'string' ? words[value] : String(value)
425
453
  },
426
454
 
455
+ /**
456
+ * The `changeLabels` rule an entry matches: the first whose field the
457
+ * entry changed, for its action and to its value when the rule names them.
458
+ *
459
+ * @param {object} entry The entry.
460
+ * @return {string|null} The label, or null when no rule matches.
461
+ */
462
+ changeLabelFor(entry) {
463
+ const changed = entry && typeof entry.changed === 'object' && entry.changed !== null ? entry.changed : {}
464
+ const action = entry.action || entry.event || ''
465
+ for (const rule of Array.isArray(this.changeLabels) ? this.changeLabels : []) {
466
+ if (!rule || typeof rule.label !== 'string' || rule.label === '' || typeof rule.field !== 'string' || !Object.hasOwn(changed, rule.field)) {
467
+ continue
468
+ }
469
+ if (rule.action && rule.action !== action) {
470
+ continue
471
+ }
472
+ if (Object.hasOwn(rule, 'to') && String(newValueOf(changed[rule.field])) !== String(rule.to)) {
473
+ continue
474
+ }
475
+ return rule.label
476
+ }
477
+ return null
478
+ },
479
+
427
480
  /**
428
481
  * The `actionLabels` entry for an entry: by action and state first,
429
482
  * then by action.
@@ -20,6 +20,8 @@
20
20
  :excludeActions="resolvedExcludeActions"
21
21
  :actionLabels="actionLabels"
22
22
  :valueLabels="valueLabels"
23
+ :changeLabels="changeLabels"
24
+ :object="ctx.object || ctx.objectData || null"
23
25
  :relatedSources="relatedSources"
24
26
  :footnote="footnote"
25
27
  v-bind="emptyText ? { noEntriesLabel: emptyText } : {}" />
@@ -193,6 +195,22 @@ export default {
193
195
  .map(([key, text]) => [key, translate(text)]))
194
196
  },
195
197
 
198
+ /**
199
+ * `content.changeLabels`, each rule's label through the app's translate.
200
+ *
201
+ * @return {Array<object>}
202
+ */
203
+ changeLabels() {
204
+ const rules = this.content.changeLabels
205
+ if (!Array.isArray(rules)) {
206
+ return []
207
+ }
208
+ const translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text
209
+ return rules
210
+ .filter((rule) => rule && typeof rule.label === 'string' && rule.label !== '')
211
+ .map((rule) => ({ ...rule, label: translate(rule.label) }))
212
+ },
213
+
196
214
  /**
197
215
  * `content.footnote`, a muted line under the trail with the record's
198
216
  * fields filled in ("Reference {identifier}"); '' when a field is empty.
@@ -17,9 +17,11 @@
17
17
  class="cn-conversation-thread__title">
18
18
  {{ title }}
19
19
  </component>
20
- <p v-if="subtitle" class="cn-conversation-thread__subtitle" data-testid="cn-conversation-subtitle">
20
+ <!-- A div, not a <p>: a theme that sets every paragraph's size with
21
+ !important would otherwise override the 14px subtitle. -->
22
+ <div v-if="subtitle" class="cn-conversation-thread__subtitle" data-testid="cn-conversation-subtitle">
21
23
  {{ subtitle }}
22
- </p>
24
+ </div>
23
25
  </div>
24
26
  <div v-if="$slots['header-actions']" class="cn-conversation-thread__header-actions">
25
27
  <!-- @slot header-actions Buttons beside the title, for example "Log contact". -->
@@ -633,6 +635,7 @@ export default {
633
635
  .cn-conversation-thread__subtitle {
634
636
  color: var(--color-text-maxcontrast);
635
637
  font-size: 14px;
638
+ line-height: normal;
636
639
  margin: 0;
637
640
  }
638
641
 
@@ -31,6 +31,7 @@
31
31
  v-else
32
32
  type="button"
33
33
  class="cn-detail-page__catalog-action"
34
+ :class="{ 'cn-detail-page__catalog-action--add': action.add === true }"
34
35
  data-testid="cn-detail-widget-header-action"
35
36
  @click="action.run()">
36
37
  <CnIcon v-if="action.icon" :name="action.icon" :size="16" />
@@ -56,7 +57,7 @@ export default {
56
57
  components: { CnIcon, CnStatusBadge, NcButton },
57
58
 
58
59
  props: {
59
- /** The reported header: `{ pill, note, actions: [{ label, icon?, variant?, run }] }`. */
60
+ /** The reported header: `{ pill, note, actions: [{ label, icon?, variant?, add?, run }] }`; `add: true` marks a list's Add. */
60
61
  header: {
61
62
  type: Object,
62
63
  required: true,
@@ -28,9 +28,12 @@
28
28
  :key="keyOf(event, index)"
29
29
  class="cn-events-card__event"
30
30
  data-testid="cn-events-card-event">
31
- <span class="cn-events-card__summary">{{ summaryOf(event) }}</span>
32
- <span class="cn-events-card__when">{{ whenOf(event) }}</span>
33
- <span v-if="event.location" class="cn-events-card__where">{{ event.location }}</span>
31
+ <span v-if="dayOf(event)" class="cn-events-card__day" aria-hidden="true">{{ dayOf(event) }}</span>
32
+ <span class="cn-events-card__text">
33
+ <span class="cn-events-card__summary">{{ summaryOf(event) }}</span>
34
+ <span class="cn-events-card__when">{{ whenOf(event) }}</span>
35
+ <span v-if="event.location" class="cn-events-card__where">{{ event.location }}</span>
36
+ </span>
34
37
  </li>
35
38
  </ul>
36
39
  <p v-else class="cn-events-card__muted" data-testid="cn-events-card-empty">
@@ -292,6 +295,15 @@ export default {
292
295
  return summary === '' ? t('nextcloud-vue', 'Appointment') : summary
293
296
  },
294
297
 
298
+ /**
299
+ * @param {object} event The event.
300
+ * @return {string} The day of the month it starts on, '' when it has no readable start.
301
+ */
302
+ dayOf(event) {
303
+ const parsed = new Date(String(event?.dtstart ?? '').trim())
304
+ return Number.isNaN(parsed.getTime()) ? '' : String(parsed.getDate())
305
+ },
306
+
295
307
  /**
296
308
  * @param {object} event The event.
297
309
  * @return {string} When it is: its start and end, or whichever it has.
@@ -333,8 +345,7 @@ function formatMoment(value) {
333
345
 
334
346
  .cn-events-card__event {
335
347
  display: flex;
336
- flex-direction: column;
337
- gap: 2px;
348
+ gap: 12px;
338
349
  padding-bottom: 8px;
339
350
  border-bottom: 1px solid var(--color-border);
340
351
  }
@@ -344,6 +355,17 @@ function formatMoment(value) {
344
355
  border-bottom: none;
345
356
  }
346
357
 
358
+ .cn-events-card__day {
359
+ display: none;
360
+ }
361
+
362
+ .cn-events-card__text {
363
+ display: flex;
364
+ flex-direction: column;
365
+ gap: 2px;
366
+ min-width: 0;
367
+ }
368
+
347
369
  .cn-events-card__summary {
348
370
  font-weight: 600;
349
371
  }
@@ -607,7 +607,7 @@ export default {
607
607
  /**
608
608
  * The card title row's actions: the add button with `add.placement: "title"`.
609
609
  *
610
- * @return {Array<{label: string, icon: string, run: () => void}>} The actions.
610
+ * @return {Array<{label: string, icon: string, add: boolean, run: () => void}>} The actions.
611
611
  */
612
612
  titleActions() {
613
613
  if (!(this.canAdd && this.addTrigger && this.addPlacement === 'title')) {
@@ -616,7 +616,7 @@ export default {
616
616
  const run = () => {
617
617
  this.adding = !this.adding
618
618
  }
619
- return [{ label: this.addLabel, icon: 'Plus', run }]
619
+ return [{ label: this.addLabel, icon: 'Plus', add: true, run }]
620
620
  },
621
621
 
622
622
  addLabel() {
@@ -2178,7 +2178,8 @@ export default {
2178
2178
  const object = {
2179
2179
  ...entry,
2180
2180
  name: node.basename,
2181
- modified: node.mtime,
2181
+ // A template drops an object value, so the Date is handed over as ISO text.
2182
+ modified: node.mtime instanceof Date ? node.mtime.toISOString() : node.mtime,
2182
2183
  size: this.formatSize(node.size),
2183
2184
  type: this.typeLabelOf(node),
2184
2185
  ...(owner ? { owner } : {}),
@@ -3648,6 +3649,19 @@ export default {
3648
3649
  width: auto;
3649
3650
  }
3650
3651
 
3652
+ /* The row button and the row menu stand side by side on one line, not
3653
+ stacked (DqZaakDocumenten: Beoordelen, then the menu, 14px apart).
3654
+ @spec openspec/changes/files-row-button-beside-menu/specs/files-row-button-beside-menu/spec.md#requirement-a-files-row-button-stands-beside-the-row-menu */
3655
+ .cn-files-browser__col-actions > .cn-files-browser__row-button,
3656
+ .cn-files-browser__col-actions > .cn-files-browser__row-menu {
3657
+ display: inline-flex;
3658
+ vertical-align: middle;
3659
+ }
3660
+
3661
+ .cn-files-browser__col-actions > .cn-files-browser__row-button + .cn-files-browser__row-menu {
3662
+ margin-inline-start: 14px;
3663
+ }
3664
+
3651
3665
  .cn-files-browser__col-name {
3652
3666
  min-width: 0;
3653
3667
  }
@@ -8940,7 +8940,7 @@ export default {
8940
8940
  // through the app's label lookup.
8941
8941
  const countText = this.presentedCountText
8942
8942
  // The note after the breadcrumb already says how many there are.
8943
- if (!countText && !this.countSubtitle && this.headerMetaCarriesCount) {
8943
+ if (!countText && !this.countSubtitle && (this.headerMetaCarriesCount || this.showCount === false)) {
8944
8944
  return this.description
8945
8945
  }
8946
8946
  if (!countText && !this.countSubtitle) {
@@ -9877,9 +9877,12 @@ export default {
9877
9877
  }
9878
9878
  const sorted = [...groups].sort((a, b) => b.count - a.count)
9879
9879
  const total = sorted.reduce((sum, g) => sum + g.count, 0)
9880
+ // Each tab asks to be counted as the list counts it (within the
9881
+ // active filters, through the list endpoint); the grouped number,
9882
+ // over the page filter only, stands until that count arrives.
9880
9883
  this.generatedQuickFilters = [
9881
- { label: from.allLabel || t('nextcloud-vue', 'All'), filter: {}, default: true, count: total },
9882
- ...sorted.map((g) => ({ label: (labels && labels[g.key]) || g.key, filter: { [from.field]: g.key }, count: g.count })),
9884
+ { label: from.allLabel || t('nextcloud-vue', 'All'), filter: {}, default: true, count: total, showCount: true },
9885
+ ...sorted.map((g) => ({ label: (labels && labels[g.key]) || g.key, filter: { [from.field]: g.key }, count: g.count, showCount: true })),
9883
9886
  ]
9884
9887
  },
9885
9888
 
@@ -9993,7 +9996,7 @@ export default {
9993
9996
  ]
9994
9997
  let counts
9995
9998
  try {
9996
- counts = await fetchFilterCounts({ register: this.register, schema, entries, baseFilter: base, ctx })
9999
+ counts = await fetchFilterCounts({ register: this.register, schema, entries, baseFilter: base, ctx, via: 'list' })
9997
10000
  } catch {
9998
10001
  counts = {}
9999
10002
  }
@@ -10050,6 +10053,8 @@ export default {
10050
10053
  })),
10051
10054
  baseFilter: resolveFilterMap(this.filter, params, ctx),
10052
10055
  ctx,
10056
+ // The chips count what the list shows: the list's own request.
10057
+ via: 'list',
10053
10058
  })
10054
10059
  if (requestKey !== this.countRequestKey) {
10055
10060
  return
@@ -19,9 +19,11 @@
19
19
  <h3 v-if="heading" class="cn-mail-tab__title">
20
20
  {{ heading }}
21
21
  </h3>
22
- <p v-if="description" class="cn-mail-tab__subtitle">
22
+ <!-- Divs, not <p>: a theme that sizes every paragraph with !important
23
+ would override the card's 14px description. -->
24
+ <div v-if="description" class="cn-mail-tab__subtitle">
23
25
  {{ description }}
24
- </p>
26
+ </div>
25
27
  </div>
26
28
  <div v-if="hasHeaderActions" class="cn-mail-tab__header-actions">
27
29
  <NcButton
@@ -77,9 +79,9 @@
77
79
  {{ t('nextcloud-vue', 'Empty draft') }}
78
80
  </NcButton>
79
81
  </div>
80
- <p v-if="hint" class="cn-mail-tab__hint">
82
+ <div v-if="hint" class="cn-mail-tab__hint">
81
83
  {{ hint }}
82
- </p>
84
+ </div>
83
85
 
84
86
  <NcNoteCard
85
87
  v-if="error"
@@ -96,6 +96,7 @@
96
96
  :key="row.note.id"
97
97
  class="cn-notes-card__note"
98
98
  :class="{ 'cn-notes-card__note--reply': row.isReply }">
99
+ <span class="cn-notes-card__initials" aria-hidden="true">{{ initialsOf(getNoteAuthorName(row.note)) }}</span>
99
100
  <div class="cn-notes-card__note-header">
100
101
  <CnUserActionMenu
101
102
  v-if="!isCurrentUser(row.note)"
@@ -175,6 +176,7 @@ import CnUserActionMenu from '../CnUserActionMenu/CnUserActionMenu.vue'
175
176
  import CnVisibilityChip from '../CnVisibilityChip/CnVisibilityChip.vue'
176
177
  import { useFileComments } from '../../composables/useFileComments.js'
177
178
  import { buildHeaders, prefixUrl } from '../../utils/index.js'
179
+ import { initialsOf } from '../../utils/initials.js'
178
180
  import { leafSourceUrl } from '../../utils/leafSourceUrl.js'
179
181
  import { extractMentionedGroupIds, extractMentionedIds } from '../../utils/mentions.js'
180
182
  import { quoteFor, supportsReplies, threadNotes, threadRootId } from '../../utils/noteThreads.js'
@@ -635,6 +637,8 @@ export default {
635
637
  return note.actorId || note.author || ''
636
638
  },
637
639
 
640
+ initialsOf,
641
+
638
642
  getNoteAuthorName(note) {
639
643
  return note.actorDisplayName || note.author || 'Unknown'
640
644
  },
@@ -870,6 +874,10 @@ export default {
870
874
  border-bottom: none;
871
875
  }
872
876
 
877
+ .cn-notes-card__initials {
878
+ display: none;
879
+ }
880
+
873
881
  .cn-notes-card__note-header {
874
882
  display: flex;
875
883
  justify-content: space-between;
@@ -1135,7 +1135,10 @@ export default {
1135
1135
  min-inline-size: 20px;
1136
1136
  block-size: 20px;
1137
1137
  min-block-size: 20px;
1138
- margin: 0;
1138
+ /* A block box, so the line's baseline adds no height: the row is
1139
+ 42px as on the board (10px, the 1px-low checkbox, 10px, the rule). */
1140
+ display: block;
1141
+ margin: 1px auto 0;
1139
1142
  }
1140
1143
 
1141
1144
  .cn-notification-matrix__group {