@conduction/nextcloud-vue 2.103.0 → 2.104.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 (74) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +23 -0
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +94 -72
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  5. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +18 -1
  6. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  7. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +3 -3
  8. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  9. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +39 -2
  10. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  11. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +14 -0
  12. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  13. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +40 -2
  14. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  15. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  16. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +159 -3
  17. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  18. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +105 -85
  19. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  20. package/dist/esm/components/CnIndexPage/useSelfFetchList.js +7 -2
  21. package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
  22. package/dist/esm/components/CnObjectRow/CnObjectRow.vue.js +1 -1
  23. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +57 -5
  24. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
  25. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +37 -4
  26. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
  27. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue.js +1 -1
  28. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js.map +1 -1
  29. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js.map +1 -1
  30. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
  31. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +18 -2
  32. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  33. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  34. package/dist/esm/composables/indexSources.js +7 -7
  35. package/dist/esm/composables/indexSources.js.map +1 -1
  36. package/dist/esm/l10n/en.json.js +4 -0
  37. package/dist/esm/l10n/en.json.js.map +1 -1
  38. package/dist/esm/l10n/nl.json.js +4 -0
  39. package/dist/esm/l10n/nl.json.js.map +1 -1
  40. package/dist/esm/library-style.js +1 -1
  41. package/dist/esm/nextcloud-vue.css +114 -33
  42. package/dist/esm/utils/nextcloudDirectory.js +18 -1
  43. package/dist/esm/utils/nextcloudDirectory.js.map +1 -1
  44. package/dist/esm/utils/sortOrder.js +61 -0
  45. package/dist/esm/utils/sortOrder.js.map +1 -0
  46. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  47. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  48. package/dist/nextcloud-vue.cjs.js +4172 -3662
  49. package/dist/nextcloud-vue.cjs.js.map +2 -2
  50. package/dist/nextcloud-vue.css +114 -33
  51. package/l10n/en.json +4 -0
  52. package/l10n/nl.json +4 -0
  53. package/package.json +1 -1
  54. package/src/components/CnActionsBar/CnActionsBar.md +1 -0
  55. package/src/components/CnActionsBar/CnActionsBar.vue +39 -2
  56. package/src/components/CnAppRoot/CnAppRoot.md +1 -1
  57. package/src/components/CnAppRoot/CnAppRoot.vue +21 -4
  58. package/src/components/CnDataTable/CnDataTable.md +1 -0
  59. package/src/components/CnDataTable/CnDataTable.vue +49 -0
  60. package/src/components/CnDetailPage/CnDetailPage.md +1 -0
  61. package/src/components/CnDetailPage/CnDetailPage.vue +40 -2
  62. package/src/components/CnIndexPage/CnIndexPage.md +4 -2
  63. package/src/components/CnIndexPage/CnIndexPage.vue +174 -8
  64. package/src/components/CnIndexPage/useSelfFetchList.js +6 -1
  65. package/src/components/CnObjectRow/CnObjectRow.vue +95 -1
  66. package/src/components/CnUserSettingsDialog/CnUserSettingsDialog.vue +7 -2
  67. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +18 -2
  68. package/src/composables/indexSources.js +7 -7
  69. package/src/css/index-page.css +19 -0
  70. package/src/css/look-board-index.css +48 -0
  71. package/src/schemas/app-manifest-v2.schema.json +23 -1
  72. package/src/utils/nextcloudDirectory.js +17 -0
  73. package/src/utils/sortOrder.js +58 -0
  74. package/src/utils/validateManifestV2.compiled.js +3 -3
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/dist/nextcloud-vue.cjs.js.map
3
- size: 42801328 bytes
4
- sha256: b97548f4dd530870f0f1ef59e594b2fba39a8e57342715169911133c1290cd90
3
+ size: 42920486 bytes
4
+ sha256: 4fb48b7d16409356887747768b7b18dc1be8cf1634acc38e4221893a49b610cc
@@ -2645,6 +2645,17 @@
2645
2645
  cursor: not-allowed;
2646
2646
  }
2647
2647
 
2648
+ /* `searchPlacement: "toolbar"`: the search field on row 1 takes the room
2649
+ before Filter and the view switch, which keep the end of the row. */
2650
+ .cn-look-board .cn-actions-bar--board > .cn-actions-bar__row--views > .cn-actions-bar__search--toolbar {
2651
+ flex: 1 1 220px;
2652
+ min-width: 180px;
2653
+ }
2654
+
2655
+ .cn-look-board .cn-actions-bar--board > .cn-actions-bar__row--views > .cn-actions-bar__search--toolbar ~ .cn-actions-bar__view-controls {
2656
+ margin-inline-start: 0;
2657
+ }
2658
+
2648
2659
  /* The search field: 40px, radius 8, not a pill. */
