@conduction/nextcloud-vue 2.69.0 → 2.71.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 (153) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +25 -0
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +155 -152
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  5. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +77 -0
  6. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  7. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +8 -8
  8. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  9. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +151 -13
  10. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +3 -1
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  13. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue.js +1 -0
  14. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue.js.map +1 -1
  15. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js +28 -0
  16. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js.map +1 -1
  17. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js +4 -2
  18. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js.map +1 -1
  19. package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue2.js +25 -1
  20. package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue2.js.map +1 -1
  21. package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue3.js +32 -17
  22. package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue3.js.map +1 -1
  23. package/dist/esm/components/CnCapabilityTable/CnCapabilityTable.vue2.js +14 -1
  24. package/dist/esm/components/CnCapabilityTable/CnCapabilityTable.vue2.js.map +1 -1
  25. package/dist/esm/components/CnCapabilityTable/CnCapabilityTable.vue3.js.map +1 -1
  26. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +43 -2
  27. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
  28. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js +3 -3
  29. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
  30. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +25 -1
  31. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  32. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +40 -43
  33. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  34. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +35 -1
  35. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  36. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  37. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +54 -3
  38. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  39. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +6 -3
  40. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  41. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +28 -0
  42. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  43. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +3 -2
  44. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  45. package/dist/esm/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue2.js +12 -0
  46. package/dist/esm/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue2.js.map +1 -1
  47. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js +9 -0
  48. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
  49. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
  50. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +29 -2
  51. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  52. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +71 -34
  53. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  54. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +54 -7
  55. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
  56. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +26 -8
  57. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
  58. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +165 -0
  59. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  60. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +69 -26
  61. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  62. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue2.js.map +1 -1
  63. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue3.js.map +1 -1
  64. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +4 -1
  65. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  66. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  67. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +1 -1
  68. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
  69. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
  70. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  71. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +46 -32
  72. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  73. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js +17 -3
  74. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js.map +1 -1
  75. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js +25 -24
  76. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js.map +1 -1
  77. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
  78. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
  79. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +27 -0
  80. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  81. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +7 -0
  82. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  83. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue2.js +24 -1
  84. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue2.js.map +1 -1
  85. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue3.js +1 -1
  86. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue3.js.map +1 -1
  87. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +11 -0
  88. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
  89. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +4 -2
  90. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
  91. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +30 -9
  92. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
  93. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
  94. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js +15 -2
  95. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js.map +1 -1
  96. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js +31 -32
  97. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js.map +1 -1
  98. package/dist/esm/composables/useWalkthrough.js +11 -3
  99. package/dist/esm/composables/useWalkthrough.js.map +1 -1
  100. package/dist/esm/nextcloud-vue.css +198 -0
  101. package/dist/esm/utils/capabilityComparison.js +29 -1
  102. package/dist/esm/utils/capabilityComparison.js.map +1 -1
  103. package/dist/esm/utils/columnFilters.js +54 -4
  104. package/dist/esm/utils/columnFilters.js.map +1 -1
  105. package/dist/esm/utils/userAutocomplete.js +69 -3
  106. package/dist/esm/utils/userAutocomplete.js.map +1 -1
  107. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  108. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  109. package/dist/nextcloud-vue.cjs.js +2778 -1630
  110. package/dist/nextcloud-vue.cjs.js.map +2 -2
  111. package/dist/nextcloud-vue.css +198 -0
  112. package/package.json +1 -1
  113. package/src/components/CnActionsBar/CnActionsBar.md +2 -0
  114. package/src/components/CnActionsBar/CnActionsBar.vue +28 -2
  115. package/src/components/CnAppNav/CnAppNav.vue +85 -8
  116. package/src/components/CnAppRoot/CnAppRoot.vue +153 -13
  117. package/src/components/CnBreadcrumbs/CnBreadcrumbs.vue +45 -0
  118. package/src/components/CnCalendarWidget/CnCalendarWidget.vue +32 -4
  119. package/src/components/CnCapabilityTable/CnCapabilityTable.vue +14 -0
  120. package/src/components/CnDashboardGrid/CnDashboardGrid.md +2 -0
  121. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +54 -2
  122. package/src/components/CnDashboardPage/CnDashboardPage.vue +46 -19
  123. package/src/components/CnDataTable/CnDataTable.vue +35 -1
  124. package/src/components/CnDetailPage/CnDetailPage.md +2 -0
  125. package/src/components/CnDetailPage/CnDetailPage.vue +57 -3
  126. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +30 -1
  127. package/src/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue +12 -0
  128. package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +9 -0
  129. package/src/components/CnFormDialog/CnFormDialog.vue +62 -2
  130. package/src/components/CnHeaderWidget/CnHeaderWidget.vue +95 -8
  131. package/src/components/CnIndexPage/CnIndexPage.vue +194 -7
  132. package/src/components/CnNextStepCard/CnNextStepCard.vue +11 -0
  133. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +4 -1
  134. package/src/components/CnObjectSidebar/CnNotesTab.vue +1 -1
  135. package/src/components/CnSetupWizard/CnSetupWizard.vue +7 -0
  136. package/src/components/CnStackedBarWidget/CnStackedBarWidget.vue +34 -7
  137. package/src/components/CnStagesWidget/CnStagesWidget.vue +19 -0
  138. package/src/components/CnStatWidget/CnStatWidget.vue +36 -0
  139. package/src/components/CnStatsBlock/CnStatsBlock.md +2 -0
  140. package/src/components/CnStatsBlock/CnStatsBlock.vue +25 -2
  141. package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +13 -0
  142. package/src/components/CnWalkthrough/CnWalkthrough.vue +30 -9
  143. package/src/components/CnWeekStripWidget/CnWeekStripWidget.vue +24 -3
  144. package/src/composables/useWalkthrough.js +11 -3
  145. package/src/css/dashboard.css +44 -0
  146. package/src/css/detail-page.css +10 -0
  147. package/src/css/index-page.css +12 -0
  148. package/src/css/kpi-card.css +15 -0
  149. package/src/schemas/app-manifest-v2.schema.json +47 -5
  150. package/src/utils/capabilityComparison.js +28 -0
  151. package/src/utils/columnFilters.js +54 -4
  152. package/src/utils/userAutocomplete.js +68 -2
  153. 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: 35499103 bytes
