@conduction/nextcloud-vue 2.56.0-beta.64 → 2.56.0-beta.65

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 (33) hide show
  1. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +2 -1
  2. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  4. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +20 -0
  5. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  6. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +68 -61
  7. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  8. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +37 -12
  9. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  10. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  11. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
  12. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js +1 -1
  13. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
  14. package/dist/esm/library-style.js +1 -1
  15. package/dist/esm/nextcloud-vue.css +98 -31
  16. package/dist/esm/utils/savedViewHelpers.js +40 -3
  17. package/dist/esm/utils/savedViewHelpers.js.map +1 -1
  18. package/dist/nextcloud-vue.cjs.js +198 -109
  19. package/dist/nextcloud-vue.cjs.js.map +2 -2
  20. package/dist/nextcloud-vue.css +98 -31
  21. package/package.json +1 -1
  22. package/src/components/CnAppRoot/CnAppRoot.vue +2 -1
  23. package/src/components/CnDetailPage/CnDetailPage.vue +35 -11
  24. package/src/components/CnIndexPage/CnIndexPage.md +1 -1
  25. package/src/components/CnIndexPage/CnIndexPage.vue +37 -12
  26. package/src/components/CnIndexSidebar/CnIndexSidebar.md +1 -1
  27. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +2 -2
  28. package/src/css/board.css +5 -2
  29. package/src/css/look-board-detail.css +19 -7
  30. package/src/css/look-board-index.css +14 -10
  31. package/src/css/look-board-reports.css +1 -1
  32. package/src/css/look-board.css +54 -9
  33. package/src/utils/savedViewHelpers.js +39 -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: 44704290 bytes
4
- sha256: e14648ce27bae8fed4271ddde5d95e10a1a5616bf123c75c6ae73769d697d1e6
3
+ size: 44711106 bytes
4
+ sha256: adc90af5daccb75c11701d4aa396b6d4d702bbbe7162340774724cc78395fa24
@@ -91,14 +91,19 @@
91
91
  /* Buttons and inputs. */
92
92
  --cn-board-control-height: 40px;
93
93
  --cn-board-control-radius: 8px;
94
+ /* A control's edge: 3:1 against every ground a control sits on (WCAG
95
+ 1.4.11). Nextcloud's border-dark is a hairline (#dbdbdb, 1.4:1); its
96
+ max-contrast border is the control step, and a theme sets both. */
97
+ --cn-board-control-border: var(--color-border-maxcontrast);
94
98
  /* Cards. */
95
99
  --cn-board-card-radius: 12px;
96
100
  --cn-board-card-padding: 20px 22px;
97
101
  /* The side inset of a dashboard card: its title and its rows. */
98
102
  --cn-board-dashboard-card-inset: 24px;
99
- /* Hairline inside a card, and the soft secondary text. */
103
+ /* Hairline inside a card, and the soft secondary text: the boards' #3d4047,
104
+ between the main text and the muted text, so it follows a theme's pair. */
100
105
  --cn-board-hairline: var(--color-border);
101
- --cn-board-text-soft: var(--color-main-text);
106
+ --cn-board-text-soft: color-mix(in srgb, var(--color-main-text) 45%, var(--color-text-maxcontrast));
102
107
  }
103
108
 
104
109
  /*
@@ -250,6 +255,37 @@
250
255
  --font-weight-element: 600;
251
256
  }
252
257
 
258
+ /* ---- A button with an icon and a label --------------------------------- */
259
+ /* The boards draw it 14px in at both sides with 14px text and a 16px icon
260
+ 8px from its label (DqZaakEmail "Notitie toevoegen", DqVthInstellingen
261
+ "Regel toevoegen"). NcButton pads its start 4px and its end 12px, and puts
262
+ a 20px icon in a 30px slot. The rules hold the condition in `:where()`, so
263
+ they weigh (0,3,0): enough to outrank NcButton's own (0,2,0), and less than
264
+ any surface rule (header, toolbar, follow pill, Meer chevron, tab strip),
265
+ which keeps its own value. The padding goes through NcButton's
266
+ `--button-padding`, so a surface that sets `padding` is never contested.
267
+ @spec openspec/changes/board-icon-label-button/specs/board-icon-label-button/spec.md#requirement-a-board-button-with-an-icon-and-a-label-is-padded-as-the-boards-draw-it */
268
+ .cn-look-board .button-vue.button-vue:where(:has(> .button-vue__wrapper > .button-vue__icon:not(:empty)):has(> .button-vue__wrapper > .button-vue__text:not(:empty)):not(.cn-look-nextcloud *):not(.vue-crumb *)) {
269
+ --button-padding: 14px;
270
+ font-size: 14px;
271
+ }
272
+
273
+ .cn-look-board :where(.button-vue:has(> .button-vue__wrapper > .button-vue__icon:not(:empty)):has(> .button-vue__wrapper > .button-vue__text:not(:empty)):not(.cn-look-nextcloud *):not(.vue-crumb *)) .button-vue__wrapper.button-vue__wrapper {
274
+ gap: 8px;
275
+ }
276
+
277
+ .cn-look-board :where(.button-vue:has(> .button-vue__wrapper > .button-vue__icon:not(:empty)):has(> .button-vue__wrapper > .button-vue__text:not(:empty)):not(.cn-look-nextcloud *):not(.vue-crumb *)) .button-vue__icon.button-vue__icon {
278
+ width: 16px;
279
+ min-width: 16px;
280
+ height: 16px;
281
+ min-height: 16px;
282
+ }
283
+
284
+ .cn-look-board :where(.button-vue:has(> .button-vue__wrapper > .button-vue__icon:not(:empty)):has(> .button-vue__wrapper > .button-vue__text:not(:empty)):not(.cn-look-nextcloud *):not(.vue-crumb *)) .button-vue__icon svg {
285
+ width: 16px;
286
+ height: 16px;
287
+ }
288
+
253
289
  /* ---- The header button ------------------------------------------------- */
