@conduction/nextcloud-vue 2.140.0 → 2.141.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/CnAssignDialog/CnAssignDialog.vue.js +1 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js +10 -2
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +11 -3
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +12 -0
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js +57 -40
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +1 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +5 -3
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +1 -0
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +65 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js +120 -69
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue.js +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js +47 -9
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +3 -3
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +2 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue2.js +10 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue3.js +34 -26
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue3.js.map +1 -1
- package/dist/esm/l10n/en.json.js +2 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +2 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +652 -391
- package/dist/esm/utils/groupSublines.js +120 -0
- package/dist/esm/utils/groupSublines.js.map +1 -0
- package/dist/nextcloud-vue.cjs.js +974 -638
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +652 -391
- package/l10n/en.json +2 -1
- package/l10n/nl.json +2 -1
- package/package.json +1 -1
- package/src/components/CnAssignDialog/CnAssignDialog.vue +10 -2
- package/src/components/CnCellRenderer/CnCellRenderer.vue +11 -3
- package/src/components/CnChartWidget/CnChartWidget.md +1 -0
- package/src/components/CnChartWidget/CnChartWidget.vue +49 -4
- package/src/components/CnDashboardPage/CnDashboardPage.vue +1 -0
- package/src/components/CnDataTable/CnDataTable.vue +6 -3
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +101 -3
- package/src/components/CnMapWidget/CnMapWidget.vue +47 -9
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +3 -3
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +20 -1
- package/src/components/CnRecordListWidget/CnRecordNoteRows.vue +33 -9
- package/src/css/dialog.css +13 -1
- package/src/css/look-board-detail.css +12 -9
- package/src/css/look-board-index.css +90 -7
- package/src/css/look-board.css +92 -0
- package/src/css/table.css +12 -0
- package/src/utils/groupSublines.js +125 -0
|
@@ -52,13 +52,11 @@
|
|
|
52
52
|
grid-row: 2;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
/*
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
.cn-look-board .cn-index-page .cn-header-trail.cn-detail-page__header-row2 .cn-detail-page__breadcrumbs .vue-crumb .button-vue.button-vue {
|
|
61
|
-
font-size: 15px;
|
|
55
|
+
/* The note after the breadcrumb ("38 contracten bij 21 leveranciers") is 14px
|
|
56
|
+
muted on every Dq list board (37 of 37).
|
|
57
|
+
@spec openspec/changes/board-index-header-meta-14/specs/index-page/spec.md */
|
|
58
|
+
.cn-look-board .cn-index-page .cn-page-header .cn-header-trail .cn-detail-page__header-meta {
|
|
59
|
+
font-size: 14px;
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
.cn-look-board .cn-index-page .cn-page-header .cn-header-trail ~ .cn-page-header__description {
|
|
@@ -982,6 +980,17 @@
|
|
|
982
980
|
text-decoration: none;
|
|
983
981
|
}
|
|
984
982
|
|
|
983
|
+
/* `rowTitle: "strong"` (DqZaaktypeStatussen "Statussen", "Resultaten"): the
|
|
984
|
+
name column of a card's list, the cell's 14px at weight 600 in the main
|
|
985
|
+
ink, no underline; the row stays a link.
|
|
986
|
+
@spec openspec/changes/table-strong-title/specs/table-strong-title/spec.md#requirement-a-tables-title-column-can-read-as-a-strong-name */
|
|
987
|
+
.cn-look-board .cn-table-container--board.cn-table-container--title-strong .cn-data-table td.cn-table-col--title {
|
|
988
|
+
color: var(--color-main-text);
|
|
989
|
+
font-size: 14px;
|
|
990
|
+
font-weight: 600;
|
|
991
|
+
text-decoration: none;
|
|
992
|
+
}
|
|
993
|
+
|
|
985
994
|
/* An avatar in the title column (PtAccounts): a 32px chip circle, 10px before
|
|
986
995
|
the name, the e-mail under the name beside it. The name keeps the title's
|
|
987
996
|
weight and size, and its underline: an inline-flex box is atomic, so the
|
|
@@ -2101,3 +2110,77 @@
|
|
|
2101
2110
|
.cn-look-board .cn-index-page--with-side-panel:not(.cn-look-nextcloud *) > .cn-index-page__quick-filter-row {
|
|
2102
2111
|
margin-bottom: calc(var(--cn-board-section-gap) + 12px);
|
|
2103
2112
|
}
|
|
2113
|
+
|
|
2114
|
+
/* ---- The side panel's filters as checkbox lists (DqZakenSorteren) ------- */
|
|
2115
|
+
/* "Filters" with "Alles wissen" beside it; per field its 14px semibold label
|
|
2116
|
+
over rows of a checkbox, the value and its count in 13px muted at the end,
|
|
2117
|
+
32px each.
|
|
2118
|
+
@spec openspec/changes/board-sidebar-filters-as-checkbox-lists/specs/index-sidebar/spec.md#requirement-the-board-look-draws-a-short-filter-as-a-checkbox-list-with-counts */
|
|
2119
|
+
.cn-look-board .cn-index-sidebar__filters-head {
|
|
2120
|
+
display: flex;
|
|
2121
|
+
align-items: center;
|
|
2122
|
+
justify-content: space-between;
|
|
2123
|
+
gap: 8px;
|
|
2124
|
+
margin-bottom: 14px;
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
.cn-look-board .cn-index-sidebar__filters-head h3 {
|
|
2128
|
+
margin: 0;
|
|
2129
|
+
font-size: 15px;
|
|
2130
|
+
font-weight: 700;
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
.cn-look-board .cn-index-sidebar__filters-clear {
|
|
2134
|
+
min-height: 0;
|
|
2135
|
+
margin: 0;
|
|
2136
|
+
padding: 0;
|
|
2137
|
+
border: 0;
|
|
2138
|
+
background: transparent;
|
|
2139
|
+
color: var(--color-primary-element);
|
|
2140
|
+
font-size: 14px;
|
|
2141
|
+
font-weight: 600;
|
|
2142
|
+
text-decoration: underline;
|
|
2143
|
+
cursor: pointer;
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
.cn-look-board .cn-index-sidebar__filter-group:has(> .cn-index-sidebar__checklist) .cn-index-sidebar__filter-label {
|
|
2147
|
+
font-size: 14px;
|
|
2148
|
+
font-weight: 600;
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
.cn-look-board .cn-index-sidebar__checklist {
|
|
2152
|
+
display: flex;
|
|
2153
|
+
flex-direction: column;
|
|
2154
|
+
gap: 4px;
|
|
2155
|
+
margin: 0;
|
|
2156
|
+
padding: 0;
|
|
2157
|
+
border: 0;
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
.cn-look-board .cn-index-sidebar__checklist-row {
|
|
2161
|
+
display: flex;
|
|
2162
|
+
align-items: center;
|
|
2163
|
+
gap: 10px;
|
|
2164
|
+
min-height: 32px;
|
|
2165
|
+
font-size: 14px;
|
|
2166
|
+
cursor: pointer;
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
.cn-look-board .cn-index-sidebar__checklist-row > input[type='checkbox'] {
|
|
2170
|
+
width: 18px;
|
|
2171
|
+
min-width: 18px;
|
|
2172
|
+
height: 18px;
|
|
2173
|
+
min-height: 18px;
|
|
2174
|
+
margin: 0;
|
|
2175
|
+
accent-color: var(--color-primary-element);
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
.cn-look-board .cn-index-sidebar__checklist-label {
|
|
2179
|
+
flex: 1;
|
|
2180
|
+
min-width: 0;
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
.cn-look-board .cn-index-sidebar__checklist-count {
|
|
2184
|
+
color: var(--color-text-maxcontrast);
|
|
2185
|
+
font-size: 13px;
|
|
2186
|
+
}
|
package/src/css/look-board.css
CHANGED
|
@@ -2173,6 +2173,26 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .v-popper__popper.action-
|
|
|
2173
2173
|
line-height: normal;
|
|
2174
2174
|
}
|
|
2175
2175
|
|
|
2176
|
+
/* A list in a side card draws a hairline over every row, the first too, so
|
|
2177
|
+
a line separates the title from the list (DqZaaktype*, DqTenant,
|
|
2178
|
+
DqSociaalDomein, DqTaak "Versies" and the like). Its row titles keep the
|
|
2179
|
+
main ink without an underline: the plain side rule marks them as links,
|
|
2180
|
+
and the board underlines on hover only. The footer note is 13px muted; the
|
|
2181
|
+
theme sets paragraphs through its tokens with `!important`, so the note
|
|
2182
|
+
hands it its size and ink through those tokens. */
|
|
2183
|
+
.cn-look-board .cn-detail-page__side .cn-record-list--rows:not(.cn-look-nextcloud *) .cn-record-list__item {
|
|
2184
|
+
border-top: 1px solid var(--cn-board-hairline);
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
.cn-look-board .cn-detail-page__side .cn-record-list--rows:not(.cn-look-nextcloud *) .cn-record-list__row:not(:hover):not(:focus-visible) .cn-record-list__title {
|
|
2188
|
+
text-decoration: none;
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
.cn-look-board .cn-record-list__footer-note:not(.cn-look-nextcloud *) {
|
|
2192
|
+
--nldesign-component-paragraph-font-size: 13px;
|
|
2193
|
+
--nldesign-component-paragraph-color: var(--color-text-maxcontrast);
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2176
2196
|
/* An audit row is a board list row (DqHandhaving, DqTenant "Historie"): 10px
|
|
2177
2197
|
above and below, a hairline over every row including the first, the head
|
|
2178
2198
|
and the line under it at their normal line height, 2px apart: 56px, the
|
|
@@ -2412,6 +2432,29 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .v-popper__popper.action-
|
|
|
2412
2432
|
gap: 2px;
|
|
2413
2433
|
}
|
|
2414
2434
|
|
|
2435
|
+
/* A stacked list (`density: "stacked"`) keeps its rows unruled and unpadded
|
|
2436
|
+
in the board look too, 10px apart (DqDagAfsluiten "Morgen eerst").
|
|
2437
|
+
@spec openspec/changes/record-list-stacked-density/specs/record-list-stacked-density/spec.md#requirement-a-record-list-can-stack-its-rows */
|
|
2438
|
+
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-record-list--stacked .cn-record-list__row {
|
|
2439
|
+
min-height: 0;
|
|
2440
|
+
padding-block: 0;
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-record-list--stacked.cn-record-list--rows .cn-record-list__item + .cn-record-list__item {
|
|
2444
|
+
border-top: 0;
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
/* A dashboard card's task list and decision list rule above every row, the
|
|
2448
|
+
first one under the title included, as the record lists with ruleAbove do
|
|
2449
|
+
(DqMijnWerk "Mijn taken", "Mijn archiefbeoordelingen"); no rule under the
|
|
2450
|
+
last row.
|
|
2451
|
+
@spec openspec/changes/board-dashboard-lists-rule-above/specs/board-dashboard-lists-rule-above/spec.md#requirement-a-dashboard-task-or-decision-list-rules-above-every-row */
|
|
2452
|
+
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-tasks-widget__row,
|
|
2453
|
+
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-decision-list__row {
|
|
2454
|
+
border-block-end: 0;
|
|
2455
|
+
border-top: 1px solid var(--color-border);
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2415
2458
|
/* Hover: the whole row takes the hover ground, 8px beyond the text so the
|
|
2416
2459
|
text does not move. */
|
|
2417
2460
|
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-record-list__row--link {
|
|
@@ -2680,6 +2723,17 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .v-popper__popper.action-
|
|
|
2680
2723
|
padding: 0;
|
|
2681
2724
|
}
|
|
2682
2725
|
|
|
2726
|
+
/* A chart in a section of a sections widget (a framed section card, 14px
|
|
2727
|
+
16px in) takes none of the dashboard card's inset either: its bars and plot
|
|
2728
|
+
start 17px in, not 39 (DqDoorlooptijd "Naleving": Naleving per zaaktype,
|
|
2729
|
+
Verdeling doorlooptijd, Maandelijkse SLA-trend, Doorstroom).
|
|
2730
|
+
@spec openspec/changes/board-sections-chart-single-inset/specs/board-sections-chart-single-inset/spec.md#requirement-a-chart-inside-a-dashboard-sections-card-is-inset-once */
|
|
2731
|
+
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-widget-wrapper:not(.cn-widget-wrapper--borderless) .cn-sections-widget__section .cn-chart-widget__html,
|
|
2732
|
+
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-widget-wrapper:not(.cn-widget-wrapper--borderless) .cn-sections-widget__section .cn-chart-widget__canvas,
|
|
2733
|
+
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-widget-wrapper:not(.cn-widget-wrapper--borderless) .cn-sections-widget__section .cn-chart-widget > .cn-chart-widget__footnote {
|
|
2734
|
+
padding: 0;
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2683
2737
|
/* ---- A plain sections widget on a dashboard card ------------------------ */
|
|
2684
2738
|
/* The card is flush, because a sections widget usually draws its own cards;
|
|
2685
2739
|
the plain one draws none, so its body takes the card's side inset and 20px
|
|
@@ -2805,6 +2859,27 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .v-popper__popper.action-
|
|
|
2805
2859
|
font-weight: 600;
|
|
2806
2860
|
}
|
|
2807
2861
|
|
|
2862
|
+
/* A toned cell takes the theme's status badge grounds, as the board's tones
|
|
2863
|
+
are (DqHandhaving: #E7F3EA, #FFF4DE, #FCEDEC); the darkest tone has its
|
|
2864
|
+
own token and otherwise deepens the error ground. Nextcloud's own mix of
|
|
2865
|
+
the status colours read a shade off each.
|
|
2866
|
+
@spec openspec/changes/board-matrix-tones/specs/board-matrix-tones/spec.md#requirement-a-board-matrix-cell-takes-the-themes-status-grounds */
|
|
2867
|
+
.cn-look-board .cn-chart-widget__matrix:not(.cn-look-nextcloud *) .cn-chart-widget__matrix-cell--success {
|
|
2868
|
+
background: var(--nldesign-component-status-badge-success-background-color, var(--color-success-light, color-mix(in srgb, var(--color-success) 15%, var(--color-main-background))));
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2871
|
+
.cn-look-board .cn-chart-widget__matrix:not(.cn-look-nextcloud *) .cn-chart-widget__matrix-cell--warning {
|
|
2872
|
+
background: var(--nldesign-component-status-badge-warning-background-color, var(--color-warning-light, color-mix(in srgb, var(--color-warning) 15%, var(--color-main-background))));
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
.cn-look-board .cn-chart-widget__matrix:not(.cn-look-nextcloud *) .cn-chart-widget__matrix-cell--error {
|
|
2876
|
+
background: var(--nldesign-component-status-badge-error-background-color, var(--color-error-light, color-mix(in srgb, var(--color-error) 12%, var(--color-main-background))));
|
|
2877
|
+
}
|
|
2878
|
+
|
|
2879
|
+
.cn-look-board .cn-chart-widget__matrix:not(.cn-look-nextcloud *) .cn-chart-widget__matrix-cell--danger {
|
|
2880
|
+
background: var(--nldesign-component-status-badge-danger-background-color, color-mix(in srgb, var(--color-error) 10%, var(--nldesign-component-status-badge-error-background-color, var(--color-main-background))));
|
|
2881
|
+
}
|
|
2882
|
+
|
|
2808
2883
|
/* A cell without a tone ("geen") sits on the page ground in the muted ink,
|
|
2809
2884
|
as the board draws it; toned and coloured cells keep their ground.
|
|
2810
2885
|
@spec openspec/changes/board-matrix-plain-cells/specs/board-matrix-plain-cells/spec.md#requirement-a-board-matrix-cell-without-a-tone-sits-on-the-ground-in-the-muted-ink */
|
|
@@ -3002,6 +3077,23 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .v-popper__popper.action-
|
|
|
3002
3077
|
line-height: normal;
|
|
3003
3078
|
}
|
|
3004
3079
|
|
|
3080
|
+
/* A bar list with the value at the bar's end (DqProcesanalyse "Doorlooptijd
|
|
3081
|
+
per status") sets its rows in 13px, the label in the secondary text, the
|
|
3082
|
+
value in the main ink at 600, rows 34px apart.
|
|
3083
|
+
@spec openspec/changes/chart-bar-list-value-at-end/specs/chart-bar-list-value-at-end/spec.md#requirement-a-bar-list-can-put-each-value-at-its-bars-end */
|
|
3084
|
+
.cn-look-board .cn-chart-widget__bar-list--value-at-end:not(.cn-look-nextcloud *) {
|
|
3085
|
+
gap: 16px;
|
|
3086
|
+
}
|
|
3087
|
+
|
|
3088
|
+
.cn-look-board .cn-chart-widget__bar-list--value-at-end:not(.cn-look-nextcloud *) .cn-chart-widget__bar-line {
|
|
3089
|
+
font-size: 13px;
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
.cn-look-board .cn-chart-widget__bar-list--value-at-end:not(.cn-look-nextcloud *) .cn-chart-widget__bar-label {
|
|
3093
|
+
--nldesign-color-on-surface: var(--cn-board-text-soft);
|
|
3094
|
+
color: var(--cn-board-text-soft);
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3005
3097
|
/* A dialog's options as the boards draw them (DqVolgendeStap, DqBesluit,
|
|
3006
3098
|
DqDocumentGenereren, DqFilterBewerken): rounded at 10px, an idle option
|
|
3007
3099
|
edged in the dark border grey, and an 18px radio. The chosen option keeps
|
package/src/css/table.css
CHANGED
|
@@ -226,6 +226,18 @@
|
|
|
226
226
|
overflow-wrap: anywhere;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
+
/* In a table fitted to its tile every column but the growing one asks for
|
|
230
|
+
as little width as its content allows (`width: 1%`). With
|
|
231
|
+
`overflow-wrap: anywhere` that is one character, so a wrapping head broke
|
|
232
|
+
letter by letter ("D o e l", DqDoorlooptijd "Prestaties per zaaktype",
|
|
233
|
+
240px tall). Here a wrapping column breaks between words and only breaks
|
|
234
|
+
a word that is longer than the column.
|
|
235
|
+
@spec openspec/changes/fit-table-wrap-by-word/specs/fit-table-wrap-by-word/spec.md#requirement-a-wrapping-column-in-a-fitted-table-breaks-between-words */
|
|
236
|
+
.cn-data-table--fit th.cn-cell--wrap,
|
|
237
|
+
.cn-data-table--fit td.cn-cell--wrap {
|
|
238
|
+
overflow-wrap: break-word;
|
|
239
|
+
}
|
|
240
|
+
|
|
229
241
|
/* Opt-in fixed layout (CnDataTable's `fixedLayout` prop).
|
|
230
242
|
|
|
231
243
|
Under the default `table-layout: auto` a column's declared `width` is only a
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The line under a team in a picker: how many members it has and what it is
|
|
3
|
+
* for ("6 leden · bezwaar, beroep en Woo-advies", board DqOverdragen).
|
|
4
|
+
*
|
|
5
|
+
* The count comes from Nextcloud's group details; that endpoint answers
|
|
6
|
+
* administrators and group admins only, so for anyone else the count is left
|
|
7
|
+
* out rather than guessed. Nextcloud keeps no description for a group, so the
|
|
8
|
+
* text comes from an app's own records when the app names them (`describe`:
|
|
9
|
+
* `{ register, schema, groupField?, textField? }`, by default the record's
|
|
10
|
+
* `ncGroupId` and `description`).
|
|
11
|
+
*
|
|
12
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
13
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
14
|
+
*
|
|
15
|
+
* @module utils/groupSublines
|
|
16
|
+
* @spec openspec/changes/assign-team-option-subline/specs/assign-dialog/spec.md
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import axios from '@nextcloud/axios'
|
|
20
|
+
import { translatePlural as n } from '@nextcloud/l10n'
|
|
21
|
+
import { generateOcsUrl } from '@nextcloud/router'
|
|
22
|
+
import { fetchReferenceObjects } from './boardReferenceColumns.js'
|
|
23
|
+
|
|
24
|
+
const descriptionCache = new Map()
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Member counts by group id for the groups a search finds. Fails soft to `{}`.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} query The search term.
|
|
30
|
+
* @return {Promise<Record<string, number>>} Counts by group id.
|
|
31
|
+
* @spec openspec/changes/assign-team-option-subline/specs/assign-dialog/spec.md
|
|
32
|
+
*/
|
|
33
|
+
async function memberCounts(query) {
|
|
34
|
+
try {
|
|
35
|
+
const response = await axios.get(generateOcsUrl('cloud/groups/details'), {
|
|
36
|
+
headers: { 'OCS-APIRequest': 'true', Accept: 'application/json' },
|
|
37
|
+
params: { search: query || '', limit: 50 },
|
|
38
|
+
})
|
|
39
|
+
const data = response && response.data && response.data.ocs ? response.data.ocs.data : (response && response.data)
|
|
40
|
+
const groups = data && Array.isArray(data.groups) ? data.groups : []
|
|
41
|
+
const out = {}
|
|
42
|
+
for (const group of groups) {
|
|
43
|
+
const count = Number(group && group.usercount)
|
|
44
|
+
if (group && group.id && Number.isFinite(count)) {
|
|
45
|
+
out[String(group.id)] = count
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return out
|
|
49
|
+
} catch {
|
|
50
|
+
return {}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Descriptions by group id from the app's records. Read once per source.
|
|
56
|
+
*
|
|
57
|
+
* @param {{register?: string, schema?: string, groupField?: string, textField?: string}|null} describe The source.
|
|
58
|
+
* @return {Promise<Record<string, string>>} Descriptions by group id.
|
|
59
|
+
* @spec openspec/changes/assign-team-option-subline/specs/assign-dialog/spec.md
|
|
60
|
+
*/
|
|
61
|
+
async function descriptions(describe) {
|
|
62
|
+
if (!describe || !describe.register || !describe.schema) {
|
|
63
|
+
return {}
|
|
64
|
+
}
|
|
65
|
+
const groupField = describe.groupField || 'ncGroupId'
|
|
66
|
+
const textField = describe.textField || 'description'
|
|
67
|
+
const key = [describe.register, describe.schema, groupField, textField].join('/')
|
|
68
|
+
if (!descriptionCache.has(key)) {
|
|
69
|
+
descriptionCache.set(key, fetchReferenceObjects({ register: describe.register, schema: describe.schema, limit: 500 })
|
|
70
|
+
.then((rows) => {
|
|
71
|
+
const out = {}
|
|
72
|
+
for (const row of rows) {
|
|
73
|
+
const gid = row && row[groupField]
|
|
74
|
+
const text = row && typeof row[textField] === 'string' ? row[textField].trim() : ''
|
|
75
|
+
if (gid && text && out[String(gid)] === undefined) {
|
|
76
|
+
out[String(gid)] = text
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return out
|
|
80
|
+
})
|
|
81
|
+
.catch(() => {
|
|
82
|
+
descriptionCache.delete(key)
|
|
83
|
+
return {}
|
|
84
|
+
}))
|
|
85
|
+
}
|
|
86
|
+
return descriptionCache.get(key)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The line under each group: its member count and its description, joined by
|
|
91
|
+
* " · ", either left out when unknown.
|
|
92
|
+
*
|
|
93
|
+
* @param {string[]} ids The group ids to describe.
|
|
94
|
+
* @param {string} query The search the groups came from.
|
|
95
|
+
* @param {object|null} [describe] Where the app keeps a group's description.
|
|
96
|
+
* @return {Promise<Record<string, string>>} The line by group id ('' when nothing is known).
|
|
97
|
+
* @spec openspec/changes/assign-team-option-subline/specs/assign-dialog/spec.md
|
|
98
|
+
*/
|
|
99
|
+
export async function groupSublines(ids, query, describe = null) {
|
|
100
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
101
|
+
return {}
|
|
102
|
+
}
|
|
103
|
+
const [counts, texts] = await Promise.all([memberCounts(query), descriptions(describe)])
|
|
104
|
+
const out = {}
|
|
105
|
+
for (const id of ids) {
|
|
106
|
+
const parts = []
|
|
107
|
+
if (counts[id] !== undefined) {
|
|
108
|
+
parts.push(n('nextcloud-vue', '{count} member', '{count} members', counts[id], { count: counts[id] }))
|
|
109
|
+
}
|
|
110
|
+
if (texts[id]) {
|
|
111
|
+
parts.push(texts[id])
|
|
112
|
+
}
|
|
113
|
+
out[id] = parts.join(' · ')
|
|
114
|
+
}
|
|
115
|
+
return out
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Forget the cached descriptions (tests, or after the source changed).
|
|
120
|
+
*
|
|
121
|
+
* @return {void}
|
|
122
|
+
*/
|
|
123
|
+
export function clearGroupSublineCache() {
|
|
124
|
+
descriptionCache.clear()
|
|
125
|
+
}
|