4
- sha256: fe1cf925335eec45a51c59903eb32d313c16bdcbdaf047c4b80fad05712db22a
3
+ size: 35657637 bytes
4
+ sha256: b7b966a528988db88e213ce0afc88832aae9c6b60341877ba5226dc3d833f211
@@ -1330,6 +1330,21 @@ a.cn-kpi-card--stacked:focus-visible {
1330
1330
  text-decoration: none !important;
1331
1331
  }
1332
1332
 
1333
+ /* A stacked stats block (CnStatsBlock `layout: "stacked"`): its unit
1334
+ ("decisions") goes on a muted line of its own under the number, where the
1335
+ stat tile puts its caption (zuiddrecht-pixel-gaps-3). */
1336
+ .cn-kpi-card--stacked .cn-stats-block__count {
1337
+ flex-direction: column;
1338
+ align-items: flex-start;
1339
+ gap: var(--cn-kpi-stacked-gap, 6px);
1340
+ }
1341
+
1342
+ .cn-kpi-card--stacked .cn-stats-block__count-label {
1343
+ font-size: var(--cn-kpi-label-size, 13px);
1344
+ font-weight: 500;
1345
+ color: var(--color-text-maxcontrast);
1346
+ }
1347
+
1333
1348
  /* Filled: the card draws its own grey box, for a KPI that is NOT already
1334
1349
  inside a wrapper that draws one. Opt-in via `content.flat: false`. */
