@conduction/nextcloud-vue 2.103.0 → 2.105.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 (154) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +23 -0
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +94 -72
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.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.map +1 -1
  7. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -1
  8. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  9. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +18 -1
  10. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +3 -3
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  13. package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
  14. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +93 -1
  15. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  16. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  17. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  18. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +6 -3
  19. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  20. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +3 -3
  21. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  22. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +39 -2
  23. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  24. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +14 -0
  25. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  26. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +99 -3
  27. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  28. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  29. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue.js +1 -1
  30. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js +1 -1
  31. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
  32. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
  33. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue.js +1 -1
  34. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js +2 -0
  35. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js.map +1 -1
  36. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js +32 -29
  37. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js.map +1 -1
  38. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue.js +1 -1
  39. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js +73 -4
  40. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js.map +1 -1
  41. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js +74 -27
  42. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js.map +1 -1
  43. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +213 -4
  44. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  45. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +105 -85
  46. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  47. package/dist/esm/components/CnIndexPage/useSelfFetchList.js +10 -4
  48. package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
  49. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue.js +9 -0
  50. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue.js.map +1 -0
  51. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue2.js +88 -0
  52. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue2.js.map +1 -0
  53. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue3.js +39 -0
  54. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue3.js.map +1 -0
  55. package/dist/esm/components/CnObjectRow/CnObjectRow.vue.js +1 -1
  56. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +57 -5
  57. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
  58. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +37 -4
  59. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
  60. package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +1 -1
  61. package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -1
  62. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +3 -3
  63. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -1
  64. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue.js +1 -1
  65. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  66. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +16 -4
  67. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  68. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue.js +1 -1
  69. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js.map +1 -1
  70. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js.map +1 -1
  71. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue.js +1 -1
  72. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js +11 -0
  73. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js.map +1 -1
  74. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js +4 -4
  75. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js.map +1 -1
  76. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +16 -14
  77. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  78. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
  79. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +18 -2
  80. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  81. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  82. package/dist/esm/composables/indexSources.js +7 -7
  83. package/dist/esm/composables/indexSources.js.map +1 -1
  84. package/dist/esm/index.js +1 -0
  85. package/dist/esm/index.js.map +1 -1
  86. package/dist/esm/l10n/en.json.js +8 -1
  87. package/dist/esm/l10n/en.json.js.map +1 -1
  88. package/dist/esm/l10n/nl.json.js +8 -1
  89. package/dist/esm/l10n/nl.json.js.map +1 -1
  90. package/dist/esm/library-style.js +1 -1
  91. package/dist/esm/nextcloud-vue.css +680 -328
  92. package/dist/esm/utils/libraryWidgetKeys.js +1 -0
  93. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  94. package/dist/esm/utils/nextcloudApps.js +45 -0
  95. package/dist/esm/utils/nextcloudApps.js.map +1 -0
  96. package/dist/esm/utils/nextcloudDirectory.js +18 -1
  97. package/dist/esm/utils/nextcloudDirectory.js.map +1 -1
  98. package/dist/esm/utils/scopeListLayout.js +9 -2
  99. package/dist/esm/utils/scopeListLayout.js.map +1 -1
  100. package/dist/esm/utils/sortOrder.js +61 -0
  101. package/dist/esm/utils/sortOrder.js.map +1 -0
  102. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  103. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  104. package/dist/esm/utils/visibleWhen.js +12 -4
  105. package/dist/esm/utils/visibleWhen.js.map +1 -1
  106. package/dist/nextcloud-vue.cjs.js +4046 -2999
  107. package/dist/nextcloud-vue.cjs.js.map +2 -2
  108. package/dist/nextcloud-vue.css +680 -328
  109. package/l10n/en.json +8 -1
  110. package/l10n/nl.json +8 -1
  111. package/package.json +1 -1
  112. package/src/components/CnActionsBar/CnActionsBar.md +1 -0
  113. package/src/components/CnActionsBar/CnActionsBar.vue +39 -2
  114. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -1
  115. package/src/components/CnAppRoot/CnAppRoot.md +1 -1
  116. package/src/components/CnAppRoot/CnAppRoot.vue +21 -4
  117. package/src/components/CnChartWidget/CnChartWidget.md +2 -0
  118. package/src/components/CnChartWidget/CnChartWidget.vue +93 -1
  119. package/src/components/CnDashboardPage/CnDashboardPage.vue +9 -6
  120. package/src/components/CnDataTable/CnDataTable.md +1 -0
  121. package/src/components/CnDataTable/CnDataTable.vue +49 -0
  122. package/src/components/CnDetailPage/CnDetailPage.md +2 -0
  123. package/src/components/CnDetailPage/CnDetailPage.vue +99 -3
  124. package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -1
  125. package/src/components/CnFeaturesBoard/CnFeaturesBoard.vue +4 -1
  126. package/src/components/CnFlowsPage/CnFlowBoard.vue +132 -8
  127. package/src/components/CnIndexPage/CnIndexPage.md +5 -2
  128. package/src/components/CnIndexPage/CnIndexPage.vue +228 -9
  129. package/src/components/CnIndexPage/useSelfFetchList.js +9 -3
  130. package/src/components/CnNoticeWidget/CnNoticeWidget.vue +153 -0
  131. package/src/components/CnNoticeWidget/index.js +12 -0
  132. package/src/components/CnObjectRow/CnObjectRow.vue +95 -1
  133. package/src/components/CnSettingsField/CnSettingsField.vue +4 -1
  134. package/src/components/CnSetupWizard/CnSetupWizard.vue +9 -1
  135. package/src/components/CnUserSettingsDialog/CnUserSettingsDialog.vue +7 -2
  136. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +15 -4
  137. package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
  138. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +18 -2
  139. package/src/components/index.js +1 -0
  140. package/src/composables/indexSources.js +7 -7
  141. package/src/css/dialog.css +20 -2
  142. package/src/css/index-page.css +19 -0
  143. package/src/css/look-board-detail.css +77 -4
  144. package/src/css/look-board-index.css +84 -2
  145. package/src/css/look-board.css +97 -12
  146. package/src/index.js +1 -0
  147. package/src/schemas/app-manifest-v2.schema.json +58 -3
  148. package/src/utils/libraryWidgetKeys.js +1 -0
  149. package/src/utils/nextcloudApps.js +50 -0
  150. package/src/utils/nextcloudDirectory.js +17 -0
  151. package/src/utils/scopeListLayout.js +9 -2
  152. package/src/utils/sortOrder.js +58 -0
  153. package/src/utils/validateManifestV2.compiled.js +3 -3
  154. package/src/utils/visibleWhen.js +12 -4
