@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
package/l10n/en.json CHANGED
@@ -1724,9 +1724,16 @@
1724
1724
  "No contacts linked": "No contacts linked",
1725
1725
  "No relations yet": "No relations yet",
1726
1726
  "Period": "Period",
1727
- "On": "On"
1727
+ "On": "On",
1728
+ "Select a step on the canvas to see its settings.": "Select a step on the canvas to see its settings.",
1729
+ "Show runs": "Show runs",
1730
+ "Back to the step settings": "Back to the step settings"
1728
1731
  },
1729
1732
  "plurals": {
1733
+ "{count} member": [
1734
+ "{count} member",
1735
+ "{count} members"
1736
+ ],
1730
1737
  "{count} selected": [
1731
1738
  "{count} selected",
1732
1739
  "{count} selected"
package/l10n/nl.json CHANGED
@@ -1937,9 +1937,16 @@
1937
1937
  "No contacts linked": "Geen contacten gekoppeld",
1938
1938
  "No relations yet": "Nog geen relaties",
1939
1939
  "Period": "Periode",
1940
- "On": "Aan"
1940
+ "On": "Aan",
1941
+ "Select a step on the canvas to see its settings.": "Kies een stap op het canvas om de instellingen te zien.",
1942
+ "Show runs": "Uitvoeringen tonen",
1943
+ "Back to the step settings": "Terug naar de stapinstellingen"
1941
1944
  },
1942
1945
  "plurals": {
1946
+ "{count} member": [
1947
+ "{count} lid",
1948
+ "{count} leden"
1949
+ ],
1943
1950
  "{count} selected": [
1944
1951
  "{count} geselecteerd",
1945
1952
  "{count} geselecteerd"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/nextcloud-vue",
3
- "version": "2.103.0",
3
+ "version": "2.105.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": {
@@ -153,6 +153,7 @@ Controlling the inline action button count — `inlineActionCount` sets how many
153
153
  | `mapLabel` | `String` | `''` | Label for the map view-toggle option (defaults to "Map"). Only shown when `showMap` |
154
154
  | `mapIcon` | `String` | `''` | MDI icon name for the map option (defaults to the built-in map-marker icon). Resolved via `CnIcon` |
155
155
  | `showSearch` | `Boolean` | `false` | Whether to show the inline search field on the left of the bar. The field takes the place of the "Showing X of Y" counter unless `showCountWithSearch` is set |
156
+ | `searchPlacement` | `String` | `''` | Board look only: `'toolbar'` draws the search field on row 1, before Filter and the view switch, instead of at the start of row 2 |
156
157
  | `searchValue` | `String` | `''` | Current value of the inline search field (controlled) |
157
158
  | `searchPlaceholder` | `String` | `''` | Placeholder / accessible label for the inline search field |
158
159
  | `showCountWithSearch` | `Boolean` | `false` | Keep the "Showing X of Y" counter visible beside the inline search field, after the search and any `#after-search` controls. No effect without `showSearch` |
@@ -18,6 +18,20 @@
18
18
  @description Under the board look: saved views as chips and the Save view button, in row 1 after the quick-filter chips.
19
19
  -->
20
20
  <slot name="actions-end" />
21
+ <!-- `searchPlacement: "toolbar"`: the search field on row 1, before
22
+ Filter and the view switch, instead of opening row 2. -->
23
+ <div v-if="showSearch && searchInToolbar"
24
+ class="cn-actions-bar__search cn-actions-bar__search--toolbar"
25
+ data-testid="cn-actions-bar-search-toolbar">
26
+ <Magnify :size="18" class="cn-actions-bar__search-icon" />
27
+ <input
28
+ type="search"
29
+ class="cn-actions-bar__search-input"
30
+ :placeholder="searchPlaceholder || t('nextcloud-vue', 'Search…')"
31
+ :value="searchValue"
32
+ :aria-label="searchPlaceholder || t('nextcloud-vue', 'Search')"
33
+ @input="onSearchInput">
34
+ </div>
21
35
  <!--
22
36
  Filter and the view switch are one unit: when row 1 is full they
23
37
  wrap together to the start of the next line (the DqZaken board),
@@ -373,7 +387,7 @@
373
387
 
374
388
  <!-- Board look, row 2: the search field, then the active filters. -->
375
389
  <div v-if="isBoardLayout" class="cn-actions-bar__row cn-actions-bar__row--search" data-testid="cn-actions-bar-row-search">
376
- <div v-if="showSearch" class="cn-actions-bar__search">
390
+ <div v-if="showSearch && !searchInToolbar" class="cn-actions-bar__search">
377
391
  <Magnify :size="18" class="cn-actions-bar__search-icon" />
378
392
  <input
379
393
  type="search"
@@ -383,7 +397,7 @@
383
397
  :aria-label="searchPlaceholder || t('nextcloud-vue', 'Search')"
384
398
  @input="onSearchInput">
385
399
  </div>
386
- <span v-else-if="showCount && hasTotal" class="cn-actions-bar__count">
400
+ <span v-else-if="showCount && hasTotal && !searchInToolbar" class="cn-actions-bar__count">
387
401
  {{ countText }}
388
402
  </span>
389
403
  <slot name="after-search" />
@@ -943,6 +957,19 @@ export default {
943
957
  default: false,
944
958
  },
945
959
 
960
+ /**
961
+ * Where the board look draws the search field: `''` (the default) at
962
+ * the start of row 2, `'toolbar'` on row 1 before Filter and the view
963
+ * switch (DqTeamwachtrij). No effect outside the board look.
964
+ *
965
+ * @type {''|'toolbar'}
966
+ */
967
+ searchPlacement: {
968
+ type: String,
969
+ default: '',
970
+ validator: (v) => ['', 'toolbar'].includes(v),
971
+ },
972
+
946
973
  /** Current value of the inline search field (controlled) */
947
974
  searchValue: {
948
975
  type: String,
@@ -1101,6 +1128,16 @@ export default {
1101
1128
  ],
1102
1129
 
1103
1130
  computed: {
1131
+ /**
1132
+ * Whether the search field sits on the board's row 1.
1133
+ *
1134
+ * @spec openspec/changes/index-search-on-toolbar/specs/index-page/spec.md#requirement-the-search-field-can-sit-on-the-toolbar-row
1135
+ * @return {boolean}
1136
+ */
1137
+ searchInToolbar() {
1138
+ return this.isBoardLayout && this.searchPlacement === 'toolbar'
1139
+ },
1140
+
1104
1141
  /**
1105
1142
  * Whether the board toolbar is drawn: the `layout` prop, else the look
1106
1143
  * the app provides.
@@ -160,7 +160,7 @@
160
160
  <template #additional-items>
161
161
  <!-- The board card: support as one fact row, the SLA address on the same line. -->
162
162
  <div v-if="compactCard && showSupport && !supportAtEnd && !$slots.footer" class="cn-version-info__item" data-testid="cn-admin-settings-support-row">
163
- <span class="cn-version-info__label">{{ supportTitle }}:</span>
163
+ <span class="cn-version-info__label">{{ supportTitle }}</span>
164
164
  <span class="cn-version-info__value">
165
165
  <a :href="`mailto:${supportEmail}`">{{ supportEmail }}</a><template v-if="slaEmail">, {{ slaInline }} <a :href="`mailto:${slaEmail}`">{{ slaEmail }}</a></template>
166
166
  </span>
@@ -67,7 +67,7 @@ With loading state, custom components, translate, permissions, and custom page t
67
67
  |------|------|----------|---------|-------------|
68
68
  | `manifest` | Object | Yes | — | Reactive manifest object from `useAppManifest`. Provides `manifest.dependencies`, `manifest.menu`, and is propagated via provide/inject |
69
69
  | `appId` | String | Yes | — | Nextcloud app id. Forwarded to `NcContent` and `CnDependencyMissing` |
70
- | `appDisplayName` | String | No | `''` | Human-readable name shown in the Nextcloud top bar. When set it overrides the technical `appId` so a virtual app shows its own name (e.g. "Pet Store") instead of the host app id. |
70
+ | `appDisplayName` | String | No | `''` | Human-readable name shown in the Nextcloud top bar. When set it overrides the technical `appId` so a virtual app shows its own name (e.g. "Pet Store") instead of the host app id. The user settings dialog, the setup wizard and the credentials section show it through the app's `translate`; without it they show the name Nextcloud's app menu gives (localised), else the id with a capital, never the bare id. The top bar's own name, which also keys the stored list-pane width (`pane-list-size-<name>`), stays untranslated so a reader's pane width survives a language switch. |
71
71
  | `persistManifestDelta` | Function | No | `null` | In-app editing (ADR-041) persistence hook, called with the minimal manifest delta on save. When Buildiq is reachable for the user, CnAppRoot provides a shared `cnManifestEditor` + `cnOpenBuildAvailable` (the inject key deliberately keeps its old spelling — see the rename note in CHANGELOG); the page action rows (`CnDashboardPage`/`CnDetailPage`/`CnIndexPage`) then render an icon-only `CnBuildiqEditButton` inline with their normal buttons, and the body grid becomes drag/resizable in edit mode. Omit to keep edits in-memory only. |
72
72
  | `dataSources` | Object \| null | No | `null` | App registers/schemas for the in-app pages editor (ADR-041). Lets the Edit-pages modal offer Register / Schema / Columns dropdowns for `index`/`detail` pages instead of free-text slug inputs, so a created page actually renders a table. Shape: `{ registers: [{ value, label, schemas: [{ value, label, columns: string[] }] }] }`. Provided to descendants as `cnDataSources`; when omitted the editor falls back to free-text register/schema fields. **A snapshot** — captured once, so it cannot show anything created after boot. Prefer `dataSourcesLoader`. |
73
73
  | `dataSourcesLoader` | Function \| null | No | `null` | Async loader for the same data, re-invoked every time a pages-editor modal opens: `async () => ({ registers: [...] })`. Because `provide()` runs once, the static `dataSources` snapshot can never change — so a register or schema created after the app booted (in another tab, the OpenRegister UI, or via the API) would never appear in the dropdowns. A loader fixes that with no page reload, and moves the fetch off the app-boot path onto the far rarer editor-open path. When both props are given, `dataSources` seeds the initial list and the loader's result replaces it on the first refresh. Provided to descendants as the `cnDataSourcesState` holder plus a `cnRefreshDataSources()` action. |
@@ -180,7 +180,7 @@
180
180
  <div class="cn-app-root__setup">
181
181
  <CnSetupWizard
182
182
  :appId="appId"
183
- :appName="appDisplayName || (manifest && manifest.name) || appId"
183
+ :appName="shownAppName"
184
184
  :steps="manifest.setup.steps"
185
185
  :cancellable="false"
186
186
  :completedStepIds="setupCompletedStepIds"
@@ -441,7 +441,7 @@
441
441
  <div v-if="setupWizardOpen" class="cn-app-root__setup-optional">
442
442
  <CnSetupWizard
443
443
  :appId="appId"
444
- :appName="appDisplayName || (manifest && manifest.name) || appId"
444
+ :appName="shownAppName"
445
445
  :steps="manifest.setup.steps"
446
446
  :cancellable="true"
447
447
  :completedStepIds="setupCompletedStepIds"
@@ -580,7 +580,7 @@
580
580
  <CnCredentials
581
581
  scope="personal"
582
582
  :appId="appId"
583
- :appName="appDisplayName || (manifest && manifest.name) || appId"
583
+ :appName="shownAppName"
584
584
  :appCredentials="(manifest && manifest.credentials) || []" />
585
585
  </NcAppSettingsSection>
586
586
  <!--
@@ -747,6 +747,7 @@ import {
747
747
  import { RegistryKindError } from '../../errors/RegistryKindError.js'
748
748
  import { useObjectStore } from '../../store/index.js'
749
749
  import { isAppInstalled } from '../../utils/appInstalled.js'
750
+ import { appShownName } from '../../utils/appShownName.js'
750
751
  import { BUILT_IN_FORMATTERS } from '../../utils/builtInFormatters.js'
751
752
  import { addDiagnosticsListener, clearReportedDiagnostics, reportDiagnostic } from '../../utils/diagnostics.js'
752
753
  import { DEFAULT_FORGE, resolveForge } from '../../utils/forge.js'
@@ -3134,6 +3135,22 @@ export default {
3134
3135
  return this.userSettingsTitle || this.libraryCopy('User settings')
3135
3136
  },
3136
3137
 
3138
+ /**
3139
+ * The app's name as the reader reads it: `appDisplayName` or the
3140
+ * manifest's `name` through the app's translate, else the name
3141
+ * Nextcloud's app menu shows (localised), else the id with a capital.
3142
+ * Used for the user settings dialog, the setup wizard and the
3143
+ * credentials section; never for NcContent's `appName`, which also
3144
+ * keys the stored list-pane width (`pane-list-size-<appName>`).
3145
+ *
3146
+ * @spec openspec/changes/app-shown-name-in-chrome/specs/app-root/spec.md#requirement-the-app-chrome-shows-the-apps-localised-name
3147
+ * @return {string}
3148
+ */
3149
+ shownAppName() {
3150
+ const given = this.appDisplayName || (this.manifest && this.manifest.name) || ''
3151
+ return appShownName(this.appId, given ? this.translate(given) : '')
3152
+ },
3153
+
3137
3154
  /**
3138
3155
  * The props of the user-settings dialog: `NcAppSettingsDialog`'s, or
3139
3156
  * under the board look CnUserSettingsDialog's (eyebrow, subtitle and
@@ -3149,7 +3166,7 @@ export default {
3149
3166
  const props = {
3150
3167
  open: this.userSettingsOpen,
3151
3168
  title: this.resolvedUserSettingsTitle,
3152
- appName: this.appDisplayName || (this.manifest && this.manifest.name) || this.appId || '',
3169
+ appName: this.shownAppName,
3153
3170
  }
3154
3171
  if (this.userSettingsEyebrow) {
3155
3172
  props.eyebrow = this.translate(this.userSettingsEyebrow)
@@ -108,6 +108,8 @@ Fallback slot — shown when ApexCharts is not available:
108
108
  | `legendPosition` | String | `''` | Legend placement override (`top`/`bottom`/`left`/`right`); empty keeps the automatic placement |
109
109
  | `barMax` | Number | `null` | `bar-list`: the value a full track stands for (100 for percentages); null takes the largest value |
110
110
  | `footnote` | String | `''` | A line under the chart, through the host's translate: under the rows of a `bar-list` or `matrix`, under the plot of a `line`, `area` or `bar` chart |
111
+ | `referenceLines` | Array | `[]` | Dashed horizontal reference lines on the value axis of a line, area or bar chart, such as a norm: `[{ value, label?, color? }]`. The label goes through the host's translate; the colour defaults to the theme's error colour (board DqDoorlooptijd). |
112
+ | `categoryFormat` | String | `''` | How date categories read on the x-axis: `month` ("jan") or `month-year` ("jan 2026"), in the user's language. Categories that are not dates are left as is. |
111
113
  | `emptyCellLabel` | String | `''` | `matrix`: the text of a cell without a value ("geen"), through the host's translate. Empty leaves the cell blank |
112
114
  | `highlight` | Object | `null` | `matrix`: the cell to outline, `{ row, column, columnKeys? }`, tokens such as `@object.behaviour` resolved |
113
115
  | `cellSource` | Object | `null` | `matrix`: read the cells from records that each hold one cell (`{ register, schema, filter?, rowField, columnField, valueField, … }`), or with `cellsPath` from the cells inside one record, see [Bar list and matrix](#bar-list-and-matrix) |
@@ -220,6 +220,22 @@ function parseBucketKey(key) {
220
220
  )
221
221
  }
222
222
 
223
+ /**
224
+ * A theme colour by CSS variable name, as a literal: ApexCharts writes colours
225
+ * into SVG attributes, where `var()` does not resolve.
226
+ *
227
+ * @param {string} name The variable, e.g. `--color-error`.
228
+ * @param {string} fallback The colour when the variable is unset (or no DOM).
229
+ * @return {string} The colour.
230
+ */
231
+ function themeColor(name, fallback) {
232
+ if (typeof document === 'undefined' || typeof getComputedStyle !== 'function') {
233
+ return fallback
234
+ }
235
+ const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim()
236
+ return value || fallback
237
+ }
238
+
223
239
  /** Event-bus channel CnWidgetWrapper's Refresh action broadcasts on. */
224
240
  const REFRESH_BUS_CHANNEL = 'cn:widget:refresh'
225
241
 
@@ -385,6 +401,31 @@ export default {
385
401
  default: '',
386
402
  },
387
403
 
404
+ /**
405
+ * Horizontal reference lines on the value axis of a line, area or bar
406
+ * chart, such as a norm: `[{ value, label?, color? }]`. Drawn dashed;
407
+ * the label goes through the host's translate. The colour defaults to
408
+ * the theme's error colour.
409
+ *
410
+ * @type {Array<{value: number, label?: string, color?: string}>}
411
+ */
412
+ referenceLines: {
413
+ type: Array,
414
+ default: () => [],
415
+ },
416
+
417
+ /**
418
+ * How date categories read on the x-axis: `month` ("jan") or
419
+ * `month-year` ("jan 2026"), in the user's language. A category that
420
+ * is not a date is shown as is. Empty shows the categories unchanged.
421
+ *
422
+ * @type {''|'month'|'month-year'}
423
+ */
424
+ categoryFormat: {
425
+ type: String,
426
+ default: '',
427
+ },
428
+
388
429
  /**
389
430
  * The `matrix` cell to outline, usually the record's on a detail page:
390
431
  * `{ row, column, columnKeys? }`, where `row` and `column` take tokens
@@ -1505,6 +1546,51 @@ export default {
1505
1546
  return this.footnote ? this.hostTranslate(this.footnote) : t('nextcloud-vue', 'Chart data')
1506
1547
  },
1507
1548
 
1549
+ /**
1550
+ * The x-axis categories as shown: with `categoryFormat`, date
1551
+ * categories as short months in the user's language.
1552
+ *
1553
+ * @return {Array<string|number>} The categories.
1554
+ */
1555
+ displayCategories() {
1556
+ const categories = Array.isArray(this.resolvedCategories) ? this.resolvedCategories : []
1557
+ if (this.categoryFormat !== 'month' && this.categoryFormat !== 'month-year') {
1558
+ return categories
1559
+ }
1560
+ const options = this.categoryFormat === 'month' ? { month: 'short' } : { month: 'short', year: 'numeric' }
1561
+ return categories.map((category) => {
1562
+ const date = typeof category === 'string' || typeof category === 'number' ? parseBucketKey(category) : null
1563
+ return date ? date.toLocaleDateString(userLocale(), options) : category
1564
+ })
1565
+ },
1566
+
1567
+ /**
1568
+ * The `referenceLines` as ApexCharts annotations: dashed, labelled,
1569
+ * in the theme's error colour unless a line names its own.
1570
+ *
1571
+ * @return {Array<object>} The annotations, possibly empty.
1572
+ */
1573
+ referenceAnnotations() {
1574
+ const lines = (Array.isArray(this.referenceLines) ? this.referenceLines : [])
1575
+ .filter((line) => line && Number.isFinite(Number(line.value)))
1576
+ if (lines.length === 0) {
1577
+ return []
1578
+ }
1579
+ const fallback = themeColor('--color-error', '#e04224')
1580
+ return lines.map((line) => {
1581
+ const color = line.color || fallback
1582
+ return {
1583
+ y: Number(line.value),
1584
+ x: Number(line.value),
1585
+ borderColor: color,
1586
+ strokeDashArray: 6,
1587
+ label: line.label
1588
+ ? { text: this.hostTranslate(line.label), borderColor: color, style: { color: '#fff', background: color } }
1589
+ : { text: '' },
1590
+ }
1591
+ })
1592
+ },
1593
+
1508
1594
  mergedOptions() {
1509
1595
  const isPieType = ['pie', 'donut', 'radialBar'].includes(this.type)
1510
1596
 
@@ -1592,7 +1678,7 @@ export default {
1592
1678
  if (!isPieType) {
1593
1679
  if (this.resolvedCategories.length > 0) {
1594
1680
  defaults.xaxis = {
1595
- categories: this.resolvedCategories,
1681
+ categories: this.displayCategories,
1596
1682
  labels: {
1597
1683
  style: {
1598
1684
  colors: 'var(--color-text-maxcontrast, #767676)',
@@ -1634,6 +1720,12 @@ export default {
1634
1720
  }
1635
1721
  }
1636
1722
 
1723
+ // Reference lines, such as a norm, on the value axis.
1724
+ if (!isPieType && this.referenceAnnotations.length > 0) {
1725
+ const axisKey = (this.type === 'bar' && this.horizontal) ? 'xaxis' : 'yaxis'
1726
+ defaults.annotations = { [axisKey]: this.referenceAnnotations }
1727
+ }
1728
+
1637
1729
  // Add labels for pie/donut
1638
1730
  if (isPieType && this.resolvedLabels.length > 0) {
1639
1731
  defaults.labels = this.resolvedLabels
@@ -347,12 +347,12 @@
347
347
  <label
348
348
  v-else
349
349
  class="cn-dashboard-page__page-filter">
350
- <span v-if="pf.label" class="cn-dashboard-page__page-filter-label">{{ pf.label }}</span>
350
+ <span v-if="pf.label" class="cn-dashboard-page__page-filter-label">{{ effectiveTranslate(pf.label) }}</span>
351
351
  <NcSelect
352
352
  :modelValue="selectedPageFilterOption(pf)"
353
- :options="pf.options || []"
353
+ :options="pageFilterSegments(pf)"
354
354
  :clearable="false"
355
- :inputLabel="pf.label || pf.key"
355
+ :inputLabel="pf.label ? effectiveTranslate(pf.label) : pf.key"
356
356
  label="label"
357
357
  :data-testid="'cn-page-filter-' + pf.key"
358
358
  @update:modelValue="onPageFilterChange(pf, $event)" />
@@ -1106,6 +1106,9 @@ const CHART_PROP_KEYS = [
1106
1106
  'highlight',
1107
1107
  'cellSource',
1108
1108
  'emptyCellLabel',
1109
+ // A norm line and month categories on a line chart (DqDoorlooptijd).
1110
+ 'referenceLines',
1111
+ 'categoryFormat',
1109
1112
  ]
1110
1113
 
1111
1114
  /**
@@ -2886,12 +2889,12 @@ export default {
2886
2889
  return null
2887
2890
  }
2888
2891
  const current = this.workspaceContext[pf.key]
2889
- return (pf.options || []).find((o) => o.value === current) || null
2892
+ return this.pageFilterSegments(pf).find((o) => o.value === current) || null
2890
2893
  },
2891
2894
 
2892
2895
  /**
2893
- * A page filter's options as segments, labels through the host
2894
- * translate.
2896
+ * A page filter's options, labels through the host translate: the
2897
+ * segments of a segmented filter and the options of a select.
2895
2898
  *
2896
2899
  * @spec openspec/changes/dashboard-board-termijnen/specs/dashboard-page/spec.md#requirement-a-page-filter-as-a-segmented-group-on-the-description-line
2897
2900
  * @param {object} pf The page-filter descriptor.
@@ -229,6 +229,7 @@ features are folded in here as opt-in props (bare-table usage is unchanged):
229
229
  - `borderless` — drop the container's card chrome so the table sits flush inside
230
230
  a parent card (e.g. a `CnWidgetWrapper` dashboard slot).
231
231
  - `limit` — show only the first N rows; with `viewAllRoute` a "View all" footer appears.
232
+ - `pageSize` — rows per page: one page of the rows the table holds, with the board's pager under them ("4 of 7", the page buttons, Next) once there is more than one page (DqCatalogus). 0 (the default) shows every row, or the first `limit`.
232
233
  - `viewAllRoute` / `viewAllLabel` — the footer control's route and label. It renders
233
234
  as a real `<a :href>` when the router resolves the route, and as a
234
235
  `<button type="button">` (emitting `view-all`) otherwise, so it always has a
@@ -389,6 +389,18 @@
389
389
  </button>
390
390
  </slot>
391
391
  </div>
392
+ <!-- `pageSize`: the board's pager under the rows, "4 of 7" and the
393
+ page buttons (DqCatalogus). -->
394
+ <CnPagination
395
+ v-if="pageSize > 0 && totalRowCount > pageSize"
396
+ variant="board"
397
+ class="cn-data-table__pager"
398
+ :currentPage="currentPage"
399
+ :totalPages="pageCount"
400
+ :totalItems="totalRowCount"
401
+ :currentPageSize="pageSize"
402
+ data-testid="cn-data-table-pager"
403
+ @pageChanged="currentPage = $event" />
392
404
  </div>
393
405
  </template>
394
406
 
@@ -400,6 +412,7 @@ import { NcCheckboxRadioSwitch, NcLoadingIcon } from '@nextcloud/vue'
400
412
  import ChevronDown from 'vue-material-design-icons/ChevronDown.vue'
401
413
  import ChevronUp from 'vue-material-design-icons/ChevronUp.vue'
402
414
  import FilterOutline from 'vue-material-design-icons/FilterOutline.vue'
415
+ import CnPagination from '../CnPagination/CnPagination.vue'
403
416
  import CnColumnFilterPopover from './CnColumnFilterPopover.vue'
404
417
  import { useClickDragGuard } from '../../composables/useClickDragGuard.js'
405
418
  import { normalizeLook } from '../../composables/useLook.js'
@@ -491,6 +504,7 @@ export default {
491
504
  CnColumnFilterPopover,
492
505
  CnIcon,
493
506
  CnLockIndicator,
507
+ CnPagination,
494
508
  CnUnreadMarker,
495
509
  ChevronDown,
496
510
  ChevronUp,
@@ -858,6 +872,19 @@ export default {
858
872
  default: 0,
859
873
  },
860
874
 
875
+ /**
876
+ * Rows per page. Above 0 the table shows one page of its rows and the
877
+ * board's pager under them ("4 of 7", page buttons, Next) once there is
878
+ * more than one page. Pages the rows the table holds; 0 (the default)
879
+ * shows them all, or the first `limit`.
880
+ *
881
+ * @spec openspec/changes/object-table-pager/specs/object-table/spec.md#requirement-an-object-table-can-page-its-rows
882
+ */
883
+ pageSize: {
884
+ type: Number,
885
+ default: 0,
886
+ },
887
+
861
888
  /**
862
889
  * vue-router route object for the "View all" footer link. The footer only
863
890
  * shows when set AND the rows are a `limit`-ed subset. Folded in from CnTableWidget.
@@ -1010,6 +1037,8 @@ export default {
1010
1037
  aggregateRequestId: 0,
1011
1038
  /** Rows fetched in self-fetch mode (register + schemaId). */
1012
1039
  fetchedRows: [],
1040
+ /** The page shown under `pageSize`. */
1041
+ currentPage: 1,
1013
1042
  /** Personal-lens reports (`@self.lenses`) of the latest self-fetch response. */
1014
1043
  fetchedLenses: {},
1015
1044
  /** True while a self-fetch request is in flight. */
@@ -1125,9 +1154,23 @@ export default {
1125
1154
  * @return {Array<object>}
1126
1155
  */
1127
1156
  effectiveRows() {
1157
+ if (this.pageSize > 0) {
1158
+ const page = Math.min(this.currentPage, this.pageCount)
1159
+ return this.sourceRows.slice((page - 1) * this.pageSize, page * this.pageSize)
1160
+ }
1128
1161
  return this.limit > 0 ? this.sourceRows.slice(0, this.limit) : this.sourceRows
1129
1162
  },
1130
1163
 
1164
+ /**
1165
+ * The number of pages under `pageSize` (at least 1).
1166
+ *
1167
+ * @spec openspec/changes/object-table-pager/specs/object-table/spec.md#requirement-an-object-table-can-page-its-rows
1168
+ * @return {number}
1169
+ */
1170
+ pageCount() {
1171
+ return this.pageSize > 0 ? Math.max(1, Math.ceil(this.sourceRows.length / this.pageSize)) : 1
1172
+ },
1173
+
1131
1174
  /**
1132
1175
  * Total row count before the `limit` cap (drives the count badge + the
1133
1176
  * "View all" footer condition).
@@ -1339,6 +1382,12 @@ export default {
1339
1382
  },
1340
1383
  },
1341
1384
 
1385
+ pageCount(count) {
1386
+ if (this.currentPage > count) {
1387
+ this.currentPage = count
1388
+ }
1389
+ },
1390
+
1342
1391
  effectiveColumns: {
1343
1392
  handler() {
1344
1393
  this.loadAggregates()
@@ -426,6 +426,8 @@ Four levels of action and the case surfaces around them. All opt in. See [the re
426
426
  | `statusPill` | Object \| null | `null` | A second pill, same shape. |
427
427
  | `headerFields` | Array | `[]` | Fields shown as chips under the title (manifest `config.headerFields`). Each entry is a property key, or `{ key, format, labelField, colorField, warnWhenPast }`; `format` is `text` (default), `mono`, `badge`, `user` or `date`. A `$ref` value shows `labelField` of the referenced object and falls back to the raw id in mono; a badge maps `colorField` (`success`, `warning`, `error`, `info`, `neutral`) to a variant; `warnWhenPast` gives a past date the error variant. An empty value shows no chip, a page without the key shows no row. Each chip reads "Title: value" to a screen reader, and the row prints as plain text. |
428
428
  | `sideColumn` | Array | `[]` | Widget definitions or widget ids, rendered as a column of cards beside the body. |
429
+ | `sideColumnFrom` | String | `''` | A field path, read like a `visibleWhen` field (e.g. `@context.caseType.sideCards`), whose list of ids picks the `sideColumn` entries this record shows and their order. Any other value keeps `sideColumn` as declared. |
430
+ | `tabsFrom` | String | `''` | A field path, read like a `visibleWhen` field, whose list of tab ids picks the tabs of the page's `tabs` widgets and their order for this record: `@context.caseType.tabs` reads the list a case type keeps (DqAanbesteding: Inschrijvingen, Publicaties, Bestanden, Historie). A tab's id is its `id`, else its `widgetId`, or the string entry. An empty or missing list keeps the declared tabs; edit mode shows them all. |
429
431
  | `isAdmin` | Boolean \| null | `null` | Shows `adminOnly` header actions. `null` reads it from Nextcloud. |
430
432
 
431
433
  ## The record as a place