254
290
  .cn-look-board .cn-index-page__header-buttons:not(.cn-look-nextcloud *),
255
291
  .cn-look-board .cn-detail-page__header-actions:not(.cn-look-nextcloud *),
@@ -279,7 +315,7 @@
279
315
  --nldesign-component-button-primary-action-font-weight: 600;
280
316
  --nldesign-component-button-secondary-action-color: var(--color-main-text);
281
317
  --nldesign-component-button-secondary-action-background-color: var(--color-main-background);
282
- --nldesign-component-button-secondary-action-border-color: var(--color-border-dark);
318
+ --nldesign-component-button-secondary-action-border-color: var(--cn-board-control-border);
283
319
  --nldesign-component-button-secondary-action-hover-background-color: var(--color-background-hover);
284
320
  }
285
321
 
@@ -293,7 +329,7 @@
293
329
  min-height: var(--cn-board-control-height);
294
330
  padding: 0 14px;
295
331
  gap: 8px;
296
- border: 1px solid var(--color-border-dark);
332
+ border: 1px solid var(--cn-board-control-border);
297
333
  border-radius: var(--cn-board-control-radius);
298
334
  background: var(--color-main-background);
299
335
  color: var(--color-main-text);
@@ -325,7 +361,7 @@
325
361
  height: var(--cn-board-control-height);
326
362
  min-height: var(--cn-board-control-height);
327
363
  padding: 0 14px;
328
- border: 1px solid var(--color-border-dark);
364
+ border: 1px solid var(--cn-board-control-border);
329
365
  border-radius: var(--cn-board-control-radius);
330
366
  background: var(--color-main-background);
331
367
  color: var(--color-main-text);
@@ -333,8 +369,9 @@
333
369
  font-weight: 600;
334
370
  }
335
371
 
336
- /* Buttons 10px apart, each icon 16px with 8px to its label (DqZaak's header,
337
- DqZaken's "Downloaden"); NcButton draws a 20px icon in a 30px slot. */
372
+ /* Buttons 10px apart, each icon 16px (DqZaak's header, DqZaken's
373
+ "Downloaden"); NcButton draws a 20px icon in a 30px slot. The 8px to the
374
+ label is the board's icon-and-label gap above. */
338
375
  .cn-look-board .cn-action-buttons:not(.cn-look-nextcloud *) {
339
376
  gap: 10px;
340
377
  }
@@ -344,7 +381,6 @@
344
381
  min-width: 16px;
345
382
  height: 16px;
346
383
  min-height: 16px;
347
- margin-inline-end: 8px;
348
384
  }
349
385
 