@@ -15,6 +15,22 @@
15
15
  @update:modelValue="$emit('select', object)" />
16
16
  </div>
17
17
 
18
+ <!-- `config.leading`: one cell before the title, as a table cell renders
19
+ it (`{ key, widget?, widgetProps?, formatter? }`), e.g. the age
20
+ "6 dagen" in the team queue. -->
21
+ <span
22
+ v-if="leadingCell"
23
+ class="cn-object-row__lead"
24
+ :style="leadingCell.width ? { width: leadingCell.width } : null"
25
+ data-testid="cn-object-row-lead">
26
+ <CnCellRenderer
27
+ :value="cellValue(leadingCell)"
28
+ :formatter="leadingCell.formatter || null"
29
+ :widget="leadingCell.widget || null"
30
+ :widgetProps="leadingCell.widgetProps || undefined"
31
+ :row="object" />
32
+ </span>
33
+
18
34
  <!-- Leading icon / image — omitted entirely when nothing is configured
19
35
  (no image, no icon name, no slot). -->
20
36
  <span v-if="hasLeadingIcon" class="cn-object-row__icon">
@@ -52,6 +68,22 @@
52
68
  </slot>
53
69
  </span>
54
70
 
71
+ <!-- `config.trailing`: cells after the badges, each in its own column
72
+ (the case type badge, the due text). -->
73
+ <span
74
+ v-for="(cell, index) in trailingCells"
75
+ :key="cell.key + '-' + index"
76
+ class="cn-object-row__trail"
77
+ :style="cell.width ? { width: cell.width } : null"
78
+ data-testid="cn-object-row-trail">
79
+ <CnCellRenderer
80
+ :value="cellValue(cell)"
81
+ :formatter="cell.formatter || null"
82
+ :widget="cell.widget || null"
83
+ :widgetProps="cell.widgetProps || undefined"
84
+ :row="object" />
85
+ </span>
86
+
55
87
  <!-- Trailing actions -->
56
88
  <span v-if="$slots.actions"
57
89
  class="cn-object-row__actions"
@@ -67,8 +99,11 @@
67
99
  <script>
68
100
  import { NcCheckboxRadioSwitch } from '@nextcloud/vue'
69
101
  import { useClickDragGuard } from '../../composables/useClickDragGuard.js'
102
+ import { joinTemplateParts } from '../../utils/fillTemplate.js'
103
+ import { readPath } from '../../utils/readPath.js'
70
104
  import { resolveImageUrl } from '../../utils/resolveImageUrl.js'
