@conduction/nextcloud-vue 2.143.0 → 2.144.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 (101) hide show
  1. package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
  2. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +7 -2
  3. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
  5. package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js +1 -1
  6. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -1
  7. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js +1 -1
  8. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -1
  9. package/dist/esm/components/CnEventsCard/CnEventsCard.vue.js +1 -1
  10. package/dist/esm/components/CnEventsCard/CnEventsCard.vue2.js +9 -0
  11. package/dist/esm/components/CnEventsCard/CnEventsCard.vue2.js.map +1 -1
  12. package/dist/esm/components/CnEventsCard/CnEventsCard.vue3.js +24 -13
  13. package/dist/esm/components/CnEventsCard/CnEventsCard.vue3.js.map +1 -1
  14. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  15. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +2 -1
  16. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  17. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  18. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +3 -1
  19. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  20. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  21. package/dist/esm/components/CnNotesCard/CnNotesCard.vue.js +1 -1
  22. package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js +3 -0
  23. package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js.map +1 -1
  24. package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js +19 -14
  25. package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js.map +1 -1
  26. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue.js +1 -1
  27. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue2.js +30 -0
  28. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue2.js.map +1 -1
  29. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue3.js +158 -102
  30. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue3.js.map +1 -1
  31. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +1 -1
  32. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -1
  33. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js +12 -0
  34. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -1
  35. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue.js +1 -1
  36. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +110 -2
  37. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  38. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +146 -80
  39. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  40. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue.js +1 -1
  41. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js +45 -0
  42. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js.map +1 -1
  43. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue3.js +67 -34
  44. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue3.js.map +1 -1
  45. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue.js +1 -1
  46. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue2.js +3 -22
  47. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue2.js.map +1 -1
  48. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue3.js.map +1 -1
  49. package/dist/esm/integrations/builtin/calendar/eventStatus.js +67 -0
  50. package/dist/esm/integrations/builtin/calendar/eventStatus.js.map +1 -0
  51. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue.js +1 -1
  52. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +39 -0
  53. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
  54. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js +81 -32
  55. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
  56. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue.js +1 -1
  57. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +5 -6
  58. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
  59. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
  60. package/dist/esm/integrations/builtin/deck/deckCardParts.js +59 -0
  61. package/dist/esm/integrations/builtin/deck/deckCardParts.js.map +1 -0
  62. package/dist/esm/l10n/en.json.js +6 -0
  63. package/dist/esm/l10n/en.json.js.map +1 -1
  64. package/dist/esm/l10n/nl.json.js +6 -0
  65. package/dist/esm/l10n/nl.json.js.map +1 -1
  66. package/dist/esm/library-style.js +1 -1
  67. package/dist/esm/nextcloud-vue.css +815 -363
  68. package/dist/esm/utils/fetchFilterCounts.js +5 -2
  69. package/dist/esm/utils/fetchFilterCounts.js.map +1 -1
  70. package/dist/esm/utils/nextcloudDirectory.js +19 -6
  71. package/dist/esm/utils/nextcloudDirectory.js.map +1 -1
  72. package/dist/nextcloud-vue.cjs.js +1347 -758
  73. package/dist/nextcloud-vue.cjs.js.map +2 -2
  74. package/dist/nextcloud-vue.css +815 -363
  75. package/l10n/en.json +6 -0
  76. package/l10n/nl.json +6 -0
  77. package/package.json +1 -1
  78. package/src/components/CnActionDialog/CnActionDialog.vue +28 -3
  79. package/src/components/CnConversationThread/CnConversationThread.vue +5 -2
  80. package/src/components/CnEventsCard/CnEventsCard.vue +27 -5
  81. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +15 -1
  82. package/src/components/CnIndexPage/CnIndexPage.vue +3 -1
  83. package/src/components/CnNotesCard/CnNotesCard.vue +8 -0
  84. package/src/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue +114 -46
  85. package/src/components/CnRetentionWidget/CnRetentionWidget.vue +26 -1
  86. package/src/components/CnTasksWidget/CnTasksWidget.vue +170 -13
  87. package/src/css/dialog.css +7 -1
  88. package/src/css/kpi-card.css +3 -0
  89. package/src/css/look-board-detail.css +317 -0
  90. package/src/css/look-board-index.css +14 -0
  91. package/src/css/look-board.css +25 -0
  92. package/src/integrations/builtin/calendar/CnCalendarCard.vue +76 -11
  93. package/src/integrations/builtin/calendar/CnCalendarTab.vue +3 -22
  94. package/src/integrations/builtin/calendar/__tests__/CnCalendarCardBoardRows.spec.js +52 -0
  95. package/src/integrations/builtin/calendar/eventStatus.js +63 -0
  96. package/src/integrations/builtin/deck/CnDeckCard.vue +66 -0
  97. package/src/integrations/builtin/deck/CnDeckTab.vue +5 -6
  98. package/src/integrations/builtin/deck/__tests__/CnDeckCardBoardRows.spec.js +49 -0
  99. package/src/integrations/builtin/deck/deckCardParts.js +56 -0
  100. package/src/utils/fetchFilterCounts.js +5 -2
  101. package/src/utils/nextcloudDirectory.js +19 -6
