@conduction/nextcloud-vue 2.79.0 → 2.80.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 (197) hide show
  1. package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js.map +1 -1
  2. package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js +3 -1
  3. package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js.map +1 -1
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +32 -3
  5. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  6. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +10 -8
  7. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  8. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +39 -3
  9. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  10. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  11. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +79 -1
  12. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  13. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +133 -96
  14. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  15. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  16. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +1 -0
  17. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  18. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +62 -4
  19. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  20. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  21. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +24 -0
  22. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  23. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +43 -4
  24. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  25. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +11 -0
  26. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js.map +1 -0
  27. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +736 -0
  28. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -0
  29. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +238 -0
  30. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -0
  31. package/dist/esm/components/CnFactsWidget/index.js +30 -0
  32. package/dist/esm/components/CnFactsWidget/index.js.map +1 -0
  33. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +10 -0
  34. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js.map +1 -0
  35. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +551 -0
  36. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -0
  37. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +183 -0
  38. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -0
  39. package/dist/esm/components/CnFieldListWidget/index.js +29 -0
  40. package/dist/esm/components/CnFieldListWidget/index.js.map +1 -0
  41. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +65 -17
  42. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  43. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +4 -4
  44. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  45. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +18 -3
  46. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
  47. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
  48. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +305 -72
  49. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  50. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +31 -13
  51. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  52. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +10 -0
  53. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js.map +1 -0
  54. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +99 -0
  55. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -0
  56. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +48 -0
  57. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -0
  58. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js +29 -1
  59. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
  60. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
  61. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +10 -0
  62. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js.map +1 -0
  63. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +387 -0
  64. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -0
  65. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +169 -0
  66. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -0
  67. package/dist/esm/components/CnRecordListWidget/index.js +31 -0
  68. package/dist/esm/components/CnRecordListWidget/index.js.map +1 -0
  69. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js +10 -0
  70. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js.map +1 -0
  71. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js +351 -0
  72. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js.map +1 -0
  73. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js +71 -0
  74. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js.map +1 -0
  75. package/dist/esm/components/CnSectionsWidget/index.js +35 -0
  76. package/dist/esm/components/CnSectionsWidget/index.js.map +1 -0
  77. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +81 -2
  78. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
  79. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
  80. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  81. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +1 -0
  82. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  83. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +122 -7
  84. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  85. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +84 -23
  86. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  87. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +4 -0
  88. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  89. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +106 -2
  90. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
  91. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +105 -53
  92. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
  93. package/dist/esm/composables/useFormDraft.js +140 -9
  94. package/dist/esm/composables/useFormDraft.js.map +1 -1
  95. package/dist/esm/index.js +4 -0
  96. package/dist/esm/index.js.map +1 -1
  97. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js +28 -20
  98. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js.map +1 -1
  99. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js +9 -0
  100. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js.map +1 -1
  101. package/dist/esm/integrations/builtin/field-inspection.js +21 -0
  102. package/dist/esm/integrations/builtin/field-inspection.js.map +1 -1
  103. package/dist/esm/integrations/offline/checklistSubmission.js +141 -0
  104. package/dist/esm/integrations/offline/checklistSubmission.js.map +1 -0
  105. package/dist/esm/integrations/offline/fieldCollectionHelpers.js +70 -5
  106. package/dist/esm/integrations/offline/fieldCollectionHelpers.js.map +1 -1
  107. package/dist/esm/integrations/offline/index.js +2 -1
  108. package/dist/esm/integrations/offline/index.js.map +1 -1
  109. package/dist/esm/integrations/offline/offlineDb.js +6 -1
  110. package/dist/esm/integrations/offline/offlineDb.js.map +1 -1
  111. package/dist/esm/integrations/offline/syncReplayService.js +8 -2
  112. package/dist/esm/integrations/offline/syncReplayService.js.map +1 -1
  113. package/dist/esm/l10n/en.json.js +12 -1
  114. package/dist/esm/l10n/en.json.js.map +1 -1
  115. package/dist/esm/l10n/nl.json.js +16 -2
  116. package/dist/esm/l10n/nl.json.js.map +1 -1
  117. package/dist/esm/nextcloud-vue.css +1000 -59
  118. package/dist/esm/utils/detailActionModel.js +60 -3
  119. package/dist/esm/utils/detailActionModel.js.map +1 -1
  120. package/dist/esm/utils/fillTemplate.js +114 -0
  121. package/dist/esm/utils/fillTemplate.js.map +1 -0
  122. package/dist/esm/utils/libraryWidgetKeys.js +4 -0
  123. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  124. package/dist/esm/utils/queryPresets.js +96 -0
  125. package/dist/esm/utils/queryPresets.js.map +1 -0
  126. package/dist/esm/utils/resolveFilterTokens.js +11 -0
  127. package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
  128. package/dist/esm/utils/schema.js +21 -0
  129. package/dist/esm/utils/schema.js.map +1 -1
  130. package/dist/esm/utils/sentinelTokens.js +3 -3
  131. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  132. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  133. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  134. package/dist/esm/utils/widgetDispatch.js +7 -1
  135. package/dist/esm/utils/widgetDispatch.js.map +1 -1
  136. package/dist/nextcloud-vue.cjs.js +12368 -7717
  137. package/dist/nextcloud-vue.cjs.js.map +2 -2
  138. package/dist/nextcloud-vue.css +1000 -59
  139. package/l10n/en.json +12 -1
  140. package/l10n/nl.json +16 -2
  141. package/package.json +1 -1
  142. package/src/components/CnActionButtons/CnActionButtons.vue +2 -0
  143. package/src/components/CnActionsBar/CnActionsBar.md +4 -0
  144. package/src/components/CnActionsBar/CnActionsBar.vue +42 -10
  145. package/src/components/CnBannerWidget/CnBannerWidget.vue +39 -3
  146. package/src/components/CnCellRenderer/CnCellRenderer.vue +188 -1
  147. package/src/components/CnDataTable/CnDataTable.vue +1 -0
  148. package/src/components/CnDetailPage/CnDetailPage.md +1 -1
  149. package/src/components/CnDetailPage/CnDetailPage.vue +63 -3
  150. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +66 -0
  151. package/src/components/CnFactsWidget/CnFactsWidget.vue +1050 -0
  152. package/src/components/CnFactsWidget/index.js +29 -0
  153. package/src/components/CnFieldListWidget/CnFieldListWidget.vue +746 -0
  154. package/src/components/CnFieldListWidget/index.js +28 -0
  155. package/src/components/CnFormDialog/CnFormDialog.md +9 -3
  156. package/src/components/CnFormDialog/CnFormDialog.vue +69 -21
  157. package/src/components/CnFormPage/CnFormPage.vue +18 -3
  158. package/src/components/CnIndexPage/CnIndexPage.vue +254 -6
  159. package/src/components/CnIndexPage/CnIndexPageWidgets.vue +169 -0
  160. package/src/components/CnPageRenderer/CnPageRenderer.vue +29 -1
  161. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +603 -0
  162. package/src/components/CnRecordListWidget/index.js +30 -0
  163. package/src/components/CnSectionsWidget/CnSectionsWidget.vue +489 -0
  164. package/src/components/CnSectionsWidget/index.js +34 -0
  165. package/src/components/CnStagesWidget/CnStagesWidget.vue +81 -2
  166. package/src/components/CnStatWidget/CnStatWidget.vue +1 -1
  167. package/src/components/CnTabsWidget/CnTabsWidget.vue +184 -8
  168. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +4 -0
  169. package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +188 -4
  170. package/src/components/index.js +4 -0
  171. package/src/composables/__tests__/useFormDraft.spec.js +69 -0
  172. package/src/composables/useFormDraft.js +139 -8
  173. package/src/css/form-field.css +14 -0
  174. package/src/css/index-page.css +71 -0
  175. package/src/css/kpi-card.css +32 -0
  176. package/src/css/look-board-detail.css +77 -0
  177. package/src/css/look-board-index.css +51 -0
  178. package/src/css/look-board.css +37 -2
  179. package/src/css/table.css +19 -0
  180. package/src/index.js +4 -0
  181. package/src/integrations/builtin/field-inspection/CnFieldInspectionCard.vue +35 -20
  182. package/src/integrations/builtin/field-inspection.js +21 -0
  183. package/src/integrations/offline/checklistSubmission.js +137 -0
  184. package/src/integrations/offline/fieldCollectionHelpers.js +69 -4
  185. package/src/integrations/offline/index.js +3 -0
  186. package/src/integrations/offline/offlineDb.js +6 -1
  187. package/src/integrations/offline/syncReplayService.js +8 -2
  188. package/src/schemas/app-manifest-v2.schema.json +104 -5
  189. package/src/utils/detailActionModel.js +59 -2
  190. package/src/utils/fillTemplate.js +110 -0
  191. package/src/utils/libraryWidgetKeys.js +4 -0
  192. package/src/utils/queryPresets.js +93 -0
  193. package/src/utils/resolveFilterTokens.js +11 -0
  194. package/src/utils/schema.js +21 -0
  195. package/src/utils/sentinelTokens.js +3 -3
  196. package/src/utils/validateManifestV2.compiled.js +3 -3
  197. package/src/utils/widgetDispatch.js +7 -1