71
105
  import { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'
106
+ import { CnCellRenderer } from '../CnCellRenderer/index.js'
72
107
  import { CnIcon } from '../CnIcon/index.js'
73
108
  import { CnStatusBadge } from '../CnStatusBadge/index.js'
74
109
 
@@ -98,6 +133,7 @@ export default {
98
133
 
99
134
  components: {
100
135
  NcCheckboxRadioSwitch,
136
+ CnCellRenderer,
101
137
  CnIcon,
102
138
  CnStatusBadge,
103
139
  },
@@ -119,7 +155,15 @@ export default {
119
155
  * Explicit field mapping. Every key is optional and overrides the
120
156
  * schema-configuration default.
121
157
  *
122
- * @type {{ titleField?: string, subtitleField?: string, imageField?: string, iconField?: string, iconName?: string, badgeField?: string, badgeVariantField?: string, badgeVariant?: string, badgeColorMap?: object }}
158
+ * `subtitleTemplate` fills `{field}` tokens from the row (empty parts and
159
+ * their separators drop). `leading` (one cell) and `trailing` (a cell or
160
+ * a list) draw cells as a table column does, `{ key, widget?,
161
+ * widgetProps?, formatter? }`: `{ key: "createdAt", widget: "age" }`
162
+ * reads "6 dagen", `{ key: "deadline", widget: "date", widgetProps: {
163
+ * due: {} } }` the due text. A cell's `width` (CSS) lines the column
164
+ * up across rows.
165
+ *
166
+ * @type {{ titleField?: string, subtitleField?: string, subtitleTemplate?: string, imageField?: string, iconField?: string, iconName?: string, badgeField?: string, badgeVariantField?: string, badgeVariant?: string, badgeColorMap?: object, leading?: object, trailing?: (object|Array<object>) }}
123
167
  */
124
168
  config: {
125
169
  type: Object,
@@ -168,6 +212,13 @@ export default {
168
212
  },
169
213
 
170
214
  subtitle() {
215
+ if (typeof this.config.subtitleTemplate === 'string' && this.config.subtitleTemplate !== '') {
216
+ const text = joinTemplateParts(this.config.subtitleTemplate, (key) => {
217
+ const value = readPath(this.object, key)
218
+ return value === null || value === undefined ? '' : String(value)
219
+ })
220
+ return text.trim() || null
221
+ }
171
222
  const field = this.config.subtitleField || this.schemaConfig.objectDescriptionField
172
223
  if (field && this.object[field] !== null && this.object[field] !== undefined && this.object[field] !== '') {
173
224
  return String(this.object[field])
@@ -215,9 +266,42 @@ export default {
215
266
  badgeColorMap() {
216
267
  return this.config.badgeColorMap || null
217
268
  },
269
+
270
+ /**
271
+ * The `config.leading` cell, or null.
272
+ *
273
+ * @spec openspec/changes/index-row-list-cells/specs/object-row/spec.md#requirement-a-list-row-can-carry-leading-and-trailing-cells
274
+ * @return {object|null}
275
+ */
276
+ leadingCell() {
277
+ const cell = this.config.leading
278
+ return cell && typeof cell === 'object' && typeof cell.key === 'string' && cell.key !== '' ? cell : null
279
+ },
280
+
281
+ /**
282
+ * The `config.trailing` cells (one object or a list), those with a key.
283
+ *
284
+ * @spec openspec/changes/index-row-list-cells/specs/object-row/spec.md#requirement-a-list-row-can-carry-leading-and-trailing-cells
285
+ * @return {Array<object>}
286
+ */
287
+ trailingCells() {
288
+ const cells = this.config.trailing
289
+ const list = Array.isArray(cells) ? cells : (cells && typeof cells === 'object' ? [cells] : [])
290
+ return list.filter((cell) => cell && typeof cell === 'object' && typeof cell.key === 'string' && cell.key !== '')
291
+ },
218
292
  },
219
293
 
220
294
  methods: {
295
+ /**
296
+ * A cell's value: the dot-path `key` read off the row.
297
+ *
298
+ * @param {{key: string}} cell The cell.
299
+ * @return {unknown} The value.
300
+ */
301
+ cellValue(cell) {
302
+ return readPath(this.object, cell.key)
303
+ },
304
+
221
305
  /**
222
306
  * Row-body click: emits `select` when `selectable` (ignoring drags),
223
307
  * otherwise emits `click` for navigation.
@@ -297,6 +381,16 @@ export default {
297
381
  background-color: var(--color-primary-element-light);
298
382
  }
299
383
 
384
+ .cn-object-row__lead,
385
+ .cn-object-row__trail {
386
+ flex: 0 0 auto;
387
+ min-width: 0;
388
+ }
389
+
390
+ .cn-object-row__lead {
391
+ font-weight: 600;
392
+ }
393
+
300
394
  .cn-object-row__checkbox {
301
395
  flex-shrink: 0;
302
396
  }
@@ -18,7 +18,10 @@
18
18
  class="cn-settings-field__image"
19
19
  role="group"
20
20
  :aria-labelledby="`${inputId}-label`">
21
- <span :id="`${inputId}-label`" class="cn-settings-field__image-label">{{ label }}</span>
21
+ <!-- `hideLabel` keeps the name for a screen reader only. -->
22
+ <span :id="`${inputId}-label`"
23
+ class="cn-settings-field__image-label"
24
+ :class="{ 'hidden-visually': field.hideLabel === true }">{{ label }}</span>
22
25
  <div class="cn-settings-field__image-row">
23
26
  <span class="cn-settings-field__image-tile" :data-testid="`cn-settings-field-image-${field.key}`">
24
27
  <img v-if="stringValue" :src="stringValue" :alt="t('nextcloud-vue', 'Current {name}', { name: label })">
@@ -126,7 +126,15 @@
126
126
  <NcNoteCard v-if="isChoiceDisabled(step)" type="warning">
127
127
  {{ dependsOnHint(step) }}
128
128
  </NcNoteCard>
129
- <p v-if="stepNote(step)" class="cn-setup-step__note" data-testid="cn-setup-step-note">
129
+ <!-- `noteStyle: "card"` sets the note as an info card (DqInstellen). -->
130
+ <NcNoteCard
131
+ v-if="stepNote(step) && step.noteStyle === 'card'"
132
+ type="info"
133
+ class="cn-setup-step__note-card"
134
+ data-testid="cn-setup-step-note">
135
+ {{ stepNote(step) }}
136
+ </NcNoteCard>
137
+ <p v-else-if="stepNote(step)" class="cn-setup-step__note" data-testid="cn-setup-step-note">
130
138
  {{ stepNote(step) }}
131
139
  </p>
132
140
  </template>
@@ -339,7 +339,11 @@ export default {
339
339
  list-style: none;
340
340
  }
341
341
 
342
- .cn-user-settings-dialog__nav-link {
342
+ /* A navigation entry, not a link in running text: no underline, also not
343
+ on hover. `!important` because a theme's `a { text-decoration }` carries
344
+ it (thematiq), and a bare `a` with `!important` beats any class. */
345
+ .cn-user-settings-dialog__nav-link,
346
+ .cn-user-settings-dialog__nav-link:visited {
343
347
  display: flex;
344
348
  align-items: center;
345
349
  gap: 10px;
@@ -348,11 +352,12 @@ export default {
348
352
  border-radius: var(--border-radius-element, 8px);
349
353
  font-size: 15px;
350
354
  color: var(--color-main-text);
351
- text-decoration: none;
355
+ text-decoration: none !important;
352
356
  }
353
357
 
354
358
  .cn-user-settings-dialog__nav-link:hover {
355
359
  background: var(--color-background-hover);
360
+ text-decoration: none !important;
356
361
  }
357
362
 
358
363
  .cn-user-settings-dialog__nav-link:focus-visible {
@@ -35,13 +35,13 @@
35
35
  <div class="cn-version-info__details">
36
36
  <!-- Application Name (the board card can leave it to its description: `showAppName`) -->
37
37
  <div v-if="!isBoard || (showAppName && !compact)" class="cn-version-info__item" data-testid="cn-version-info-app-name">
38
- <span class="cn-version-info__label">{{ fieldLabels.appName }}:</span>
38
+ <span class="cn-version-info__label">{{ fieldLabels.appName }}{{ labelColon }}</span>
39
39
  <span class="cn-version-info__value">{{ appName }}</span>
40
40
  </div>
41
41
 
42
42
  <!-- Version -->
43
43
  <div class="cn-version-info__item">
44
- <span class="cn-version-info__label">{{ fieldLabels.version }}:</span>
44
+ <span class="cn-version-info__label">{{ fieldLabels.version }}{{ labelColon }}</span>
45
45
  <span class="cn-version-info__value">{{ appVersion }}</span>
46
46
  </div>
47
47
 
@@ -49,13 +49,13 @@
49
49
  which draws the row ("Unknown" until the app reports it) with the
50
50
  up-to-date state inline ("0.4.47, bijgewerkt"). -->
51
51
  <div v-if="configuredVersion || isBoard" class="cn-version-info__item" data-testid="cn-version-info-configured">
52
- <span class="cn-version-info__label">{{ fieldLabels.configuredVersion }}:</span>
52
+ <span class="cn-version-info__label">{{ fieldLabels.configuredVersion }}{{ labelColon }}</span>
53
53
  <span class="cn-version-info__value">{{ isBoard ? boardConfiguredText : configuredVersion }}</span>
54
54
  </div>
55
55
 
56
56
  <!-- Additional items -->
57
57
  <div v-for="item in additionalItems" :key="item.label" class="cn-version-info__item">
58
- <span class="cn-version-info__label">{{ item.label }}:</span>
58
+ <span class="cn-version-info__label">{{ item.label }}{{ labelColon }}</span>
59
59
  <span class="cn-version-info__value" :class="item.statusClass">{{ item.value }}</span>
60
60
  </div>
61
61
 
@@ -320,6 +320,17 @@ export default {
320
320
  },
321
321
 
322
322
  computed: {
323
+ /**
324
+ * The colon after each label: none on the compact board card, which
325
+ * sets each label beside its value ("Versie 0.4.47", DqInstellingenAlgemeen);
326
+ * one otherwise, PqBeheer's card included.
327
+ *
328
+ * @return {string} The colon, or ''.
329
+ */
330
+ labelColon() {
331
+ return this.isBoard && this.compact ? '' : ':'
332
+ },
333
+
323
334
  /**
324
335
  * The field labels: the app's, else the library's in the reader's language.
325
336
  *
@@ -21,6 +21,7 @@ import CnExportsWidget from '../CnExportsWidget/CnExportsWidget.vue'
21
21
  import CnHeaderWidget from '../CnHeaderWidget/CnHeaderWidget.vue'
22
22
  import CnIntegrationWidget from '../CnIntegrationWidget/CnIntegrationWidget.vue'
23
23
  import CnNavCardGrid from '../CnNavCardGrid/CnNavCardGrid.vue'
24
+ import CnNoticeWidget from '../CnNoticeWidget/CnNoticeWidget.vue'
24
25
  import CnObjectDataWidget from '../CnObjectDataWidget/CnObjectDataWidget.vue'
25
26
  import CnObjectGeoWidget from '../CnObjectGeoWidget/CnObjectGeoWidget.vue'
26
27
  import CnObjectMetadataWidget from '../CnObjectMetadataWidget/CnObjectMetadataWidget.vue'
@@ -86,6 +87,7 @@ export const BUILT_IN_WIDGETS = {
86
87
  related: CnRelatedObjectsWidget,
87
88
  integration: CnIntegrationWidget,
88
89
  banner: CnBannerWidget,
90
+ notice: CnNoticeWidget,
89
91
  'audit-trail': CnAuditTrailWidget,
90
92
  timeline: CnTimelineWidget,
91
93
  header: CnHeaderWidget,
@@ -368,6 +368,20 @@ export default {
368
368
  default: 0,
369
369
  },
370
370
 
371
+ /**
372
+ * Rows per page: the board's pager under the table ("4 of 7", page
373
+ * buttons, Next), forwarded to CnDataTable. It pages the rows the
374
+ * widget holds; with a `source`, `source.limit` is how many are read
375
+ * (OpenRegister's default page otherwise). 0 (the default) shows no
376
+ * pager.
377
+ *
378
+ * @spec openspec/changes/object-table-pager/specs/object-table/spec.md#requirement-an-object-table-can-page-its-rows
379
+ */
380
+ pageSize: {
381
+ type: Number,
382
+ default: 0,
383
+ },
384
+
371
385
  /**
372
386
  * vue-router route object for the "View all" footer link. Forwarded
373
387
  * to CnDataTable (footer shows when the rows are a `limit`-ed subset).
@@ -611,7 +625,9 @@ export default {
611
625
  params[`_order[${field}]`] = dir === 'desc' ? 'desc' : 'asc'
612
626
  }
613
627
  if (typeof src.limit === 'number' && src.limit > 0) {
614
- params._limit = src.limit + 1
628
+ // Under a pager `limit` is how many rows the pages hold, not a
629
+ // "View all" cap that needs to see one more.
630
+ params._limit = this.pageSize > 0 ? src.limit : src.limit + 1
615
631
  }
616
632
  // `source.extend` (#91 Wave 3): forwarded as OR's repeated
617
633
  // `_extend[]` param so virtual/declarative fields (e.g.
@@ -736,7 +752,7 @@ export default {
736
752
  inner.register = this.source.register
737
753
  inner.schemaId = this.source.schema
738
754
  inner.fetchParams = this.resolvedFetchParams
739
- if (typeof this.source.limit === 'number' && this.source.limit > 0) {
755
+ if (typeof this.source.limit === 'number' && this.source.limit > 0 && !(this.pageSize > 0)) {
740
756
  inner.limit = this.source.limit
741
757
  }
742
758
  }
@@ -131,6 +131,7 @@ export { CnContactPicker } from './CnContactPicker/index.js'
131
131
  export { CnContactCreate } from './CnContactCreate/index.js'
132
132
  export { CnResourceSelect } from './CnResourceSelect/index.js'
133
133
  export { CnSearchSelect } from './CnSearchSelect/index.js'
134
+ export { CnNoticeWidget } from './CnNoticeWidget/index.js'
134
135
  export { CnPropertySourceField } from './CnPropertySourceField/index.js'
135
136
  export { CnDurationField } from './CnDurationField/index.js'
136
137
  export { CnFavouriteToggle } from './CnFavouriteToggle/index.js'
@@ -14,7 +14,7 @@ import { formatRelativeDate } from '../utils/boardDate.js'
14
14
  import { fetchReferenceObjects } from '../utils/boardReferenceColumns.js'
15
15
  import { fillTemplate } from '../utils/fillTemplate.js'
16
16
  import { FLOW_RUN_ERROR_STATUSES, flowRunStatuses } from '../utils/flowRunStatus.js'
17
- import { fetchGroupMembers, fetchPresence, fetchStatuses } from '../utils/nextcloudDirectory.js'
17
+ import { fetchGroupMembers, fetchPresence, fetchStatuses, fetchUsers } from '../utils/nextcloudDirectory.js'
18
18
  import { readPath } from '../utils/readPath.js'
19
19
  import { useFlowStore } from './useFlowStore.js'
20
20
  import { organisationRecord, useOrganisationStore } from './useOrganisationStore.js'
@@ -1054,7 +1054,8 @@ function searchSource(config = {}, context = {}) {
1054
1054
  * DqAfdelingen). Pair it with `folderSidebar: { source: 'groups',
1055
1055
  * filterField: 'group' }`, which lists the groups as the folder tree; the
1056
1056
  * selected group arrives as `group`. `sourceConfig.group` is the group shown
1057
- * before one is picked.
1057
+ * before one is picked; without one, All lists every user the reader may
1058
+ * list (all users for an admin, their groups' users for a sub-admin).
1058
1059
  *
1059
1060
  * Today is the dav app's out-of-office period when one runs now ("Absent
1060
1061
  * until 14 Oct"), else the user status (present, away) and its message. The
@@ -1088,14 +1089,13 @@ function usersSource() {
1088
1089
  load: async (config) => {
1089
1090
  const group = String((config && (config.group || config.defaultGroup)) || '')
1090
1091
  const mine = ++seq
1091
- if (group === '') {
1092
- state.rows = []
1093
- return
1094
- }
1095
1092
  state.loading = true
1096
1093
  try {
1097
1094
  const { default: axios } = await import('@nextcloud/axios')
1098
- const [members, statuses] = await Promise.all([fetchGroupMembers(axios, group), fetchStatuses(axios)])
1095
+ const [members, statuses] = await Promise.all([
1096
+ group === '' ? fetchUsers(axios, { limit: (config && config.limit) || 500 }) : fetchGroupMembers(axios, group),
1097
+ fetchStatuses(axios),
1098
+ ])
1099
1099
  const functionField = (config && config.functionField) || 'headline'
1100
1100
  const rolesField = (config && config.rolesField) || 'role'
1101
1101
  const rows = await Promise.all(members.map(async (user) => {
@@ -13,7 +13,11 @@
13
13
  * --cn-dialog-eyebrow-tracking letter spacing; 0 by default, 0.06em suits capitals
14
14
  */
15
15
 
16
+ /* The nldesign theme sets a dialog's radius with `!important` through its
17
+ dialog radius token, so the board's 12px goes to that token.
18
+ @spec openspec/changes/board-dialog-theme/specs/board-dialog-theme/spec.md#requirement-a-board-dialog-keeps-its-frame-and-inset-under-a-theme */
16
19
  .cn-dialog-board {
20
+ --nldesign-component-dialog-border-radius: var(--cn-board-card-radius, 12px);
17
21
  --cn-dialog-width: 640px;
18
22
  --cn-dialog-danger: var(--color-error);
19
23
  --cn-dialog-eyebrow-color: var(--color-primary-element-text, var(--color-main-text));
@@ -26,16 +30,26 @@
26
30
  background-color: color-mix(in srgb, var(--color-main-text) 45%, transparent);
27
31
  }
28
32
 
29
- /* Width by role, capped at the viewport minus a 16px gutter each side. */
33
+ /* Width by role, capped at the viewport minus a 16px gutter each side. The
34
+ shadow carries `!important`: the nldesign theme removes every
35
+ `.modal-container`'s shadow with `!important` and offers no token for it. */
30
36
  .modal-mask.cn-dialog-board .modal-wrapper--normal > .modal-container,
31
37
  .modal-mask.cn-dialog-board .modal-wrapper .modal-container {
32
38
  width: var(--cn-dialog-width);
33
39
  max-width: min(var(--cn-dialog-width), calc(100% - 32px));
34
40
  border-radius: var(--cn-board-card-radius, 12px);
35
- box-shadow: 0 12px 40px color-mix(in srgb, var(--color-main-text) 28%, transparent);
41
+ box-shadow: 0 12px 40px color-mix(in srgb, var(--color-main-text) 28%, transparent) !important;
36
42
  overflow: hidden;
37
43
  }
38
44
 
45
+ /* The board's header and body carry their own 24px inset. NcDialog pads its
46
+ container 4px at the top and 12px at the start, which put the title and
47
+ the fields 36px in; its rule outranked the one above through the scoped
48
+ `.dialog__modal[data-v-…]`, so this one names the class too. */
49
+ .modal-mask.cn-dialog-board.dialog__modal .modal-wrapper .modal-container {
50
+ padding: 0;
51
+ }
52
+
39
53
  /* The header is ours: NcModal's own close button and NcDialog's heading give
40
54
  way (the heading stays as the accessible name, visually hidden). The mask
41
55
  class raises the rule above NcButton's `.button-vue[data-v-…]`, whose
@@ -269,6 +283,10 @@
269
283
  border-radius: 16px 16px 0 0;
270
284
  }
271
285
 
286
+ .cn-dialog-board {
287
+ --nldesign-component-dialog-border-radius: 16px 16px 0 0;
288
+ }
289
+
272
290
  .modal-mask.cn-dialog-board .modal-container::before {
273
291
  content: '';
274
292
  display: block;
@@ -190,6 +190,25 @@
190
190
  font-size: 14px;
191
191
  }
192
192
 
193
+ /* The selected folder's heading above the list. */
194
+ .cn-index-page__scope-heading {
195
+ display: flex;
196
+ flex-direction: column;
197
+ gap: 4px;
198
+ margin-bottom: 12px;
199
+ }
200
+
201
+ .cn-index-page__scope-title {
202
+ margin: 0;
203
+ font-size: 1.25em;
204
+ font-weight: 700;
205
+ }
206
+
207
+ .cn-index-page__scope-subtitle {
208
+ font-size: 14px;
209
+ color: var(--color-text-maxcontrast);
210
+ }
211
+
193
212
  /* Opt-in folder navigation pane (left of the list). */
194
213
  .cn-index-page__folder-pane {
195
214
  flex: 0 0 240px;
@@ -203,6 +203,58 @@
203
203
  width: auto;
204
204
  }
205
205
 
206
+ /* ---- The follow pill (DqZaak "Volgen") ---------------------------------- */
207
+ /* The board draws the follow control under the title as a 28px pill: 13px
208
+ text at 600 on the main background, a grey border, a 14px icon 6px from its
209
+ label. Live it was Nextcloud's 34px tertiary button, 15px bold in the
210
+ primary colour. The theme tokens carry the weight and radius past the
211
+ nldesign theme's `!important`; the follower count takes the same pill.
212
+ @spec openspec/changes/board-follow-pill/specs/board-follow-pill/spec.md#requirement-the-board-follow-control-is-a-pill */
213
+ .cn-look-board .cn-detail-page .cn-follow-toggle .button-vue {
214
+ --nldesign-component-button-font-weight: 600;
215
+ --nldesign-component-button-border-radius: 14px;
216
+ --nldesign-component-button-secondary-action-color: var(--color-main-text);
217
+ --nldesign-component-button-secondary-action-background-color: var(--color-main-background);
218
+ --nldesign-component-button-secondary-action-border-color: var(--color-border-dark);
219
+ --nldesign-component-button-secondary-action-hover-background-color: var(--color-background-hover);
220
+ box-sizing: border-box;
221
+ height: 28px;
222
+ min-height: 28px;
223
+ min-width: 0;
224
+ padding: 0 10px;
225
+ border: 1px solid var(--color-border-dark);
226
+ border-radius: 14px;
227
+ background: var(--color-main-background);
228
+ color: var(--color-main-text);
229
+ font-size: 13px;
230
+ }
231
+
232
+ .cn-look-board .cn-detail-page .cn-follow-toggle .button-vue:hover {
233
+ background: var(--color-background-hover);
234
+ }
235
+
236
+ .cn-look-board .cn-detail-page .cn-follow-toggle .button-vue .button-vue__wrapper {
237
+ gap: 6px;
238
+ }
239
+
240
+ .cn-look-board .cn-detail-page .cn-follow-toggle .button-vue .button-vue__icon {
241
+ width: 14px;
242
+ min-width: 14px;
243
+ height: 14px;
244
+ min-height: 14px;
245
+ color: var(--color-main-text);
246
+ }
247
+
248
+ .cn-look-board .cn-detail-page .cn-follow-toggle .button-vue .button-vue__icon svg {
249
+ width: 14px;
250
+ height: 14px;
251
+ }
252
+
253
+ .cn-look-board .cn-detail-page .cn-follow-toggle .button-vue .button-vue__text {
254
+ margin: 0;
255
+ padding: 0;
256
+ }
257
+
206
258
  /* ---- Folder tabs ------------------------------------------------------- */
207
259
 
208
260
  /* The strip draws no line of its own (design-system pull request 147). The card under it
@@ -222,7 +274,7 @@
222
274
  }
223
275
 
224
276
  /* Tabs sit on the card's top edge; only the open tab reaches 1px further
225
- down (min-height 35px below), over the card's top border. Top-aligned so
277
+ down (min-height 38px below), over the card's top border. Top-aligned so
226
278
  the inactive tabs do not stretch to the open tab's height. */
227
279
  .cn-look-board .cn-tabs--board .cn-tabs__nav {
228
280
  align-items: flex-start;
@@ -256,8 +308,15 @@
256
308
 
257
309
  /* `margin: 0`: a tab is a <button>, and Nextcloud's core stylesheet gives
258
310
  every `button:not(.button-vue)` `margin: 3px 3px 3px 0`, which put the strip
259
- 3px low and the tabs 7px apart instead of 4 (DqZaak "Zaakonderdelen"). */
311
+ 3px low and the tabs 7px apart instead of 4 (DqZaak "Zaakonderdelen").
312
+ The nldesign theme sets every button's weight and radius with `!important`
313
+ through its button tokens, which drew every tab bold with four rounded
314
+ corners; the tab hands the board's values to those tokens. A tab is 37px
315
+ with or without a count (DqZaak), not 34px without one.
316
+ @spec openspec/changes/board-tabs-theme/specs/board-tabs-theme/spec.md#requirement-board-folder-tabs-keep-the-boards-weight-shape-and-height-under-a-theme */
260
317
  .cn-look-board .cn-tabs--board .cn-tabs__nav-item {
318
+ --nldesign-component-button-font-weight: 400;
319
+ --nldesign-component-button-border-radius: 8px 8px 0 0;
261
320
  background-color: var(--color-background-dark);
262
321
  border: 1px solid var(--color-border-dark);
263
322
  border-bottom: none;
@@ -265,17 +324,18 @@
265
324
  font-size: 14px;
266
325
  font-weight: 400;
267
326
  margin: 0;
268
- min-height: 34px;
327
+ min-height: 37px;
269
328
  padding: 8px 12px;
270
329
  }
271
330
 
272
331
  /* The open tab: the panel's colour, bold, and no coloured top edge. */
273
332
  .cn-look-board .cn-tabs--board .cn-tabs__nav-item.cn-tabs__nav-item--active {
333
+ --nldesign-component-button-font-weight: 700;
274
334
  background-color: var(--color-main-background);
275
335
  border-bottom: 1px solid var(--color-main-background);
276
336
  border-top: 1px solid var(--color-border-dark);
277
337
  font-weight: 700;
278
- min-height: 35px;
338
+ min-height: 38px;
279
339
  padding-bottom: 9px;
280
340
  padding-top: 8px;
281
341
  position: relative;
@@ -456,6 +516,19 @@
456
516
  padding: 10px 22px 20px;
457
517
  }
458
518
 
519
+ /* A stat tile in a side card (DqZaak "Termijn") sits on the card's inset as
520
+ the board draws it. The card-fit wrapper is flush, and the wrapper's own
521
+ flush rule loads after this sheet and outranked the side card's padding,
522
+ so the value sat 11px from the card's edge instead of 23px.
523
+ @spec openspec/changes/board-dashboard-widget-inset/specs/board-dashboard-widget-inset/spec.md#requirement-a-stat-tile-in-a-detail-side-card-keeps-the-cards-inset */
524
+ .cn-look-board .cn-detail-page__side .cn-detail-page__card-fit > .cn-widget-wrapper__content:has(> .cn-kpi-card--board) {
525
+ padding: 10px 22px 20px;
526
+ }
527
+
528
+ .cn-look-board .cn-detail-page__side .cn-detail-page__card-fit > .cn-widget-wrapper__content > .cn-kpi-card--board {
529
+ padding: 0;
530
+ }
531
+
459
532
  /* No Actions menu on a side card. */
460
533
  .cn-look-board .cn-detail-page__side .cn-widget-wrapper__actions {
461
534
  display: none;