2649
2660
  .cn-look-board .cn-actions-bar--board .cn-actions-bar__search {
2650
2661
  flex: 1 1 260px;
@@ -3381,6 +3392,43 @@
3381
3392
  .cn-look-board:has(.cn-index-page) > .app-sidebar__toggle {
3382
3393
  display: none;
3383
3394
  }
3395
+
3396
+ /* The folder pane as a card (DqAfdelingen): 280px, a hairline border, 12px
3397
+ corners, with each folder's count in a grey pill. */
3398
+ .cn-look-board .cn-index-page__folder-pane {
3399
+ flex: 0 0 280px;
3400
+ align-self: flex-start;
3401
+ margin-inline-end: 20px;
3402
+ padding: 14px;
3403
+ border: 1px solid var(--cn-board-hairline);
3404
+ border-radius: var(--cn-board-card-radius, 12px);
3405
+ background: var(--color-main-background);
3406
+ }
3407
+
3408
+ .cn-look-board .cn-index-page__folder-pane .cn-folder-tree__count {
3409
+ display: inline-flex;
3410
+ align-items: center;
3411
+ justify-content: center;
3412
+ min-width: 20px;
3413
+ height: 20px;
3414
+ padding: 0 6px;
3415
+ box-sizing: border-box;
3416
+ border-radius: 10px;
3417
+ background: var(--color-background-dark);
3418
+ color: var(--color-main-text);
3419
+ font-size: 12px;
3420
+ font-weight: 600;
3421
+ }
3422
+
3423
+ /* The selected folder's heading above the list: a 20px title over a soft line. */
3424
+ .cn-look-board .cn-index-page__scope-heading {
3425
+ margin-bottom: 16px;
3426
+ }
3427
+
3428
+ .cn-look-board .cn-index-page__scope-title {
3429
+ font-size: 20px;
3430
+ font-weight: 700;
3431
+ }
3384
3432
  /* ========================================
3385
3433
  @conduction/nextcloud-vue — Table Styles
3386
3434
  ======================================== */
@@ -6193,6 +6241,25 @@ a.cn-kpi-card--board * {
6193
6241
  font-size: 14px;
6194
6242
  }
6195
6243
 
6244
+ /* The selected folder's heading above the list. */
6245
+ .cn-index-page__scope-heading {
6246
+ display: flex;
6247
+ flex-direction: column;
6248
+ gap: 4px;
6249
+ margin-bottom: 12px;
6250
+ }
6251
+
6252
+ .cn-index-page__scope-title {
6253
+ margin: 0;
6254
+ font-size: 1.25em;
6255
+ font-weight: 700;
6256
+ }
6257
+
6258
+ .cn-index-page__scope-subtitle {
6259
+ font-size: 14px;
6260
+ color: var(--color-text-maxcontrast);
6261
+ }
6262
+
6196
6263
  /* Opt-in folder navigation pane (left of the list). */
6197
6264
  .cn-index-page__folder-pane {
6198
6265
  flex: 0 0 240px;
@@ -26771,16 +26838,16 @@ button.cn-stepper__step[data-v-776581d4] {
26771
26838
  }
26772
26839
 
26773
26840
 
26774
- .cn-widget-object-table[data-v-dfdcad2a] {
26841
+ .cn-widget-object-table[data-v-d628ab02] {
26775
26842
  width: 100%;
26776
26843
  }
26777
- .cn-widget-object-table__footer[data-v-dfdcad2a] {
26844
+ .cn-widget-object-table__footer[data-v-d628ab02] {
26778
26845
  display: flex;
26779
26846
  gap: 4px;
26780
26847
  padding: 4px 8px;
26781
26848
  border-top: 1px solid var(--color-border);
26782
26849
  }
26783
- .cn-widget-object-table__error[data-v-dfdcad2a] {
26850
+ .cn-widget-object-table__error[data-v-d628ab02] {
26784
26851
  color: var(--color-text-error, var(--color-error-text));
26785
26852
  font-size: 0.85em;
26786
26853
  margin: 4px 8px 0;
@@ -38455,7 +38522,7 @@ to {
38455
38522
  color: var(--color-text-maxcontrast);
38456
38523
  }
38457
38524
 
38458
- .cn-object-row[data-v-a4a78a68] {
38525
+ .cn-object-row[data-v-6e2b35c2] {
38459
38526
  display: flex;
38460
38527
  align-items: center;
38461
38528
  gap: 12px;
@@ -38463,18 +38530,26 @@ to {
38463
38530
  border-bottom: 1px solid var(--color-border);
38464
38531
  cursor: pointer;
38465
38532
  }
38466
- .cn-object-row[data-v-a4a78a68]:hover {
38533
+ .cn-object-row[data-v-6e2b35c2]:hover {
38467
38534
  background-color: var(--color-background-hover);
38468
38535
  }
38469
- .cn-object-row--selected[data-v-a4a78a68] {
38536
+ .cn-object-row--selected[data-v-6e2b35c2] {
38470
38537
  /* Inset accent — never border-left (would shift cell content). */
38471
38538
  box-shadow: inset 3px 0 0 0 var(--color-primary-element);
38472
38539
  background-color: var(--color-primary-element-light);
38473
38540
  }
38474
- .cn-object-row__checkbox[data-v-a4a78a68] {
38541
+ .cn-object-row__lead[data-v-6e2b35c2],
38542
+ .cn-object-row__trail[data-v-6e2b35c2] {
38543
+ flex: 0 0 auto;
38544
+ min-width: 0;
38545
+ }
38546
+ .cn-object-row__lead[data-v-6e2b35c2] {
38547
+ font-weight: 600;
38548
+ }
38549
+ .cn-object-row__checkbox[data-v-6e2b35c2] {
38475
38550
  flex-shrink: 0;
38476
38551
  }
38477
- .cn-object-row__icon[data-v-a4a78a68] {
38552
+ .cn-object-row__icon[data-v-6e2b35c2] {
38478
38553
  flex-shrink: 0;
38479
38554
  display: flex;
38480
38555
  align-items: center;
@@ -38483,38 +38558,38 @@ to {
38483
38558
  height: 24px;
38484
38559
  color: var(--color-text-maxcontrast);
38485
38560
  }
38486
- .cn-object-row__image[data-v-a4a78a68] {
38561
+ .cn-object-row__image[data-v-6e2b35c2] {
38487
38562
  width: 24px;
38488
38563
  height: 24px;
38489
38564
  object-fit: contain;
38490
38565
  border-radius: var(--border-radius);
38491
38566
  }
38492
- .cn-object-row__main[data-v-a4a78a68] {
38567
+ .cn-object-row__main[data-v-6e2b35c2] {
38493
38568
  display: flex;
38494
38569
  flex-direction: column;
38495
38570
  min-width: 0;
38496
38571
  flex: 1;
38497
38572
  }
38498
- .cn-object-row__title[data-v-a4a78a68] {
38573
+ .cn-object-row__title[data-v-6e2b35c2] {
38499
38574
  font-weight: 600;
38500
38575
  overflow: hidden;
38501
38576
  text-overflow: ellipsis;
38502
38577
  white-space: nowrap;
38503
38578
  }
38504
- .cn-object-row__subtitle[data-v-a4a78a68] {
38579
+ .cn-object-row__subtitle[data-v-6e2b35c2] {
38505
38580
  font-size: 13px;
38506
38581
  color: var(--color-text-maxcontrast);
38507
38582
  overflow: hidden;
38508
38583
  text-overflow: ellipsis;
38509
38584
  white-space: nowrap;
38510
38585
  }
38511
- .cn-object-row__badges[data-v-a4a78a68] {
38586
+ .cn-object-row__badges[data-v-6e2b35c2] {
38512
38587
  flex-shrink: 0;
38513
38588
  display: flex;
38514
38589
  align-items: center;
38515
38590
  gap: 6px;
38516
38591
  }
38517
- .cn-object-row__actions[data-v-a4a78a68] {
38592
+ .cn-object-row__actions[data-v-6e2b35c2] {
38518
38593
  flex-shrink: 0;
38519
38594
  display: flex;
38520
38595
  align-items: center;
@@ -46285,17 +46360,17 @@ a.cn-integration-card__title[data-v-093a874d]:hover {
46285
46360
  }
46286
46361
 
46287
46362
 
46288
- .cn-user-settings-dialog__body[data-v-e537e50a] {
46363
+ .cn-user-settings-dialog__body[data-v-6d3e749e] {
46289
46364
  display: flex;
46290
46365
  flex-wrap: wrap;
46291
46366
  gap: 0 24px;
46292
46367
  }
46293
- .cn-user-settings-dialog__nav[data-v-e537e50a] {
46368
+ .cn-user-settings-dialog__nav[data-v-6d3e749e] {
46294
46369
  flex: 0 0 200px;
46295
46370
  width: 200px;
46296
46371
  padding-top: 18px;
46297
46372
  }
46298
- .cn-user-settings-dialog__nav ul[data-v-e537e50a] {
46373
+ .cn-user-settings-dialog__nav ul[data-v-6d3e749e] {
46299
46374
  display: flex;
46300
46375
  flex-direction: column;
46301
46376
  gap: 2px;
@@ -46303,7 +46378,12 @@ a.cn-integration-card__title[data-v-093a874d]:hover {
46303
46378
  padding: 0;
46304
46379
  list-style: none;
46305
46380
  }
46306
- .cn-user-settings-dialog__nav-link[data-v-e537e50a] {
46381
+
46382
+ /* A navigation entry, not a link in running text: no underline, also not
46383
+ on hover. `!important` because a theme's `a { text-decoration }` carries
46384
+ it (thematiq), and a bare `a` with `!important` beats any class. */
46385
+ .cn-user-settings-dialog__nav-link[data-v-6d3e749e],
46386
+ .cn-user-settings-dialog__nav-link[data-v-6d3e749e]:visited {
46307
46387
  display: flex;
46308
46388
  align-items: center;
46309
46389
  gap: 10px;
@@ -46312,22 +46392,23 @@ a.cn-integration-card__title[data-v-093a874d]:hover {
46312
46392
  border-radius: var(--border-radius-element, 8px);
46313
46393
  font-size: 15px;
46314
46394
  color: var(--color-main-text);
46315
- text-decoration: none;
46395
+ text-decoration: none !important;
46316
46396
  }
46317
- .cn-user-settings-dialog__nav-link[data-v-e537e50a]:hover {
46397
+ .cn-user-settings-dialog__nav-link[data-v-6d3e749e]:hover {
46318
46398
  background: var(--color-background-hover);
46399
+ text-decoration: none !important;
46319
46400
  }
46320
- .cn-user-settings-dialog__nav-link[data-v-e537e50a]:focus-visible {
46401
+ .cn-user-settings-dialog__nav-link[data-v-6d3e749e]:focus-visible {
46321
46402
  outline: 2px solid var(--color-primary-element);
46322
46403
  outline-offset: 2px;
46323
46404
  }
46324
- .cn-user-settings-dialog__nav-link--active[data-v-e537e50a],
46325
- .cn-user-settings-dialog__nav-link--active[data-v-e537e50a]:hover {
46405
+ .cn-user-settings-dialog__nav-link--active[data-v-6d3e749e],
46406
+ .cn-user-settings-dialog__nav-link--active[data-v-6d3e749e]:hover {
46326
46407
  background: var(--color-primary-element-light);
46327
46408
  color: var(--color-primary-element-light-text);
46328
46409
  font-weight: 600;
46329
46410
  }
46330
- .cn-user-settings-dialog__nav-icon[data-v-e537e50a] {
46411
+ .cn-user-settings-dialog__nav-icon[data-v-6d3e749e] {
46331
46412
  display: inline-flex;
46332
46413
  flex: none;
46333
46414
  width: 18px;
@@ -46335,18 +46416,18 @@ a.cn-integration-card__title[data-v-093a874d]:hover {
46335
46416
  align-items: center;
46336
46417
  justify-content: center;
46337
46418
  }
46338
- .cn-user-settings-dialog__sections[data-v-e537e50a] {
46419
+ .cn-user-settings-dialog__sections[data-v-6d3e749e] {
46339
46420
  display: flex;
46340
46421
  flex: 1 1 380px;
46341
46422
  flex-direction: column;
46342
46423
  min-width: 0;
46343
46424
  }
46344
- .cn-user-settings-dialog__sections[data-v-e537e50a] .app-settings-section {
46425
+ .cn-user-settings-dialog__sections[data-v-6d3e749e] .app-settings-section {
46345
46426
  margin: 0;
46346
46427
  padding: 18px 0;
46347
46428
  border-top: 1px solid var(--color-border);
46348
46429
  }
46349
- .cn-user-settings-dialog__sections[data-v-e537e50a] .app-settings-section__name {
46430
+ .cn-user-settings-dialog__sections[data-v-6d3e749e] .app-settings-section__name {
46350
46431
  margin: 0 0 12px;
46351
46432
  font-size: 17px;
46352
46433
  font-weight: 700;
@@ -46354,33 +46435,33 @@ a.cn-integration-card__title[data-v-093a874d]:hover {
46354
46435
  align-items: center;
46355
46436
  gap: 10px;
46356
46437
  }
46357
- .cn-user-settings-dialog__note[data-v-e537e50a] {
46438
+ .cn-user-settings-dialog__note[data-v-6d3e749e] {
46358
46439
  flex: 1 1 auto;
46359
46440
  margin: 0;
46360
46441
  font-size: 14px;
46361
46442
  color: var(--color-text-maxcontrast);
46362
46443
  }
46363
- .cn-user-settings-dialog__nc-links[data-v-e537e50a] {
46444
+ .cn-user-settings-dialog__nc-links[data-v-6d3e749e] {
46364
46445
  margin-bottom: 2px;
46365
46446
  }
46366
- .cn-user-settings-dialog__group[data-v-e537e50a] {
46447
+ .cn-user-settings-dialog__group[data-v-6d3e749e] {
46367
46448
  margin: 10px 0 2px;
46368
46449
  padding: 0 12px;
46369
46450
  font-size: 15px;
46370
46451
  font-weight: 600;
46371
46452
  color: var(--color-main-text);
46372
46453
  }
46373
- .cn-user-settings-dialog__group-sections[data-v-e537e50a] {
46454
+ .cn-user-settings-dialog__group-sections[data-v-6d3e749e] {
46374
46455
  padding-inline-start: 12px;
46375
46456
  }
46376
- .cn-user-settings-dialog__heading-icon[data-v-e537e50a] {
46457
+ .cn-user-settings-dialog__heading-icon[data-v-6d3e749e] {
46377
46458
  display: inline-flex;
46378
46459
  order: -1;
46379
46460
  color: var(--color-text-maxcontrast);
46380
46461
  }
46381
46462
 
46382
46463
  /* One section at a time: the others are mounted but hidden. */
46383
- .cn-user-settings-dialog__sections[data-v-e537e50a] .app-settings-section[hidden] {
46464
+ .cn-user-settings-dialog__sections[data-v-6d3e749e] .app-settings-section[hidden] {
46384
46465
  display: none;
46385
46466
  }
46386
46467
 
package/l10n/en.json CHANGED
@@ -1727,6 +1727,10 @@
1727
1727
  "On": "On"
1728
1728
  },
1729
1729
  "plurals": {
1730
+ "{count} member": [
1731
+ "{count} member",
1732
+ "{count} members"
1733
+ ],
1730
1734
  "{count} selected": [
1731
1735
  "{count} selected",
1732
1736
  "{count} selected"
package/l10n/nl.json CHANGED
@@ -1940,6 +1940,10 @@
1940
1940
  "On": "Aan"
1941
1941
  },
1942
1942
  "plurals": {
1943
+ "{count} member": [
1944
+ "{count} lid",
1945
+ "{count} leden"
1946
+ ],
1943
1947
  "{count} selected": [
1944
1948
  "{count} geselecteerd",
1945
1949
  "{count} geselecteerd"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/nextcloud-vue",
3
- "version": "2.103.0",
3
+ "version": "2.104.0",
4
4
  "description": "Shared Vue component library for Conduction Nextcloud apps — complements @nextcloud/vue with higher-level components, OpenRegister integration, and NL Design System support",
5
5
  "homepage": "https://github.com/ConductionNL/nextcloud-vue#readme",
6
6
  "bugs": {
@@ -153,6 +153,7 @@ Controlling the inline action button count — `inlineActionCount` sets how many
153
153
  | `mapLabel` | `String` | `''` | Label for the map view-toggle option (defaults to "Map"). Only shown when `showMap` |
154
154
  | `mapIcon` | `String` | `''` | MDI icon name for the map option (defaults to the built-in map-marker icon). Resolved via `CnIcon` |
155
155
  | `showSearch` | `Boolean` | `false` | Whether to show the inline search field on the left of the bar. The field takes the place of the "Showing X of Y" counter unless `showCountWithSearch` is set |
156
+ | `searchPlacement` | `String` | `''` | Board look only: `'toolbar'` draws the search field on row 1, before Filter and the view switch, instead of at the start of row 2 |
156
157
  | `searchValue` | `String` | `''` | Current value of the inline search field (controlled) |
157
158
  | `searchPlaceholder` | `String` | `''` | Placeholder / accessible label for the inline search field |
158
159
  | `showCountWithSearch` | `Boolean` | `false` | Keep the "Showing X of Y" counter visible beside the inline search field, after the search and any `#after-search` controls. No effect without `showSearch` |
@@ -18,6 +18,20 @@
18
18
  @description Under the board look: saved views as chips and the Save view button, in row 1 after the quick-filter chips.
19
19
  -->
20
20
  <slot name="actions-end" />
21
+ <!-- `searchPlacement: "toolbar"`: the search field on row 1, before
22
+ Filter and the view switch, instead of opening row 2. -->
23
+ <div v-if="showSearch && searchInToolbar"
24
+ class="cn-actions-bar__search cn-actions-bar__search--toolbar"
25
+ data-testid="cn-actions-bar-search-toolbar">
26
+ <Magnify :size="18" class="cn-actions-bar__search-icon" />
27
+ <input
28
+ type="search"
29
+ class="cn-actions-bar__search-input"
30
+ :placeholder="searchPlaceholder || t('nextcloud-vue', 'Search…')"
31
+ :value="searchValue"
32
+ :aria-label="searchPlaceholder || t('nextcloud-vue', 'Search')"
33
+ @input="onSearchInput">
34
+ </div>
21
35
  <!--
22
36
  Filter and the view switch are one unit: when row 1 is full they
23
37
  wrap together to the start of the next line (the DqZaken board),
@@ -373,7 +387,7 @@
373
387
 
374
388
  <!-- Board look, row 2: the search field, then the active filters. -->
375
389
  <div v-if="isBoardLayout" class="cn-actions-bar__row cn-actions-bar__row--search" data-testid="cn-actions-bar-row-search">
376
- <div v-if="showSearch" class="cn-actions-bar__search">
390
+ <div v-if="showSearch && !searchInToolbar" class="cn-actions-bar__search">
377
391
  <Magnify :size="18" class="cn-actions-bar__search-icon" />
378
392
  <input
379
393
  type="search"
@@ -383,7 +397,7 @@
383
397
  :aria-label="searchPlaceholder || t('nextcloud-vue', 'Search')"
384
398
  @input="onSearchInput">
385
399
  </div>
386
- <span v-else-if="showCount && hasTotal" class="cn-actions-bar__count">
400
+ <span v-else-if="showCount && hasTotal && !searchInToolbar" class="cn-actions-bar__count">
387
401
  {{ countText }}
388
402
  </span>
389
403
  <slot name="after-search" />
@@ -943,6 +957,19 @@ export default {
943
957
  default: false,
944
958
  },
945
959
 
960
+ /**
961
+ * Where the board look draws the search field: `''` (the default) at
962
+ * the start of row 2, `'toolbar'` on row 1 before Filter and the view
963
+ * switch (DqTeamwachtrij). No effect outside the board look.
964
+ *
965
+ * @type {''|'toolbar'}
966
+ */
967
+ searchPlacement: {
968
+ type: String,
969
+ default: '',
970
+ validator: (v) => ['', 'toolbar'].includes(v),
971
+ },
972
+
946
973
  /** Current value of the inline search field (controlled) */
947
974
  searchValue: {
948
975
  type: String,
@@ -1101,6 +1128,16 @@ export default {
1101
1128
  ],
1102
1129
 
1103
1130
  computed: {
1131
+ /**
1132
+ * Whether the search field sits on the board's row 1.
1133
+ *
1134
+ * @spec openspec/changes/index-search-on-toolbar/specs/index-page/spec.md#requirement-the-search-field-can-sit-on-the-toolbar-row
1135
+ * @return {boolean}
1136
+ */
1137
+ searchInToolbar() {
1138
+ return this.isBoardLayout && this.searchPlacement === 'toolbar'
1139
+ },
1140
+
1104
1141
  /**
1105
1142
  * Whether the board toolbar is drawn: the `layout` prop, else the look
1106
1143
  * the app provides.
@@ -67,7 +67,7 @@ With loading state, custom components, translate, permissions, and custom page t
67
67
  |------|------|----------|---------|-------------|
68
68
  | `manifest` | Object | Yes | — | Reactive manifest object from `useAppManifest`. Provides `manifest.dependencies`, `manifest.menu`, and is propagated via provide/inject |
69
69
  | `appId` | String | Yes | — | Nextcloud app id. Forwarded to `NcContent` and `CnDependencyMissing` |
70
- | `appDisplayName` | String | No | `''` | Human-readable name shown in the Nextcloud top bar. When set it overrides the technical `appId` so a virtual app shows its own name (e.g. "Pet Store") instead of the host app id. |
70
+ | `appDisplayName` | String | No | `''` | Human-readable name shown in the Nextcloud top bar. When set it overrides the technical `appId` so a virtual app shows its own name (e.g. "Pet Store") instead of the host app id. The user settings dialog, the setup wizard and the credentials section show it through the app's `translate`; without it they show the name Nextcloud's app menu gives (localised), else the id with a capital, never the bare id. The top bar's own name, which also keys the stored list-pane width (`pane-list-size-<name>`), stays untranslated so a reader's pane width survives a language switch. |
71
71
  | `persistManifestDelta` | Function | No | `null` | In-app editing (ADR-041) persistence hook, called with the minimal manifest delta on save. When Buildiq is reachable for the user, CnAppRoot provides a shared `cnManifestEditor` + `cnOpenBuildAvailable` (the inject key deliberately keeps its old spelling — see the rename note in CHANGELOG); the page action rows (`CnDashboardPage`/`CnDetailPage`/`CnIndexPage`) then render an icon-only `CnBuildiqEditButton` inline with their normal buttons, and the body grid becomes drag/resizable in edit mode. Omit to keep edits in-memory only. |
72
72
  | `dataSources` | Object \| null | No | `null` | App registers/schemas for the in-app pages editor (ADR-041). Lets the Edit-pages modal offer Register / Schema / Columns dropdowns for `index`/`detail` pages instead of free-text slug inputs, so a created page actually renders a table. Shape: `{ registers: [{ value, label, schemas: [{ value, label, columns: string[] }] }] }`. Provided to descendants as `cnDataSources`; when omitted the editor falls back to free-text register/schema fields. **A snapshot** — captured once, so it cannot show anything created after boot. Prefer `dataSourcesLoader`. |
73
73
  | `dataSourcesLoader` | Function \| null | No | `null` | Async loader for the same data, re-invoked every time a pages-editor modal opens: `async () => ({ registers: [...] })`. Because `provide()` runs once, the static `dataSources` snapshot can never change — so a register or schema created after the app booted (in another tab, the OpenRegister UI, or via the API) would never appear in the dropdowns. A loader fixes that with no page reload, and moves the fetch off the app-boot path onto the far rarer editor-open path. When both props are given, `dataSources` seeds the initial list and the loader's result replaces it on the first refresh. Provided to descendants as the `cnDataSourcesState` holder plus a `cnRefreshDataSources()` action. |
@@ -180,7 +180,7 @@
180
180
  <div class="cn-app-root__setup">
181
181
  <CnSetupWizard
182
182
  :appId="appId"
183
- :appName="appDisplayName || (manifest && manifest.name) || appId"
183
+ :appName="shownAppName"
184
184
  :steps="manifest.setup.steps"
185
185
  :cancellable="false"
186
186
  :completedStepIds="setupCompletedStepIds"
@@ -441,7 +441,7 @@
441
441
  <div v-if="setupWizardOpen" class="cn-app-root__setup-optional">
442
442
  <CnSetupWizard
443
443
  :appId="appId"
444
- :appName="appDisplayName || (manifest && manifest.name) || appId"
444
+ :appName="shownAppName"
445
445
  :steps="manifest.setup.steps"
446
446
  :cancellable="true"
447
447
  :completedStepIds="setupCompletedStepIds"
@@ -580,7 +580,7 @@
580
580
  <CnCredentials
581
581
  scope="personal"
582
582
  :appId="appId"
583
- :appName="appDisplayName || (manifest && manifest.name) || appId"
583
+ :appName="shownAppName"
584
584
  :appCredentials="(manifest && manifest.credentials) || []" />
585
585
  </NcAppSettingsSection>
586
586
  <!--
@@ -747,6 +747,7 @@ import {
747
747
  import { RegistryKindError } from '../../errors/RegistryKindError.js'
748
748
  import { useObjectStore } from '../../store/index.js'
749
749
  import { isAppInstalled } from '../../utils/appInstalled.js'
750
+ import { appShownName } from '../../utils/appShownName.js'
750
751
  import { BUILT_IN_FORMATTERS } from '../../utils/builtInFormatters.js'
751
752
  import { addDiagnosticsListener, clearReportedDiagnostics, reportDiagnostic } from '../../utils/diagnostics.js'
752
753
  import { DEFAULT_FORGE, resolveForge } from '../../utils/forge.js'
@@ -3134,6 +3135,22 @@ export default {
3134
3135
  return this.userSettingsTitle || this.libraryCopy('User settings')
3135
3136
  },
3136
3137
 
3138
+ /**
3139
+ * The app's name as the reader reads it: `appDisplayName` or the
3140
+ * manifest's `name` through the app's translate, else the name
3141
+ * Nextcloud's app menu shows (localised), else the id with a capital.
3142
+ * Used for the user settings dialog, the setup wizard and the
3143
+ * credentials section; never for NcContent's `appName`, which also
3144
+ * keys the stored list-pane width (`pane-list-size-<appName>`).
3145
+ *
3146
+ * @spec openspec/changes/app-shown-name-in-chrome/specs/app-root/spec.md#requirement-the-app-chrome-shows-the-apps-localised-name
3147
+ * @return {string}
3148
+ */
3149
+ shownAppName() {
3150
+ const given = this.appDisplayName || (this.manifest && this.manifest.name) || ''
3151
+ return appShownName(this.appId, given ? this.translate(given) : '')
3152
+ },
3153
+
3137
3154
  /**
3138
3155
  * The props of the user-settings dialog: `NcAppSettingsDialog`'s, or
3139
3156
  * under the board look CnUserSettingsDialog's (eyebrow, subtitle and
@@ -3149,7 +3166,7 @@ export default {
3149
3166
  const props = {
3150
3167
  open: this.userSettingsOpen,
3151
3168
  title: this.resolvedUserSettingsTitle,
3152
- appName: this.appDisplayName || (this.manifest && this.manifest.name) || this.appId || '',
3169
+ appName: this.shownAppName,
3153
3170
  }
3154
3171
  if (this.userSettingsEyebrow) {
3155
3172
  props.eyebrow = this.translate(this.userSettingsEyebrow)
@@ -229,6 +229,7 @@ features are folded in here as opt-in props (bare-table usage is unchanged):
229
229
  - `borderless` — drop the container's card chrome so the table sits flush inside
230
230
  a parent card (e.g. a `CnWidgetWrapper` dashboard slot).
231
231
  - `limit` — show only the first N rows; with `viewAllRoute` a "View all" footer appears.
232
+ - `pageSize` — rows per page: one page of the rows the table holds, with the board's pager under them ("4 of 7", the page buttons, Next) once there is more than one page (DqCatalogus). 0 (the default) shows every row, or the first `limit`.
232
233
  - `viewAllRoute` / `viewAllLabel` — the footer control's route and label. It renders
233
234
  as a real `<a :href>` when the router resolves the route, and as a
234
235
  `<button type="button">` (emitting `view-all`) otherwise, so it always has a
@@ -389,6 +389,18 @@
389
389
  </button>
390
390
  </slot>
391
391
  </div>
392
+ <!-- `pageSize`: the board's pager under the rows, "4 of 7" and the
393
+ page buttons (DqCatalogus). -->
394
+ <CnPagination
395
+ v-if="pageSize > 0 && totalRowCount > pageSize"
396
+ variant="board"
397
+ class="cn-data-table__pager"
398
+ :currentPage="currentPage"
399
+ :totalPages="pageCount"
400
+ :totalItems="totalRowCount"
401
+ :currentPageSize="pageSize"
402
+ data-testid="cn-data-table-pager"
403
+ @pageChanged="currentPage = $event" />
392
404
  </div>
393
405
  </template>
394
406
 
@@ -400,6 +412,7 @@ import { NcCheckboxRadioSwitch, NcLoadingIcon } from '@nextcloud/vue'
400
412
  import ChevronDown from 'vue-material-design-icons/ChevronDown.vue'
401
413
  import ChevronUp from 'vue-material-design-icons/ChevronUp.vue'
402
414
  import FilterOutline from 'vue-material-design-icons/FilterOutline.vue'
415
+ import CnPagination from '../CnPagination/CnPagination.vue'
403
416
  import CnColumnFilterPopover from './CnColumnFilterPopover.vue'
404
417
  import { useClickDragGuard } from '../../composables/useClickDragGuard.js'
405
418
  import { normalizeLook } from '../../composables/useLook.js'
@@ -491,6 +504,7 @@ export default {
491
504
  CnColumnFilterPopover,
492
505
  CnIcon,
493
506
  CnLockIndicator,
507
+ CnPagination,
494
508
  CnUnreadMarker,
495
509
  ChevronDown,
496
510
  ChevronUp,
@@ -858,6 +872,19 @@ export default {
858
872
  default: 0,
859
873
  },
860
874
 
875
+ /**
876
+ * Rows per page. Above 0 the table shows one page of its rows and the
877
+ * board's pager under them ("4 of 7", page buttons, Next) once there is
878
+ * more than one page. Pages the rows the table holds; 0 (the default)
879
+ * shows them all, or the first `limit`.
880
+ *
881
+ * @spec openspec/changes/object-table-pager/specs/object-table/spec.md#requirement-an-object-table-can-page-its-rows
882
+ */
883
+ pageSize: {
884
+ type: Number,
885
+ default: 0,
886
+ },
887
+
861
888
  /**
862
889
  * vue-router route object for the "View all" footer link. The footer only
863
890
  * shows when set AND the rows are a `limit`-ed subset. Folded in from CnTableWidget.
@@ -1010,6 +1037,8 @@ export default {
1010
1037
  aggregateRequestId: 0,
1011
1038
  /** Rows fetched in self-fetch mode (register + schemaId). */
1012
1039
  fetchedRows: [],
1040
+ /** The page shown under `pageSize`. */
1041
+ currentPage: 1,
1013
1042
  /** Personal-lens reports (`@self.lenses`) of the latest self-fetch response. */
1014
1043
  fetchedLenses: {},
1015
1044
  /** True while a self-fetch request is in flight. */
@@ -1125,9 +1154,23 @@ export default {
1125
1154
  * @return {Array<object>}
1126
1155
  */
1127
1156
  effectiveRows() {
1157
+ if (this.pageSize > 0) {
1158
+ const page = Math.min(this.currentPage, this.pageCount)
1159
+ return this.sourceRows.slice((page - 1) * this.pageSize, page * this.pageSize)
1160
+ }
1128
1161
  return this.limit > 0 ? this.sourceRows.slice(0, this.limit) : this.sourceRows
1129
1162
  },
1130
1163
 
1164
+ /**
1165
+ * The number of pages under `pageSize` (at least 1).
1166
+ *
1167
+ * @spec openspec/changes/object-table-pager/specs/object-table/spec.md#requirement-an-object-table-can-page-its-rows
1168
+ * @return {number}
1169
+ */
1170
+ pageCount() {
1171
+ return this.pageSize > 0 ? Math.max(1, Math.ceil(this.sourceRows.length / this.pageSize)) : 1
1172
+ },
1173
+
1131
1174
  /**
1132
1175
  * Total row count before the `limit` cap (drives the count badge + the
1133
1176
  * "View all" footer condition).
@@ -1339,6 +1382,12 @@ export default {
1339
1382
  },
1340
1383
  },
1341
1384
 
1385
+ pageCount(count) {
1386
+ if (this.currentPage > count) {
1387
+ this.currentPage = count
1388
+ }
1389
+ },
1390
+
1342
1391
  effectiveColumns: {
1343
1392
  handler() {
1344
1393
  this.loadAggregates()
@@ -426,6 +426,7 @@ Four levels of action and the case surfaces around them. All opt in. See [the re
426
426
  | `statusPill` | Object \| null | `null` | A second pill, same shape. |
427
427
  | `headerFields` | Array | `[]` | Fields shown as chips under the title (manifest `config.headerFields`). Each entry is a property key, or `{ key, format, labelField, colorField, warnWhenPast }`; `format` is `text` (default), `mono`, `badge`, `user` or `date`. A `$ref` value shows `labelField` of the referenced object and falls back to the raw id in mono; a badge maps `colorField` (`success`, `warning`, `error`, `info`, `neutral`) to a variant; `warnWhenPast` gives a past date the error variant. An empty value shows no chip, a page without the key shows no row. Each chip reads "Title: value" to a screen reader, and the row prints as plain text. |
428
428
  | `sideColumn` | Array | `[]` | Widget definitions or widget ids, rendered as a column of cards beside the body. |
429
+ | `sideColumnFrom` | String | `''` | A field path, read like a `visibleWhen` field (e.g. `@context.caseType.sideCards`), whose list of ids picks the `sideColumn` entries this record shows and their order. Any other value keeps `sideColumn` as declared. |
429
430
  | `isAdmin` | Boolean \| null | `null` | Shows `adminOnly` header actions. `null` reads it from Nextcloud. |
430
431
 
431
432
  ## The record as a place