package/l10n/en.json CHANGED
@@ -67,6 +67,7 @@
67
67
  "Follow this record to get its updates": "Follow this record to get its updates",
68
68
  "You will see it under Following. This register sends no change notifications.": "You will see it under Following. This register sends no change notifications.",
69
69
  "Since {date}": "Since {date}",
70
+ "since {date}": "since {date}",
70
71
  "Remove {name} as a follower": "Remove {name} as a follower",
71
72
  "Could not change following.": "Could not change following.",
72
73
  "Could not add this colleague.": "Could not add this colleague.",
@@ -226,6 +227,7 @@
226
227
  "Access URL": "Access URL",
227
228
  "Action": "Action",
228
229
  "Actions": "Actions",
230
+ "optional": "optional",
229
231
  "Add": "Add",
230
232
  "Add condition": "Add condition",
231
233
  "Add enum value": "Add enum value",
@@ -300,6 +302,9 @@
300
302
  "Constant": "Constant",
301
303
  "Copy": "Copy",
302
304
  "Copied": "Copied",
305
+ "Show": "Show",
306
+ "Copy {label}": "Copy {label}",
307
+ "Show {label}": "Show {label}",
303
308
  "Copy item": "Copy item",
304
309
  "Copy items": "Copy items",
305
310
  "Copy of {name}": "Copy of {name}",
