@conduction/nextcloud-vue 2.144.0 → 2.146.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.
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +1 -0
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnAiCompanion/CnAiChatPanel.vue2.js.map +1 -1
- package/dist/esm/components/CnAiCompanion/CnAiChatPanel.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +55 -2
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue.js +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js +16 -0
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js +3 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue.js +1 -1
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +12 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +42 -30
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +22 -0
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +2 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js +3 -3
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +46 -8
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +3 -3
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +2 -2
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +6 -3
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnMailTab/CnMailTab.vue.js +1 -1
- package/dist/esm/components/CnMailTab/CnMailTab.vue2.js.map +1 -1
- package/dist/esm/components/CnMailTab/CnMailTab.vue3.js +2 -2
- package/dist/esm/components/CnMailTab/CnMailTab.vue3.js.map +1 -1
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue.js +1 -1
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js.map +1 -1
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js +1 -0
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js.map +1 -1
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue.js +1 -1
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue2.js +21 -2
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue2.js.map +1 -1
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +73 -12
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +165 -126
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +1 -1
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +1 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js +14 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +2 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -8
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue.js +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +10 -2
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
- package/dist/esm/composables/indexSources.js +35 -9
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +2 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +1204 -467
- package/dist/esm/utils/builtInFormatters.js +18 -1
- package/dist/esm/utils/builtInFormatters.js.map +1 -1
- package/dist/esm/utils/detailObjectContext.js +7 -2
- package/dist/esm/utils/detailObjectContext.js.map +1 -1
- package/dist/esm/utils/localHeaderRows.js +95 -0
- package/dist/esm/utils/localHeaderRows.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/visibleWhen.js +6 -0
- package/dist/esm/utils/visibleWhen.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +686 -239
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1204 -467
- package/l10n/nl.json +2 -0
- package/package.json +1 -1
- package/src/components/CnActionDialog/CnActionDialog.vue +8 -1
- package/src/components/CnAiCompanion/CnAiChatPanel.vue +3 -0
- package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +55 -2
- package/src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue +18 -0
- package/src/components/CnChecklistRunWidget/CnChecklistRunWidget.vue +4 -2
- package/src/components/CnDataTable/CnDataTable.vue +19 -2
- package/src/components/CnDetailPage/CnDetailPage.md +1 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +23 -0
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue +3 -1
- package/src/components/CnFactsWidget/CnFactsWidget.vue +74 -9
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +2 -2
- package/src/components/CnIndexPage/CnIndexPage.vue +6 -3
- package/src/components/CnMailTab/CnMailTab.vue +6 -4
- package/src/components/CnNotesCard/CnNotesCard.vue +7 -2
- package/src/components/CnNotificationMatrix/CnNotificationMatrix.vue +25 -3
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +132 -20
- package/src/components/CnRetentionWidget/CnRetentionWidget.vue +2 -0
- package/src/components/CnSettingsField/CnSettingsField.vue +15 -0
- package/src/components/CnTabsWidget/CnTabsWidget.vue +6 -1
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -8
- package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +10 -2
- package/src/composables/indexSources.js +35 -9
- package/src/css/dialog.css +25 -9
- package/src/css/look-board-detail.css +370 -30
- package/src/css/look-board-index.css +116 -4
- package/src/css/look-board.css +168 -8
- package/src/css/table.css +17 -1
- package/src/schemas/app-manifest-v2.schema.json +2 -2
- package/src/utils/builtInFormatters.js +17 -0
- package/src/utils/detailObjectContext.js +7 -2
- package/src/utils/localHeaderRows.js +91 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
- package/src/utils/visibleWhen.js +6 -0
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
grid-column: 1 / -1;
|
|
78
78
|
grid-row: 2;
|
|
79
79
|
justify-self: start;
|
|
80
|
-
margin-block: 6px
|
|
80
|
+
margin-block: 6px 0;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
.cn-look-board.cn-index-page--sidebar-open .cn-page-header .cn-header-trail,
|
|
@@ -90,6 +90,13 @@
|
|
|
90
90
|
grid-row: 4;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/* Without a breadcrumb trail the count line takes the trail's row, so no
|
|
94
|
+
empty row adds a second gap (DqZakenSorteren: 58px under the title). */
|
|
95
|
+
.cn-look-board.cn-index-page--sidebar-open .cn-page-header:not(:has(.cn-header-trail)) .cn-page-header__text > .cn-page-header__description,
|
|
96
|
+
.cn-look-board .cn-index-page--sidebar-open .cn-page-header:not(:has(.cn-header-trail)) .cn-page-header__text > .cn-page-header__description {
|
|
97
|
+
grid-row: 3;
|
|
98
|
+
}
|
|
99
|
+
|
|
93
100
|
/* The header buttons keep the board's button under a theme that styles every
|
|
94
101
|
secondary or primary button with `!important` (the nldesign theme: a
|
|
95
102
|
primary border, 8px 16px padding): Download and the Actions menu are 40px,
|
|
@@ -190,6 +197,15 @@
|
|
|
190
197
|
gap: 8px;
|
|
191
198
|
}
|
|
192
199
|
|
|
200
|
+
/* With the side panel open the boards keep Filter on row 1 after Save view
|
|
201
|
+
and wrap the view switch alone to row 2 (DqFilterBeheren, DqZakenSorteren):
|
|
202
|
+
the unit dissolves into the bar's flex row.
|
|
203
|
+
@spec openspec/changes/board-filter-row-one-beside-side-panel/specs/board-filter-row-one-beside-side-panel/spec.md */
|
|
204
|
+
.cn-look-board.cn-index-page--sidebar-open .cn-actions-bar--board .cn-actions-bar__view-controls,
|
|
205
|
+
.cn-look-board .cn-index-page--sidebar-open .cn-actions-bar--board .cn-actions-bar__view-controls {
|
|
206
|
+
display: contents;
|
|
207
|
+
}
|
|
208
|
+
|
|
193
209
|
/* `quickFilterPlacement: "toolbar"` (DqTeamwachtrij): the chips take the room
|
|
194
210
|
the Filter button and the view switch leave on row 1, so the three stay on
|
|
195
211
|
one row in the column beside a side panel; the chips that do not fit move
|
|
@@ -891,6 +907,16 @@
|
|
|
891
907
|
max-width: calc(100% - 26px);
|
|
892
908
|
}
|
|
893
909
|
|
|
910
|
+
/* A plain (unsortable) label wraps by word beside its funnel, as a sort
|
|
911
|
+
button does, instead of the funnel dropping onto a line of its own
|
|
912
|
+
("Lopende / zaken", DqOrganisaties: head 64 -> 59, board 58.5).
|
|
913
|
+
@spec openspec/changes/board-head-label-beside-funnel/specs/data-table/spec.md#requirement-a-board-heads-plain-label-wraps-beside-its-filter */
|
|
914
|
+
.cn-look-board .cn-index-page__main--table .cn-table-container--board .cn-data-table th > span:first-child:has(~ .cn-table-header__filter) {
|
|
915
|
+
display: inline-block;
|
|
916
|
+
max-width: calc(100% - 26px);
|
|
917
|
+
vertical-align: middle;
|
|
918
|
+
}
|
|
919
|
+
|
|
894
920
|
/* The board's funnel: a 22px button 4px after the sort chevron. */
|
|
895
921
|
.cn-look-board .cn-table-container--board .cn-data-table th .cn-table-header__filter {
|
|
896
922
|
width: 22px;
|
|
@@ -909,16 +935,21 @@
|
|
|
909
935
|
`#content[class*="app-"]`, an id selector no class rule outranks, so a plain
|
|
910
936
|
`content-box` here was ignored and the cell left 12px for the checkbox (the
|
|
911
937
|
header's checkbox showed as a stray bracket). */
|
|
912
|
-
/* The
|
|
913
|
-
|
|
938
|
+
/* The cell pads 16px as the boards do and the checkbox starts 4px further in,
|
|
939
|
+
a native checkbox's margin (DqZoekresultaten, DqPostvak, DqContracten), in
|
|
940
|
+
the same 60px. */
|
|
914
941
|
.cn-look-board .cn-table-container--board .cn-data-table .cn-table-col--checkbox {
|
|
915
942
|
box-sizing: border-box !important;
|
|
916
943
|
width: 60px;
|
|
917
944
|
min-width: 60px;
|
|
918
|
-
padding-inline:
|
|
945
|
+
padding-inline: 16px;
|
|
919
946
|
text-align: start;
|
|
920
947
|
}
|
|
921
948
|
|
|
949
|
+
.cn-look-board .cn-table-container--board .cn-data-table .cn-table-col--checkbox .checkbox-radio-switch {
|
|
950
|
+
margin-inline-start: 4px;
|
|
951
|
+
}
|
|
952
|
+
|
|
922
953
|
.cn-look-board .cn-table-container--board .cn-data-table .checkbox-radio-switch {
|
|
923
954
|
--icon-size: 18px;
|
|
924
955
|
}
|
|
@@ -968,6 +999,16 @@
|
|
|
968
999
|
width: 100%;
|
|
969
1000
|
}
|
|
970
1001
|
|
|
1002
|
+
/* A wrapping title column (`cn-cell--wrap`, DqZoekresultaten) runs its
|
|
1003
|
+
secondary line at a normal line height over its lines, 2px under the
|
|
1004
|
+
title, as the board draws it; a single-line secondary keeps the table's
|
|
1005
|
+
1.35.
|
|
1006
|
+
@spec openspec/changes/board-wrapped-secondary-line-height/specs/board-wrapped-secondary-line-height/spec.md */
|
|
1007
|
+
.cn-look-board .cn-table-container--board .cn-data-table td.cn-table-col--title.cn-cell--wrap .cn-table-cell__secondary {
|
|
1008
|
+
margin-top: 2px;
|
|
1009
|
+
line-height: normal;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
971
1012
|
/* `rowTitle: "plain"` (PqTickets, OcPublicaties): the title is bold text in
|
|
972
1013
|
the main text colour without an underline, the secondary line 13px muted,
|
|
973
1014
|
2px under it. The row stays clickable through its stretched row link. */
|
|
@@ -1076,6 +1117,22 @@
|
|
|
1076
1117
|
line-height: 16px;
|
|
1077
1118
|
}
|
|
1078
1119
|
|
|
1120
|
+
/* The grey person chip with a second line (DqContacten): 14px above and
|
|
1121
|
+
under, the name 2px above a 14px address on its normal line, a 66px row.
|
|
1122
|
+
@spec openspec/changes/board-person-chip-row-height/specs/data-table/spec.md#requirement-a-board-person-chip-row-is-66px */
|
|
1123
|
+
.cn-look-board .cn-table-container--board .cn-data-table td:has(.cn-cell-renderer__avatar-initials--neutral + .cn-cell-renderer__avatar-text .cn-cell-renderer__avatar-secondary) {
|
|
1124
|
+
padding-block: 14px;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
.cn-look-board .cn-table-container--board .cn-data-table .cn-cell-renderer__avatar-initials--neutral + .cn-cell-renderer__avatar-text:has(.cn-cell-renderer__avatar-secondary) {
|
|
1128
|
+
gap: 2px;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
.cn-look-board .cn-table-container--board .cn-data-table .cn-cell-renderer__avatar-initials--neutral + .cn-cell-renderer__avatar-text .cn-cell-renderer__avatar-secondary {
|
|
1132
|
+
font-size: 14px;
|
|
1133
|
+
line-height: normal;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1079
1136
|
/* A status pill: 3px 10px, radius 12, 13px weight 600, 21px tall on a
|
|
1080
1137
|
normal line (a 1.4 line made it 24.2). The colours stay the status colour
|
|
1081
1138
|
map's.
|
|
@@ -1428,6 +1485,19 @@
|
|
|
1428
1485
|
padding-top: 0;
|
|
1429
1486
|
}
|
|
1430
1487
|
|
|
1488
|
+
/* The selection box sits flush where the boards draw it: NcCheckbox pads its
|
|
1489
|
+
content 7px on each side (DqAanMijToegewezen card head, DqTeamwachtrij rows,
|
|
1490
|
+
whose box starts 18px in).
|
|
1491
|
+
@spec openspec/changes/board-selection-box-flush/specs/board-selection-box-flush/spec.md#requirement-a-cards-and-a-rows-selection-box-sits-where-the-board-draws-it */
|
|
1492
|
+
.cn-look-board .cn-object-card__record-head > .cn-object-card__checkbox .checkbox-radio-switch__content,
|
|
1493
|
+
.cn-look-board .cn-object-row__checkbox .checkbox-radio-switch__content {
|
|
1494
|
+
padding-inline: 0;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
.cn-look-board .cn-object-row__checkbox {
|
|
1498
|
+
margin-inline-start: 6px;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1431
1501
|
.cn-look-board .cn-object-card--board .cn-object-card__record-head > .cn-object-card__checkbox ~ .cn-object-card__record-pill {
|
|
1432
1502
|
margin-inline-start: auto;
|
|
1433
1503
|
}
|
|
@@ -2292,3 +2362,45 @@
|
|
|
2292
2362
|
.cn-look-board .cn-data-table th.cn-table-col--icon + th {
|
|
2293
2363
|
padding-inline-start: 12px;
|
|
2294
2364
|
}
|
|
2365
|
+
|
|
2366
|
+
/* ---- The table beside the open side panel (DqZakenSorteren, DqFilterBeheren) */
|
|
2367
|
+
/* The table takes its column's width: its configured column widths make way
|
|
2368
|
+
for the content, titles and names wrap (a long word hyphenated, an
|
|
2369
|
+
unbreakable token such as an id anywhere), a pill
|
|
2370
|
+
wraps before the row scrolls, and each header keeps its label and icons on
|
|
2371
|
+
one line. No sideways scroll.
|
|
2372
|
+
@spec openspec/changes/board-table-fits-beside-side-panel/specs/board-table-fits-beside-side-panel/spec.md#requirement-the-table-fits-its-column-beside-the-open-side-panel */
|
|
2373
|
+
.cn-look-board .cn-index-page--sidebar-open .cn-table-container--board .cn-data-table {
|
|
2374
|
+
table-layout: auto;
|
|
2375
|
+
width: 100% !important;
|
|
2376
|
+
min-width: 0 !important;
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
.cn-look-board .cn-index-page--sidebar-open .cn-table-container--board .cn-data-table th:not(.cn-table-col--checkbox):not(.cn-table-col--actions) {
|
|
2380
|
+
width: auto !important;
|
|
2381
|
+
min-width: 0 !important;
|
|
2382
|
+
max-width: none !important;
|
|
2383
|
+
white-space: nowrap;
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
.cn-look-board .cn-index-page--sidebar-open .cn-table-container--board .cn-data-table td:not(.cn-table-col--checkbox):not(.cn-table-col--actions) {
|
|
2387
|
+
width: auto !important;
|
|
2388
|
+
min-width: 0 !important;
|
|
2389
|
+
max-width: none !important;
|
|
2390
|
+
white-space: normal;
|
|
2391
|
+
hyphens: auto;
|
|
2392
|
+
overflow-wrap: anywhere;
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
.cn-look-board .cn-index-page--sidebar-open .cn-table-container--board .cn-data-table td :is(.cn-cell-renderer, .cn-cell-renderer__avatar-text, .cn-cell-renderer__avatar-name, .cn-table-row__link) {
|
|
2396
|
+
min-width: 0;
|
|
2397
|
+
white-space: normal;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
.cn-look-board .cn-index-page--sidebar-open .cn-table-container--board .cn-data-table td .cn-status-badge {
|
|
2401
|
+
white-space: normal;
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
.cn-look-board .cn-index-page--sidebar-open .cn-table-container--board .cn-data-table th .cn-table-header__sort {
|
|
2405
|
+
min-width: max-content;
|
|
2406
|
+
}
|
package/src/css/look-board.css
CHANGED
|
@@ -664,12 +664,24 @@
|
|
|
664
664
|
}
|
|
665
665
|
|
|
666
666
|
/* Two buttons in a card's title row stand 8px apart (DqZaakContact "Live
|
|
667
|
-
gesprek", DqZaakEmail "Mail koppelen" / "Mail opstellen")
|
|
667
|
+
gesprek", DqZaakEmail "Mail koppelen" / "Mail opstellen"), also in an
|
|
668
|
+
integration card's CnDetailCard header (DqZaakTaken "Afspraken", "Kaarten").
|
|
668
669
|
@spec openspec/changes/board-card-header-actions-gap/specs/board-card-header-actions-gap/spec.md#requirement-a-board-cards-title-row-buttons-stand-8px-apart */
|
|
669
670
|
.cn-look-board .cn-widget-wrapper__actions:not(.cn-look-nextcloud *) {
|
|
670
671
|
gap: 8px;
|
|
671
672
|
}
|
|
672
673
|
|
|
674
|
+
/* A card's own header items (a count note, a status pill) stand before the
|
|
675
|
+
manifest's title-row buttons, as the boards draw them (DqZaakTaken "Wat
|
|
676
|
+
hierna komt": "2 gepland", then "+ Toevoegen"). */
|
|
677
|
+
.cn-look-board .cn-widget-wrapper__actions > .cn-detail-widget-header-items:not(.cn-look-nextcloud *) {
|
|
678
|
+
order: -1;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
.cn-look-board .cn-detail-card__header-right:not(.cn-look-nextcloud *) {
|
|
682
|
+
gap: 8px;
|
|
683
|
+
}
|
|
684
|
+
|
|
673
685
|
/* A detail page marked `cardButtons: "regular"` draws them at the 40px
|
|
674
686
|
control height: the case page's tab cards (DqZaakContact "Live gesprek",
|
|
675
687
|
DqZaakHistorie, DqZaakBetrokkenen), where setting and case-type pages keep 34.
|
|
@@ -1467,6 +1479,103 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .v-popper__popper.action-
|
|
|
1467
1479
|
text-decoration: underline;
|
|
1468
1480
|
}
|
|
1469
1481
|
|
|
1482
|
+
/* A plain table an app draws in a settings card (dossiq's ZGW mapping, StUF
|
|
1483
|
+
endpoints and audit log, AWB terms) reads as the board's settings tables:
|
|
1484
|
+
14px, a near-white head with muted 600 labels, 10px 12px cells, soft rules.
|
|
1485
|
+
The theme paints every th through its table header tokens, so the head
|
|
1486
|
+
hands those tokens the board's values too.
|
|
1487
|
+
@spec openspec/changes/board-settings-plain-tables/specs/settings-board-look/spec.md */
|
|
1488
|
+
.cn-look-board .settings-section table:not(.cn-data-table):not(.cn-look-nextcloud *) {
|
|
1489
|
+
width: 100%;
|
|
1490
|
+
border-collapse: collapse;
|
|
1491
|
+
font-size: 14px;
|
|
1492
|
+
line-height: 1.2;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
.cn-look-board .settings-section table:not(.cn-data-table):not(.cn-look-nextcloud *) thead th {
|
|
1496
|
+
--nldesign-component-table-header-background-color: var(--cn-board-table-head-bg, color-mix(in srgb, var(--color-background-hover) 40%, var(--color-main-background)));
|
|
1497
|
+
--nldesign-component-table-header-color: var(--color-text-maxcontrast);
|
|
1498
|
+
--nldesign-component-table-header-font-weight: 600;
|
|
1499
|
+
padding: 10px 12px;
|
|
1500
|
+
border-bottom: 1px solid var(--color-border);
|
|
1501
|
+
background: var(--cn-board-table-head-bg, color-mix(in srgb, var(--color-background-hover) 40%, var(--color-main-background)));
|
|
1502
|
+
color: var(--color-text-maxcontrast);
|
|
1503
|
+
font-size: 14px;
|
|
1504
|
+
font-weight: 600;
|
|
1505
|
+
line-height: 1.2;
|
|
1506
|
+
text-align: start;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
/* The working calendar's month reads as the board draws it: the month name
|
|
1510
|
+
first, at the content edge, with the previous and next month buttons after
|
|
1511
|
+
it, and the buttons add no height to the line, so the month sits 20px under
|
|
1512
|
+
the card title (DqInstellingenAlgemeen "Werkdagen en feestdagen": the
|
|
1513
|
+
arrow put the month 30px in and 28px down).
|
|
1514
|
+
@spec openspec/changes/working-calendar-board-head/specs/settings-board-look/spec.md */
|
|
1515
|
+
.cn-look-board .cn-working-calendar__title:not(.cn-look-nextcloud *) {
|
|
1516
|
+
order: -1;
|
|
1517
|
+
min-width: 0;
|
|
1518
|
+
margin-inline-end: 4px;
|
|
1519
|
+
line-height: 1.2;
|
|
1520
|
+
text-align: start;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
/* Body rows are 42px as on the boards (10px, a 21px line, 10px, the rule).
|
|
1524
|
+
A row header (dossiq's prerequisites) reads as a strong cell, not as a
|
|
1525
|
+
head: the theme paints every th, so its tokens are handed the cell's. */
|
|
1526
|
+
.cn-look-board .settings-section table:not(.cn-data-table):not(.cn-look-nextcloud *) td,
|
|
1527
|
+
.cn-look-board .settings-section table:not(.cn-data-table):not(.cn-look-nextcloud *) tbody th {
|
|
1528
|
+
padding: 10px 12px;
|
|
1529
|
+
border-bottom: 1px solid var(--color-background-dark);
|
|
1530
|
+
color: var(--color-main-text);
|
|
1531
|
+
font-size: 14px;
|
|
1532
|
+
line-height: 1.5;
|
|
1533
|
+
vertical-align: middle;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
.cn-look-board .settings-section table:not(.cn-data-table):not(.cn-look-nextcloud *) tbody th {
|
|
1537
|
+
--nldesign-component-table-header-background-color: transparent;
|
|
1538
|
+
--nldesign-component-table-header-color: var(--color-main-text);
|
|
1539
|
+
--nldesign-component-table-header-font-weight: 600;
|
|
1540
|
+
background: transparent;
|
|
1541
|
+
font-weight: 600;
|
|
1542
|
+
text-align: start;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
/* A strong cell's name in a settings card reads 15px bold over its muted
|
|
1546
|
+
second line, as the boards draw a connection's name (DqInstellingen
|
|
1547
|
+
"Koppelingen": <strong> 15px), not the index tables' 14px 500.
|
|
1548
|
+
@spec openspec/changes/board-settings-strong-cell-name/specs/settings-board-look/spec.md */
|
|
1549
|
+
.cn-look-board .settings-section .cn-data-table td.cn-cell--strong:not(.cn-look-nextcloud *) {
|
|
1550
|
+
font-weight: 400;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
.cn-look-board .settings-section .cn-data-table td.cn-cell--strong:not(.cn-look-nextcloud *) > .cn-cell-renderer {
|
|
1554
|
+
font-size: 15px;
|
|
1555
|
+
font-weight: 700;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
.cn-look-board .cn-working-calendar__head:not(.cn-look-nextcloud *) > .button-vue:not(.cn-working-calendar__add) {
|
|
1559
|
+
margin-block: -8px;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
/* In a one-column settings card a single-line field is at most 560px wide,
|
|
1563
|
+
its hint under it, as the boards draw a field on its own row
|
|
1564
|
+
(DqBeheerGeavanceerd "Financiële integratie", "Storeregister"); a text
|
|
1565
|
+
area, a note or a switch still spans the card.
|
|
1566
|
+
@spec openspec/changes/board-settings-single-field-width/specs/settings-board-look/spec.md */
|
|
1567
|
+
.cn-look-board .cn-settings-fields__grid--1:not(.cn-look-nextcloud *) > .cn-settings-field--text,
|
|
1568
|
+
.cn-look-board .cn-settings-fields__grid--1:not(.cn-look-nextcloud *) > .cn-settings-field--password,
|
|
1569
|
+
.cn-look-board .cn-settings-fields__grid--1:not(.cn-look-nextcloud *) > .cn-settings-field--secret,
|
|
1570
|
+
.cn-look-board .cn-settings-fields__grid--1:not(.cn-look-nextcloud *) > .cn-settings-field--number,
|
|
1571
|
+
.cn-look-board .cn-settings-fields__grid--1:not(.cn-look-nextcloud *) > .cn-settings-field--email,
|
|
1572
|
+
.cn-look-board .cn-settings-fields__grid--1:not(.cn-look-nextcloud *) > .cn-settings-field--url,
|
|
1573
|
+
.cn-look-board .cn-settings-fields__grid--1:not(.cn-look-nextcloud *) > .cn-settings-field--select,
|
|
1574
|
+
.cn-look-board .cn-settings-fields__grid--1:not(.cn-look-nextcloud *) > .cn-settings-field--date,
|
|
1575
|
+
.cn-look-board .cn-settings-fields__grid--1:not(.cn-look-nextcloud *) > .cn-settings-field--datetime-local {
|
|
1576
|
+
max-width: 560px;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1470
1579
|
/* Section actions sit at the end of the title row. The row is a grid, so a
|
|
1471
1580
|
long title wraps beside the buttons instead of running under them. A
|
|
1472
1581
|
half-width card keeps them there (DqVthInstellingen "Inspectiechecklists",
|
|
@@ -1523,6 +1632,24 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .v-popper__popper.action-
|
|
|
1523
1632
|
align-self: center;
|
|
1524
1633
|
}
|
|
1525
1634
|
|
|
1635
|
+
/* A switch or radio in a board settings card starts at the card's content
|
|
1636
|
+
edge, its label 12px (switch) or 10px (radio) after the control, as the
|
|
1637
|
+
boards draw them (DqBeheerinstellingen "AI-ondersteunde verwerking").
|
|
1638
|
+
NcCheckboxRadioSwitch pads its content 9px on each side, which set the
|
|
1639
|
+
control 9px in from the fields above and below it.
|
|
1640
|
+
@spec openspec/changes/board-settings-switch-radio-flush/specs/settings-board-look/spec.md */
|
|
1641
|
+
.cn-look-board .cn-settings-fields:not(.cn-look-nextcloud *) .checkbox-content {
|
|
1642
|
+
padding-inline: 0;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
.cn-look-board .cn-settings-fields:not(.cn-look-nextcloud *) .checkbox-content-switch {
|
|
1646
|
+
gap: 12px;
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
.cn-look-board .cn-settings-fields:not(.cn-look-nextcloud *) .checkbox-content-radio {
|
|
1650
|
+
gap: 10px;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1526
1653
|
/* The "Uitleg tonen" toggle is a text link flush with the card's right edge
|
|
1527
1654
|
(DqBeheerinstellingen Zaak-e-mail), not a padded tertiary button.
|
|
1528
1655
|
@spec openspec/changes/board-settings-title-centred-and-link-flush/specs/settings-board-look/spec.md */
|
|
@@ -1596,8 +1723,12 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .v-popper__popper.action-
|
|
|
1596
1723
|
max-width: none;
|
|
1597
1724
|
}
|
|
1598
1725
|
|
|
1599
|
-
|
|
1600
|
-
|
|
1726
|
+
/* A board card's title and subtitle run the card's width, wide or not
|
|
1727
|
+
(DqBeheerGeavanceerd "Eerste keer", "StUF-ZKN-endpoints": the subtitle
|
|
1728
|
+
wrapped at NcSettingsSection's 900px measure, the board's does not).
|
|
1729
|
+
@spec openspec/changes/board-settings-desc-full-width/specs/settings-board-look/spec.md */
|
|
1730
|
+
.cn-look-board .settings-section:not(.cn-look-nextcloud *) .settings-section__name,
|
|
1731
|
+
.cn-look-board .settings-section:not(.cn-look-nextcloud *) .settings-section__desc {
|
|
1601
1732
|
max-width: none;
|
|
1602
1733
|
}
|
|
1603
1734
|
|
|
@@ -2156,7 +2287,7 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .v-popper__popper.action-
|
|
|
2156
2287
|
12px follows it: the boards' card is title, 6px, rule, row (DqMijnTeam
|
|
2157
2288
|
"Vastgelopen zaken", DqMijnWerk "Zaken die u volgt").
|
|
2158
2289
|
@spec openspec/changes/board-card-title-above-ruled-list/specs/board-card-title-above-ruled-list/spec.md#requirement-a-ruled-list-starts-6px-under-the-card-title */
|
|
2159
|
-
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-widget-wrapper:not(.cn-widget-wrapper--nc-dashboard):not(.cn-widget-wrapper--borderless):has(> .cn-widget-wrapper__content .cn-record-list--rule-above, > .cn-widget-wrapper__content .cn-tasks-widget__list, > .cn-widget-wrapper__content .cn-decision-list__rows) .cn-widget-wrapper__header {
|
|
2290
|
+
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-widget-wrapper:not(.cn-widget-wrapper--nc-dashboard):not(.cn-widget-wrapper--borderless):has(> .cn-widget-wrapper__content .cn-record-list--rule-above, > .cn-widget-wrapper__content .cn-tasks-widget__list, > .cn-widget-wrapper__content .cn-decision-list__rows, > .cn-widget-wrapper__content .cn-nc-widget-widget__body--vertical) .cn-widget-wrapper__header {
|
|
2160
2291
|
padding-bottom: 6px;
|
|
2161
2292
|
}
|
|
2162
2293
|
|
|
@@ -2692,13 +2823,13 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .v-popper__popper.action-
|
|
|
2692
2823
|
|
|
2693
2824
|
/* A vertical NC widget is the board's activity list (DqMijnWerk "Recente
|
|
2694
2825
|
activiteit"): hairline rows 12px above and below the text, a 10px dot in
|
|
2695
|
-
the icon's place, the title 14px in the main ink and wrapping
|
|
2696
|
-
|
|
2697
|
-
|
|
2826
|
+
the icon's place, the title 14px in the main ink and wrapping. Thematiq underlines links with a heavier
|
|
2827
|
+
selector, hence the !important. The first hairline stands 6px under the
|
|
2828
|
+
title, as on every Dq list card (design-system #432).
|
|
2698
2829
|
@spec openspec/changes/board-nc-widget-activity-rows/specs/board-nc-widget-activity-rows/spec.md#requirement-a-board-dashboard-nc-widget-draws-its-items-as-activity-rows */
|
|
2699
2830
|
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-widget-wrapper:not(.cn-widget-wrapper--borderless) .cn-nc-widget-widget__body--vertical {
|
|
2700
2831
|
gap: 0;
|
|
2701
|
-
padding:
|
|
2832
|
+
padding: 0 var(--cn-board-dashboard-card-inset) 10px;
|
|
2702
2833
|
}
|
|
2703
2834
|
|
|
2704
2835
|
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-nc-widget-widget__body--vertical a.cn-nc-widget-widget__item--vertical {
|
|
@@ -3132,6 +3263,25 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .v-popper__popper.action-
|
|
|
3132
3263
|
line-height: normal;
|
|
3133
3264
|
}
|
|
3134
3265
|
|
|
3266
|
+
/* A bar list in a framed section of a sections card is the board's small
|
|
3267
|
+
list: 13px rows 8px apart, a 150px label column, a 10px bar and a 48px
|
|
3268
|
+
value column (DqDoorlooptijd "Naleving per zaaktype").
|
|
3269
|
+
@spec openspec/changes/board-section-bar-list-compact/specs/board-section-bar-list-compact/spec.md#requirement-a-bar-list-in-a-sections-card-section-is-compact */
|
|
3270
|
+
.cn-look-board .cn-sections-widget__section .cn-chart-widget__bar-list:not(.cn-look-nextcloud *) {
|
|
3271
|
+
gap: 8px;
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
.cn-look-board .cn-sections-widget__section .cn-chart-widget__bar-list:not(.cn-look-nextcloud *) .cn-chart-widget__bar-line {
|
|
3275
|
+
grid-template-columns: 150px minmax(0, 1fr) 48px;
|
|
3276
|
+
gap: 10px;
|
|
3277
|
+
font-size: 13px;
|
|
3278
|
+
}
|
|
3279
|
+
|
|
3280
|
+
.cn-look-board .cn-sections-widget__section .cn-chart-widget__bar-list:not(.cn-look-nextcloud *) .cn-chart-widget__bar-track {
|
|
3281
|
+
height: 10px;
|
|
3282
|
+
border-radius: 5px;
|
|
3283
|
+
}
|
|
3284
|
+
|
|
3135
3285
|
/* A bar list with the value at the bar's end (DqProcesanalyse "Doorlooptijd
|
|
3136
3286
|
per status") sets its rows in 13px, the label in the secondary text, the
|
|
3137
3287
|
value in the main ink at 600, rows 34px apart.
|
|
@@ -3310,3 +3460,13 @@ html :where(.cn-look-board) :is(h2, h3, h5, h6):where(.cn-inner-heading) {
|
|
|
3310
3460
|
.cn-look-board .cn-timeline-manage__draft-label:first-child:not(.cn-look-nextcloud *) {
|
|
3311
3461
|
margin-block-start: 0;
|
|
3312
3462
|
}
|
|
3463
|
+
|
|
3464
|
+
/* ---- The share dialog's audience rows ----------------------------------- */
|
|
3465
|
+
/* A person or group the view is shared with sits on the page's own ground at
|
|
3466
|
+
radius 8, as DqFilterDelen draws it; Nextcloud's look keeps its hover grey
|
|
3467
|
+
and large radius.
|
|
3468
|
+
@spec openspec/changes/board-share-rows-ground/specs/board-share-rows-ground/spec.md */
|
|
3469
|
+
.cn-look-board .cn-saved-view-share__mode:not(.cn-look-nextcloud *) {
|
|
3470
|
+
border-radius: 8px;
|
|
3471
|
+
background: var(--cn-board-sunken-ground);
|
|
3472
|
+
}
|
package/src/css/table.css
CHANGED
|
@@ -310,7 +310,7 @@
|
|
|
310
310
|
.cn-data-table--fit td.cn-table-col--grow.cn-cell--wrap {
|
|
311
311
|
min-width: 8em;
|
|
312
312
|
max-width: none;
|
|
313
|
-
overflow-wrap:
|
|
313
|
+
overflow-wrap: break-word;
|
|
314
314
|
white-space: normal;
|
|
315
315
|
}
|
|
316
316
|
|
|
@@ -359,6 +359,13 @@
|
|
|
359
359
|
font-weight: 500;
|
|
360
360
|
}
|
|
361
361
|
|
|
362
|
+
/* A lead cell's value reads 15px, a person's name over their function
|
|
363
|
+
(DqAfdelingen). */
|
|
364
|
+
.cn-data-table td.cn-cell--lead > .cn-cell-renderer,
|
|
365
|
+
.cn-data-table td.cn-cell--lead .cn-table-row__link > .cn-cell-renderer {
|
|
366
|
+
font-size: 15px;
|
|
367
|
+
}
|
|
368
|
+
|
|
362
369
|
.cn-data-table td.cn-cell--muted {
|
|
363
370
|
color: var(--color-text-maxcontrast);
|
|
364
371
|
}
|
|
@@ -609,6 +616,15 @@
|
|
|
609
616
|
cursor: grab;
|
|
610
617
|
}
|
|
611
618
|
|
|
619
|
+
/* The handle before a reorderable row's name: a muted 16px grip 10px before it. */
|
|
620
|
+
.cn-table-row__handle {
|
|
621
|
+
display: inline-flex;
|
|
622
|
+
vertical-align: middle;
|
|
623
|
+
margin-inline-end: 10px;
|
|
624
|
+
color: var(--color-text-maxcontrast);
|
|
625
|
+
cursor: grab;
|
|
626
|
+
}
|
|
627
|
+
|
|
612
628
|
.cn-table-row--drop-target {
|
|
613
629
|
box-shadow: inset 0 2px 0 var(--color-primary-element);
|
|
614
630
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"$id": "https://raw.githubusercontent.com/ConductionNL/nextcloud-vue/main/src/schemas/app-manifest-v2.schema.json",
|
|
4
4
|
"title": "Conduction App Manifest v2",
|
|
5
5
|
"description": "v2 schema for the JSON-driven page and navigation manifest consumed by @conduction/nextcloud-vue. Introduces a uniform widgets[] array on every page type with a per-slot grid coordinate system, a typed actions[] discriminator, and the required $schema field for version detection. v1 manifests continue to validate against app-manifest.schema.json (unchanged).",
|
|
6
|
-
"version": "2.
|
|
6
|
+
"version": "2.156.0",
|
|
7
7
|
"type": "object",
|
|
8
8
|
"required": ["$schema", "version"],
|
|
9
9
|
"allOf": [
|
|
@@ -5533,7 +5533,7 @@
|
|
|
5533
5533
|
"gridFloat": {
|
|
5534
5534
|
"type": "boolean",
|
|
5535
5535
|
"default": true,
|
|
5536
|
-
"description": "type='dashboard': `false` packs widgets upward (GridStack `float: false`), so a size-to-content widget that shrinks closes the gap under it instead of leaving an empty band (zuiddrecht-pixel-gaps-3). Maps to CnDashboardPage `gridFloat`."
|
|
5536
|
+
"description": "type='dashboard' and type='detail': `false` packs widgets upward (GridStack `float: false`), so a size-to-content widget that shrinks closes the gap under it instead of leaving an empty band (zuiddrecht-pixel-gaps-3). Maps to CnDashboardPage and CnDetailPage `gridFloat`."
|
|
5537
5537
|
},
|
|
5538
5538
|
"showTitle": {
|
|
5539
5539
|
"type": "boolean",
|
|
@@ -2,6 +2,7 @@ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
|
|
|
2
2
|
import { formatRelativeDate } from './boardDate.js'
|
|
3
3
|
import { formatMoney } from './formatMetric.js'
|
|
4
4
|
import { parseDuration } from './isoDuration.js'
|
|
5
|
+
import { userDisplayName } from './userDisplayName.js'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @file Built-in formatters merged into the `cnFormatters` registry
|
|
@@ -523,6 +524,21 @@ export function formatIsoDuration(value) {
|
|
|
523
524
|
return words[period.unit] ? words[period.unit]() : String(value)
|
|
524
525
|
}
|
|
525
526
|
|
|
527
|
+
/**
|
|
528
|
+
* A Nextcloud user id as its display name (`userName` registry key): the id
|
|
529
|
+
* while the name is looked up, and for a user nobody answers for.
|
|
530
|
+
*
|
|
531
|
+
* @spec openspec/changes/user-name-formatter/specs/user-name-formatter/spec.md#requirement-a-user-id-can-read-as-its-display-name
|
|
532
|
+
* @param {unknown} value The user id.
|
|
533
|
+
* @return {string} The display name, the id, or ''.
|
|
534
|
+
*/
|
|
535
|
+
export function formatUserName(value) {
|
|
536
|
+
if (value === null || value === undefined || value === '' || typeof value === 'object') {
|
|
537
|
+
return ''
|
|
538
|
+
}
|
|
539
|
+
return userDisplayName(value) || String(value)
|
|
540
|
+
}
|
|
541
|
+
|
|
526
542
|
export const BUILT_IN_FORMATTERS = {
|
|
527
543
|
date: formatDate,
|
|
528
544
|
datetime: formatDateTime,
|
|
@@ -538,4 +554,5 @@ export const BUILT_IN_FORMATTERS = {
|
|
|
538
554
|
masked: formatMasked,
|
|
539
555
|
isoDuration: formatIsoDuration,
|
|
540
556
|
valueMap: formatValueMap,
|
|
557
|
+
userName: formatUserName,
|
|
541
558
|
}
|
|
@@ -47,8 +47,10 @@ function unwrap(v) {
|
|
|
47
47
|
*
|
|
48
48
|
* @param {object|null} objectCtx The `cnObjectContext` inject (ref or plain object).
|
|
49
49
|
* @param {object|null} detailCtx The `cnDetailObjectContext` inject (`{ value }` holder).
|
|
50
|
-
* @return {{objectId: (string|number|null), object: (object|null), register: string, schema: (string|object)}|null}
|
|
51
|
-
* The merged context, or null when neither inject resolves.
|
|
50
|
+
* @return {{objectId: (string|number|null), object: (object|null), register: string, schema: (string|object), context?: object}|null}
|
|
51
|
+
* The merged context, or null when neither inject resolves. `context` is the
|
|
52
|
+
* records the page's conditions name (`@context.<name>`), when the primary
|
|
53
|
+
* inject carries them.
|
|
52
54
|
*/
|
|
53
55
|
export function resolveObjectTokenContext(objectCtx, detailCtx) {
|
|
54
56
|
const primary = unwrap(objectCtx)
|
|
@@ -81,6 +83,9 @@ export function resolveObjectTokenContext(objectCtx, detailCtx) {
|
|
|
81
83
|
if (primary.schema) {
|
|
82
84
|
out.schema = primary.schema
|
|
83
85
|
}
|
|
86
|
+
if (primary.context && typeof primary.context === 'object') {
|
|
87
|
+
out.context = primary.context
|
|
88
|
+
}
|
|
84
89
|
}
|
|
85
90
|
return out
|
|
86
91
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
*
|
|
5
|
+
* Header sort and filter applied to rows the table already holds (an
|
|
6
|
+
* `endpointSource` table): the same `{ field, dir }` sort and
|
|
7
|
+
* `{ paramKey: values[] }` filter map a self-fetching table sends to the
|
|
8
|
+
* server, read here in the browser.
|
|
9
|
+
*
|
|
10
|
+
* @module utils/localHeaderRows
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { CONTAINS_OPERATOR } from './columnFilters.js'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A row value as one comparable scalar: an array counts as its joined items.
|
|
17
|
+
*
|
|
18
|
+
* @param {unknown} value The raw value.
|
|
19
|
+
* @return {string|number|null} The value to compare.
|
|
20
|
+
*/
|
|
21
|
+
function scalar(value) {
|
|
22
|
+
if (value === null || value === undefined) {
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
return value.map((item) => (item && typeof item === 'object' ? (item.title || item.name || '') : String(item))).join(', ')
|
|
27
|
+
}
|
|
28
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
29
|
+
return typeof value === 'boolean' ? String(value) : value
|
|
30
|
+
}
|
|
31
|
+
return typeof value === 'object' ? (value.title || value.name || '') : String(value)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Whether a row holds a value the filter entry asks for.
|
|
36
|
+
*
|
|
37
|
+
* @param {object} row The row.
|
|
38
|
+
* @param {string} param The filter key (`field`, `field[like]`, `field[gte]`, `field[lte]`).
|
|
39
|
+
* @param {Array<string>} values The chosen values.
|
|
40
|
+
* @return {boolean} True when the row passes.
|
|
41
|
+
*/
|
|
42
|
+
function passes(row, param, values) {
|
|
43
|
+
const match = /^(.+)\[(\w+)\]$/.exec(param)
|
|
44
|
+
const field = match ? match[1] : param
|
|
45
|
+
const op = match ? match[2] : ''
|
|
46
|
+
const value = scalar(row[field])
|
|
47
|
+
if (op === CONTAINS_OPERATOR) {
|
|
48
|
+
const term = String(values[0]).toLowerCase()
|
|
49
|
+
return value !== null && String(value).toLowerCase().includes(term)
|
|
50
|
+
}
|
|
51
|
+
if (op === 'gte' || op === 'lte') {
|
|
52
|
+
if (value === null) {
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
const left = typeof value === 'number' ? value : String(value)
|
|
56
|
+
const right = typeof value === 'number' ? Number(values[0]) : String(values[0])
|
|
57
|
+
return op === 'gte' ? left >= right : left <= right
|
|
58
|
+
}
|
|
59
|
+
return value !== null && values.map(String).includes(String(value))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The rows that pass every active header filter, in the header sort's order.
|
|
64
|
+
*
|
|
65
|
+
* @param {Array<object>} rows The rows.
|
|
66
|
+
* @param {{field: string, dir: string}|null} sort The header sort.
|
|
67
|
+
* @param {object} filters The header filters, `{ paramKey: values[] }`.
|
|
68
|
+
* @return {Array<object>} The rows to show (the same array when nothing applies).
|
|
69
|
+
*/
|
|
70
|
+
export function localHeaderRows(rows, sort, filters) {
|
|
71
|
+
if (!Array.isArray(rows)) {
|
|
72
|
+
return []
|
|
73
|
+
}
|
|
74
|
+
const active = Object.entries(filters || {}).filter(([, values]) => Array.isArray(values) && values.length > 0)
|
|
75
|
+
let out = active.length > 0 ? rows.filter((row) => row && active.every(([param, values]) => passes(row, param, values))) : rows
|
|
76
|
+
if (sort && sort.field) {
|
|
77
|
+
const sign = sort.dir === 'desc' ? -1 : 1
|
|
78
|
+
out = [...out].sort((a, b) => {
|
|
79
|
+
const left = scalar(a && a[sort.field])
|
|
80
|
+
const right = scalar(b && b[sort.field])
|
|
81
|
+
if (left === null || right === null) {
|
|
82
|
+
return left === right ? 0 : (left === null ? 1 : -1)
|
|
83
|
+
}
|
|
84
|
+
if (typeof left === 'number' && typeof right === 'number') {
|
|
85
|
+
return sign * (left - right)
|
|
86
|
+
}
|
|
87
|
+
return sign * String(left).localeCompare(String(right), undefined, { numeric: true, sensitivity: 'base' })
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
return out
|
|
91
|
+
}
|