@@ -47,17 +47,42 @@
47
47
  <table v-else class="cn-tasks-widget__table" data-testid="cn-tasks-widget-table">
48
48
  <thead>
49
49
  <tr>
50
- <th scope="col">
51
- {{ tr('Task') }}
52
- </th>
53
- <th scope="col">
54
- {{ tr('Handler') }}
55
- </th>
56
- <th scope="col">
57
- {{ tr('Task deadline') }}
58
- </th>
59
- <th scope="col">
60
- {{ tr('Status') }}
50
+ <!-- `content.sortable` / `content.filterable`: a head sorts by its
51
+ column and filters on its values (DqZaakTaken). -->
52
+ <th v-for="column in headColumns"
53
+ :key="column.key"
54
+ scope="col"
55
+ :aria-sort="headSort.key === column.key ? (headSort.dir === 'asc' ? 'ascending' : 'descending') : null"
56
+ :data-testid="`cn-tasks-widget-head-${column.key}`">
57
+ <span v-if="content.sortable === true || content.filterable === true" class="cn-tasks-widget__head">
58
+ <button v-if="content.sortable === true"
59
+ type="button"
60
+ class="cn-tasks-widget__head-sort"
61
+ :data-testid="`cn-tasks-widget-sort-${column.key}`"
62
+ @click="sortBy(column.key)">
63
+ {{ column.label }}
64
+ <ChevronUp v-if="headSort.key === column.key && headSort.dir === 'asc'" :size="14" />
65
+ <ChevronDown v-else :size="14" />
66
+ </button>
67
+ <template v-else>{{ column.label }}</template>
68
+ <NcActions v-if="content.filterable === true"
69
+ class="cn-tasks-widget__head-filter"
70
+ :aria-label="tr('Filter by {column}').replace('{column}', column.label)"
71
+ :data-testid="`cn-tasks-widget-filter-${column.key}`">
72
+ <template #icon>
73
+ <FilterOutline :size="14" />
74
+ </template>
75
+ <NcActionCheckbox v-for="option in headOptions(column.key)"
76
+ :key="option.value"
77
+ :modelValue="(headFilters[column.key] || []).includes(option.value)"
78
+ @update:modelValue="toggleHeadFilter(column.key, option.value)">
79
+ {{ option.label }}
80
+ </NcActionCheckbox>
81
+ </NcActions>
82
+ </span>
83
+ <template v-else>
84
+ {{ column.label }}
85
+ </template>
61
86
  </th>
62
87
  <th scope="col">
63
88
  <span class="hidden-visually">{{ tr('Task actions') }}</span>
@@ -375,10 +400,13 @@ import axios from '@nextcloud/axios'
375
400
  import { showError, showSuccess } from '@nextcloud/dialogs'
376
401
  import { translate as t } from '@nextcloud/l10n'
377
402
  import { generateUrl } from '@nextcloud/router'
378
- import { NcActionButton, NcActions, NcAvatar, NcButton, NcLoadingIcon, NcTextField } from '@nextcloud/vue'
403
+ import { NcActionButton, NcActionCheckbox, NcActions, NcAvatar, NcButton, NcLoadingIcon, NcTextField } from '@nextcloud/vue'
379
404
  import { inject, ref } from 'vue'
380
405
  import CheckCircle from 'vue-material-design-icons/CheckCircle.vue'
406
+ import ChevronDown from 'vue-material-design-icons/ChevronDown.vue'
407
+ import ChevronUp from 'vue-material-design-icons/ChevronUp.vue'
381
408
  import CircleOutline from 'vue-material-design-icons/CircleOutline.vue'
409
+ import FilterOutline from 'vue-material-design-icons/FilterOutline.vue'
382
410
  import CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'
383
411
  import CnTaskFormDialog from '../CnTaskFormDialog/CnTaskFormDialog.vue'
384
412
  import CnTaskWorkCard from './CnTaskWorkCard.vue'
