@conduction/nextcloud-vue 2.105.0 → 2.106.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/CnCardGrid/CnCardGrid.vue.js +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +18 -0
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js +3 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +18 -0
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +53 -4
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js +9 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js.map +1 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js +24 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js.map +1 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js +49 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js.map +1 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +36 -18
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +17 -38
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +53 -2
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +119 -111
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue.js +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +62 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +159 -50
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +121 -98
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +24 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +20 -4
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js +22 -4
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +112 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js +100 -44
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue.js +1 -1
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js +67 -4
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js +1 -1
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue.js +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +295 -12
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +267 -144
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +4 -0
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +3 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +93 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +322 -246
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue.js +1 -1
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js +122 -7
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js +189 -93
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue.js +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +42 -0
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +38 -19
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue.js +9 -0
- package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue.js.map +1 -0
- package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue2.js +258 -0
- package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue3.js +69 -0
- package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +12 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +21 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +21 -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 +803 -453
- package/dist/esm/utils/dueText.js +18 -3
- package/dist/esm/utils/dueText.js.map +1 -1
- package/dist/esm/utils/libraryWidgetKeys.js +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/sortOrder.js +11 -1
- package/dist/esm/utils/sortOrder.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +8281 -6350
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +803 -453
- package/l10n/en.json +21 -1
- package/l10n/nl.json +21 -1
- package/package.json +1 -1
- package/src/components/CnCardGrid/CnCardGrid.md +2 -0
- package/src/components/CnCardGrid/CnCardGrid.vue +20 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +19 -1
- package/src/components/CnDetailPage/CnDetailPage.md +2 -2
- package/src/components/CnDetailPage/CnDetailPage.vue +53 -4
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue +60 -0
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +36 -29
- package/src/components/CnFactsWidget/CnFactsWidget.vue +4 -0
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +70 -4
- package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +62 -1
- package/src/components/CnIndexPage/CnIndexPage.md +7 -5
- package/src/components/CnIndexPage/CnIndexPage.vue +187 -58
- package/src/components/CnIndexPage/CnIndexPageWidgets.vue +36 -1
- package/src/components/CnIndexPage/useSelfFetchList.js +22 -4
- package/src/components/CnIndexSidebar/CnIndexSidebar.md +2 -0
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +148 -1
- package/src/components/CnNcWidgetWidget/CnNcWidgetWidget.vue +68 -5
- package/src/components/CnObjectCard/CnObjectCard.md +1 -0
- package/src/components/CnObjectCard/CnObjectCard.vue +354 -13
- package/src/components/CnObjectSidebar/CnFilesTab.vue +6 -0
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +190 -4
- package/src/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue +209 -9
- package/src/components/CnTabs/CnTabs.vue +11 -0
- package/src/components/CnTabsWidget/CnTabsWidget.vue +91 -18
- package/src/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.md +11 -0
- package/src/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue +382 -0
- package/src/components/CnTeamWorkloadWidget/index.js +14 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +12 -0
- package/src/components/index.js +1 -0
- package/src/css/index-page.css +14 -2
- package/src/css/look-board-detail.css +48 -4
- package/src/css/look-board-index.css +64 -2
- package/src/css/look-board.css +25 -2
- package/src/index.js +1 -1
- package/src/schemas/app-manifest-v2.schema.json +75 -6
- package/src/utils/dueText.js +18 -3
- package/src/utils/libraryWidgetKeys.js +1 -0
- package/src/utils/sortOrder.js +10 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
|
@@ -33,6 +33,31 @@
|
|
|
33
33
|
{{ noticeText }}
|
|
34
34
|
</p>
|
|
35
35
|
</div>
|
|
36
|
+
<!-- table: one row per record under a header, one cell per `columns`
|
|
37
|
+
entry; a cell past its `alertAbove` reads bold in the danger colour. -->
|
|
38
|
+
<table v-else-if="presentation === 'table'" class="cn-record-list__table" data-testid="cn-record-list-table">
|
|
39
|
+
<thead>
|
|
40
|
+
<tr>
|
|
41
|
+
<th
|
|
42
|
+
v-for="col in tableColumns"
|
|
43
|
+
:key="col.key"
|
|
44
|
+
scope="col"
|
|
45
|
+
:class="{ 'cn-record-list__cell--end': col.align === 'end' }">
|
|
46
|
+
{{ col.label }}
|
|
47
|
+
</th>
|
|
48
|
+
</tr>
|
|
49
|
+
</thead>
|
|
50
|
+
<tbody>
|
|
51
|
+
<tr v-for="item in tableRows" :key="item.key" data-testid="cn-record-list-item">
|
|
52
|
+
<td
|
|
53
|
+
v-for="cell in item.cells"
|
|
54
|
+
:key="cell.key"
|
|
55
|
+
:class="{ 'cn-record-list__cell--end': cell.align === 'end', 'cn-record-list__cell--alert': cell.alert }">
|
|
56
|
+
{{ cell.text }}
|
|
57
|
+
</td>
|
|
58
|
+
</tr>
|
|
59
|
+
</tbody>
|
|
60
|
+
</table>
|
|
36
61
|
<ul v-else class="cn-record-list__list">
|
|
37
62
|
<li
|
|
38
63
|
v-for="item in items"
|
|
@@ -48,6 +73,11 @@
|
|
|
48
73
|
class="cn-record-list__avatar"
|
|
49
74
|
aria-hidden="true"
|
|
50
75
|
data-testid="cn-record-list-avatar">{{ item.initials }}</span>
|
|
76
|
+
<span v-if="item.dot"
|
|
77
|
+
class="cn-record-list__dot"
|
|
78
|
+
:style="{ background: item.dot }"
|
|
79
|
+
aria-hidden="true"
|
|
80
|
+
data-testid="cn-record-list-dot" />
|
|
51
81
|
<span class="cn-record-list__main">
|
|
52
82
|
<component
|
|
53
83
|
:is="item.tag"
|
|
@@ -59,6 +89,7 @@
|
|
|
59
89
|
</component>
|
|
60
90
|
<span v-if="item.meta" class="cn-record-list__meta">{{ item.meta }}</span>
|
|
61
91
|
</span>
|
|
92
|
+
<span v-if="item.flag" class="cn-record-list__flag" data-testid="cn-record-list-flag">{{ item.flag }}</span>
|
|
62
93
|
<span v-if="item.pill || item.badges.length > 0" class="cn-record-list__pills">
|
|
63
94
|
<CnStatusBadge
|
|
64
95
|
v-if="item.pill"
|
|
@@ -76,7 +107,7 @@
|
|
|
76
107
|
<span
|
|
77
108
|
v-if="item.due"
|
|
78
109
|
class="cn-record-list__due cn-record-list__due--column"
|
|
79
|
-
:class="`cn-record-list__due--${item.due.tone}
|
|
110
|
+
:class="[`cn-record-list__due--${item.due.tone}`, { 'cn-record-list__due--soon': item.due.soon }]"
|
|
80
111
|
data-testid="cn-record-list-due">{{ item.due.text }}</span>
|
|
81
112
|
<span v-if="item.trailing" class="cn-record-list__trailing">{{ item.trailing }}</span>
|
|
82
113
|
<component
|
|
@@ -99,11 +130,17 @@
|
|
|
99
130
|
class="cn-record-list__avatar"
|
|
100
131
|
aria-hidden="true"
|
|
101
132
|
data-testid="cn-record-list-avatar">{{ item.initials }}</span>
|
|
133
|
+
<span v-if="item.dot"
|
|
134
|
+
class="cn-record-list__dot"
|
|
135
|
+
:style="{ background: item.dot }"
|
|
136
|
+
aria-hidden="true"
|
|
137
|
+
data-testid="cn-record-list-dot" />
|
|
102
138
|
<span class="cn-record-list__main">
|
|
103
139
|
<span v-if="item.title" class="cn-record-list__title">{{ item.title }}</span>
|
|
104
140
|
<span v-if="item.meta" class="cn-record-list__meta">{{ item.meta }}</span>
|
|
105
141
|
</span>
|
|
106
|
-
<span v-if="item.pill || item.badges.length > 0 || item.due || item.trailing" class="cn-record-list__aside">
|
|
142
|
+
<span v-if="item.pill || item.badges.length > 0 || item.due || item.trailing || item.flag" class="cn-record-list__aside">
|
|
143
|
+
<span v-if="item.flag" class="cn-record-list__flag" data-testid="cn-record-list-flag">{{ item.flag }}</span>
|
|
107
144
|
<CnStatusBadge
|
|
108
145
|
v-if="item.pill"
|
|
109
146
|
:label="item.pill.label"
|
|
@@ -119,7 +156,7 @@
|
|
|
119
156
|
<span
|
|
120
157
|
v-if="item.due"
|
|
121
158
|
class="cn-record-list__due"
|
|
122
|
-
:class="`cn-record-list__due--${item.due.tone}
|
|
159
|
+
:class="[`cn-record-list__due--${item.due.tone}`, { 'cn-record-list__due--soon': item.due.soon }]"
|
|
123
160
|
data-testid="cn-record-list-due">{{ item.due.text }}</span>
|
|
124
161
|
<span v-if="item.trailing" class="cn-record-list__trailing">{{ item.trailing }}</span>
|
|
125
162
|
</span>
|
|
@@ -201,6 +238,9 @@
|
|
|
201
238
|
</span>
|
|
202
239
|
</li>
|
|
203
240
|
</ul>
|
|
241
|
+
<p v-if="footnote && !error" class="cn-record-list__footnote" data-testid="cn-record-list-footnote">
|
|
242
|
+
{{ footnote }}
|
|
243
|
+
</p>
|
|
204
244
|
<!-- `footerAction`: one action under the list, also under the empty
|
|
205
245
|
text ("Beroep registreren"). With `disabledWhen` it greys out and
|
|
206
246
|
says why (`disabledReason`: "Kan zodra de beslissing is
|
|
@@ -255,7 +295,7 @@ const PAGE_REFRESH_CHANNEL = 'cn:page:refresh'
|
|
|
255
295
|
|
|
256
296
|
/** Gives each list's footer reason its own element id. */
|
|
257
297
|
let footerSeq = 0
|
|
258
|
-
const PRESENTATIONS = ['rows', 'feed', 'inline', 'cards', 'notice']
|
|
298
|
+
const PRESENTATIONS = ['rows', 'feed', 'inline', 'cards', 'notice', 'table']
|
|
259
299
|
|
|
260
300
|
/** The tones a `notice` list takes. */
|
|
261
301
|
const NOTICE_VARIANTS = ['info', 'warning', 'error']
|
|
@@ -343,6 +383,9 @@ export default {
|
|
|
343
383
|
* `presentation: "notice"` draws the rows as sentences in one box toned by
|
|
344
384
|
* `variant` (`info`, `warning` or `error`), after an optional `lead`
|
|
345
385
|
* (`{total}` filled in).
|
|
386
|
+
* `dot` (a field, or `{ field, colorMap? }`) draws a colour dot before
|
|
387
|
+
* the title; `flags` (`[{ field, label, value? }]`) draws the labels
|
|
388
|
+
* of the flags a row has ("Start", "End") at its end.
|
|
346
389
|
* `footerAction` (`{ label, icon?, type, ..., disabledWhen?,
|
|
347
390
|
* disabledReason? }`) ends the card with one action, also under the
|
|
348
391
|
* empty text; while the visibleWhen condition `disabledWhen` holds the
|
|
@@ -579,10 +622,55 @@ export default {
|
|
|
579
622
|
due: this.dueOf(row),
|
|
580
623
|
icon: this.iconOf(row),
|
|
581
624
|
bar: this.barOf(row, barScale),
|
|
625
|
+
dot: this.dotOf(row),
|
|
626
|
+
flag: this.flagOf(row),
|
|
582
627
|
}
|
|
583
628
|
})
|
|
584
629
|
},
|
|
585
630
|
|
|
631
|
+
/**
|
|
632
|
+
* The `table` presentation's columns: `{ label, value, align?, alertAbove? }`,
|
|
633
|
+
* `value` a template over the row.
|
|
634
|
+
*
|
|
635
|
+
* @return {Array<object>} The columns with a key and a translated label.
|
|
636
|
+
* @spec openspec/changes/record-list-table/specs/record-list-table/spec.md#requirement-a-record-list-can-draw-its-rows-as-a-small-table
|
|
637
|
+
*/
|
|
638
|
+
tableColumns() {
|
|
639
|
+
const columns = Array.isArray(this.content?.columns) ? this.content.columns : []
|
|
640
|
+
return columns
|
|
641
|
+
.filter((col) => col && typeof col === 'object' && typeof col.value === 'string')
|
|
642
|
+
.map((col, index) => ({ ...col, key: `col-${index}`, label: typeof col.label === 'string' ? this.translate(col.label) : '' }))
|
|
643
|
+
},
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* The `table` presentation's rows: one cell per column.
|
|
647
|
+
*
|
|
648
|
+
* @return {Array<object>} `{ key, cells: [{ key, text, align, alert }] }`.
|
|
649
|
+
* @spec openspec/changes/record-list-table/specs/record-list-table/spec.md#requirement-a-record-list-can-draw-its-rows-as-a-small-table
|
|
650
|
+
*/
|
|
651
|
+
tableRows() {
|
|
652
|
+
return this.items.map((item) => ({
|
|
653
|
+
key: item.key,
|
|
654
|
+
cells: this.tableColumns.map((col) => {
|
|
655
|
+
const text = this.fill(col.value, item.row)
|
|
656
|
+
const limit = Number(col.alertAbove)
|
|
657
|
+
const value = Number(text)
|
|
658
|
+
return {
|
|
659
|
+
key: col.key,
|
|
660
|
+
text,
|
|
661
|
+
align: col.align,
|
|
662
|
+
alert: col.alertAbove !== undefined && Number.isFinite(limit) && text !== '' && Number.isFinite(value) && value > limit,
|
|
663
|
+
}
|
|
664
|
+
}),
|
|
665
|
+
}))
|
|
666
|
+
},
|
|
667
|
+
|
|
668
|
+
/** @return {string} The note under the list (`footnote`), translated. */
|
|
669
|
+
footnote() {
|
|
670
|
+
const note = this.content?.footnote
|
|
671
|
+
return typeof note === 'string' && note !== '' ? this.translate(note) : ''
|
|
672
|
+
},
|
|
673
|
+
|
|
586
674
|
/**
|
|
587
675
|
* The largest `bar.valueField` over the rows shown, the scale of a
|
|
588
676
|
* `bar` without `maxField`. 0 without a `bar`.
|
|
@@ -699,6 +787,50 @@ export default {
|
|
|
699
787
|
return typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text
|
|
700
788
|
},
|
|
701
789
|
|
|
790
|
+
/**
|
|
791
|
+
* A row's colour dot (`dot`: a field name, or `{ field, colorMap? }`):
|
|
792
|
+
* the field's colour, or its value through `colorMap`. Only a hex
|
|
793
|
+
* colour, an `rgb()`/`hsl()` colour or a `var(--…)` token is drawn.
|
|
794
|
+
*
|
|
795
|
+
* @spec openspec/changes/record-list-dot-flags/specs/record-list/spec.md#requirement-a-record-list-row-can-carry-a-colour-dot-and-flag-text
|
|
796
|
+
* @param {object} row The row.
|
|
797
|
+
* @return {string} The colour, or ''.
|
|
798
|
+
*/
|
|
799
|
+
dotOf(row) {
|
|
800
|
+
const dot = this.content?.dot
|
|
801
|
+
const def = typeof dot === 'string' ? { field: dot } : dot
|
|
802
|
+
if (!def || typeof def !== 'object' || typeof def.field !== 'string' || def.field === '') {
|
|
803
|
+
return ''
|
|
804
|
+
}
|
|
805
|
+
const raw = readPath(row, def.field)
|
|
806
|
+
if (raw === null || raw === undefined || raw === '') {
|
|
807
|
+
return ''
|
|
808
|
+
}
|
|
809
|
+
const mapped = def.colorMap && typeof def.colorMap === 'object' && typeof def.colorMap[String(raw)] === 'string' ? def.colorMap[String(raw)] : String(raw)
|
|
810
|
+
return /^(#[0-9a-f]{3,8}|(rgb|rgba|hsl|hsla)\([^;{}]*\)|var\(--[a-z0-9-]+(,[^;{}]*)?\))$/i.test(mapped.trim()) ? mapped.trim() : ''
|
|
811
|
+
},
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* A row's flag text (`flags`: `[{ field, label, value? }]`): the
|
|
815
|
+
* labels of the flags whose field is set (or equals `value`), through
|
|
816
|
+
* the host translate and joined with " · " ("Start", "End").
|
|
817
|
+
*
|
|
818
|
+
* @spec openspec/changes/record-list-dot-flags/specs/record-list/spec.md#requirement-a-record-list-row-can-carry-a-colour-dot-and-flag-text
|
|
819
|
+
* @param {object} row The row.
|
|
820
|
+
* @return {string} The text, or ''.
|
|
821
|
+
*/
|
|
822
|
+
flagOf(row) {
|
|
823
|
+
const flags = Array.isArray(this.content?.flags) ? this.content.flags : []
|
|
824
|
+
return flags
|
|
825
|
+
.filter((flag) => flag && typeof flag.field === 'string' && typeof flag.label === 'string' && flag.label !== '')
|
|
826
|
+
.filter((flag) => {
|
|
827
|
+
const raw = readPath(row, flag.field)
|
|
828
|
+
return flag.value !== undefined ? String(raw) === String(flag.value) : raw === true || raw === 'true' || raw === 1
|
|
829
|
+
})
|
|
830
|
+
.map((flag) => this.translate(flag.label))
|
|
831
|
+
.join(' · ')
|
|
832
|
+
},
|
|
833
|
+
|
|
702
834
|
/**
|
|
703
835
|
* Settle whether the footer action is disabled: `disabledWhen` is a
|
|
704
836
|
* visibleWhen condition read against the page's record (`field`), the
|
|
@@ -1408,6 +1540,19 @@ export default {
|
|
|
1408
1540
|
text-decoration: underline;
|
|
1409
1541
|
}
|
|
1410
1542
|
|
|
1543
|
+
.cn-record-list__dot {
|
|
1544
|
+
flex: 0 0 14px;
|
|
1545
|
+
width: 14px;
|
|
1546
|
+
height: 14px;
|
|
1547
|
+
border-radius: 50%;
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
.cn-record-list__flag {
|
|
1551
|
+
color: var(--color-text-maxcontrast);
|
|
1552
|
+
font-size: 14px;
|
|
1553
|
+
white-space: nowrap;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1411
1556
|
.cn-record-list__avatar {
|
|
1412
1557
|
align-items: center;
|
|
1413
1558
|
background: var(--color-background-hover);
|
|
@@ -1462,6 +1607,47 @@ export default {
|
|
|
1462
1607
|
font-weight: 600;
|
|
1463
1608
|
}
|
|
1464
1609
|
|
|
1610
|
+
.cn-record-list__table {
|
|
1611
|
+
width: 100%;
|
|
1612
|
+
border-collapse: collapse;
|
|
1613
|
+
font-size: 14px;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
.cn-record-list__table th {
|
|
1617
|
+
padding: 6px 0;
|
|
1618
|
+
border-bottom: 1px solid var(--color-border);
|
|
1619
|
+
color: var(--color-text-maxcontrast);
|
|
1620
|
+
font-weight: 600;
|
|
1621
|
+
text-align: start;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
.cn-record-list__table td {
|
|
1625
|
+
padding: 8px 0;
|
|
1626
|
+
border-bottom: 1px solid var(--color-border);
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
.cn-record-list__table .cn-record-list__cell--end {
|
|
1630
|
+
padding-inline-start: 12px;
|
|
1631
|
+
text-align: end;
|
|
1632
|
+
font-variant-numeric: tabular-nums;
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
.cn-record-list__table .cn-record-list__cell--alert {
|
|
1636
|
+
color: var(--color-text-error, var(--color-error-text));
|
|
1637
|
+
font-weight: 700;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
.cn-record-list__footnote {
|
|
1641
|
+
margin: 8px 0 0;
|
|
1642
|
+
color: var(--color-text-maxcontrast);
|
|
1643
|
+
font-size: 13px;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
.cn-record-list__due--soon {
|
|
1647
|
+
color: var(--color-text-warning, var(--color-warning-text));
|
|
1648
|
+
font-weight: 600;
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1465
1651
|
.cn-record-list__due--empty {
|
|
1466
1652
|
color: var(--color-text-maxcontrast);
|
|
1467
1653
|
}
|
|
@@ -42,7 +42,53 @@
|
|
|
42
42
|
<div class="cn-related-objects-widget">
|
|
43
43
|
<!-- Tabbed self-fetch mode -->
|
|
44
44
|
<template v-if="useTabs">
|
|
45
|
-
|
|
45
|
+
<!-- `layout: "sections"`: every group under its own heading in one
|
|
46
|
+
list, a heading action beside it (DqZaakGerelateerd). -->
|
|
47
|
+
<template v-if="layout === 'sections'">
|
|
48
|
+
<section v-for="group in visibleGroups"
|
|
49
|
+
:key="`section-${group.key}`"
|
|
50
|
+
class="cn-related-objects-widget__section"
|
|
51
|
+
:aria-labelledby="`${sectionIdBase}-${group.key}`"
|
|
52
|
+
data-testid="cn-related-section">
|
|
53
|
+
<div class="cn-related-objects-widget__section-head">
|
|
54
|
+
<h4 :id="`${sectionIdBase}-${group.key}`" class="cn-related-objects-widget__section-title">
|
|
55
|
+
{{ group.label }}
|
|
56
|
+
</h4>
|
|
57
|
+
<button v-if="groupAction(group)"
|
|
58
|
+
type="button"
|
|
59
|
+
class="cn-related-objects-widget__section-action"
|
|
60
|
+
data-testid="cn-related-section-action"
|
|
61
|
+
@click="onGroupAction(group)">
|
|
62
|
+
<CnIcon v-if="groupAction(group).icon" :name="groupAction(group).icon" :size="16" />
|
|
63
|
+
{{ groupAction(group).label }}
|
|
64
|
+
</button>
|
|
65
|
+
</div>
|
|
66
|
+
<ul class="cn-related-objects-widget__list">
|
|
67
|
+
<li v-for="(item, i) in group.items" :key="`item-${group.key}-${item.id || i}`">
|
|
68
|
+
<a v-if="groupHref(group, item)"
|
|
69
|
+
class="cn-related-objects-widget__row"
|
|
70
|
+
:href="groupHref(group, item)"
|
|
71
|
+
target="_blank"
|
|
72
|
+
rel="noopener noreferrer">
|
|
73
|
+
<CnIcon :name="group.icon" :size="20" class="cn-related-objects-widget__icon" />
|
|
74
|
+
<span class="cn-related-objects-widget__label">{{ item.label }}</span>
|
|
75
|
+
<span v-if="item.meta" class="cn-related-objects-widget__meta">{{ item.meta }}</span>
|
|
76
|
+
</a>
|
|
77
|
+
<div v-else
|
|
78
|
+
class="cn-related-objects-widget__row"
|
|
79
|
+
tabindex="0"
|
|
80
|
+
role="button"
|
|
81
|
+
@click="onSelectGroupItem(group, item)"
|
|
82
|
+
@keydown.enter="onSelectGroupItem(group, item)">
|
|
83
|
+
<CnIcon :name="group.icon" :size="20" class="cn-related-objects-widget__icon" />
|
|
84
|
+
<span class="cn-related-objects-widget__label">{{ item.label }}</span>
|
|
85
|
+
<span v-if="item.meta" class="cn-related-objects-widget__meta">{{ item.meta }}</span>
|
|
86
|
+
</div>
|
|
87
|
+
</li>
|
|
88
|
+
</ul>
|
|
89
|
+
</section>
|
|
90
|
+
</template>
|
|
91
|
+
<div v-if="layout !== 'sections' && tabStripVisible" class="cn-related-objects-widget__tabs" role="tablist">
|
|
46
92
|
<button v-for="group in visibleGroups"
|
|
47
93
|
:key="`tab-${group.key}`"
|
|
48
94
|
type="button"
|
|
@@ -57,7 +103,7 @@
|
|
|
57
103
|
</button>
|
|
58
104
|
</div>
|
|
59
105
|
|
|
60
|
-
<section v-if="activeGroup" class="cn-related-objects-widget__panel" role="tabpanel">
|
|
106
|
+
<section v-if="layout !== 'sections' && activeGroup" class="cn-related-objects-widget__panel" role="tabpanel">
|
|
61
107
|
<ul class="cn-related-objects-widget__list">
|
|
62
108
|
<li v-for="(item, i) in activeGroup.items"
|
|
63
109
|
:key="`item-${activeGroup.key}-${item.id || i}`">
|
|
@@ -445,6 +491,8 @@ export default {
|
|
|
445
491
|
* reads in the app's language. Without it a title reads as stored.
|
|
446
492
|
*/
|
|
447
493
|
cnTranslate: { default: () => (key) => key },
|
|
494
|
+
/** The page's pre-bound action dispatcher (CnPageRenderer), for a group's heading action. */
|
|
495
|
+
cnDispatchAction: { default: null },
|
|
448
496
|
},
|
|
449
497
|
|
|
450
498
|
props: {
|
|
@@ -507,15 +555,46 @@ export default {
|
|
|
507
555
|
|
|
508
556
|
/**
|
|
509
557
|
* Render mode. `'tabs'` (default) self-fetches from OpenRegister and
|
|
510
|
-
* renders a tab per non-empty group. `'
|
|
558
|
+
* renders a tab per non-empty group. `'sections'` renders every group
|
|
559
|
+
* under its own heading in one list. `'list'` forces the deprecated
|
|
511
560
|
* store-action list path.
|
|
512
561
|
*
|
|
513
|
-
* @type {'tabs'|'list'}
|
|
562
|
+
* @type {'tabs'|'list'|'sections'}
|
|
514
563
|
*/
|
|
515
564
|
layout: {
|
|
516
565
|
type: String,
|
|
517
566
|
default: 'tabs',
|
|
518
|
-
validator: (value) => ['tabs', 'list'].includes(value),
|
|
567
|
+
validator: (value) => ['tabs', 'list', 'sections'].includes(value),
|
|
568
|
+
},
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Split the Objects group into one group per relation type, labelled
|
|
572
|
+
* with the label OpenRegister answers for the direction
|
|
573
|
+
* (`relation.displayLabel` on each row: "Onderwerp van deze zaak",
|
|
574
|
+
* "Wacht op"), through the host translate. A row without one stays in
|
|
575
|
+
* Objects.
|
|
576
|
+
*
|
|
577
|
+
* @spec openspec/changes/related-groups-by-relation/specs/related-objects/spec.md#requirement-related-objects-can-be-grouped-by-relation-type-under-headings
|
|
578
|
+
*/
|
|
579
|
+
groupByRelation: {
|
|
580
|
+
type: Boolean,
|
|
581
|
+
default: false,
|
|
582
|
+
},
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* An action on a group's heading in `layout: "sections"`, keyed by the
|
|
586
|
+
* group key (`objects`, `files`, an `extraSections` key, or
|
|
587
|
+
* `relation:<label>` for a relation group): `{ label, icon?, ...action }`
|
|
588
|
+
* ("Vervolg plannen"). An `extraSections` entry may carry its own
|
|
589
|
+
* `action`. Pressing it dispatches the action through the page and
|
|
590
|
+
* emits `group-action`.
|
|
591
|
+
*
|
|
592
|
+
* @type {Record<string, object>}
|
|
593
|
+
* @spec openspec/changes/related-groups-by-relation/specs/related-objects/spec.md#requirement-related-objects-can-be-grouped-by-relation-type-under-headings
|
|
594
|
+
*/
|
|
595
|
+
groupActions: {
|
|
596
|
+
type: Object,
|
|
597
|
+
default: () => ({}),
|
|
519
598
|
},
|
|
520
599
|
|
|
521
600
|
/** Include `/contracts` relations in the Objects group (opt-in). */
|
|
@@ -688,6 +767,7 @@ export default {
|
|
|
688
767
|
|
|
689
768
|
emits: [
|
|
690
769
|
'add',
|
|
770
|
+
'group-action',
|
|
691
771
|
'file-uploaded',
|
|
692
772
|
'note-added',
|
|
693
773
|
'open-integration',
|
|
@@ -724,6 +804,8 @@ export default {
|
|
|
724
804
|
groups: [],
|
|
725
805
|
/** Active tab key. */
|
|
726
806
|
activeKey: '',
|
|
807
|
+
/** Prefix for the section heading ids in `layout: "sections"`. */
|
|
808
|
+
sectionIdBase: `cn-related-${Math.random().toString(36).slice(2, 9)}`,
|
|
727
809
|
/** Keys of the tabbed sections whose request is still in flight (`objects`, `files`, `relations`). */
|
|
728
810
|
pendingSections: [],
|
|
729
811
|
/** Tabbed sections whose request failed: `{ key, label }`. */
|
|
@@ -774,7 +856,7 @@ export default {
|
|
|
774
856
|
|
|
775
857
|
/** True when the tabbed self-fetch path is usable. */
|
|
776
858
|
useTabs() {
|
|
777
|
-
return this.layout === 'tabs'
|
|
859
|
+
return (this.layout === 'tabs' || this.layout === 'sections')
|
|
778
860
|
&& Boolean(this.resolvedRegister && this.resolvedSchema && this.resolvedId)
|
|
779
861
|
},
|
|
780
862
|
|
|
@@ -809,6 +891,7 @@ export default {
|
|
|
809
891
|
items,
|
|
810
892
|
total: items.length,
|
|
811
893
|
isExtra: true,
|
|
894
|
+
action: section.action && typeof section.action === 'object' ? section.action : null,
|
|
812
895
|
}
|
|
813
896
|
})
|
|
814
897
|
},
|
|
@@ -824,7 +907,8 @@ export default {
|
|
|
824
907
|
const allow = Array.isArray(this.includeGroups) ? this.includeGroups : []
|
|
825
908
|
const own = this.groups.filter((group) => (group.total > 0 || group.items.length > 0)
|
|
826
909
|
&& (allow.length === 0 || allow.includes(group.key)))
|
|
827
|
-
|
|
910
|
+
const split = this.groupByRelation ? own.flatMap((group) => (group.key === 'objects' ? this.splitByRelation(group) : [group])) : own
|
|
911
|
+
return [...split, ...this.extraGroups]
|
|
828
912
|
},
|
|
829
913
|
|
|
830
914
|
/**
|
|
@@ -892,7 +976,7 @@ export default {
|
|
|
892
976
|
*/
|
|
893
977
|
activeGroupHrefs() {
|
|
894
978
|
const group = this.activeGroup
|
|
895
|
-
if (!group || group.isExtra || group.key === 'objects') {
|
|
979
|
+
if (!group || group.isExtra || group.key === 'objects' || String(group.key).startsWith('relation:')) {
|
|
896
980
|
return []
|
|
897
981
|
}
|
|
898
982
|
return group.items.map((item) => this.resolveItemHref(group.key, item.raw || {}))
|
|
@@ -1195,6 +1279,83 @@ export default {
|
|
|
1195
1279
|
this.$emit('select-file', raw)
|
|
1196
1280
|
},
|
|
1197
1281
|
|
|
1282
|
+
/**
|
|
1283
|
+
* The Objects group split by relation type: one group per
|
|
1284
|
+
* `relation.displayLabel`, in the order they first appear; rows
|
|
1285
|
+
* without a label stay in an Objects group.
|
|
1286
|
+
*
|
|
1287
|
+
* @spec openspec/changes/related-groups-by-relation/specs/related-objects/spec.md#requirement-related-objects-can-be-grouped-by-relation-type-under-headings
|
|
1288
|
+
* @param {object} group The Objects group.
|
|
1289
|
+
* @return {Array<object>} The groups.
|
|
1290
|
+
*/
|
|
1291
|
+
splitByRelation(group) {
|
|
1292
|
+
const byLabel = new Map()
|
|
1293
|
+
for (const item of group.items) {
|
|
1294
|
+
const relation = item.raw && item.raw.relation
|
|
1295
|
+
const label = relation && typeof relation.displayLabel === 'string' && relation.displayLabel !== '' ? relation.displayLabel : ''
|
|
1296
|
+
const key = label === '' ? 'objects' : `relation:${label}`
|
|
1297
|
+
if (!byLabel.has(key)) {
|
|
1298
|
+
byLabel.set(key, { ...group, key, label: label === '' ? group.label : this.cnTranslate(label), relationLabel: label, items: [], total: 0 })
|
|
1299
|
+
}
|
|
1300
|
+
const target = byLabel.get(key)
|
|
1301
|
+
target.items.push(item)
|
|
1302
|
+
target.total = target.items.length
|
|
1303
|
+
}
|
|
1304
|
+
return [...byLabel.values()]
|
|
1305
|
+
},
|
|
1306
|
+
|
|
1307
|
+
/**
|
|
1308
|
+
* The heading action of a group: `groupActions[group.key]`, else the
|
|
1309
|
+
* extra section's own `action`; null without one or without a label.
|
|
1310
|
+
*
|
|
1311
|
+
* @param {object} group The group.
|
|
1312
|
+
* @return {{label: string, icon: string, action: object}|null}
|
|
1313
|
+
*/
|
|
1314
|
+
groupAction(group) {
|
|
1315
|
+
const declared = (this.groupActions && this.groupActions[group.key]) || group.action || null
|
|
1316
|
+
if (!declared || typeof declared !== 'object' || typeof declared.label !== 'string' || declared.label === '') {
|
|
1317
|
+
return null
|
|
1318
|
+
}
|
|
1319
|
+
return { label: this.cnTranslate(declared.label), icon: typeof declared.icon === 'string' ? declared.icon : '', action: declared }
|
|
1320
|
+
},
|
|
1321
|
+
|
|
1322
|
+
/**
|
|
1323
|
+
* Run a group's heading action through the page's dispatcher, and say
|
|
1324
|
+
* so with `group-action`.
|
|
1325
|
+
*
|
|
1326
|
+
* @spec openspec/changes/related-groups-by-relation/specs/related-objects/spec.md#requirement-related-objects-can-be-grouped-by-relation-type-under-headings
|
|
1327
|
+
* @param {object} group The group.
|
|
1328
|
+
* @return {void}
|
|
1329
|
+
*/
|
|
1330
|
+
onGroupAction(group) {
|
|
1331
|
+
const resolved = this.groupAction(group)
|
|
1332
|
+
if (!resolved) {
|
|
1333
|
+
return
|
|
1334
|
+
}
|
|
1335
|
+
if (typeof this.cnDispatchAction === 'function' && resolved.action.type) {
|
|
1336
|
+
this.cnDispatchAction(resolved.action)
|
|
1337
|
+
}
|
|
1338
|
+
/**
|
|
1339
|
+
* @event group-action A group's heading action was pressed. Payload: `{ group, action }`, the group key and the declared action.
|
|
1340
|
+
*/
|
|
1341
|
+
this.$emit('group-action', { group: group.key, action: resolved.action })
|
|
1342
|
+
},
|
|
1343
|
+
|
|
1344
|
+
/**
|
|
1345
|
+
* The owning-app link of one row in a group, '' where the row routes
|
|
1346
|
+
* through the host (related objects, host sections).
|
|
1347
|
+
*
|
|
1348
|
+
* @param {object} group The group.
|
|
1349
|
+
* @param {object} item The row.
|
|
1350
|
+
* @return {string}
|
|
1351
|
+
*/
|
|
1352
|
+
groupHref(group, item) {
|
|
1353
|
+
if (!group || group.isExtra || group.key === 'objects' || String(group.key).startsWith('relation:')) {
|
|
1354
|
+
return ''
|
|
1355
|
+
}
|
|
1356
|
+
return this.resolveItemHref(group.key, item.raw || {})
|
|
1357
|
+
},
|
|
1358
|
+
|
|
1198
1359
|
/**
|
|
1199
1360
|
* Handle a click on a tabbed-path row that has no owning-app link
|
|
1200
1361
|
* (rows with one render as a real `<a>`): emit a host-routed event.
|
|
@@ -1213,7 +1374,7 @@ export default {
|
|
|
1213
1374
|
this.onSelectExtra(group.key, item.raw)
|
|
1214
1375
|
return
|
|
1215
1376
|
}
|
|
1216
|
-
if (group.key === 'objects') {
|
|
1377
|
+
if (group.key === 'objects' || String(group.key).startsWith('relation:')) {
|
|
1217
1378
|
this.onSelectObject(item.raw)
|
|
1218
1379
|
return
|
|
1219
1380
|
}
|
|
@@ -1860,6 +2021,45 @@ export default {
|
|
|
1860
2021
|
</script>
|
|
1861
2022
|
|
|
1862
2023
|
<style scoped>
|
|
2024
|
+
.cn-related-objects-widget__section + .cn-related-objects-widget__section {
|
|
2025
|
+
margin-top: 16px;
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
.cn-related-objects-widget__section-head {
|
|
2029
|
+
display: flex;
|
|
2030
|
+
align-items: center;
|
|
2031
|
+
justify-content: space-between;
|
|
2032
|
+
gap: 12px;
|
|
2033
|
+
padding-bottom: 8px;
|
|
2034
|
+
border-bottom: 1px solid var(--color-border);
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
.cn-related-objects-widget__section-title {
|
|
2038
|
+
margin: 0;
|
|
2039
|
+
font-size: 15px;
|
|
2040
|
+
font-weight: 700;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
.cn-related-objects-widget__section-action {
|
|
2044
|
+
display: inline-flex;
|
|
2045
|
+
align-items: center;
|
|
2046
|
+
gap: 6px;
|
|
2047
|
+
height: 34px;
|
|
2048
|
+
padding: 0 12px;
|
|
2049
|
+
border: none;
|
|
2050
|
+
border-radius: 17px;
|
|
2051
|
+
background: var(--color-primary-element-light);
|
|
2052
|
+
color: var(--color-primary-element-light-text);
|
|
2053
|
+
font-size: 13px;
|
|
2054
|
+
font-weight: 600;
|
|
2055
|
+
cursor: pointer;
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
.cn-related-objects-widget__section-action:focus-visible {
|
|
2059
|
+
outline: 2px solid var(--color-main-text);
|
|
2060
|
+
outline-offset: 2px;
|
|
2061
|
+
}
|
|
2062
|
+
|
|
1863
2063
|
.cn-related-objects-widget__group {
|
|
1864
2064
|
padding: calc(2 * var(--default-grid-baseline, 4px)) 0;
|
|
1865
2065
|
}
|
|
@@ -947,7 +947,16 @@ export default defineComponent({
|
|
|
947
947
|
overflow: visible;
|
|
948
948
|
}
|
|
949
949
|
|
|
950
|
+
/* `margin: 0`: a tab is a <button>, and Nextcloud's core stylesheet gives
|
|
951
|
+
every plain button a 3px margin, which put the tabs 7px apart and 3px low
|
|
952
|
+
(DqInstellingen: 4px apart, on the strip's top). The nldesign theme sets a
|
|
953
|
+
button's weight, corner and border width with `!important` through its
|
|
954
|
+
button tokens, which drew every tab bold with a 1px underline; the tab
|
|
955
|
+
hands its own values to those tokens. */
|
|
950
956
|
.cn-tabs--underline .cn-tabs__nav-item {
|
|
957
|
+
--nldesign-component-button-font-weight: 500;
|
|
958
|
+
--nldesign-component-button-border-radius: 0;
|
|
959
|
+
--nldesign-component-button-border-width: 0 0 3px;
|
|
951
960
|
background-color: transparent;
|
|
952
961
|
border: 0;
|
|
953
962
|
border-bottom: 3px solid transparent;
|
|
@@ -956,6 +965,7 @@ export default defineComponent({
|
|
|
956
965
|
font-size: 14px;
|
|
957
966
|
font-weight: 500;
|
|
958
967
|
gap: 8px;
|
|
968
|
+
margin: 0;
|
|
959
969
|
min-height: 44px;
|
|
960
970
|
padding: 0 9px;
|
|
961
971
|
}
|
|
@@ -975,6 +985,7 @@ export default defineComponent({
|
|
|
975
985
|
.cn-tabs--underline .cn-tabs__nav-item--active,
|
|
976
986
|
.cn-tabs--underline .cn-tabs__nav-item--active:hover,
|
|
977
987
|
.cn-tabs--underline .cn-tabs__nav-item--active:focus-visible {
|
|
988
|
+
--nldesign-component-button-font-weight: 700;
|
|
978
989
|
border-bottom: 3px solid var(--color-primary-element);
|
|
979
990
|
color: var(--color-main-text);
|
|
980
991
|
font-weight: 700;
|