@@ -794,6 +799,7 @@
794
799
  "View all ({total})": "View all ({total})",
795
800
  "No items": "No items",
796
801
  "Nothing here yet": "Nothing here yet",
802
+ "This list could not be loaded.": "This list could not be loaded.",
797
803
  "No items match this filter": "No items match this filter",
798
804
  "Could not load these records": "Could not load these records",
799
805
  "No conversations yet": "No conversations yet",
@@ -1361,9 +1367,14 @@
1361
1367
  "Quick filters": "Quick filters",
1362
1368
  "{count} filters": "{count} filters",
1363
1369
  "{count} more filters": "{count} more filters",
1364
- "{label} — show the other filters": "{label} — show the other filters"
1370
+ "{label} — show the other filters": "{label} — show the other filters",
1371
+ "{view}: not available on this list": "{view}: not available on this list"
1365
1372
  },
1366
1373
  "plurals": {
1374
+ "{count} selected": [
1375
+ "{count} selected",
1376
+ "{count} selected"
1377
+ ],
1367
1378
  "%n year": [
1368
1379
  "%n year",
1369
1380
  "%n years"
package/l10n/nl.json CHANGED
@@ -67,6 +67,7 @@
67
67
  "Follow this record to get its updates": "Volg dit dossier om de wijzigingen te ontvangen",
68
68
  "You will see it under Following. This register sends no change notifications.": "U ziet het onder Volgend. Dit register stuurt geen wijzigingsmeldingen.",
69
69
  "Since {date}": "Sinds {date}",
70
+ "since {date}": "sinds {date}",
70
71
  "Remove {name} as a follower": "{name} als volger verwijderen",
71
72
  "Could not change following.": "Volgen kon niet worden gewijzigd.",
72
73
  "Could not add this colleague.": "Deze collega kon niet worden toegevoegd.",
@@ -225,6 +226,7 @@
225
226
  "Access URL": "Toegangs-URL",
226
227
  "Action": "Actie",
227
228
  "Actions": "Acties",
229
+ "optional": "niet verplicht",
228
230
  "Add": "Toevoegen",
229
231
  "Add condition": "Voorwaarde toevoegen",
230
232
  "Add enum value": "Opsommingswaarde toevoegen",
@@ -299,6 +301,9 @@
299
301
  "Constant": "Constante",
300
302
  "Copy": "Kopiëren",
301
303
  "Copied": "Gekopieerd",
304
+ "Show": "Tonen",
305
+ "Copy {label}": "{label} kopiëren",
306
+ "Show {label}": "{label} tonen",
302
307
  "Copy item": "Item kopiëren",
303
308
  "Copy items": "Items kopiëren",
304
309
  "Copy of {name}": "Kopie van {name}",
@@ -569,6 +574,10 @@
569
574
  "Open": "Openen",
570
575
  "Open access:": "Openbare toegang:",
571
576
  "Open row": "Rij openen",
577
+ "Select all rows": "Alle rijen selecteren",
578
+ "Select row": "Rij selecteren",
579
+ "Scrollable table": "Scrollbare tabel",
580
+ "{title} — scrollable table": "{title}, scrollbare tabel",
572
581
  "Operator": "Operator",
573
582
  "Order": "Volgorde",
574
583
  "Owner:": "Eigenaar:",
@@ -793,6 +802,7 @@
793
802
  "View all ({total})": "Alles bekijken ({total})",
794
803
  "No items": "Geen items",
795
804
  "Nothing here yet": "Hier staat nog niets",
805
+ "This list could not be loaded.": "Deze lijst kon niet worden geladen.",
796
806
  "No items match this filter": "Geen items voldoen aan dit filter",
797
807
  "Could not load these records": "Deze gegevens konden niet worden geladen",
798
808
  "No conversations yet": "Nog geen gesprekken",
@@ -1438,7 +1448,6 @@
1438
1448
  "You cannot see this folder": "Je kunt deze map niet zien",
1439
1449
  "{action} did not succeed": "{action} is niet gelukt",
1440
1450
  "Error:": "Fout:",
1441
- "optional": "optioneel",
1442
1451
  "Copy link": "Link kopiëren",
1443
1452
  "Details and sharing": "Details en delen",
1444
1453
  "Link copied": "Link gekopieerd",
@@ -1456,9 +1465,14 @@
1456
1465
  "1 day left": "Nog 1 dag",
1457
1466
  "{count} days left": "Nog {count} dagen",
1458
1467
  "1 day overdue": "1 dag te laat",
1459
- "{count} days overdue": "{count} dagen te laat"
1468
+ "{count} days overdue": "{count} dagen te laat",
1469
+ "{view}: not available on this list": "{view}: niet beschikbaar in deze lijst"
1460
1470
  },
1461
1471
  "plurals": {
1472
+ "{count} selected": [
1473
+ "{count} geselecteerd",
1474
+ "{count} geselecteerd"
1475
+ ],
1462
1476
  "%n year": [
1463
1477
  "%n jaar",
1464
1478
  "%n jaar"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/nextcloud-vue",
3
- "version": "2.79.0",
3
+ "version": "2.80.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": {
@@ -175,6 +175,8 @@
175
175
  :register="formRegister"
176
176
  :initialData="formInitialValues || {}"
177
177
  :dialogTitle="tr(formEntry.formTitle) || ''"
178
+ :eyebrow="tr(formEntry.formEyebrow) || ''"
179
+ :subtitle="tr(formEntry.formSubtitle) || ''"
178
180
  :includeFields="formEntry.includeFields || null"
179
181
  :excludeFields="formEntry.excludeFields || []"
180
182
  :fieldOverrides="formEntry.fieldOverrides || {}"
@@ -222,3 +222,7 @@ Controlling the inline action button count — `inlineActionCount` sets how many
222
222
  `showBuildiqButton` is documented in the prop table of the component reference (board look, screens-chrome-parity).
223
223
 
224
224
  Slot `actions-end`: custom buttons after the primary Add button (CnIndexPage's saved views). Under the board look it renders in row 1, after the quick-filter chips.
225
+
226
+ ### Disabled view segments
227
+
228
+ Under the board look `disabledViewModes` (an array of modes) draws view switch segments the page cannot open, in the fixed order table, cards, board, map, disabled and titled "<mode>: not available on this list". A mode that is also offered renders once, enabled. `CnIndexPage` fills it from `config.viewSwitch`.
@@ -45,24 +45,27 @@
45
45
  <div v-if="showViewToggle && viewSegments.length > 1"
46
46
  class="cn-actions-bar__view-toggle"
47
47
  role="group"
48
- :aria-label="t('nextcloud-vue', 'View')">
48
+ :aria-label="t('nextcloud-vue', 'View mode')">
49
+ <!-- A segment the page cannot open (`disabledViewModes`) is drawn, as the board does, but disabled and named so. -->
49
50
  <button
50
51
  v-for="seg in viewSegments"
51
52
  :key="seg.mode"
52
53
  type="button"
53
54
  class="cn-actions-bar__view-toggle-btn"
54
- :class="{ 'cn-actions-bar__view-toggle-btn--active': viewMode === seg.mode }"
55
+ :class="{ 'cn-actions-bar__view-toggle-btn--active': viewMode === seg.mode, 'cn-actions-bar__view-toggle-btn--disabled': seg.disabled }"
55
56
  :aria-pressed="viewMode === seg.mode"
56
57
  :aria-label="seg.label"
57
- :title="seg.label"
58
- @click="$emit('view-mode-change', seg.mode)">
58
+ :title="seg.disabled ? unavailableViewLabel(seg.label) : seg.label"
59
+ :disabled="seg.disabled"
60
+ :data-mode="seg.mode"
61
+ @click="seg.disabled || $emit('view-mode-change', seg.mode)">
59
62
  <CnIcon v-if="seg.icon"
60
63
  :name="seg.icon"
61
- :size="24"
64
+ :size="18"
62
65
  class="cn-actions-bar__view-toggle-icon" />
63
- <component :is="seg.fallback"
66
+ <component :is="seg.boardFallback || seg.fallback"
64
67
  v-else
65
- :size="24"
68
+ :size="18"
66
69
  class="cn-actions-bar__view-toggle-icon" />
67
70
  </button>
68
71
  </div>
@@ -527,6 +530,7 @@ import CalendarMonthOutline from 'vue-material-design-icons/CalendarMonthOutline
527
530
  import Close from 'vue-material-design-icons/Close.vue'
528
531
  import ContentCopy from 'vue-material-design-icons/ContentCopy.vue'
529
532
  import Export from 'vue-material-design-icons/Export.vue'
533
+ import FormatListBulleted from 'vue-material-design-icons/FormatListBulleted.vue'
530
534
  import FormatListBulletedSquare from 'vue-material-design-icons/FormatListBulletedSquare.vue'
531
535
  import Import from 'vue-material-design-icons/Import.vue'
532
536
  import Magnify from 'vue-material-design-icons/Magnify.vue'
@@ -585,6 +589,7 @@ export default {
585
589
  Tune,
586
590
  ViewColumnOutline,
587
591
  ViewGridOutline,
592
+ FormatListBulleted,
588
593
  FormatListBulletedSquare,
589
594
  CalendarMonthOutline,
590
595
  MapMarkerOutline,
@@ -634,6 +639,19 @@ export default {
634
639
  default: () => [],
635
640
  },
636
641
 
642
+ /**
643
+ * Board look: view modes the switch draws although the page cannot
644
+ * open them, as disabled segments (the boards always draw table, cards,
645
+ * board and map). Ignored without the board look.
646
+ *
647
+ * @type {Array<string>}
648
+ * @spec openspec/changes/screens-view-switch-parity/specs/view-switch-board-look/spec.md#requirement-the-manifest-can-draw-segments-the-page-cannot-open
649
+ */
650
+ disabledViewModes: {
651
+ type: Array,
652
+ default: () => [],
653
+ },
654
+
637
655
  /**
638
656
  * The number on the Filter button's badge. Empty (`null`) counts the
639
657
  * `activeFilterChips`.
@@ -1165,11 +1183,15 @@ export default {
1165
1183
  // only when the page offers it. A mode the board switch does not
1166
1184
  // name (list, calendar) follows, so it is never lost.
1167
1185
  defs.board = { label: t('nextcloud-vue', 'Board'), icon: '', fallback: ViewColumnOutline }
1186
+ // The board's list icon: plain bullets, not the boxed ones.
1187
+ defs.table.boardFallback = FormatListBulleted
1188
+ const disabled = (this.disabledViewModes || []).filter((mode) => !modes.includes(mode))
1189
+ const all = [...modes, ...disabled]
1168
1190
  const fixed = ['table', 'cards', 'board', 'map']
1169
- const rest = modes.filter((mode) => !fixed.includes(mode))
1170
- return [...fixed.filter((mode) => modes.includes(mode)), ...rest]
1191
+ const rest = all.filter((mode) => !fixed.includes(mode))
1192
+ return [...fixed.filter((mode) => all.includes(mode)), ...rest]
1171
1193
  .filter((mode) => defs[mode])
1172
- .map((mode) => ({ mode, ...defs[mode] }))
1194
+ .map((mode) => ({ mode, ...defs[mode], disabled: disabled.includes(mode) }))
1173
1195
  }
1174
1196
  return modes
1175
1197
  .filter((mode) => defs[mode])
@@ -1228,6 +1250,16 @@ export default {
1228
1250
  },
1229
1251
 
1230
1252
  methods: {
1253
+ /**
1254
+ * The tooltip of a segment the page cannot open.
1255
+ *
1256
+ * @param {string} label The segment's name.
1257
+ * @return {string} "Map: not available on this list".
1258
+ */
1259
+ unavailableViewLabel(label) {
1260
+ return t('nextcloud-vue', '{view}: not available on this list', { view: label })
1261
+ },
1262
+
1231
1263
  /**
1232
1264
  * The accessible name of a filter chip's remove button.
1233
1265
  *
@@ -64,6 +64,7 @@
64
64
  <script>
65
65
  import { translate as t } from '@nextcloud/l10n'
66
66
  import { NcNoteCard } from '@nextcloud/vue'
67
+ import { normalizeLook } from '../../composables/useLook.js'
67
68
  import { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'
68
69
  import { safeHref } from '../../utils/safeHref.js'
69
70
  import { nextUid } from '../../utils/uid.js'
@@ -78,6 +79,12 @@ const LAYOUTS = ['banner', 'attention']
78
79
  /** An attention card offers a primary and a secondary action, no more. */
79
80
  const MAX_ACTIONS = 2
80
81
 
82
+ /**
83
+ * Under the board look the card takes what the boards draw: DqMijnWerk's
84
+ * "Vandaag eerst" has three secondary actions and the primary one.
85
+ */
86
+ const MAX_ACTIONS_BOARD = 4
87
+
81
88
  /**
82
89
  * CnBannerWidget — declarative notice banner for dashboards and v2 pages
83
90
  * (`banner` widget type, Wave 1 of nextcloud-vue#91).
@@ -148,6 +155,8 @@ export default {
148
155
  * label that is not a key renders as written.
149
156
  */
150
157
  cnTranslate: { default: () => (key) => key },
158
+ /** The app's look, provided by CnAppRoot or CnPageRenderer (`nextcloud` or `board`). */
159
+ cnLook: { default: 'nextcloud' },
151
160
  },
152
161
 
153
162
  props: {
@@ -232,7 +241,8 @@ export default {
232
241
  },
233
242
 
234
243
  /**
235
- * Attention card: at most two actions. Each is `{ label, route?,
244
+ * Attention card: its actions, two at most (four under the board look,
245
+ * or `maxActions`). Each is `{ label, route?,
236
246
  * href?, primary?, id? }`: `route` is a route name or location,
237
247
  * `href` an URL. An action with neither renders a button that emits
238
248
  * `action`. The first action is the primary one unless one sets
@@ -245,6 +255,17 @@ export default {
245
255
  default: null,
246
256
  },
247
257
 
258
+ /**
259
+ * Attention card: how many actions it shows at most. Null takes the
260
+ * default: two, four under the board look.
261
+ *
262
+ * @spec openspec/changes/screens-banner-actions-parity/specs/banner-actions/spec.md#requirement-the-board-attention-card-shows-the-boards-actions
263
+ */
264
+ maxActions: {
265
+ type: Number,
266
+ default: null,
267
+ },
268
+
248
269
  /**
249
270
  * Stored content blob (CnDashboardPage registry branch) carrying the
250
271
  * same keys as the flat props: `{ variant, text, visibleWhen, route,
@@ -403,12 +424,27 @@ export default {
403
424
  },
404
425
 
405
426
  /**
406
- * The attention card's actions: the first two that have a label, each
427
+ * The attention card's actions: the first `actionCap` that have a label, each
407
428
  * with its href and whether it is the primary one.
408
429
  *
409
430
  * @return {Array<{key: string, id: (string|undefined), label: string, href: string, target: (object|null), primary: boolean, index: number}>}
410
431
  * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card
411
432
  */
433
+ /**
434
+ * How many actions the attention card shows: `maxActions` (prop, then
435
+ * content), else four under the board look and two without it.
436
+ *
437
+ * @return {number} The cap.
438
+ * @spec openspec/changes/screens-banner-actions-parity/specs/banner-actions/spec.md#requirement-the-board-attention-card-shows-the-boards-actions
439
+ */
440
+ actionCap() {
441
+ const explicit = this.maxActions ?? (this.content ? this.content.maxActions : undefined)
442
+ if (Number.isInteger(explicit) && explicit > 0) {
443
+ return explicit
444
+ }
445
+ return normalizeLook(this.cnLook) === 'board' ? MAX_ACTIONS_BOARD : MAX_ACTIONS
446
+ },
447
+
412
448
  resolvedActions() {
413
449
  const raw = this.actions || (this.content && this.content.actions) || []
414
450
  if (!Array.isArray(raw)) {
@@ -416,7 +452,7 @@ export default {
416
452
  }
417
453
  const usable = raw
418
454
  .filter((action) => action && typeof action === 'object' && typeof action.label === 'string' && action.label !== '')
419
- .slice(0, MAX_ACTIONS)
455
+ .slice(0, this.actionCap)
420
456
  const declaredPrimary = usable.findIndex((action) => action.primary === true)
421
457
  const primaryIndex = declaredPrimary === -1 ? 0 : declaredPrimary
422
458
  return usable.map((action, index) => {
@@ -144,6 +144,30 @@
144
144
  <span v-else class="cn-cell-renderer__dash">—</span>
145
145
  </template>
146
146
 
147
+ <!-- Built-in "progress" widget: a bar with the percentage beside it,
148
+ toned by widgetProps.thresholds, and an optional age badge
149
+ ("Out of date 12 days") from widgetProps.badge. -->
150
+ <template v-else-if="widget === 'progress'">
151
+ <span v-if="progressPercent === null" class="cn-cell-renderer__dash">—</span>
152
+ <span v-else class="cn-cell-renderer__progress">
153
+ <span class="cn-cell-renderer__progress-line">
154
+ <span class="cn-cell-renderer__progress-bar" aria-hidden="true">
155
+ <span
156
+ class="cn-cell-renderer__progress-fill"
157
+ :class="'cn-cell-renderer__progress-fill--' + progressTone"
158
+ :style="{ width: progressPercent + '%' }" />
159
+ </span>
160
+ <strong
161
+ class="cn-cell-renderer__progress-value"
162
+ :class="'cn-cell-renderer__progress-value--' + progressTone">{{ progressPercent }}%</strong>
163
+ </span>
164
+ <span
165
+ v-if="progressBadge"
166
+ class="cn-cell-renderer__progress-badge"
167
+ :class="'cn-cell-renderer__progress-badge--' + progressBadge.tone">{{ progressBadge.label }}</span>
168
+ </span>
169
+ </template>
170
+
147
171
  <!-- Explicit column formatter — overrides the type-aware paths below -->
148
172
  <template v-else-if="hasFormatter">
149
173
  <span :title="rawTitle">{{ formattedValue }}</span>
@@ -229,10 +253,11 @@ import CheckBold from 'vue-material-design-icons/CheckBold.vue'
229
253
  import CnFkResolveCell from '../CnFkResolveCell/CnFkResolveCell.vue'
230
254
  import CnGroupNameCell from './CnGroupNameCell.vue'
231
255
  import { normalizeLook } from '../../composables/useLook.js'
232
- import { formatAge, formatBoardDate, resolveAgeVariant } from '../../utils/boardDate.js'
256
+ import { ageInDays, formatAge, formatBoardDate, resolveAgeVariant } from '../../utils/boardDate.js'
233
257
  import { parseDateValue, resolveDateVariant } from '../../utils/dateVariant.js'
234
258
  import { safeCurrencyCode } from '../../utils/formatMetric.js'
235
259
  import { objectFieldValue } from '../../utils/objectName.js'
260
+ import { readPath } from '../../utils/readPath.js'
236
261
  import { safeHref } from '../../utils/safeHref.js'
237
262
  import { formatValue, isGroupProp } from '../../utils/schema.js'
238
263
  import { CnStatusBadge } from '../CnStatusBadge/index.js'
@@ -609,6 +634,83 @@ export default {
609
634
  return formatAge(this.value)
610
635
  },
611
636
 
637
+ /**
638
+ * `progress` widget: the value as a whole percentage of
639
+ * `widgetProps.max` (default 100), clamped to 0..100. An empty value
640
+ * falls back to the first `widgetProps.fallbackFields` row field that
641
+ * holds a number. Null when nothing holds one.
642
+ *
643
+ * @return {number|null}
644
+ * @spec openspec/changes/abstract-progress-cell/specs/progress-cell/spec.md#requirement-a-progress-cell-draws-a-toned-bar-with-its-percentage
645
+ */
646
+ progressPercent() {
647
+ const props = this.widgetProps || {}
648
+ const fallbacks = Array.isArray(props.fallbackFields) ? props.fallbackFields : []
649
+ const candidates = [this.value, ...fallbacks.map((field) => readPath(this.row || {}, field))]
650
+ const raw = candidates.find((v) => v !== null && v !== undefined && v !== '' && !Number.isNaN(Number(v)))
651
+ if (raw === undefined) {
652
+ return null
653
+ }
654
+ const max = Number(props.max) > 0 ? Number(props.max) : 100
655
+ return Math.max(0, Math.min(100, Math.round((Number(raw) / max) * 100)))
656
+ },
657
+
658
+ /**
659
+ * `progress` widget: the tone. `widgetProps.thresholds`
660
+ * `{ good: 60, warn: 30 }` (the defaults): at or above `good` is
661
+ * success, at or above `warn` is warning, below is error. With
662
+ * `widgetProps.lowerIsBetter` the scale turns around.
663
+ *
664
+ * @return {'success'|'warning'|'error'}
665
+ * @spec openspec/changes/abstract-progress-cell/specs/progress-cell/spec.md#requirement-a-progress-cell-draws-a-toned-bar-with-its-percentage
666
+ */
667
+ progressTone() {
668
+ const props = this.widgetProps || {}
669
+ const thresholds = props.thresholds || {}
670
+ const good = Number.isFinite(Number(thresholds.good)) ? Number(thresholds.good) : 60
671
+ const warn = Number.isFinite(Number(thresholds.warn)) ? Number(thresholds.warn) : 30
672
+ const value = props.lowerIsBetter === true ? 100 - this.progressPercent : this.progressPercent
673
+ if (value >= good) {
674
+ return 'success'
675
+ }
676
+ return value >= warn ? 'warning' : 'error'
677
+ },
678
+
679
+ /**
680
+ * `progress` widget: the age badge, or null. `widgetProps.badge`
681
+ * `{ label, dateField, afterDays, when?, tone? }`: shown when the row's
682
+ * `dateField` (default `@self.updated`) lies `afterDays` or more days
683
+ * back and every `when` field equals its value. `label` is translated
684
+ * and takes `{days}`.
685
+ *
686
+ * @return {{label: string, tone: string}|null}
687
+ * @spec openspec/changes/abstract-progress-cell/specs/progress-cell/spec.md#requirement-a-progress-cell-can-carry-an-age-badge
688
+ */
689
+ progressBadge() {
690
+ const badge = this.widgetProps && this.widgetProps.badge
691
+ if (!badge || typeof badge !== 'object' || !badge.label) {
692
+ return null
693
+ }
694
+ const row = this.row || {}
695
+ const when = badge.when && typeof badge.when === 'object' ? badge.when : {}
696
+ for (const [field, expected] of Object.entries(when)) {
697
+ const actual = readPath(row, field)
698
+ if (actual !== undefined && actual !== null && actual !== '' && String(actual) !== String(expected)) {
699
+ return null
700
+ }
701
+ }
702
+ const days = ageInDays(readPath(row, badge.dateField || '@self.updated'))
703
+ const after = Number(badge.afterDays)
704
+ if (days === null || !Number.isFinite(after) || days < after) {
705
+ return null
706
+ }
707
+ const label = this.cnTranslate(badge.label)
708
+ return {
709
+ label: String(label).replace(/\{days\}/g, String(days)),
710
+ tone: ['error', 'warning', 'success'].includes(badge.tone) ? badge.tone : 'error',
711
+ }
712
+ },
713
+
612
714
  /**
613
715
  * `age` widget: the variant of the first `widgetProps.variantWhen`
614
716
  * rule the shown days match, or '' for none.
@@ -1236,6 +1338,91 @@ export default {
1236
1338
  white-space: nowrap;
1237
1339
  }
1238
1340
 
1341
+ /* The progress cell (abstract-progress-cell): a 56 x 8 bar, the percentage
1342
+ 8px beside it in the bar's tone, and an optional age badge after them, as
1343
+ PqLeads' Winkans column. The value is bold, so the tone is not the only signal. */
1344
+ .cn-cell-renderer__progress {
1345
+ display: inline-flex;
1346
+ flex-wrap: wrap;
1347
+ align-items: center;
1348
+ gap: 4px 8px;
1349
+ }
1350
+
1351
+ .cn-cell-renderer__progress-line {
1352
+ display: inline-flex;
1353
+ align-items: center;
1354
+ gap: 8px;
1355
+ white-space: nowrap;
1356
+ }
1357
+
1358
+ .cn-cell-renderer__progress-bar {
1359
+ display: inline-block;
1360
+ width: 56px;
1361
+ height: 8px;
1362
+ border-radius: 4px;
1363
+ background: var(--color-background-dark);
1364
+ overflow: hidden;
1365
+ }
1366
+
1367
+ .cn-cell-renderer__progress-fill {
1368
+ display: block;
1369
+ height: 100%;
1370
+ }
1371
+
1372
+ .cn-cell-renderer__progress-fill--success {
1373
+ background: var(--color-element-success, var(--color-success));
1374
+ }
1375
+
1376
+ .cn-cell-renderer__progress-fill--warning {
1377
+ background: var(--color-element-warning, var(--color-warning));
1378
+ }
1379
+
1380
+ .cn-cell-renderer__progress-fill--error {
1381
+ background: var(--color-element-error, var(--color-error));
1382
+ }
1383
+
1384
+ .cn-cell-renderer__progress-value {
1385
+ font-size: 14px;
1386
+ font-weight: 700;
1387
+ }
1388
+
1389
+ .cn-cell-renderer__progress-value--success {
1390
+ color: var(--color-text-success, var(--color-success-text));
1391
+ }
1392
+
1393
+ .cn-cell-renderer__progress-value--warning {
1394
+ color: var(--color-text-warning, var(--color-warning-text));
1395
+ }
1396
+
1397
+ .cn-cell-renderer__progress-value--error {
1398
+ color: var(--color-text-error, var(--color-error-text));
1399
+ }
1400
+
1401
+ .cn-cell-renderer__progress-badge {
1402
+ display: inline-block;
1403
+ padding: 3px 10px;
1404
+ border-radius: 12px;
1405
+ font-size: 13px;
1406
+ font-weight: 600;
1407
+ white-space: nowrap;
1408
+ }
1409
+
1410
+ .cn-cell-renderer__progress-badge--error {
1411
+ color: var(--color-text-error, var(--color-error-text));
1412
+ background: var(--color-error-hover, var(--color-background-dark));
1413
+ background: color-mix(in srgb, var(--color-error) 12%, transparent);
1414
+ }
1415
+
1416
+ .cn-cell-renderer__progress-badge--warning {
1417
+ color: var(--color-text-warning, var(--color-warning-text));
1418
+ background: color-mix(in srgb, var(--color-warning) 14%, transparent);
1419
+ }
1420
+
1421
+ .cn-cell-renderer__progress-badge--success {
1422
+ color: var(--color-text-success, var(--color-success-text));
1423
+ background: color-mix(in srgb, var(--color-success) 12%, transparent);
1424
+ }
1425
+
1239
1426
  .cn-cell-renderer__age--success {
1240
1427
  color: var(--color-text-success, var(--color-success-text));
1241
1428
  }
@@ -130,6 +130,7 @@
130
130
  col.class || '',
131
131
  pinClass(leadingCount + colIndex),
132
132
  isGrowColumn(col) ? 'cn-table-col--grow' : '',
133
+ colIndex === 0 ? 'cn-table-head--title' : '',
133
134
  ]"
134
135
  :style="{ ...(col.width ? { width: col.width } : {}), ...pinStyle(leadingCount + colIndex) }"
135
136
  :aria-sort="ariaSortFor(col)"
@@ -422,7 +422,7 @@ Four levels of action and the case surfaces around them. All opt in. See [the re
422
422
  | `quickActions` | Array | `[]` | At most three always visible header buttons. |
423
423
  | `actionsMenu` | Object \| null | `null` | `{ showRefresh?, showHelpLinks?, label? }` for the overflow menu. |
424
424
  | `nextStep` | Object \| null | `null` | The "what now" card per stage, rendered by `CnNextStepCard`. |
425
- | `typePill` | Object \| null | `null` | `{ field, colorMap?, labels?, variant? }`, a pill above the title. |
425
+ | `typePill` | Object \| null | `null` | `{ field, colorMap?, labels?, variant? }`, a pill above the title. Instead of `field`: `text` (a constant) or `countFrom` + `countLabel` (a count of a related list). |
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. |