1335
1350
  .cn-kpi-card--filled {
@@ -2283,6 +2298,18 @@ a.cn-kpi-card--stacked:focus-visible {
2283
2298
  padding: calc(5 * var(--default-grid-baseline));
2284
2299
  }
2285
2300
 
2301
+ /* The board's header buttons (`headerButtons`) at the end of the title row,
2302
+ centred on the title and its count line. They wrap under the title on a
2303
+ narrow page. */
2304
+ .cn-index-page__header-buttons {
2305
+ display: flex;
2306
+ flex-wrap: wrap;
2307
+ align-items: center;
2308
+ align-self: center;
2309
+ gap: calc(2 * var(--default-grid-baseline));
2310
+ margin-inline-start: auto;
2311
+ }
2312
+
2286
2313
  .cn-index-page__below-header {
2287
2314
  margin-bottom: calc(4 * var(--default-grid-baseline));
2288
2315
  }
@@ -2814,6 +2841,50 @@ a.cn-kpi-card--stacked:focus-visible {
2814
2841
  .cn-requires-app.cn-requires-app .empty-content__action {
2815
2842
  margin-top: var(--default-grid-baseline, 4px);
2816
2843
  }
2844
+
2845
+ /* NcEmptyContent forces its icon's svg to 64px with !important while the
2846
+ * rule above lets the icon box shrink, so the svg spilled out of its box and
2847
+ * over the name. Hold the svg to the size the caller asked for. */
2848
+ .cn-requires-app.cn-requires-app .empty-content__icon svg {
2849
+ width: 32px !important;
2850
+ height: 32px !important;
2851
+ max-width: 32px !important;
2852
+ max-height: 32px !important;
2853
+ }
2854
+
2855
+ /* The dashboard's compact "Install <app>" placeholder: two lines of text and
2856
+ * the button, nothing else, so it fits a two-row tile even under a title row.
2857
+ * `safe center` keeps the top in view when a tile is smaller still, and the
2858
+ * body scrolls rather than clipping the button. */
2859
+ .cn-requires-app--compact {
2860
+ display: flex;
2861
+ flex-direction: column;
2862
+ align-items: center;
2863
+ justify-content: safe center;
2864
+ gap: calc(2 * var(--default-grid-baseline, 4px));
2865
+ box-sizing: border-box;
2866
+ height: 100%;
2867
+ min-height: 0;
2868
+ overflow: auto;
2869
+ padding: calc(2 * var(--default-grid-baseline, 4px));
2870
+ text-align: center;
2871
+ }
2872
+
2873
+ .cn-requires-app--compact .cn-requires-app__description {
2874
+ display: -webkit-box;
2875
+ margin: 0;
2876
+ overflow: hidden;
2877
+ color: var(--color-text-maxcontrast);
2878
+ font-size: 0.9em;
2879
+ line-height: 1.4;
2880
+ -webkit-box-orient: vertical;
2881
+ -webkit-line-clamp: 2;
2882
+ line-clamp: 2;
2883
+ }
2884
+
2885
+ .cn-requires-app--compact .button-vue {
2886
+ flex-shrink: 0;
2887
+ }
2817
2888
  /* ========================================
2818
2889
  @conduction/nextcloud-vue — CnDetailPage Styles
2819
2890
  ======================================== */
@@ -2862,6 +2933,16 @@ a.cn-kpi-card--stacked:focus-visible {
2862
2933
  row-gap: var(--cn-detail-header-row-gap, 22px);
2863
2934
  }
2864
2935
 
2936
+ /* That wrap must not take the actions off the title row. With `flex-basis:
2937
+ auto` a long title claimed its whole text width, so the actions wrapped
2938
+ under it. From a zero basis the title takes what the actions leave and
2939
+ wraps (two lines, then an ellipsis) inside it; only on a page too narrow
2940
+ for 16rem of title beside the actions do the actions drop a row. */
2941
+ .cn-detail-page__header--with-widget > :first-child {
2942
+ flex: 1 1 0;
2943
+ min-width: min(100%, 16rem);
2944
+ }
2945
+
2865
2946
  .cn-detail-page__header-widget {
2866
2947
  flex: 1 0 100%;
2867
2948
  min-width: 0;
@@ -6173,6 +6254,17 @@ svg.leaflet-image-layer.leaflet-interactive path {
6173
6254
  margin: 0;
6174
6255
  text-transform: uppercase;
6175
6256
  }
6257
+
6258
+ /* The title is a small kicker, not a section heading, but it is an <h3> (or
6259
+ whatever `titleTag` says) for the outline, and a theme that sizes every
6260
+ heading with !important (thematiq's nldesign sheet: `h3 { font-size:
6261
+ var(--nldesign-component-heading-3-font-size) !important }`) blew it up to
6262
+ an 18px uppercase heading. Only !important beats !important; the doubled
6263
+ class makes this the more specific rule. Without such a theme the size is
6264
+ the 0.85em above, unchanged. */
6265
+ .cn-next-step-card__title.cn-next-step-card__title[data-v-3a9ce962] {
6266
+ font-size: 0.85em !important;
6267
+ }
6176
6268
  .cn-next-step-card__list[data-v-3a9ce962] {
6177
6269
  display: grid;
6178
6270
  gap: calc(2 * var(--default-grid-baseline));
@@ -15597,6 +15689,15 @@ a.cn-xwiki-card__title[data-v-54e85fe3]:hover {
15597
15689
  }
15598
15690
 
15599
15691
 
15692
+ /* A select option breaks between words only, never inside one. */
15693
+ .cn-form-dialog__option[data-v-c3eeb0a2] {
15694
+ display: block;
15695
+ min-width: 0;
15696
+ white-space: normal;
15697
+ overflow-wrap: normal;
15698
+ word-break: normal;
15699
+ hyphens: manual;
15700
+ }
15600
15701
  .cn-form-dialog__draft-actions[data-v-c3eeb0a2] {
15601
15702
  display: flex;
15602
15703
  gap: 8px;
@@ -16465,6 +16566,14 @@ a.cn-xwiki-card__title[data-v-54e85fe3]:hover {
16465
16566
  gap: 16px;
16466
16567
  width: 100%;
16467
16568
  }
16569
+
16570
+ /* `content.inset: true`: the board's inset inside the card (zuiddrecht-pixel-
16571
+ gaps-3). A dashboard renders its widgets flush, so without it the strip ran
16572
+ from card edge to card edge. Theme hook: --cn-widget-board-inset. */
16573
+ .cn-stacked-bar--inset[data-v-c0bac1a2] {
16574
+ box-sizing: border-box;
16575
+ padding: var(--cn-widget-board-inset, 16px 24px 22px);
16576
+ }
16468
16577
  .cn-stacked-bar__state[data-v-c0bac1a2] {
16469
16578
  display: flex;
16470
16579
  align-items: center;
@@ -16980,6 +17089,25 @@ button.cn-stages-widget__bar-label[data-v-358fde0f] {
16980
17089
  line-height: inherit;
16981
17090
  cursor: pointer;
16982
17091
  }
17092
+
17093
+ /* The same label under a theme that restyles every button with !important
17094
+ (thematiq's nldesign sheet: `button:not(.action-button)` gets 8px 16px
17095
+ padding, `min-width: max-content` and `overflow: visible`), and under core's
17096
+ `margin: 3px 3px 3px 0`, which outranks the scoped base rule. Together they
17097
+ put each clickable label about 10px below the current step's <span> and let
17098
+ a long label run over its neighbours, so eight Woo steps overlapped instead
17099
+ of ending in an ellipsis. Only !important beats !important; this selector is
17100
+ the more specific of the two. Without such a theme nothing changes: these
17101
+ are the values the base rule already sets. */
17102
+ button.cn-stages-widget__bar-label.cn-stages-widget__bar-label[data-v-358fde0f] {
17103
+ min-width: 0 !important;
17104
+ margin: 0 !important;
17105
+ padding: 0 !important;
17106
+ border-width: 0 !important;
17107
+ overflow: hidden !important;
17108
+ text-overflow: ellipsis !important;
17109
+ white-space: nowrap !important;
17110
+ }
16983
17111
  button.cn-stages-widget__bar-label[aria-disabled="true"][data-v-358fde0f] {
16984
17112
  cursor: not-allowed;
16985
17113
  }
@@ -17459,6 +17587,21 @@ a.cn-kpi-card--stacked:focus-visible {
17459
17587
  text-decoration: none !important;
17460
17588
  }
17461
17589
 
17590
+ /* A stacked stats block (CnStatsBlock `layout: "stacked"`): its unit
17591
+ ("decisions") goes on a muted line of its own under the number, where the
17592
+ stat tile puts its caption (zuiddrecht-pixel-gaps-3). */
17593
+ .cn-kpi-card--stacked .cn-stats-block__count {
17594
+ flex-direction: column;
17595
+ align-items: flex-start;
17596
+ gap: var(--cn-kpi-stacked-gap, 6px);
17597
+ }
17598
+
17599
+ .cn-kpi-card--stacked .cn-stats-block__count-label {
17600
+ font-size: var(--cn-kpi-label-size, 13px);
17601
+ font-weight: 500;
17602
+ color: var(--color-text-maxcontrast);
17603
+ }
17604
+
17462
17605
  /* Filled: the card draws its own grey box, for a KPI that is NOT already
17463
17606
  inside a wrapper that draws one. Opt-in via `content.flat: false`. */
17464
17607
  .cn-kpi-card--filled {
@@ -18405,6 +18548,30 @@ to { background-position: -200% 0;
18405
18548
  flex: 1 1 320px;
18406
18549
  width: auto;
18407
18550
  }
18551
+
18552
+ /* The emblem beside the greeting (`content.emblem`): on one row with the
18553
+ date line and heading, centred on them, as LpStart draws it. */
18554
+ .cn-header-widget--with-emblem[data-v-1b97430f] {
18555
+ display: flex;
18556
+ column-gap: var(--cn-header-emblem-gap, 16px);
18557
+ }
18558
+ .cn-header-widget--with-emblem .cn-header-widget__content[data-v-1b97430f] {
18559
+ flex: 1 1 auto;
18560
+ width: auto;
18561
+ min-width: 0;
18562
+ }
18563
+ .cn-header-widget__emblem[data-v-1b97430f] {
18564
+ position: relative;
18565
+ z-index: 1;
18566
+ flex: none;
18567
+ align-self: center;
18568
+ height: var(--cn-header-emblem-size, 52px);
18569
+ width: auto;
18570
+ }
18571
+ .cn-header-widget__emblem--theme[data-v-1b97430f] {
18572
+ width: var(--cn-header-emblem-size, 52px);
18573
+ background: var(--nldesign-emblem-url) center / contain no-repeat;
18574
+ }
18408
18575
  .cn-header-widget__views[data-v-1b97430f] {
18409
18576
  position: relative;
18410
18577
  z-index: 1;
@@ -19507,6 +19674,14 @@ to { background-position: -200% 0;
19507
19674
  .cn-week-strip[data-v-153dd4cb] {
19508
19675
  width: 100%;
19509
19676
  }
19677
+
19678
+ /* `content.inset: true`: the board's inset inside the card (zuiddrecht-pixel-
19679
+ gaps-3). A dashboard renders its widgets flush, so without it the strip ran
19680
+ from card edge to card edge. Theme hook: --cn-widget-board-inset. */
19681
+ .cn-week-strip--inset[data-v-153dd4cb] {
19682
+ box-sizing: border-box;
19683
+ padding: var(--cn-widget-board-inset, 16px 24px 22px);
19684
+ }
19510
19685
  .cn-week-strip__state[data-v-153dd4cb] {
19511
19686
  display: flex;
19512
19687
  align-items: center;
@@ -30052,6 +30227,19 @@ to {
30052
30227
  }
30053
30228
 
30054
30229
 
30230
+ /* `separator`: a text separator in place of the chevron. The chevron's own
30231
+ element stays (it carries NcBreadcrumbs' spacing and its "no separator after
30232
+ the last crumb" rule); only its icon gives way to the text. */
30233
+ .cn-breadcrumbs--text-separator[data-v-296fde9b] .vue-crumb__separator svg {
30234
+ display: none;
30235
+ }
30236
+ .cn-breadcrumbs--text-separator[data-v-296fde9b] .vue-crumb__separator::before {
30237
+ content: var(--cn-breadcrumbs-separator);
30238
+ padding-inline: var(--cn-breadcrumbs-separator-gap, 0);
30239
+ color: var(--color-text-maxcontrast);
30240
+ }
30241
+
30242
+
30055
30243
  .cn-run-node-dialog__form[data-v-3eb7c55d] {
30056
30244
  display: flex;
30057
30245
  flex-direction: column;
@@ -30223,6 +30411,16 @@ to {
30223
30411
  outline-offset: 2px;
30224
30412
  border-radius: var(--border-radius-container-large, 16px);
30225
30413
  }
30414
+
30415
+ /* A size-to-content cell (`sizeToContent`) is measured by GridStack from the
30416
+ content's first child, and a widget wrapper there filled the cell
30417
+ (`height: 100%`), so the measure returned the cell's own height and the
30418
+ cell never shrank: "Waiting for me" stayed a tall card around one row. In
30419
+ such a cell the child takes its own height. A cell without the key keeps
30420
+ the filling child. */
30421
+ .cn-dashboard-grid__item--fit[data-v-9f6b1a22] > .grid-stack-item-content > * {
30422
+ height: auto;
30423
+ }
30226
30424
  [data-v-9f6b1a22] .grid-stack-item-content {
30227
30425
  background: var(--color-main-background);
30228
30426
  border: none;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/nextcloud-vue",
3
- "version": "2.69.0",
3
+ "version": "2.71.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": {
@@ -204,3 +204,5 @@ Controlling the inline action button count — `inlineActionCount` sets how many
204
204
  | `sortOptions` | Array | `[]` | Options `{ value, label }` for the sort dropdown. |
205
205
  | `sortValue` | String | `''` | Selected sort option value (controlled). |
206
206
  | `sortLabel` | String | *(i18n)* | Accessible label for the sort dropdown. |
207
+
208
+ `showCount` (Boolean, default `true`): `false` drops the "Showing 20 of 258" line. `showActionsMenu` (Boolean, default `true`): `false` drops the overflow Actions menu, for a page that offers its actions as buttons elsewhere (CnIndexPage `headerButtons`).
@@ -12,13 +12,13 @@
12
12
  :aria-label="searchPlaceholder || t('nextcloud-vue', 'Search')"
13
13
  @input="onSearchInput">
14
14
  </div>
15
- <span v-else-if="hasTotal" class="cn-actions-bar__count">
15
+ <span v-else-if="showCount && hasTotal" class="cn-actions-bar__count">
16
16
  {{ countText }}
17
17
  </span>
18
18
  <!-- @slot after-search Refinement controls rendered beside the search field on the LEFT side of the bar (e.g. a filter menu button). Convention: the left side groups the VISUAL controls — search, filters, view toggle — while the right cluster holds the ACT controls (add, overflow); the standalone sort select is a display control too but keeps its legacy right-side placement. -->
19
19
  <slot name="after-search" />
20
20
  <!-- Counter beside the search (`showCountWithSearch`): after the search and its #after-search controls. The live region stays mounted while there is no total, so results coming back after none are announced too. -->
21
- <span v-if="showSearch && showCountWithSearch"
21
+ <span v-if="showCount && showSearch && showCountWithSearch"
22
22
  class="cn-actions-bar__count cn-actions-bar__count--beside-search"
23
23
  :class="{ 'cn-actions-bar__count--empty': !hasTotal }"
24
24
  aria-live="polite">{{ hasTotal ? countText : '' }}</span>
@@ -140,6 +140,7 @@
140
140
 
141
141
  <!-- Actions menu (Refresh, Import, Export, mass actions) -->
142
142
  <NcActions
143
+ v-if="showActionsMenu"
143
144
  :forceName="true"
144
145
  :inline="inlineActionCount"
145
146
  :menuName="actionsMenuName"
@@ -683,6 +684,31 @@ export default {
683
684
  default: true,
684
685
  },
685
686
 
687
+ /**
688
+ * Whether the "Showing 20 of 258" line renders where the search field
689
+ * is not. `false` drops it, for a page whose title line already says
690
+ * how many there are. True by default.
691
+ *
692
+ * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-index-page-can-take-the-board-header
693
+ */
694
+ showCount: {
695
+ type: Boolean,
696
+ default: true,
697
+ },
698
+
699
+ /**
700
+ * Whether the overflow Actions menu (Refresh, Import, Export, mass
701
+ * actions, header actions) renders. `false` drops it, for a page that
702
+ * offers its actions as buttons elsewhere (CnIndexPage
703
+ * `headerButtons`). True by default.
704
+ *
705
+ * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-index-page-can-take-the-board-header
706
+ */
707
+ showActionsMenu: {
708
+ type: Boolean,
709
+ default: true,
710
+ },
711
+
686
712
  /**
687
713
  * Whether to show the Search/Columns sidebar toggle button. Lets the
688
714
  * index sidebar default to closed and be opened on demand.
@@ -188,8 +188,8 @@
188
188
  v-else
189
189
  :key="item.id"
190
190
  :name="resolveLabel(item)"
191
- :to="itemTo(item)"
192
- :href="itemHref(item)"
191
+ :to="linkTo(item)"
192
+ :href="linkHref(item)"
193
193
  :icon="cssIconClass(item)"
194
194
  :active="isActive(item)"
195
195
  :pinned="Boolean(item.pinned)"
@@ -224,8 +224,8 @@
224
224
  v-for="child in visibleChildren(item)"
225
225
  :key="child.id"
226
226
  :name="resolveLabel(child)"
227
- :to="itemTo(child)"
228
- :href="itemHref(child)"
227
+ :to="linkTo(child)"
228
+ :href="linkHref(child)"
229
229
  :icon="cssIconClass(child)"
230
230
  :active="isActive(child)"
231
231
  :pinned="Boolean(child.pinned)"
@@ -304,8 +304,8 @@
304
304
  v-for="item in footerItems"
305
305
  :key="item.id"
306
306
  :name="resolveLabel(item)"
307
- :to="itemTo(item)"
308
- :href="itemHref(item)"
307
+ :to="linkTo(item)"
308
+ :href="linkHref(item)"
309
309
  :icon="cssIconClass(item)"
310
310
  :active="isActive(item)"
311
311
  :data-testid="`cn-nav-entry-${item.id}`"
@@ -402,8 +402,8 @@
402
402
  v-else
403
403
  :key="item.id"
404
404
  :name="resolveLabel(item)"
405
- :to="itemTo(item)"
406
- :href="itemHref(item)"
405
+ :to="linkTo(item)"
406
+ :href="linkHref(item)"
407
407
  :icon="cssIconClass(item)"
408
408
  :active="isActive(item)"
409
409
  :data-cn-route="item.route"
@@ -2026,6 +2026,72 @@ export default {
2026
2026
  return to
2027
2027
  },
2028
2028
 
2029
+ /**
2030
+ * Whether another visible entry links to the same route, so the two
2031
+ * differ only in `query` or `params` ("All cases" and "Woo requests"
2032
+ * on one list).
2033
+ *
2034
+ * @param {object} item Menu entry.
2035
+ * @return {boolean} True when the route is shared.
2036
+ * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-only-the-best-matching-menu-entry-is-active
2037
+ */
2038
+ sharesRoute(item) {
2039
+ if (!item || item.href || item.action || !item.route) {
2040
+ return false
2041
+ }
2042
+ return this.flatEntries.some((entry) => entry !== item && !entry.href && !entry.action && entry.route === item.route)
2043
+ },
2044
+
2045
+ /**
2046
+ * The app's router, or null when the nav is mounted without one. Read
2047
+ * from the app's global properties: `this.$router` warns during render
2048
+ * on an instance that has none.
2049
+ *
2050
+ * @return {object|null} The router.
2051
+ * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-only-the-best-matching-menu-entry-is-active
2052
+ */
2053
+ appRouter() {
2054
+ return this.$?.appContext?.config?.globalProperties?.$router ?? null
2055
+ },
2056
+
2057
+ /**
2058
+ * The `:to` an entry renders with. An entry that shares its route
2059
+ * renders as a plain link instead (see `linkHref`): NcAppNavigationItem
2060
+ * marks a router link active whenever vue-router calls it active, and
2061
+ * vue-router ignores the query, so "Woo requests" lit up beside "All
2062
+ * cases" on the unfiltered list. As a plain link only `isActive`
2063
+ * decides, which marks the entry that matches best.
2064
+ *
2065
+ * @param {object} item Menu entry.
2066
+ * @return {object|null} The router target, or null.
2067
+ * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-only-the-best-matching-menu-entry-is-active
2068
+ */
2069
+ linkTo(item) {
2070
+ if (this.appRouter() && this.sharesRoute(item)) {
2071
+ return null
2072
+ }
2073
+ return this.itemTo(item)
2074
+ },
2075
+
2076
+ /**
2077
+ * The `:href` an entry renders with: `itemHref`, or for an entry that
2078
+ * shares its route the address vue-router resolves its target to (a
2079
+ * click on it is still routed in the app, see `onItemClick`).
2080
+ *
2081
+ * @param {object} item Menu entry.
2082
+ * @return {string|null} The address, or null.
2083
+ * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-only-the-best-matching-menu-entry-is-active
2084
+ */
2085
+ linkHref(item) {
2086
+ const own = this.itemHref(item)
2087
+ const router = this.appRouter()
2088
+ if (own || !router || !this.sharesRoute(item)) {
2089
+ return own
2090
+ }
2091
+ const to = this.itemTo(item)
2092
+ return to ? router.resolve(to).href : null
2093
+ },
2094
+
2029
2095
  /**
2030
2096
  * Build the `:href` value for an `NcAppNavigationItem`. Returns
2031
2097
  * the item's `href` so the entry renders as a real anchor whose
@@ -2175,6 +2241,17 @@ export default {
2175
2241
  this.cnReplayWalkthrough(item.tourId)
2176
2242
  return
2177
2243
  }
2244
+ // An entry that shares its route is a plain link (see linkTo); a
2245
+ // plain click still navigates inside the app. A modified click
2246
+ // (new tab, new window) is left to the browser.
2247
+ if (this.appRouter() && this.sharesRoute(item) && event && !event.defaultPrevented
2248
+ && !(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) && event.button !== 1) {
2249
+ const to = this.itemTo(item)
2250
+ if (to) {
2251
+ event.preventDefault()
2252
+ this.appRouter().push(to).catch(() => {})
2253
+ }
2254
+ }
2178
2255
  // An entry left without a link (see itemTo) renders a `#` anchor;
2179
2256
  // following it would change the address, so the click goes nowhere.
2180
2257
  if (!item.href && this.unfilledRouteParam(item) && event && typeof event.preventDefault === 'function') {