@conduction/nextcloud-vue 2.56.0-beta.24 → 2.56.0-beta.26

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 (126) hide show
  1. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +19 -14
  2. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +2 -2
  4. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  5. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +77 -26
  6. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  7. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +22 -14
  8. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  9. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +23 -4
  10. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  11. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  12. package/dist/esm/components/CnBrandBar/CnBrandBar.vue.js +9 -0
  13. package/dist/esm/components/CnBrandBar/CnBrandBar.vue.js.map +1 -0
  14. package/dist/esm/components/CnBrandBar/CnBrandBar.vue2.js +107 -0
  15. package/dist/esm/components/CnBrandBar/CnBrandBar.vue2.js.map +1 -0
  16. package/dist/esm/components/CnBrandBar/CnBrandBar.vue3.js +79 -0
  17. package/dist/esm/components/CnBrandBar/CnBrandBar.vue3.js.map +1 -0
  18. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +6 -0
  19. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  20. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js +3 -2
  21. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  22. package/dist/esm/components/CnCredentials/CnCredentials.vue2.js +11 -10
  23. package/dist/esm/components/CnCredentials/CnCredentials.vue2.js.map +1 -1
  24. package/dist/esm/components/CnCredentials/CnCredentials.vue3.js.map +1 -1
  25. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +8 -5
  26. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  27. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  28. package/dist/esm/components/CnEnvironmentBanner/CnEnvironmentBanner.vue2.js +4 -1
  29. package/dist/esm/components/CnEnvironmentBanner/CnEnvironmentBanner.vue2.js.map +1 -1
  30. package/dist/esm/components/CnEnvironmentBanner/CnEnvironmentBanner.vue3.js.map +1 -1
  31. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +7 -0
  32. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  33. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +14 -7
  34. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  35. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +32 -0
  36. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
  37. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js +6 -3
  38. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
  39. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +144 -6
  40. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  41. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +14 -13
  42. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  43. package/dist/esm/components/CnIndexPage/selfModeActions.js +90 -14
  44. package/dist/esm/components/CnIndexPage/selfModeActions.js.map +1 -1
  45. package/dist/esm/components/CnIndexPage/useSelfFetchList.js +15 -3
  46. package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
  47. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +26 -0
  48. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  49. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +26 -9
  50. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  51. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +19 -3
  52. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  53. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  54. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +18 -2
  55. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
  56. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js +2 -2
  57. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
  58. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js +153 -38
  59. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js.map +1 -1
  60. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js +74 -56
  61. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js.map +1 -1
  62. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue2.js +16 -0
  63. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue2.js.map +1 -1
  64. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue3.js +2 -2
  65. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue3.js.map +1 -1
  66. package/dist/esm/composables/useObjectLock.js +41 -10
  67. package/dist/esm/composables/useObjectLock.js.map +1 -1
  68. package/dist/esm/directives/cnSelectAria.js +55 -0
  69. package/dist/esm/directives/cnSelectAria.js.map +1 -0
  70. package/dist/esm/index.js +1 -0
  71. package/dist/esm/index.js.map +1 -1
  72. package/dist/esm/l10n/en.json.js +7 -1
  73. package/dist/esm/l10n/en.json.js.map +1 -1
  74. package/dist/esm/l10n/nl.json.js +8 -2
  75. package/dist/esm/l10n/nl.json.js.map +1 -1
  76. package/dist/esm/nextcloud-vue.css +154 -2
  77. package/dist/esm/store/useObjectStore.js +37 -9
  78. package/dist/esm/store/useObjectStore.js.map +1 -1
  79. package/dist/esm/utils/cnFetch.js +17 -2
  80. package/dist/esm/utils/cnFetch.js.map +1 -1
  81. package/dist/esm/utils/objectSignals.js +44 -0
  82. package/dist/esm/utils/objectSignals.js.map +1 -0
  83. package/dist/esm/utils/resolveBrand.js +38 -0
  84. package/dist/esm/utils/resolveBrand.js.map +1 -0
  85. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  86. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  87. package/dist/nextcloud-vue.cjs.js +9640 -8649
  88. package/dist/nextcloud-vue.cjs.js.map +2 -2
  89. package/dist/nextcloud-vue.css +154 -2
  90. package/l10n/en.json +7 -1
  91. package/l10n/nl.json +8 -2
  92. package/package.json +1 -1
  93. package/src/components/CnAppNav/CnAppNav.vue +21 -16
  94. package/src/components/CnAppRoot/CnAppRoot.vue +53 -0
  95. package/src/components/CnBannerWidget/CnBannerWidget.vue +23 -4
  96. package/src/components/CnBrandBar/CnBrandBar.vue +230 -0
  97. package/src/components/CnCardGrid/CnCardGrid.md +1 -0
  98. package/src/components/CnCardGrid/CnCardGrid.vue +7 -0
  99. package/src/components/CnCredentials/CnCredentials.vue +17 -16
  100. package/src/components/CnDetailPage/CnDetailPage.vue +8 -5
  101. package/src/components/CnEnvironmentBanner/CnEnvironmentBanner.vue +4 -1
  102. package/src/components/CnFormDialog/CnFormDialog.vue +10 -0
  103. package/src/components/CnFormPage/CnFormPage.vue +33 -0
  104. package/src/components/CnIndexPage/CnIndexPage.md +15 -0
  105. package/src/components/CnIndexPage/CnIndexPage.vue +157 -18
  106. package/src/components/CnIndexPage/selfModeActions.js +89 -13
  107. package/src/components/CnIndexPage/useSelfFetchList.js +15 -3
  108. package/src/components/CnObjectCard/CnObjectCard.md +1 -0
  109. package/src/components/CnObjectCard/CnObjectCard.vue +89 -1
  110. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +19 -3
  111. package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +20 -4
  112. package/src/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue +181 -38
  113. package/src/components/CnStatsBlock/CnStatsBlock.vue +18 -2
  114. package/src/composables/__tests__/useObjectLock.endpoints.spec.js +83 -0
  115. package/src/composables/useObjectLock.js +41 -10
  116. package/src/css/form-field.css +1 -1
  117. package/src/css/look-board-index.css +35 -1
  118. package/src/directives/cnSelectAria.js +54 -0
  119. package/src/index.js +1 -0
  120. package/src/schemas/app-manifest-v2.schema.json +6 -1
  121. package/src/store/useObjectStore.js +37 -9
  122. package/src/utils/cnFetch.js +16 -1
  123. package/src/utils/index.js +1 -0
  124. package/src/utils/objectSignals.js +41 -0
  125. package/src/utils/resolveBrand.js +37 -0
  126. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/dist/nextcloud-vue.cjs.js.map