@@ -458,12 +486,16 @@ export default {
458
486
 
459
487
  components: {
460
488
  CheckCircle,
489
+ ChevronDown,
490
+ ChevronUp,
461
491
  CircleOutline,
462
492
  CnStatusBadge,
463
493
  CnTaskFormDialog,
464
494
  CnTaskWorkCard,
495
+ FilterOutline,
465
496
  NcActions,
466
497
  NcActionButton,
498
+ NcActionCheckbox,
467
499
  NcAvatar,
468
500
  NcButton,
469
501
  NcLoadingIcon,
@@ -618,6 +650,10 @@ export default {
618
650
  data() {
619
651
  return {
620
652
  pollTimer: null,
653
+ /** The table's head sort, `{ key, dir }`; no key keeps open tasks first. */
654
+ headSort: { key: '', dir: 'asc' },
655
+ /** The table's head filters, `{ column: values[] }`. */
656
+ headFilters: {},
621
657
  /** Uuids of the tasks a checkbox is completing, until the next read. */
622
658
  completing: [],
623
659
  /** Whether the table's create form is open. */
@@ -777,7 +813,26 @@ export default {
777
813
  * @spec openspec/changes/record-task-table/specs/record-task-table/spec.md#requirement-a-records-tasks-show-as-a-table-with-their-summary-and-new-task-in-the-header
778
814
  */
779
815
  tableRows() {
780
- return [...this.rows.filter((task) => task.isTerminal !== true), ...this.rows.filter((task) => task.isTerminal === true)]
816
+ const kept = this.rows.filter((task) => Object.entries(this.headFilters).every(([key, values]) => values.length === 0 || values.includes(this.headValue(task, key))))
817
+ if (this.headSort.key) {
818
+ const dir = this.headSort.dir === 'desc' ? -1 : 1
819
+ return [...kept].sort((a, b) => {
820
+ const x = this.headSortValue(a, this.headSort.key)
821
+ const y = this.headSortValue(b, this.headSort.key)
822
+ return (x < y ? -1 : (x > y ? 1 : 0)) * dir
823
+ })
824
+ }
825
+ return [...kept.filter((task) => task.isTerminal !== true), ...kept.filter((task) => task.isTerminal === true)]
826
+ },
827
+
828
+ /** @return {Array<{key: string, label: string}>} The table's data columns. */
829
+ headColumns() {
830
+ return [
831
+ { key: 'title', label: this.tr('Task') },
832
+ { key: 'handler', label: this.tr('Handler') },
833
+ { key: 'due', label: this.tr('Task deadline') },
834
+ { key: 'state', label: this.tr('Status') },
835
+ ]
781
836
  },
782
837
 
783
838
  /**
@@ -1158,6 +1213,84 @@ export default {
1158
1213
  * @param {object} task The task row.
1159
1214
  * @return {string} The name.
1160
1215
  */
1216
+ /**
1217
+ * A row's value in a column, as a head filter keeps it.
1218
+ *
1219
+ * @param {object} task The task row.
1220
+ * @param {string} key The column.
1221
+ * @return {string}
1222
+ */
1223
+ headValue(task, key) {
1224
+ if (key === 'title') {
1225
+ return this.titleOf(task)
1226
+ }
1227
+ if (key === 'handler') {
1228
+ return String(task.assignee || '')
1229
+ }
1230
+ if (key === 'due') {
1231
+ return this.content.dueFormat === 'date' ? this.dueDate(task) : this.dueLabel(task)
1232
+ }
1233
+ return String(task.state || '')
1234
+ },
1235
+
1236
+ /**
1237
+ * A row's value in a column, as its head sorts it: the deadline by date.
1238
+ *
1239
+ * @param {object} task The task row.
1240
+ * @param {string} key The column.
1241
+ * @return {string|number}
1242
+ */
1243
+ headSortValue(task, key) {
1244
+ if (key === 'due') {
1245
+ const time = Date.parse(task.dueAt || task.dueDate || '')
1246
+ return Number.isNaN(time) ? Number.POSITIVE_INFINITY : time
1247
+ }
1248
+ if (key === 'handler') {
1249
+ return this.handlerName(task).toLowerCase()
1250
+ }
1251
+ if (key === 'state') {
1252
+ return this.stateLabel(task).toLowerCase()
1253
+ }
1254
+ return this.titleOf(task).toLowerCase()
1255
+ },
1256
+
1257
+ /**
1258
+ * The values a column's head filter offers, one per value the rows hold.
1259
+ *
1260
+ * @param {string} key The column.
1261
+ * @return {Array<{value: string, label: string}>}
1262
+ */
1263
+ headOptions(key) {
1264
+ const seen = new Map()
1265
+ for (const task of this.rows) {
1266
+ const value = this.headValue(task, key)
1267
+ if (value !== '' && !seen.has(value)) {
1268
+ seen.set(value, key === 'handler' ? this.handlerName(task) : (key === 'state' ? this.stateLabel(task) : value))
1269
+ }
1270
+ }
1271
+ return [...seen].map(([value, label]) => ({ value, label }))
1272
+ },
1273
+
1274
+ /**
1275
+ * Sort by a column: ascending first, then descending.
1276
+ *
1277
+ * @param {string} key The column.
1278
+ */
1279
+ sortBy(key) {
1280
+ this.headSort = this.headSort.key === key && this.headSort.dir === 'asc' ? { key, dir: 'desc' } : { key, dir: 'asc' }
1281
+ },
1282
+
1283
+ /**
1284
+ * Keep or drop one value of a column's head filter.
1285
+ *
1286
+ * @param {string} key The column.
1287
+ * @param {string} value The value.
1288
+ */
1289
+ toggleHeadFilter(key, value) {
1290
+ const current = this.headFilters[key] || []
1291
+ this.headFilters = { ...this.headFilters, [key]: current.includes(value) ? current.filter((v) => v !== value) : [...current, value] }
1292
+ },
1293
+
1161
1294
  handlerName(task) {
1162
1295
  return userDisplayName(task.assignee) || String(task.assignee || '')
1163
1296
  },
@@ -1594,6 +1727,30 @@ function recordUuid(configured, objectId) {
1594
1727
  font-size: 14px;
1595
1728
  }
1596
1729
 
1730
+ .cn-tasks-widget__head {
1731
+ display: inline-flex;
1732
+ align-items: center;
1733
+ gap: 4px;
1734
+ }
1735
+
1736
+ .cn-tasks-widget__head-sort {
1737
+ display: inline-flex;
1738
+ align-items: center;
1739
+ gap: 4px;
1740
+ min-height: 0;
1741
+ margin: 0;
1742
+ padding: 0;
1743
+ border: 0;
1744
+ background: transparent;
1745
+ color: inherit;
1746
+ font: inherit;
1747
+ cursor: pointer;
1748
+ }
1749
+
1750
+ .cn-tasks-widget__head-filter {
1751
+ --button-size: 22px;
1752
+ }
1753
+
1597
1754
  .cn-tasks-widget__table th {
1598
1755
  padding: 12px 16px;
1599
1756
  background: color-mix(in srgb, var(--color-background-hover) 40%, var(--color-main-background));
@@ -216,9 +216,15 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .modal-mask {
216
216
  /* A choice card with a checkbox (CnAssignDialog's toggles) is as tight as
217
217
  the board draws it (DqOverdragen, 76px for a label and a two-line note):
218
218
  10px 14px padding, the checkbox row at the 24px minimum target instead of
219
- NcCheckboxRadioSwitch's 34px, the note at 1.2 line height. */
219
+ NcCheckboxRadioSwitch's 34px, the note at 1.2 line height; the card's
220
+ corners 8px and the label on an 18px line, as the board draws them. */
220
221
  .cn-dialog-board .cn-assign-dialog__toggle {
221
222
  padding: 10px 14px;
223
+ border-radius: 8px;
224
+ }
225
+
226
+ .cn-dialog-board .cn-assign-dialog__toggle .checkbox-content__text {
227
+ line-height: 18px;
222
228
  }
223
229
 
224
230
  .cn-dialog-board .cn-assign-dialog__toggle .checkbox-radio-switch-checkbox .checkbox-content,
@@ -547,6 +547,9 @@ a.cn-kpi-card--board * {
547
547
  .cn-kpi-card--board.cn-kpi-card--stacked:has(.cn-kpi-card__title--badge-end) {
548
548
  --cn-kpi-stacked-padding: 20px 22px;
549
549
  --cn-kpi-stacked-gap: 8px;
550
+ /* Its label is set at 600, as every board draws this tile (DqDashboard,
551
+ DqMijnTeam, DqPersoonlijkeInstellingen); the plain stacked tile keeps 400. */
552
+ --cn-kpi-stacked-title-weight: 600;
550
553
  }
551
554
 
552
555
  .cn-kpi-card--board.cn-kpi-card--stacked:has(.cn-kpi-card__title--badge-end) .cn-kpi-card__value {
@@ -1235,6 +1235,13 @@
1235
1235
 
1236
1236
  .cn-look-board .cn-retention .cn-retention__head-text .cn-retention__muted {
1237
1237
  font-size: 14px;
1238
+ line-height: 1.2;
1239
+ }
1240
+
1241
+ /* The board stacks the archiving card's parts 10px apart (r26a: the head
1242
+ row 45.9 vs 41 and the facts 55.9 vs 30 under the title). */
1243
+ .cn-look-board .cn-retention.cn-retention {
1244
+ gap: 10px;
1238
1245
  }
1239
1246
 
1240
1247
  .cn-look-board .cn-retention .cn-retention__fact {
@@ -1400,6 +1407,15 @@
1400
1407
  margin: 0;
1401
1408
  }
1402
1409
 
1410
+ /* ---- A sidebar tab standing in a page card --------------------------------- */
1411
+ /* The Files, Notes, Tasks... tabs pad themselves 12px for the sidebar. On a
1412
+ detail page the card around them already pads, and the boards draw their
1413
+ toolbar, table and rows flush with the card title (DqZaakDocumenten, DqZaakEmail).
1414
+ @spec openspec/changes/board-sidebar-tab-flush-in-card/specs/board-sidebar-tab-flush-in-card/spec.md#requirement-a-sidebar-tab-in-a-page-card-adds-no-padding-of-its-own */
1415
+ .cn-look-board .cn-detail-widget-host .cn-sidebar-tab:not(.cn-look-nextcloud *) {
1416
+ padding: 0;
1417
+ }
1418
+
1403
1419
  /* ---- A file's side card (CnFilesBrowser) --------------------------------- */
1404
1420
  /* The board's side card: radius 12, 20px 22px of padding, the title a 15px
1405
1421
  muted heading at 600, the facts 8px 16px apart with the values at 500
@@ -1529,6 +1545,25 @@
1529
1545
  gap: 14px 16px;
1530
1546
  }
1531
1547
 
1548
+ /* In a page card the mail tab is the card's body: no padding of its own, 12px
1549
+ between its parts, the heading at the card title's 17px/700 and the
1550
+ description at 14px (DqZaakEmail "E-mail").
1551
+ @spec openspec/changes/board-mail-tab-in-card/specs/board-mail-tab-in-card/spec.md#requirement-a-mail-tab-in-a-page-card-reads-as-the-card */
1552
+ .cn-look-board .cn-detail-widget-host .cn-mail-tab:not(.cn-look-nextcloud *) {
1553
+ gap: 12px;
1554
+ padding: 0;
1555
+ }
1556
+
1557
+ .cn-look-board .cn-detail-widget-host .cn-mail-tab__title:not(.cn-look-nextcloud *) {
1558
+ font-size: 17px;
1559
+ font-weight: 700;
1560
+ line-height: 1.2;
1561
+ }
1562
+
1563
+ .cn-look-board .cn-detail-widget-host .cn-mail-tab__subtitle:not(.cn-look-nextcloud *) {
1564
+ font-size: 14px;
1565
+ }
1566
+
1532
1567
  /* ---- The attention flag's buttons --------------------------------------- */
1533
1568
  /* The case boards draw "Markeer voor aandacht" and the flag's other labelled
1534
1569
  buttons at the 40px control height (DqZaak and the case tab boards);
@@ -1549,6 +1584,31 @@
1549
1584
  line-height: normal;
1550
1585
  }
1551
1586
 
1587
+ /* In a page card the notes tab's "Add note" stands at the end of the card's
1588
+ title row, and each note thread opens with a hairline (DqZaakEmail
1589
+ "Notities": the button beside the title, rows 12px 0 under a rule). The
1590
+ card (CnWidgetWrapper) is the positioned box; the add form, when open,
1591
+ stays in the body.
1592
+ @spec openspec/changes/board-notes-card-add-in-title-row/specs/board-notes-card-add-in-title-row/spec.md#requirement-a-notes-card-carries-its-add-in-the-title-row */
1593
+ .cn-look-board .cn-detail-widget-host .cn-widget-wrapper .cn-sidebar-tab__composer-toggle:not(.cn-look-nextcloud *) {
1594
+ position: absolute;
1595
+ top: 20px;
1596
+ inset-inline-end: var(--cn-board-card-inline-padding);
1597
+ }
1598
+
1599
+ .cn-look-board .cn-detail-widget-host .cn-sidebar-tab__action:has(> .cn-sidebar-tab__composer-toggle):not(:has(.cn-sidebar-tab__composer)):not(.cn-look-nextcloud *) {
1600
+ margin-bottom: 0;
1601
+ }
1602
+
1603
+ .cn-look-board .cn-detail-widget-host .cn-sidebar-tab__list:not(.cn-look-nextcloud *) {
1604
+ gap: 0;
1605
+ }
1606
+
1607
+ .cn-look-board .cn-detail-widget-host .cn-notes-tab__thread:not(.cn-look-nextcloud *) {
1608
+ padding: 12px 0;
1609
+ border-top: 1px solid var(--cn-board-hairline);
1610
+ }
1611
+
1552
1612
  /* The notes tab's "Add note" button, when the add form waits behind it, is
1553
1613
  the board's 40px control (DqZaakEmail "Notitie toevoegen").
1554
1614
  @spec openspec/changes/notes-tab-composer-button/specs/notes-tab-composer-button/spec.md#requirement-the-notes-tab-can-keep-its-composer-behind-a-button */
@@ -1722,3 +1782,260 @@
1722
1782
  .cn-look-board .cn-checklist-widget__item .checkbox-content__text {
1723
1783
  font-weight: 600;
1724
1784
  }
1785
+
1786
+ /* The parties widget's "Dit zaaktype accepteert" strip as DqZaakBetrokkenen
1787
+ draws it: a sunken strip rounded at 8px, the label 14px bold, the kinds
1788
+ as outlined 13px chips.
1789
+ @spec openspec/changes/board-parties-accepted-strip/specs/parties/spec.md */
1790
+ .cn-look-board .cn-object-parties .cn-object-parties__accepted {
1791
+ gap: 6px 8px;
1792
+ padding: 8px 12px;
1793
+ border-radius: 8px;
1794
+ background-color: var(--cn-board-sunken-ground);
1795
+ }
1796
+
1797
+ .cn-look-board .cn-object-parties .cn-object-parties__accepted-label {
1798
+ font-size: 14px;
1799
+ font-weight: 700;
1800
+ line-height: 1.2;
1801
+ }
1802
+
1803
+ .cn-look-board .cn-object-parties .cn-object-parties__accepted-kind {
1804
+ padding: 2px 8px;
1805
+ border: 1px solid var(--color-border-maxcontrast);
1806
+ border-radius: 12px;
1807
+ font-size: 13px;
1808
+ font-weight: 600;
1809
+ }
1810
+
1811
+ /* A short side list (`density: "stacked"`, DqOrganisatie "Lopende zaken")
1812
+ starts 10px under the title with no hairline and no row padding; a main
1813
+ contact (DqOrganisatie "Hoofdcontact") starts 12px under it, its two lines
1814
+ at their normal height so they centre on the 40px tile.
1815
+ @spec openspec/changes/board-side-lead-gaps/specs/board-side-lead-gaps/spec.md#requirement-a-board-side-card-starts-a-stacked-list-or-main-contact-as-drawn */
1816
+ .cn-look-board .cn-detail-page__side .cn-widget-wrapper__content:has(> .cn-record-list--stacked) {
1817
+ padding-top: 10px;
1818
+ }
1819
+
1820
+ .cn-look-board .cn-detail-page__side .cn-widget-wrapper__content:has(> .cn-object-parties > .cn-object-parties__main-card) {
1821
+ padding-top: 12px;
1822
+ }
1823
+
1824
+ .cn-look-board .cn-detail-page__side .cn-record-list--stacked.cn-record-list--rows:not(.cn-look-nextcloud *) .cn-record-list__item {
1825
+ border-top: 0;
1826
+ }
1827
+
1828
+ .cn-look-board .cn-detail-page__side .cn-record-list--stacked.cn-record-list--rows:not(.cn-look-nextcloud *) .cn-record-list__row {
1829
+ padding: 0;
1830
+ }
1831
+
1832
+ .cn-look-board .cn-detail-page__side .cn-object-parties__main-text {
1833
+ line-height: normal;
1834
+ }
1835
+
1836
+ /* Appointment and note rows (DqTaak Afspraken, Notities): a hairline above
1837
+ every row including the first, 12px 0 padding, a 32px round tile (the day
1838
+ an appointment starts, the author's initials) 12px before the text, the
1839
+ title 15/600 and one 13px muted meta line joined with " · ".
1840
+ @spec openspec/changes/board-task-cards-rows/specs/task-cards/spec.md#requirement-board-appointment-and-note-rows-carry-a-tile-and-a-meta-line */
1841
+ .cn-look-board .cn-events-card__list {
1842
+ gap: 0;
1843
+ }
1844
+
1845
+ .cn-look-board .cn-events-card__event,
1846
+ .cn-look-board .cn-events-card__event:last-child,
1847
+ .cn-look-board .cn-notes-card__note,
1848
+ .cn-look-board .cn-notes-card__note:last-child {
1849
+ padding: 12px 0;
1850
+ border-top: 1px solid var(--cn-board-hairline);
1851
+ border-bottom: none;
1852
+ }
1853
+
1854
+ .cn-look-board .cn-events-card__day,
1855
+ .cn-look-board .cn-notes-card__initials {
1856
+ display: inline-flex;
1857
+ flex: 0 0 32px;
1858
+ align-items: center;
1859
+ justify-content: center;
1860
+ width: 32px;
1861
+ height: 32px;
1862
+ border-radius: 50%;
1863
+ background: var(--color-primary-element-light);
1864
+ color: var(--color-primary-element-light-text);
1865
+ font-size: 12px;
1866
+ font-weight: 700;
1867
+ }
1868
+
1869
+ .cn-look-board .cn-events-card__text {
1870
+ flex-flow: row wrap;
1871
+ row-gap: 2px;
1872
+ }
1873
+
1874
+ .cn-look-board .cn-events-card__summary,
1875
+ .cn-look-board .cn-notes-card__body {
1876
+ font-size: 15px;
1877
+ font-weight: 600;
1878
+ line-height: 1.35;
1879
+ }
1880
+
1881
+ .cn-look-board .cn-events-card__summary {
1882
+ flex-basis: 100%;
1883
+ }
1884
+
1885
+ .cn-look-board .cn-events-card__when,
1886
+ .cn-look-board .cn-events-card__where,
1887
+ .cn-look-board .cn-notes-card__author,
1888
+ .cn-look-board .cn-notes-card__time {
1889
+ color: var(--color-text-maxcontrast);
1890
+ font-size: 13px;
1891
+ font-weight: 400;
1892
+ }
1893
+
1894
+ .cn-look-board .cn-events-card__where::before,
1895
+ .cn-look-board .cn-notes-card__time::before {
1896
+ content: ' · ';
1897
+ white-space: pre;
1898
+ }
1899
+
1900
+ .cn-look-board .cn-notes-card__note {
1901
+ display: flex;
1902
+ flex-direction: column;
1903
+ gap: 2px;
1904
+ padding-inline-start: 44px;
1905
+ }
1906
+
1907
+ .cn-look-board .cn-notes-card__initials {
1908
+ position: absolute;
1909
+ top: 12px;
1910
+ inset-inline-start: 0;
1911
+ }
1912
+
1913
+ .cn-look-board .cn-notes-card__body {
1914
+ order: -1;
1915
+ }
1916
+
1917
+ .cn-look-board .cn-notes-card__note-header {
1918
+ justify-content: flex-start;
1919
+ margin-bottom: 0;
1920
+ }
1921
+
1922
+ .cn-look-board .cn-notes-card__time {
1923
+ margin-inline-start: 0;
1924
+ }
1925
+
1926
+ /* The parties widget's rows as DqZaakBetrokkenen "Rollen" draws them: one
1927
+ ruled row per party, a 32px initials tile, the name over a muted meta line
1928
+ that names the role, the pills at the end. The role headings stay for
1929
+ assistive tech only.
1930
+ @spec openspec/changes/board-parties-rows/specs/parties/spec.md */
1931
+ .cn-look-board .cn-object-parties--board .cn-object-parties__role-label {
1932
+ position: absolute;
1933
+ width: 1px;
1934
+ height: 1px;
1935
+ overflow: hidden;
1936
+ clip-path: inset(50%);
1937
+ white-space: nowrap;
1938
+ }
1939
+
1940
+ .cn-look-board .cn-object-parties--board .cn-object-parties__list {
1941
+ gap: 0;
1942
+ }
1943
+
1944
+ .cn-look-board .cn-object-parties--board .cn-object-parties__party {
1945
+ flex-wrap: nowrap;
1946
+ gap: 12px;
1947
+ padding: 12px 0;
1948
+ border-top: 1px solid var(--cn-board-hairline);
1949
+ border-radius: 0;
1950
+ }
1951
+
1952
+ .cn-look-board .cn-object-parties--board .cn-object-parties__tile {
1953
+ display: inline-flex;
1954
+ flex: none;
1955
+ align-items: center;
1956
+ justify-content: center;
1957
+ width: 32px;
1958
+ height: 32px;
1959
+ border-radius: 16px;
1960
+ background: var(--color-background-hover);
1961
+ color: var(--color-main-text);
1962
+ font-size: 12px;
1963
+ font-weight: 700;
1964
+ }
1965
+
1966
+ .cn-look-board .cn-object-parties--board .cn-object-parties__text {
1967
+ display: flex;
1968
+ flex: 1 1 auto;
1969
+ flex-direction: column;
1970
+ gap: 2px;
1971
+ min-width: 0;
1972
+ }
1973
+
1974
+ .cn-look-board .cn-object-parties--board .cn-object-parties__name {
1975
+ font-size: 15px;
1976
+ font-weight: 600;
1977
+ }
1978
+
1979
+ .cn-look-board .cn-object-parties--board .cn-object-parties__meta {
1980
+ font-size: 13px;
1981
+ color: var(--color-text-maxcontrast);
1982
+ }
1983
+
1984
+ /* The calendar and Deck cards' rows as DqZaakTaken "Afspraken" and "Kaarten"
1985
+ draw them: ruled rows, the title over a muted meta line, the pill at the
1986
+ end, and the link to the app under the rows.
1987
+ @spec openspec/changes/board-deck-calendar-rows/specs/integrations/spec.md */
1988
+ .cn-look-board .cn-detail-page .cn-calendar-card__row,
1989
+ .cn-look-board .cn-detail-page .cn-deck-card__row {
1990
+ display: flex;
1991
+ gap: 16px;
1992
+ align-items: center;
1993
+ justify-content: space-between;
1994
+ padding: 12px 0;
1995
+ border-top: 1px solid var(--cn-board-hairline);
1996
+ }
1997
+
1998
+ .cn-look-board .cn-detail-page .cn-calendar-card__row-main,
1999
+ .cn-look-board .cn-detail-page .cn-deck-card__row-main {
2000
+ display: flex;
2001
+ flex-direction: column;
2002
+ gap: 2px;
2003
+ min-width: 0;
2004
+ }
2005
+
2006
+ .cn-look-board .cn-detail-page .cn-calendar-card__title,
2007
+ .cn-look-board .cn-detail-page .cn-deck-card__row-title {
2008
+ font-size: 15px;
2009
+ font-weight: 600;
2010
+ color: var(--color-main-text);
2011
+ text-decoration: none;
2012
+ }
2013
+
2014
+ .cn-look-board .cn-detail-page .cn-calendar-card__meta,
2015
+ .cn-look-board .cn-detail-page .cn-deck-card__row-meta {
2016
+ font-size: 13px;
2017
+ color: var(--color-text-maxcontrast);
2018
+ }
2019
+
2020
+ .cn-look-board .cn-object-parties--board .cn-object-parties__end {
2021
+ display: flex;
2022
+ flex: none;
2023
+ flex-wrap: wrap;
2024
+ gap: 8px;
2025
+ align-items: center;
2026
+ }
2027
+
2028
+ .cn-look-board .cn-detail-page .cn-deck-card__row-list {
2029
+ margin: 0;
2030
+ padding: 0;
2031
+ list-style: none;
2032
+ }
2033
+
2034
+ .cn-look-board .cn-detail-page .cn-calendar-card__open,
2035
+ .cn-look-board .cn-detail-page .cn-deck-card__open {
2036
+ display: inline-block;
2037
+ margin-top: 12px;
2038
+ font-size: 14px;
2039
+ font-weight: 600;
2040
+ text-decoration: underline;
2041
+ }
@@ -991,6 +991,20 @@
991
991
  text-decoration: none;
992
992
  }
993
993
 
994
+ /* In such a plain log an icon-and-name cell is regular too (DqZaakContact
995
+ Kanaal "Mijn Zuiddrecht"); elsewhere the icon label stays 600.
996
+ @spec openspec/changes/board-log-table-icon-label-regular/specs/board-log-table-icon-label-regular/spec.md#requirement-a-plain-logs-icon-label-cell-is-regular */
997
+ .cn-look-board .cn-table-container--board.cn-table-container--title-text .cn-cell-renderer__icon-label:not(.cn-look-nextcloud *) {
998
+ font-weight: 400;
999
+ }
1000
+
1001
+ /* In such a plain log a wrapping prose cell runs at line height 1.45, as
1002
+ DqZaakContact draws the contact summary (rows 61.6 high, not 57).
1003
+ @spec openspec/changes/board-log-table-prose-line-height/specs/board-log-table-prose-line-height/spec.md#requirement-a-plain-logs-prose-cell-runs-at-line-height-1-45 */
1004
+ .cn-look-board .cn-table-container--board.cn-table-container--title-text .cn-data-table td.cn-cell--wrap:not(.cn-look-nextcloud *) {
1005
+ line-height: 1.45;
1006
+ }
1007
+
994
1008
  /* `rowTitle: "strong"` (DqZaaktypeStatussen "Statussen", "Resultaten"): the
995
1009
  name column of a card's list, the cell's 14px at weight 600 in the main
996
1010
  ink, no underline; the row stays a link.
@@ -329,9 +329,11 @@
329
329
  .cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-banner-widget__kicker {
330
330
  --nldesign-component-paragraph-font-size: 13px;
331
331
  --nldesign-component-paragraph-font-weight: 700;
332
+ --nldesign-component-paragraph-line-height: normal;
332
333
  font-size: 13px;
333
334
  font-weight: 700;
334
335
  letter-spacing: 0;
336
+ line-height: normal;
335
337
  text-transform: none;
336
338
  }
337
339
 
@@ -1513,6 +1515,24 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .v-popper__popper.action-
1513
1515
  align-self: center;
1514
1516
  }
1515
1517
 
1518
+ /* Without a subtitle the title centres on the title-row buttons, as the boards
1519
+ draw the card head (DqVthInstellingen "Inspectiechecklists",
1520
+ DqInstellingenAlgemeen "Werkdagen en feestdagen": title 27.5px down, live 21).
1521
+ @spec openspec/changes/board-settings-title-centred-and-link-flush/specs/settings-board-look/spec.md */
1522
+ .cn-look-board .settings-section:not(.cn-look-nextcloud *):has(> .cn-settings-section__actions):not(:has(> .settings-section__desc)) > .settings-section__name {
1523
+ align-self: center;
1524
+ }
1525
+
1526
+ /* The "Uitleg tonen" toggle is a text link flush with the card's right edge
1527
+ (DqBeheerinstellingen Zaak-e-mail), not a padded tertiary button.
1528
+ @spec openspec/changes/board-settings-title-centred-and-link-flush/specs/settings-board-look/spec.md */
1529
+ .cn-look-board .cn-settings-fields__hints-toggle:not(.cn-look-nextcloud *) .button-vue {
1530
+ --button-padding: 0px;
1531
+ --button-padding-default: 0px;
1532
+ padding-inline: 0;
1533
+ min-width: 0;
1534
+ }
1535
+
1516
1536
  @container (max-width: 400px) {
1517
1537
  .cn-look-board .settings-section:not(.cn-look-nextcloud *) > .cn-settings-section__actions,
1518
1538
  .cn-look-board .settings-section:not(.cn-look-nextcloud *):has(> .settings-section__desc) > .cn-settings-section__actions {
@@ -3157,6 +3177,11 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .v-popper__popper.action-
3157
3177
  line-height: 1.2;
3158
3178
  }
3159
3179
 
3180
+ /* An option's title bold, as the dialog boards draw it (a <strong>). */
3181
+ .cn-look-board .cn-choice-cards .cn-choice-cards__row-title:not(.cn-look-nextcloud *) {
3182
+ font-weight: 700;
3183
+ }
3184
+
3160
3185
  .cn-look-board .cn-choice-cards .cn-choice-cards__row-icon:not(.cn-look-nextcloud *) {
3161
3186
  align-self: flex-start;
3162
3187
  }