350
386
  .cn-look-board .cn-action-buttons .button-vue .button-vue__icon:not(.cn-look-nextcloud *) svg {
@@ -379,7 +415,7 @@
379
415
  body ink onto every span (thematiq). */
380
416
  .cn-look-board .action-item.action-item--secondary .action-item__menutoggle.button-vue--secondary:not(.cn-look-nextcloud *) {
381
417
  --nldesign-color-on-surface: var(--color-main-text);
382
- border: 1px solid var(--color-border-dark);
418
+ border: 1px solid var(--cn-board-control-border);
383
419
  border-radius: var(--cn-board-control-radius);
384
420
  background: var(--color-main-background);
385
421
  color: var(--color-main-text);
@@ -1176,6 +1212,15 @@
1176
1212
  line-height: normal;
1177
1213
  }
1178
1214
 
1215
+ /* A neutral board tag ("Normaal", "Uit") is the soft text on the chip ground,
1216
+ not the main text on the darker background a Nextcloud tag takes.
1217
+ @spec openspec/changes/board-pill-tones/specs/board-pill-tones/spec.md#requirement-a-neutral-board-tag-is-soft-text-on-the-chip-ground */
1218
+ .cn-look-board .cn-status-badge--default:not(.cn-status-badge--solid):not(.cn-look-nextcloud *) {
1219
+ --cn-status-badge-ink: var(--cn-board-text-soft);
1220
+ background-color: var(--color-background-hover);
1221
+ color: var(--cn-status-badge-ink);
1222
+ }
1223
+
1179
1224
  /* A pill paints its own pairing of ink and fill. A theme that forces the body
1180
1225
  ink onto every span (thematiq's text rule, `!important` at id level) reads
1181
1226
  that ink from --nldesign-color-on-surface when the surface sets it, so the
@@ -2099,10 +2144,13 @@
2099
2144
  margin-inline-start: auto;
2100
2145
  padding: 1px 8px;
2101
2146
  border-radius: 10px;
2147
+ --nldesign-color-on-surface: var(--cn-board-text-soft, var(--color-main-text));
2102
2148
  background-color: var(--color-main-background);
2103
- color: var(--color-main-text);
2149
+ color: var(--cn-board-text-soft, var(--color-main-text));
2104
2150
  font-size: 12px;
2105
- font-weight: 400;
2151
+ font-weight: 700;
2152
+ /* 16px tall on its 1px pad (DqWerkbord's column counts). */
2153
+ line-height: 14px;
2106
2154
  }
2107
2155
 
2108
2156
  /* A column at its limit: the count in the error colour; "full" is said in words too. */
@@ -2474,7 +2522,7 @@
2474
2522
  height: var(--cn-board-control-height) !important;
2475
2523
  min-height: var(--cn-board-control-height) !important;
2476
2524
  padding: 0 14px !important;
2477
- border: 1px solid var(--color-border-dark) !important;
2525
+ border: 1px solid var(--cn-board-control-border) !important;
2478
2526
  border-radius: var(--cn-board-control-radius) !important;
2479
2527
  background: var(--color-main-background) !important;
2480
2528
  color: var(--color-main-text) !important;
@@ -2598,7 +2646,7 @@
2598
2646
  min-height: 38px;
2599
2647
  margin: 0;
2600
2648
  padding: 0 14px;
2601
- border: 1px solid var(--color-border-dark);
2649
+ border: 1px solid var(--cn-board-control-border);
2602
2650
  border-radius: 19px;
2603
2651
  background: var(--color-main-background);
2604
2652
  color: var(--color-main-text);
@@ -2640,20 +2688,22 @@
2640
2688
  padding: 1px 7px;
2641
2689
  border: 0;
2642
2690
  border-radius: 10px;
2691
+ --nldesign-color-on-surface: var(--cn-board-text-soft);
2643
2692
  background: var(--color-border);
2644
2693
  color: var(--cn-board-text-soft);
2645
2694
  font-size: 12px;
2646
2695
  font-weight: 600;
2647
- line-height: 1.4;
2696
+ /* 16px tall on its 1px pad, as the boards draw it (DqTaken, DqWerkbord).
2697
+ @spec openspec/changes/board-pill-tones/specs/board-pill-tones/spec.md#requirement-a-board-count-is-16px-tall */
2698
+ line-height: 14px;
2648
2699
  }
2649
2700
 
2650
2701
  .cn-look-board .cn-saved-views__chip--selected .cn-saved-views__chip-count,
2651
- /* On the selected chip the count is a lighter bubble on the dark chip
2652
- (DqZaken: #3d4047 on #1b1c1d); --cn-board-text-soft resolves to the main
2653
- text, the chip's own colour, which hid the bubble. */
2702
+ /* On the selected chip the count is the soft ink as a lighter bubble on the
2703
+ dark chip (DqZaken: #3d4047 on #1b1c1d). */
2654
2704
  .cn-look-board .cn-quick-filter-bar .cn-quick-filter-bar__tab--active .cn-quick-filter-bar__count {
2655
2705
  --nldesign-color-on-surface: var(--color-primary-element-text);
2656
- background: color-mix(in srgb, var(--color-main-background) 15%, var(--color-main-text));
2706
+ background: var(--cn-board-text-soft);
2657
2707
  color: var(--color-primary-element-text);
2658
2708
  }
2659
2709
 
@@ -2702,7 +2752,7 @@
2702
2752
  --nldesign-component-button-font-weight: 600;
2703
2753
  --nldesign-component-button-secondary-action-color: var(--color-main-text);
2704
2754
  --nldesign-component-button-secondary-action-background-color: var(--color-main-background);
2705
- --nldesign-component-button-secondary-action-border-color: var(--color-border-dark);
2755
+ --nldesign-component-button-secondary-action-border-color: var(--cn-board-control-border);
2706
2756
  --nldesign-component-button-secondary-action-hover-background-color: var(--color-background-hover);