3
- size: 38245574 bytes
4
- sha256: a5678854a3205a4621af802be47ea65067c29bb280f4f1765558f532f8d9e13c
3
+ size: 38337896 bytes
4
+ sha256: a7d0d4bb2ccffd14adbe47978a666f89a6e3b1376dfecb854356280ac6440360
@@ -795,7 +795,7 @@
795
795
  }
796
796
 
797
797
  /* An invalid field: 2px error border on the control, 4px edge on the group. */
798
- .cn-look-board .cn-form-field--invalid input,
798
+ .cn-look-board .cn-form-field--invalid input:not([type='checkbox']):not([type='radio']),
799
799
  .cn-look-board .cn-form-field--invalid textarea,
800
800
  .cn-look-board .cn-form-field--invalid .select {
801
801
  border: var(--cn-field-error-border) solid var(--color-error);
@@ -1490,7 +1490,10 @@
1490
1490
  }
1491
1491
 
1492
1492
  .cn-look-board .cn-index-page__main.cn-index-page__main--table {
1493
- flex: 0 1 auto;
1493
+ /* The body is a row: the card takes the width left beside a sidebar (the
1494
+ board's table card spans the content), and keeps the height of its rows. */
1495
+ flex: 1 1 auto;
1496
+ align-self: flex-start;
1494
1497
  border: 1px solid var(--color-border);
1495
1498
  border-radius: var(--cn-board-card-radius, 12px);
1496
1499
  background: var(--color-main-background);
@@ -1680,6 +1683,37 @@
1680
1683
  background: var(--color-primary-element-light);
1681
1684
  }