2707
2757
  }
2708
2758
 
@@ -2729,6 +2779,8 @@
2729
2779
  margin-inline-start: 6px;
2730
2780
  padding: 0 6px;
2731
2781
  border-radius: 10px;
2782
+ /* A theme that forces the body ink onto every span reads it from here. */
2783
+ --nldesign-color-on-surface: var(--color-primary-element-text);
2732
2784
  background: var(--color-primary-element);
2733
2785
  color: var(--color-primary-element-text);
2734
2786
  font-size: 12px;
@@ -2837,7 +2889,7 @@
2837
2889
  height: 40px;
2838
2890
  padding-block: 0;
2839
2891
  padding-inline: 38px 12px !important;
2840
- border: 1px solid var(--color-border-dark);
2892
+ border: 1px solid var(--cn-board-control-border);
2841
2893
  border-radius: 8px;
2842
2894
  font-size: 14px;
2843
2895
  }
@@ -3214,7 +3266,7 @@
3214
3266
  height: 34px !important;
3215
3267
  min-height: 34px !important;
3216
3268
  padding: 0 !important;
3217
- border: 1px solid var(--color-border-dark) !important;
3269
+ border: 1px solid var(--cn-board-control-border) !important;
3218
3270
  border-radius: 8px !important;
3219
3271
  color: var(--cn-board-text-soft) !important;
3220
3272
  }
@@ -8000,7 +8052,7 @@ a.cn-kpi-card--board * {
8000
8052
  height: var(--cn-board-control-height) !important;
8001
8053
  min-height: var(--cn-board-control-height) !important;
8002
8054
  padding: 0 14px !important;
8003
- border: 1px solid var(--color-border-dark) !important;
8055
+ border: 1px solid var(--cn-board-control-border) !important;
8004
8056
  border-radius: var(--cn-board-control-radius) !important;
8005
8057
  background: var(--color-main-background) !important;
8006
8058
  color: var(--color-main-text) !important;
@@ -8013,13 +8065,13 @@ a.cn-kpi-card--board * {
8013
8065
  background: var(--color-background-hover) !important;
8014
8066
  }
8015
8067
 
8016
- /* Each header icon is 16px with 8px to its label; NcButton draws 20px in a 30px slot. */
8068
+ /* Each header icon is 16px; NcButton draws 20px in a 30px slot. The 8px to
8069
+ the label is the board's icon-and-label gap (look-board.css). */
8017
8070
  .cn-look-board .cn-detail-page .cn-detail-page__header-actions > .button-vue--secondary .button-vue__icon {
8018
8071
  width: 16px;
8019
8072
  min-width: 16px;
8020
8073
  height: 16px;
8021
8074
  min-height: 16px;
8022
- margin-inline-end: 8px;
8023
8075
  }
8024
8076
 
8025
8077
  .cn-look-board .cn-detail-page .cn-detail-page__header-actions > .button-vue--secondary .button-vue__icon svg {
@@ -8027,6 +8079,17 @@ a.cn-kpi-card--board * {
8027
8079
  height: 16px;
8028
8080
  }
8029
8081
 
8082
+ /* Row 3: the Follow control under the breadcrumb, on the full width.
8083
+ @spec openspec/changes/board-follow-row/specs/board-follow-row/spec.md#requirement-the-board-follow-control-sits-under-the-breadcrumb */
8084
+ .cn-look-board .cn-detail-page__header-row3 {
8085
+ align-items: center;
8086
+ display: flex;
8087
+ flex: 1 0 100%;
8088
+ flex-wrap: wrap;
8089
+ gap: 8px;
8090
+ min-width: 0;
8091
+ }
8092
+
8030
8093
  /* Row 2: pills, breadcrumb, dot, meta, chips. */
8031
8094
  .cn-look-board .cn-detail-page__header-row2 {
8032
8095
  align-items: center;
@@ -8184,14 +8247,14 @@ a.cn-kpi-card--board * {
8184
8247
  --nldesign-component-button-border-radius: 14px;
8185
8248
  --nldesign-component-button-secondary-action-color: var(--color-main-text);
8186
8249
  --nldesign-component-button-secondary-action-background-color: var(--color-main-background);
8187
- --nldesign-component-button-secondary-action-border-color: var(--color-border-dark);
8250
+ --nldesign-component-button-secondary-action-border-color: var(--cn-board-control-border);
8188
8251
  --nldesign-component-button-secondary-action-hover-background-color: var(--color-background-hover);
8189
8252
  box-sizing: border-box;
8190
8253
  height: 28px;
8191
8254
  min-height: 28px;
8192
8255
  min-width: 0;
8193
8256
  padding: 0 10px;
8194
- border: 1px solid var(--color-border-dark);
8257
+ border: 1px solid var(--cn-board-control-border);
8195
8258
  border-radius: 14px;
8196
8259
  background: var(--color-main-background);
8197
8260
  color: var(--color-main-text);
@@ -8329,13 +8392,14 @@ a.cn-kpi-card--board * {
8329
8392
 
8330
8393
  /* The Actions button at the end of the strip: at the top, 8px in from the
8331
8394
  end and 8px clear of the card, so the tabs, aligned to the bottom, sit 6px
8332
- lower than the button (DqZaak "Acties"). */
8395
+ lower than the button, 12px in at both sides (DqZaak "Acties"). */
8333
8396
  .cn-look-board .cn-tabs--board .cn-tabs__nav-end {
8334
8397
  align-self: flex-start;
8335
8398
  margin: 0 8px 8px 0;
8336
8399
  }
8337
8400
 
8338
8401
  .cn-look-board .cn-tabs--board .cn-tabs__nav-end .button-vue {
8402
+ --button-padding: 12px;
8339
8403
  min-height: 34px;
8340
8404
  block-size: 34px;
8341
8405
  }
@@ -8659,7 +8723,7 @@ a.cn-kpi-card--board * {
8659
8723
  .cn-look-board .cn-activity-tab--board .cn-activity-tab__kind {
8660
8724
  align-items: center;
8661
8725
  background: var(--color-main-background);
8662
- border: 1px solid var(--color-border-dark);
8726
+ border: 1px solid var(--cn-board-control-border);
8663
8727
  border-radius: 18px;
8664
8728
  color: var(--color-main-text);
8665
8729
  cursor: pointer;
@@ -8934,7 +8998,7 @@ a.cn-kpi-card--board * {
8934
8998
  height: var(--cn-board-control-height) !important;
8935
8999
  min-height: var(--cn-board-control-height) !important;
8936
9000
  padding: 0 14px !important;
8937
- border: 1px solid var(--color-border-dark) !important;
9001
+ border: 1px solid var(--cn-board-control-border) !important;
8938
9002
  border-radius: var(--cn-board-control-radius) !important;
8939
9003
  background: var(--color-main-background) !important;
8940
9004
  color: var(--color-main-text) !important;
@@ -39027,10 +39091,13 @@ to {
39027
39091
  margin-inline-start: auto;
39028
39092
  padding: 1px 8px;
39029
39093
  border-radius: 10px;
39094
+ --nldesign-color-on-surface: var(--cn-board-text-soft, var(--color-main-text));
39030
39095
  background-color: var(--color-main-background);
39031
- color: var(--color-main-text);
39096
+ color: var(--cn-board-text-soft, var(--color-main-text));
39032
39097
  font-size: 12px;
39033
- font-weight: 400;
39098
+ font-weight: 700;
39099
+ /* 16px tall on its 1px pad (DqWerkbord's column counts). */
39100
+ line-height: 14px;
39034
39101
  }
39035
39102
 
39036
39103
  /* A column at its limit: the count in the error colour; "full" is said in words too. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/nextcloud-vue",
3
- "version": "2.56.0-beta.64",
3
+ "version": "2.56.0-beta.65",
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": {
@@ -758,6 +758,7 @@ import { installModalStack, uninstallModalStack } from '../../utils/modalStack.j
758
758
  import { OBJECTS_CHANGED_EVENT } from '../../utils/objectSignals.js'
759
759
  import { resolveBrand } from '../../utils/resolveBrand.js'
760
760
  import { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
761
+ import { pageOffersSavedViews } from '../../utils/savedViewHelpers.js'
761
762
  import { normalizeSavedViewMenu, SAVED_VIEW_MENU_KEY, SAVED_VIEW_MENU_PREFIX, syncSavedViewMenu, toggleSavedViewMenuEntry, withSavedViewMenu } from '../../utils/savedViewMenu.js'
762
763
  import { passesContextPredicates } from '../../utils/visibleIfContext.js'
763
764
 
@@ -2400,7 +2401,7 @@ export default {
2400
2401
  */
2401
2402
  offersSavedViews() {
2402
2403
  const pages = this.manifest && Array.isArray(this.manifest.pages) ? this.manifest.pages : []
2403
- return pages.some((page) => page && page.config && page.config.allowSavedViews === true)
2404
+ return pages.some((page) => page && page.type === 'index' && pageOffersSavedViews(page.config))
2404
2405
  },
2405
2406
 
2406
2407
  /**
@@ -126,20 +126,13 @@
126
126
  </component>
127
127
  <!-- The one Follow control beside the title (manifest `config.follow`):
128
128
  shown when the object carries `@self.watching`. There is no star:
129
- a favourite is a follow with notifications off. -->
129
+ a favourite is a follow with notifications off. The board look
130
+ draws it on a row of its own under the breadcrumb. -->
130
131
  <span
131
- v-if="showFollowToggle"
132
+ v-if="showFollowToggle && !isBoard"
132
133
  class="cn-detail-page__interactions"
133
134
  data-testid="cn-detail-page-interactions">
134
- <CnFollowToggle
135
- :register="register"
136
- :schema="schema"
137
- :objectId="String(objectId)"
138
- :watching="resolvedSelf.watching === true"
139
- :notify="typeof resolvedSelf.watchNotify === 'boolean' ? resolvedSelf.watchNotify : null"
140
- :watcherCount="typeof resolvedSelf.watcherCount === 'number' ? resolvedSelf.watcherCount : null"
141
- :canManage="!!(resolvedSelf.can && resolvedSelf.can.manage === true)"
142
- :notifies="followNotifies" />
135
+ <CnFollowToggle v-bind="followToggleProps" />
143
136
  </span>
144
137
  </div>
145
138
  <!--
@@ -517,6 +510,17 @@
517
510
  :schema="currentSchema"
518
511
  :register="register" />
519
512
  </div>
513
+ <!-- Row 3 of the board look's header: the Follow control under the
514
+ breadcrumb, clear of the title row and its actions (DqZaak
515
+ "Volgen"). -->
516
+ <div
517
+ v-if="isBoard && showFollowToggle"
518
+ class="cn-detail-page__header-row3"
519
+ data-testid="cn-detail-page-header-row3">
520
+ <span class="cn-detail-page__interactions" data-testid="cn-detail-page-interactions">
521
+ <CnFollowToggle v-bind="followToggleProps" />
522
+ </span>
523
+ </div>
520
524
  <!-- A widget inside the header (manifest `config.headerWidget`), on
521
525
  a row of its own under the title and the actions: the stages
522
526
  bars of the board's case card. Drawn without a card of its own,
@@ -4319,6 +4323,26 @@ export default {
4319
4323
  && this.register !== '' && this.schema !== '' && this.objectId !== ''
4320
4324
  },
4321
4325
 
4326
+ /**
4327
+ * The props of the Follow control, wherever the header draws it.
4328
+ *
4329
+ * @return {object} The CnFollowToggle props.
4330
+ * @spec openspec/changes/board-follow-row/specs/board-follow-row/spec.md#requirement-the-board-follow-control-sits-under-the-breadcrumb
4331
+ */
4332
+ followToggleProps() {
4333
+ const self = this.resolvedSelf
4334
+ return {
4335
+ register: this.register,
4336
+ schema: this.schema,
4337
+ objectId: String(this.objectId),
4338
+ watching: self.watching === true,
4339
+ notify: typeof self.watchNotify === 'boolean' ? self.watchNotify : null,
4340
+ watcherCount: typeof self.watcherCount === 'number' ? self.watcherCount : null,
4341
+ canManage: !!(self.can && self.can.manage === true),
4342
+ notifies: this.followNotifies,
4343
+ }
4344
+ },
4345
+
4322
4346
  /**
4323
4347
  * The `_extend[]` values of the object read: the page's own, plus
4324
4348
  * `@self.can` while the Follow toggle can render (OpenRegister returns
@@ -368,7 +368,7 @@ export default {
368
368
  | `showMassImport` | Boolean | `true` | Whether to show the mass Import action |
369
369
  | `showMassCopy` | Boolean | `true` | Whether to show the mass Copy action |
370
370
  | `allowExport` | Boolean | `false` | Opt-in flag for the native Export menu (CSV/Excel) next to the Add button. Renders only when `true` AND the resolved schema is flagged `exportable: true`; navigates to OpenRegister's export leaf (`GET /apps/openregister/api/objects/{register}/{schema}/export`), passing `$route.query` through as filters. Distinct from `showMassExport`, which exports the fetched/selected rows via a blob download. |
371
- | `allowSavedViews` | Boolean | `false` | Opt-in flag for the saved-views control (saved-views-ui): a Views dropdown listing the user's OpenRegister saved-search views (`GET /apps/openregister/api/views`). Applying one writes its stored filters/search/sort into the route query (`_search` and `_order` reserved keys + plain filter keys); "Save current view…" persists the current route-query state via POST and toasts either way, naming the view (a failure also keeps the dialog open with the reason); own views can be deleted after confirmation. Emits `apply-view`. |
371
+ | `allowSavedViews` | Boolean | unset | Saved views (saved-views-ui). Unset, the sidebar's Views tab (save, apply, edit, share, add to menu, copy a default, delete) is on for every page with a sidebar and a source a view can be scoped to: register and schema, `entitySource` or `savedViewsScope`. `false` turns it off. `true` also forces the sidebar and adds the toolbar control: a Views dropdown listing the user's OpenRegister saved-search views (`GET /apps/openregister/api/views`). Applying one writes its stored filters/search/sort into the route query (`_search` and `_order` reserved keys + plain filter keys); "Save current view…" persists the current route-query state via POST and toasts either way, naming the view (a failure also keeps the dialog open with the reason); own views can be deleted after confirmation. Emits `apply-view`. |
372
372
  | `savedViewsScope` | String | `''` | Which pages share this page's saved views. By default a view is shared by every page over the same `register` and `schema` and shown on no other page, since a view is filters over one schema's fields. Set a name to share views across pages over different sources, or to keep two pages over one source apart. Written into the saved view's `query.scope` on save; views saved before scoping existed carry no scope and stay visible everywhere. |
373
373
  | `massActionNameField` | String | `'title'` | Property name used to display item names in dialogs |
374
374
  | `nameFormatter` | Function | `null` | Custom formatter for item names in dialogs; overrides `massActionNameField` |
@@ -1379,7 +1379,7 @@ import { isRowActionVisible, rowActionPayload } from '../../utils/rowActionItem.
1379
1379
  import { isNewTabClick, isNewTabHandled, isRowMiddleClick, markNewTabHandled, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'
1380
1380
  import { compileRowClass } from '../../utils/rowClassRules.js'
1381
1381
  import { DEFAULT_ROW_INDICATOR_CAP } from '../../utils/rowIndicators.js'
1382
- import { buildRouteQueryFromViewState, buildViewCreatePayload, defaultFilterViews, extractViewState, extractViewStateFromRouteQuery, normalizeSharedWith, savedViewScope, schemaSlug, viewMatchesScope } from '../../utils/savedViewHelpers.js'
1382
+ import { buildRouteQueryFromViewState, buildViewCreatePayload, defaultFilterViews, extractViewState, extractViewStateFromRouteQuery, normalizeSharedWith, pageOffersSavedViews, savedViewScope, schemaSlug, viewMatchesScope } from '../../utils/savedViewHelpers.js'
1383
1383
  import { savedViewBadge, savedViewOverrides } from '../../utils/savedViewOverrides.js'
1384
1384
  import { columnsFromSchema, fieldsFromSchema } from '../../utils/schema.js'
1385
1385
  import { resolveScopeLayout, resolveScopePresentation } from '../../utils/scopeListLayout.js'
@@ -1500,11 +1500,22 @@ function declaredFilterFields(props) {
1500
1500
  * @return {object} The `{ fieldKey: values }` map, empty when the link carries none.
1501
1501
  */
1502
1502
  function namedFiltersFromRoute(instance, props) {
1503
+ const route = instance && instance.proxy && instance.proxy.$route
1504
+ return namedFiltersFromQuery((route && route.query) || {}, props)
1505
+ }
1506
+
1507
+ /**
1508
+ * The named-source filters a query carries, as `namedFiltersFromRoute` reads them.
1509
+ *
1510
+ * @param {object} query The route query.
1511
+ * @param {object} props The CnIndexPage props.
1512
+ *
1513
+ * @return {object} The `{ fieldKey: values }` map.
1514
+ */
1515
+ function namedFiltersFromQuery(query, props) {
1503
1516
  if (!props.entitySource) {
1504
1517
  return {}
1505
1518
  }
1506
- const route = instance && instance.proxy && instance.proxy.$route
1507
- const query = (route && route.query) || {}
1508
1519
  const properties = declaredFilterFields(props)
1509
1520
  const out = {}
1510
1521
 
@@ -2947,9 +2958,11 @@ export default {
2947
2958
  },
2948
2959
 
2949
2960
  /**
2950
- * Opt-in flag for the saved-views control (saved-views-ui) rendered
2951
- * in the toolbar. Defaults to `false` — an app must explicitly
2952
- * enable it per page. When `true`, a Views dropdown lists the
2961
+ * Saved views (saved-views-ui). Unset, the sidebar's Views tab is on
2962
+ * for every page with a sidebar and a source a view can be scoped to
2963
+ * (register and schema, `entitySource` or `savedViewsScope`); `false`
2964
+ * turns it off. `true` also forces the sidebar and adds the toolbar
2965
+ * control, where a Views dropdown lists the
2953
2966
  * current user's OpenRegister saved-search views
2954
2967
  * (`GET /apps/openregister/api/views`); applying one writes its
2955
2968
  * stored filters/search/sort into the route query (reusing the
@@ -2961,7 +2974,7 @@ export default {
2961
2974
  */
2962
2975
  allowSavedViews: {
2963
2976
  type: Boolean,
2964
- default: false,
2977
+ default: null,
2965
2978
  },
2966
2979
 
2967
2980
  /**
@@ -5523,7 +5536,7 @@ export default {
5523
5536
  * @spec openspec/changes/saved-views-manage-and-defaults/tasks.md#task-6
5524
5537
  */
5525
5538
  savedViewsForSidebar() {
5526
- if (!this.allowSavedViews) {
5539
+ if (!this.savedViewsOffered) {
5527
5540
  return null
5528
5541
  }
5529
5542
  return {
@@ -6664,7 +6677,16 @@ export default {
6664
6677
  * @return {{ scope: string, register: string, schema: object|string|null }} The page's source.
6665
6678
  */
6666
6679
  savedViewsPage() {
6667
- return { scope: this.savedViewsScope, register: this.register, schema: this.schema }
6680
+ return { scope: this.savedViewsScope, register: this.register, schema: this.schema, source: this.entitySource }
6681
+ },
6682
+
6683
+ /**
6684
+ * Whether this page offers saved views in its sidebar.
6685
+ *
6686
+ * @return {boolean}
6687
+ */
6688
+ savedViewsOffered() {
6689
+ return pageOffersSavedViews(this)
6668
6690
  },
6669
6691
 
6670
6692
  /**
@@ -8237,7 +8259,7 @@ export default {
8237
8259
  created() {
8238
8260
  this.applyViewModeFromRoute()
8239
8261
  this.pushAiContext()
8240
- if (this.allowSavedViews) {
8262
+ if (this.savedViewsOffered) {
8241
8263
  this.fetchSavedViews()
8242
8264
  }
8243
8265
  this.selfActions = createSelfModeActions({
@@ -11345,8 +11367,8 @@ export default {
11345
11367
 
11346
11368
  /**
11347
11369
  * Fetch the current user's saved views (own + public) from
11348
- * OpenRegister's views API. Called from created() when
11349
- * `allowSavedViews` is enabled.
11370
+ * OpenRegister's views API. Called from created() when the page
11371
+ * offers saved views.
11350
11372
  */
11351
11373
  async fetchSavedViews() {
11352
11374
  this.savedViewsLoading = true
@@ -11416,6 +11438,9 @@ export default {
11416
11438
  query.savedView = this.appliedSavedViewId
11417
11439
  }
11418
11440
  const nav = this.$router.replace({ query })
11441
+ if (this.isNamedSource) {
11442
+ this.namedActiveFilters = namedFiltersFromQuery(query, this)
11443
+ }
11419
11444
  // Swallow the duplicate-navigation rejection (Vue Router 3)
11420
11445
  // when the applied view matches the current query.
11421
11446
  if (nav && typeof nav.catch === 'function') {
@@ -216,7 +216,7 @@ With admin-only filter control:
216
216
  | `propertiesGroupLabel` | String | `''` | Override label for the schema properties group. Falls back to `schema.title` |
217
217
  | `defaultTab` | String | `'search-tab'` | Tab id that is active when the sidebar opens. Built-in ids: `'search-tab'`, `'columns-tab'` |
218
218
  | `activeTab` | String | `''` | Switch to a tab while mounted, e.g. `'filters-tab'` |
219
- | `savedViews` | Object | `null` | The page's saved views, shown in a third tab, Filters (CnSavedViewsPanel); every intent comes back as one `saved-view` event `{ action, view }` |
219
+ | `savedViews` | Object | `null` | The page's saved views, shown in a third tab, Views (`filters-tab`, CnSavedViewsPanel); every intent comes back as one `saved-view` event `{ action, view }` |
220
220
  | `userIsAdmin` | Boolean | `true` | When `false`, schema properties with `adminOnly: true` are hidden from filters |
221
221
  | `filterFields` | Object | `null` | Filter declarations for a page with no schema of its own. Wins over `schema`, and feeds the Search tab only |
222
222
 
@@ -287,11 +287,11 @@
287
287
  </div>
288
288
  </NcAppSidebarTab>
289
289
 
290
- <!-- Filters Tab: the page's saved views (save, list, manage, add to menu). -->
290
+ <!-- Views tab (id filters-tab): the page's saved views (save, list, manage, add to menu). -->
291
291
  <NcAppSidebarTab
292
292
  v-if="savedViews"
293
293
  id="filters-tab"
294
- :name="t('nextcloud-vue', 'Filters')"
294
+ :name="t('nextcloud-vue', 'Views')"
295
295
  :order="3">
296
296
  <template #icon>
297
297
  <BookmarkOutline :size="20" />
package/src/css/board.css CHANGED
@@ -55,10 +55,13 @@
55
55
  margin-inline-start: auto;
56
56
  padding: 1px 8px;
57
57
  border-radius: 10px;
58
+ --nldesign-color-on-surface: var(--cn-board-text-soft, var(--color-main-text));
58
59
  background-color: var(--color-main-background);
59
- color: var(--color-main-text);
60
+ color: var(--cn-board-text-soft, var(--color-main-text));
60
61
  font-size: 12px;
61
- font-weight: 400;
62
+ font-weight: 700;
63
+ /* 16px tall on its 1px pad (DqWerkbord's column counts). */
64
+ line-height: 14px;
62
65
  }
63
66
 
64
67
  /* A column at its limit: the count in the error colour; "full" is said in words too. */