1682
1685
 
1686
+ /* The status accent outranks the board's border, also on hover and selection. */
1687
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent {
1688
+ border-inline-start-width: 4px;
1689
+ padding-inline-start: 13px;
1690
+ }
1691
+
1692
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-success,
1693
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-success:hover {
1694
+ border-inline-start-color: var(--color-element-success);
1695
+ }
1696
+
1697
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-warning,
1698
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-warning:hover {
1699
+ border-inline-start-color: var(--color-element-warning);
1700
+ }
1701
+
1702
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-error,
1703
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-error:hover {
1704
+ border-inline-start-color: var(--color-element-error);
1705
+ }
1706
+
1707
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-info,
1708
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-info:hover {
1709
+ border-inline-start-color: var(--color-element-info);
1710
+ }
1711
+
1712
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-primary,
1713
+ .cn-look-board .cn-object-card.cn-object-card--board.cn-object-card--accent-primary:hover {
1714
+ border-inline-start-color: var(--color-primary-element);
1715
+ }
1716
+
1683
1717
  .cn-look-board .cn-object-card--board .cn-object-card__content {
1684
1718
  display: flex;
1685
1719
  flex: 1 1 auto;
@@ -19425,6 +19459,18 @@ a.cn-xwiki-card__title[data-v-54e85fe3]:hover {
19425
19459
  color: var(--color-text-maxcontrast);
19426
19460
  font-style: italic;
19427
19461
  }
19462
+ .cn-related-objects-widget__section-status[data-v-44c2988f] {
19463
+ margin: 0;
19464
+ padding: var(--default-grid-baseline, 4px) calc(2 * var(--default-grid-baseline, 4px));
19465
+ color: var(--color-text-maxcontrast);
19466
+ font-size: 0.85em;
19467
+ }
19468
+ .cn-related-objects-widget__section-error[data-v-44c2988f] {
19469
+ margin: 0;
19470
+ padding: var(--default-grid-baseline, 4px) calc(2 * var(--default-grid-baseline, 4px));
19471
+ color: var(--color-error-text, var(--color-error));
19472
+ font-size: 0.85em;
19473
+ }
19428
19474
  .cn-related-objects-widget__empty-state[data-v-44c2988f] {
19429
19475
  /* NcEmptyContent defaults to filling tall parents; inside a widget cell a
19430
19476
  compact block reads better. */
@@ -22090,6 +22136,47 @@ to { background-position: -200% 0;
22090
22136
  border-color: var(--color-primary-element);
22091
22137
  background: var(--color-primary-element-light);
22092
22138
  }
22139
+
22140
+ /* The 4px start border takes 3px of padding, so the content stays put. */
22141
+ .cn-object-card.cn-object-card--accent[data-v-6f672c8f] {
22142
+ border-inline-start-width: 4px;
22143
+ padding-inline-start: 13px;
22144
+ }
22145
+ .cn-object-card.cn-object-card--accent-success[data-v-6f672c8f] {
22146
+ border-inline-start-color: var(--color-element-success);
22147
+ }
22148
+ .cn-object-card.cn-object-card--accent-warning[data-v-6f672c8f] {
22149
+ border-inline-start-color: var(--color-element-warning);
22150
+ }
22151
+ .cn-object-card.cn-object-card--accent-error[data-v-6f672c8f] {
22152
+ border-inline-start-color: var(--color-element-error);
22153
+ }
22154
+ .cn-object-card.cn-object-card--accent-info[data-v-6f672c8f] {
22155
+ border-inline-start-color: var(--color-element-info);
22156
+ }
22157
+ .cn-object-card.cn-object-card--accent-primary[data-v-6f672c8f] {
22158
+ border-inline-start-color: var(--color-primary-element);
22159
+ }
22160
+ .cn-object-card__accent-icon[data-v-6f672c8f] {
22161
+ display: inline-flex;
22162
+ vertical-align: -2px;
22163
+ margin-inline-end: 4px;
22164
+ }
22165
+ .cn-object-card--accent-success .cn-object-card__accent-icon[data-v-6f672c8f] {
22166
+ color: var(--color-text-success);
22167
+ }
22168
+ .cn-object-card--accent-warning .cn-object-card__accent-icon[data-v-6f672c8f] {
22169
+ color: var(--color-text-warning);
22170
+ }
22171
+ .cn-object-card--accent-error .cn-object-card__accent-icon[data-v-6f672c8f] {
22172
+ color: var(--color-text-error);
22173
+ }
22174
+ .cn-object-card--accent-info .cn-object-card__accent-icon[data-v-6f672c8f] {
22175
+ color: var(--color-element-info);
22176
+ }
22177
+ .cn-object-card--accent-primary .cn-object-card__accent-icon[data-v-6f672c8f] {
22178
+ color: var(--color-primary-element);
22179
+ }
22093
22180
  .cn-object-card__checkbox[data-v-6f672c8f] {
22094
22181
  flex-shrink: 0;
22095
22182
  padding-top: 2px;
@@ -38840,6 +38927,71 @@ a.cn-integration-card__title[data-v-f7e9bbfa]:hover {
38840
38927
  }
38841
38928
 
38842
38929
 
38930
+ /* The host is one flex item at the start of Nextcloud's header. */
38931
+ .cn-brand-bar-host {
38932
+ flex: none;
38933
+ display: flex;
38934
+ align-items: center;
38935
+ height: 100%;
38936
+ margin-inline-end: 12px;
38937
+ }
38938
+ .cn-brand-bar {
38939
+ box-sizing: border-box;
38940
+ display: flex;
38941
+ align-items: center;
38942
+ gap: 12px;
38943
+ width: var(--cn-board-brand-width, 237px);
38944
+ }
38945
+ .cn-brand-bar__brand {
38946
+ flex: 1 1 0;
38947
+ box-sizing: border-box;
38948
+ display: flex;
38949
+ align-items: center;
38950
+ gap: 10px;
38951
+ min-width: 0;
38952
+ min-height: 48px;
38953
+ max-height: 100%;
38954
+ padding: 0 6px;
38955
+ border-radius: 8px;
38956
+ color: var(--color-background-plain-text, var(--color-main-text));
38957
+ }
38958
+ .cn-brand-bar__emblem {
38959
+ flex: none;
38960
+ height: var(--cn-nav-emblem-size, 34px);
38961
+ width: auto;
38962
+ object-fit: contain;
38963
+ }
38964
+ .cn-brand-bar__emblem--theme {
38965
+ display: inline-block;
38966
+ width: var(--cn-nav-emblem-size, 34px);
38967
+ background: var(--nldesign-emblem-url) center / contain no-repeat;
38968
+ }
38969
+ .cn-brand-bar__text {
38970
+ display: flex;
38971
+ flex-direction: column;
38972
+ line-height: 1.15;
38973
+ min-width: 0;
38974
+ }
38975
+ .cn-brand-bar__caption {
38976
+ font-size: 13px;
38977
+ white-space: nowrap;
38978
+ opacity: 0.8;
38979
+ }
38980
+ .cn-brand-bar__name {
38981
+ font-size: 18px;
38982
+ font-weight: 700;
38983
+ white-space: nowrap;
38984
+ }
38985
+ .cn-brand-bar__divider {
38986
+ flex: none;
38987
+ width: 1px;
38988
+ height: 28px;
38989
+ background: currentColor;
38990
+ opacity: 0.25;
38991
+ color: var(--color-background-plain-text, var(--color-main-text));
38992
+ }
38993
+
38994
+
38843
38995
  .cn-notif-prefs__intro[data-v-72cc0056] {
38844
38996
  margin-bottom: 12px;
38845
38997
  color: var(--color-text-maxcontrast);
package/l10n/en.json CHANGED
@@ -1,5 +1,10 @@
1
1
  {
2
2
  "translations": {
3
+ "Linked items": "Linked items",
4
+ "Still loading: {sections}": "Still loading: {sections}",
5
+ "Could not load {section}.": "Could not load {section}.",
6
+ "Organisation credentials let apps act for your organisation. Keepiq, the credential vault of Nextcloud, keeps the secret. The app never holds it. You choose which apps may use each credential.": "Organisation credentials let apps act for your organisation. Keepiq, the credential vault of Nextcloud, keeps the secret. The app never holds it. You choose which apps may use each credential.",
7
+ "Apps sometimes act for you on an external service. You give the secret to Nextcloud once, and Keepiq keeps it in an encrypted vault. Apps make the call through Keepiq and never see the secret. You choose which apps may use each credential. Share one across apps, or keep one per app.": "Apps sometimes act for you on an external service. You give the secret to Nextcloud once, and Keepiq keeps it in an encrypted vault. Apps make the call through Keepiq and never see the secret. You choose which apps may use each credential. Share one across apps, or keep one per app.",
3
8
  "Show as": "Show as",
4
9
  "Board": "Board",
5
10
  "Calendar": "Calendar",
@@ -1284,7 +1289,8 @@
1284
1289
  "Development environment": "Development environment",
1285
1290
  "Test environment": "Test environment",
1286
1291
  "Acceptance environment": "Acceptance environment",
1287
- "About {label}": "About {label}"
1292
+ "About {label}": "About {label}",
1293
+ "Could not load the form for this item.": "Could not load the form for this item."
1288
1294
  },
1289
1295
  "plurals": {
1290
1296
  "%n year": [
package/l10n/nl.json CHANGED
@@ -1,5 +1,10 @@
1
1
  {
2
2
  "translations": {
3
+ "Linked items": "Gekoppelde items",
4
+ "Still loading: {sections}": "Nog aan het laden: {sections}",
5
+ "Could not load {section}.": "{section} kon niet worden geladen.",
6
+ "Organisation credentials let apps act for your organisation. Keepiq, the credential vault of Nextcloud, keeps the secret. The app never holds it. You choose which apps may use each credential.": "Met inloggegevens van de organisatie handelen apps namens je organisatie. Keepiq, de kluis voor inloggegevens van Nextcloud, bewaart het geheim. De app krijgt het nooit in handen. Je kiest welke apps elk inloggegeven mogen gebruiken.",
7
+ "Apps sometimes act for you on an external service. You give the secret to Nextcloud once, and Keepiq keeps it in an encrypted vault. Apps make the call through Keepiq and never see the secret. You choose which apps may use each credential. Share one across apps, or keep one per app.": "Apps handelen soms namens jou bij een externe dienst. Je geeft het geheim één keer aan Nextcloud, en Keepiq bewaart het in een versleutelde kluis. Apps doen de aanvraag via Keepiq en zien het geheim nooit. Je kiest welke apps elk inloggegeven mogen gebruiken. Deel er één tussen apps, of houd er één per app.",
3
8
  "Show as": "Toon als",
4
9
  "Board": "Bord",
5
10
  "Calendar": "Kalender",
@@ -171,7 +176,7 @@
171
176
  "This integration is not set up yet. Configure a connection to start using it.": "Deze integratie is nog niet ingesteld. Configureer een verbinding om hem te gebruiken.",
172
177
  "This integration is not set up yet. Install and enable the {app} app to start using it.": "Deze integratie is nog niet ingesteld. Installeer en activeer de app {app} om hem te gebruiken.",
173
178
  "Set up {app}": "{app} instellen",
174
- "Add {type}": "Nieuwe {type}",
179
+ "Add {type}": "{type} toevoegen",
175
180
  "Add new contact": "Nieuw contact toevoegen",
176
181
  "Link contact": "Contact koppelen",
177
182
  "Search contacts": "Contacten zoeken",
@@ -1335,7 +1340,8 @@
1335
1340
  "Development environment": "Ontwikkelomgeving",
1336
1341
  "Test environment": "Testomgeving",
1337
1342
  "Acceptance environment": "Acceptatieomgeving",
1338
- "About {label}": "Over {label}"
1343
+ "About {label}": "Over {label}",
1344
+ "Could not load the form for this item.": "Kon het formulier voor dit item niet laden."
1339
1345
  },
1340
1346
  "plurals": {
1341
1347
  "%n year": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/nextcloud-vue",
3
- "version": "2.56.0-beta.24",
3
+ "version": "2.56.0-beta.26",
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": {
@@ -75,7 +75,7 @@
75
75
  -->
76
76
  <template>
77
77
  <NcAppNavigation :aria-label="ariaLabel" data-testid="cn-nav">
78
- <template v-if="$slots.search || $slots.brand || resolvedBrand" #search>
78
+ <template v-if="$slots.search || $slots.brand || navBrand" #search>
79
79
  <!--
80
80
  @slot brand
81
81
  @description Replace the brand block at the very top of the
@@ -86,7 +86,7 @@
86
86
  -->
87
87
  <slot name="brand" :brand="resolvedBrand">
88
88
  <div
89
- v-if="resolvedBrand"
89
+ v-if="navBrand"
90
90
  class="cn-app-nav__brand"
91
91
  data-testid="cn-nav-brand">
92
92
  <!-- Decorative beside a name: the name already says whose
@@ -481,6 +481,7 @@ import CnMenuItemIcon from '../CnMenuWidget/CnMenuItemIcon.vue'
481
481
  import { useLook } from '../../composables/useLook.js'
482
482
  import { isAppInstalled } from '../../utils/appInstalled.js'
483
483
  import { isSvgPath } from '../../utils/iconUtils.js'
484
+ import { resolveBrand } from '../../utils/resolveBrand.js'
484
485
  import { passesContextPredicates } from '../../utils/visibleIfContext.js'
485
486
  import { CnActionButtons } from '../CnActionButtons/index.js'
486
487
  // The legacy `icon-*` → MDI bridge lives beside CnIcon now, so the menu EDITOR
@@ -579,6 +580,12 @@ export default {
579
580
 
580
581
  inject: {
581
582
  cnManifest: { default: null },
583
+ /**
584
+ * Provided by CnAppRoot as true while the board look draws the brand
585
+ * block in Nextcloud's header (screens-brand-block-top-bar): the
586
+ * navigation then skips its own.
587
+ */
588
+ cnBrandInHeader: { default: false },
582
589
  cnTranslate: { default: () => (key) => key },
583
590
  /**
584
591
  * Provided by CnAppRoot — opens the host app's
@@ -808,6 +815,17 @@ export default {
808
815
  return this.translate ?? this.cnTranslate
809
816
  },
810
817
 
818
+ /**
819
+ * The brand the navigation draws itself: the resolved brand, or null
820
+ * while the app's top bar draws it.
821
+ *
822
+ * @spec openspec/changes/screens-brand-block-top-bar/specs/layout-components/spec.md#requirement-the-navigation-does-not-draw-a-brand-block-the-bar-already-draws
823
+ * @return {object|null}
824
+ */
825
+ navBrand() {
826
+ return this.cnBrandInHeader ? null : this.resolvedBrand
827
+ },
828
+
811
829
  /**
812
830
  * The brand to draw: the `brand` prop, else the manifest's
813
831
  * `nav.brand`. Name and caption go through the translate function,
@@ -819,20 +837,7 @@ export default {
819
837
  * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-navigation-brand
820
838
  */
821
839
  resolvedBrand() {
822
- const declared = this.brand ?? this.effectiveManifest?.nav?.brand
823
- if (!declared || typeof declared !== 'object') {
824
- return null
825
- }
826
- const text = (value) => (typeof value === 'string' && value !== '' ? this.effectiveTranslate(value) : '')
827
- const brand = {
828
- logo: typeof declared.logo === 'string' ? declared.logo : '',
829
- // A URL, or `true` for the theme's emblem (--nldesign-emblem-url).
830
- emblem: declared.emblem === true ? true : (typeof declared.emblem === 'string' ? declared.emblem : ''),
831
- name: text(declared.name),
832
- caption: text(declared.caption),
833
- alt: text(declared.alt),
834
- }
835
- return (brand.logo || brand.emblem || brand.name || brand.caption) ? brand : null
840
+ return resolveBrand(this.brand ?? this.effectiveManifest?.nav?.brand, (value) => this.effectiveTranslate(value))
836
841
  },
837
842
 
838
843
  /**
@@ -189,6 +189,8 @@
189
189
 
190
190
  <!-- Phase 3: shell -->
191
191
  <template v-else>
192
+ <!-- The brand block in Nextcloud's own header (board look); teleported, renders nothing here. -->
193
+ <CnBrandBar v-if="brandInHeader" :brand="resolvedBrandBlock" @unavailable="headerMissing = true" />
192
194
  <!--
193
195
  @slot menu
194
196
  @description Left-rail navigation surface. Default:
@@ -681,6 +683,7 @@ import Restart from 'vue-material-design-icons/Restart.vue'
681
683
  import CnAiCompanion from '../CnAiCompanion/CnAiCompanion.vue'
682
684
  import CnAppLoading from '../CnAppLoading/CnAppLoading.vue'
683
685
  import CnAppNav from '../CnAppNav/CnAppNav.vue'
686
+ import CnBrandBar from '../CnBrandBar/CnBrandBar.vue'
684
687
  import CnCommandPalette from '../CnCommandPalette/CnCommandPalette.vue'
685
688
  import CnCredentials from '../CnCredentials/CnCredentials.vue'
686
689
  import CnDependencyMissing from '../CnDependencyMissing/CnDependencyMissing.vue'
@@ -721,6 +724,7 @@ import { DEFAULT_FORGE, resolveForge } from '../../utils/forge.js'
721
724
  import { BUILT_IN_KB_PROVIDERS } from '../../utils/kbSearchProviders.js'
722
725
  import { createManifestTranslate } from '../../utils/manifestTranslate.js'
723
726
  import { installModalStack, uninstallModalStack } from '../../utils/modalStack.js'
727
+ import { resolveBrand } from '../../utils/resolveBrand.js'
724
728
  import { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
725
729
  import { passesContextPredicates } from '../../utils/visibleIfContext.js'
726
730
 
@@ -781,6 +785,7 @@ export default {
781
785
  name: 'CnAppRoot',
782
786
 
783
787
  components: {
788
+ CnBrandBar,
784
789
  NcAppContent,
785
790
  NcAppSettingsDialog,
786
791
  NcAppSettingsSection,
@@ -831,6 +836,9 @@ export default {
831
836
  // value once, so a getter would freeze the look at creation.
832
837
  cnLook: computed(() => self.resolvedLook),
833
838
 
839
+ // True while CnBrandBar draws the block in Nextcloud's header, so CnAppNav skips its own.
840
+ cnBrandInHeader: computed(() => self.brandInHeader),
841
+
834
842
  get cnManifest() {
835
843
  return self.manifestEditor ? self.manifestEditor.source.value : self.manifest
836
844
  },
@@ -1591,6 +1599,19 @@ export default {
1591
1599
  default: '',
1592
1600
  },
1593
1601
 
1602
+ /**
1603
+ * The brand block (`{ logo, emblem, name, caption, alt, placement }`)
1604
+ * the board look draws at the start of Nextcloud's header. Empty falls back
1605
+ * to the manifest's `nav.brand`. `placement: "nav"` keeps the block
1606
+ * in the navigation.
1607
+ *
1608
+ * @type {object|null}
1609
+ */
1610
+ brand: {
1611
+ type: Object,
1612
+ default: null,
1613
+ },
1614
+
1594
1615
  /**
1595
1616
  * Title rendered at the top of the user-settings modal
1596
1617
  * (NcAppSettingsDialog `name` prop). Defaults to the
@@ -1790,6 +1811,12 @@ export default {
1790
1811
  data() {
1791
1812
  const willCheck = Array.isArray(this.requiresApps) && this.requiresApps.length > 0
1792
1813
  return {
1814
+ // Nextcloud's own header, present on a server-rendered page; the
1815
+ // brand block goes into it (CnBrandBar). Read once at creation so
1816
+ // the navigation never draws a block that then moves.
1817
+ headerPresent: typeof document !== 'undefined' && !!document.getElementById('header'),
1818
+ // Set when CnBrandBar finds the header gone: the navigation draws the block.
1819
+ headerMissing: false,
1793
1820
  capabilitiesLoading: willCheck,
1794
1821
  missingApps: [],
1795
1822
  guardError: null,
@@ -2062,6 +2089,32 @@ export default {
2062
2089
  return (this.look || this.manifest?.look) === 'board' ? 'board' : 'nextcloud'
2063
2090
  },
2064
2091
 
2092
+ /**
2093
+ * The brand CnBrandBar draws in the header: the `brand` prop, else the manifest's
2094
+ * `nav.brand`, read into one shape.
2095
+ *
2096
+ * @return {object|null} The resolved brand, or null.
2097
+ */
2098
+ resolvedBrandBlock() {
2099
+ return resolveBrand(this.brand || this.manifest?.nav?.brand, (text) => this.manifestTranslate(text))
2100
+ },
2101
+
2102
+ /**
2103
+ * Whether CnBrandBar draws the brand block in Nextcloud's header: the
2104
+ * board look, a brand that can be drawn, a placement other than `nav`,
2105
+ * and a `#header` to put it in. Without `#header` the navigation draws it.
2106
+ *
2107
+ * @spec openspec/changes/screens-brand-block-top-bar/specs/layout-components/spec.md#requirement-the-board-look-draws-the-brand-block-in-the-nextcloud-header
2108
+ * @return {boolean}
2109
+ */
2110
+ brandInHeader() {
2111
+ return this.resolvedLook === 'board'
2112
+ && !this.headerMissing
2113
+ && this.headerPresent
2114
+ && !!this.resolvedBrandBlock
2115
+ && this.resolvedBrandBlock.placement !== 'nav'
2116
+ },
2117
+
2065
2118
  /**
2066
2119
  * The label lookup provided as `cnTranslate`: the manifest's own
2067
2120
  * translations for the language first, then the host `translate`, then the
@@ -142,6 +142,14 @@ export default {
142
142
 
143
143
  components: { NcNoteCard },
144
144
 
145
+ inject: {
146
+ /**
147
+ * The host's label lookup (CnAppRoot provides it); identity without one, so a
148
+ * label that is not a key renders as written.
149
+ */
150
+ cnTranslate: { default: () => (key) => key },
151
+ },
152
+
145
153
  props: {
146
154
  /**
147
155
  * Banner severity variant: `info | warning | error` (plus `success`
@@ -362,7 +370,7 @@ export default {
362
370
  */
363
371
  resolvedKicker() {
364
372
  const kicker = this.kicker || (this.content && this.content.kicker) || ''
365
- return kicker
373
+ return kicker ? this.tl(kicker) : ''
366
374
  },
367
375
 
368
376
  /**
@@ -373,7 +381,7 @@ export default {
373
381
  */
374
382
  displayTitle() {
375
383
  const title = this.title || (this.content && this.content.title) || ''
376
- return title ? this.fillValue(title) : this.displayText
384
+ return title ? this.fillValue(this.tl(title)) : this.displayText
377
385
  },
378
386
 
379
387
  /**
@@ -386,7 +394,7 @@ export default {
386
394
  displayReason() {
387
395
  const reason = this.reason || (this.content && this.content.reason) || ''
388
396
  if (reason) {
389
- return this.fillValue(reason)
397
+ return this.fillValue(this.tl(reason))
390
398
  }
391
399
  const title = this.title || (this.content && this.content.title) || ''
392
400
  // A card that sets `text` to the same words as its `title` says
@@ -424,7 +432,7 @@ export default {
424
432
  return {
425
433
  key: `${index}-${action.id || action.label}`,
426
434
  id: action.id,
427
- label: action.label,
435
+ label: this.tl(action.label),
428
436
  href,
429
437
  target,
430
438
  primary: index === primaryIndex,
@@ -526,6 +534,17 @@ export default {
526
534
  },
527
535
 
528
536
  methods: {
537
+ /**
538
+ * A manifest string through the host's label lookup (`cnTranslate`), so a key
539
+ * such as "Mine" reads in the user's language.
540
+ *
541
+ * @param {string} text The text as written in the manifest.
542
+ * @return {string} The translated text.
543
+ */
544
+ tl(text) {
545
+ return typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text
546
+ },
547
+
529
548
  /**
530
549
  * Replace `{value}` in a text by the value the predicate read, once
531
550
  * one is known.