@conduction/nextcloud-vue 2.138.0 → 2.140.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/CnAppRoot/CnAppRoot.vue2.js +9 -2
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +17 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +2 -2
- 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 +52 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js +23 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +106 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js +30 -7
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +59 -3
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +44 -34
- 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 +28 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js +7 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +5 -0
- package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +129 -6
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +2 -2
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +2 -0
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js +13 -2
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +28 -3
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +1 -1
- 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/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.map +1 -1
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue.js +1 -1
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js +12 -1
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js +1 -1
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue.js +1 -1
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue2.js +23 -1
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue2.js.map +1 -1
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue3.js +43 -13
- package/dist/esm/components/CnTransitionInputFields/CnTransitionInputFields.vue3.js.map +1 -1
- package/dist/esm/composables/indexSources.js +48 -6
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/composables/useTaskInboxStore.js +4 -3
- package/dist/esm/composables/useTaskInboxStore.js.map +1 -1
- package/dist/esm/l10n/en.json.js +4 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +7 -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 +604 -407
- package/dist/esm/utils/builtInFormatters.js +23 -1
- package/dist/esm/utils/builtInFormatters.js.map +1 -1
- package/dist/esm/utils/fillTemplate.js +7 -1
- package/dist/esm/utils/fillTemplate.js.map +1 -1
- package/dist/esm/utils/myTeam.js +24 -4
- package/dist/esm/utils/myTeam.js.map +1 -1
- package/dist/esm/utils/resolveFilterTokens.js +5 -4
- package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
- package/dist/esm/utils/transitionInputs.js +43 -2
- package/dist/esm/utils/transitionInputs.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +932 -236
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +604 -407
- package/l10n/en.json +4 -1
- package/l10n/nl.json +7 -1
- package/package.json +1 -1
- package/src/components/CnAppRoot/CnAppRoot.vue +9 -2
- package/src/components/CnCellRenderer/CnCellRenderer.vue +19 -3
- package/src/components/CnChartWidget/CnChartWidget.md +1 -0
- package/src/components/CnChartWidget/CnChartWidget.vue +76 -1
- package/src/components/CnConversationThread/CnConversationWidget.vue +126 -1
- package/src/components/CnFactsWidget/CnFactsWidget.vue +78 -9
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +39 -1
- package/src/components/CnFilesBrowser/filesBrowser.js +7 -1
- package/src/components/CnFilesBrowser/filesBrowserColumns.js +5 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +131 -8
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +10 -2
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +28 -3
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +1 -1
- package/src/components/CnRecordListWidget/CnRecordNoteRows.vue +10 -0
- package/src/components/CnStackedBarWidget/CnStackedBarWidget.vue +68 -2
- package/src/components/CnTransitionInputFields/CnTransitionInputFields.vue +43 -1
- package/src/composables/indexSources.js +48 -6
- package/src/composables/useTaskInboxStore.js +4 -3
- package/src/css/dialog.css +18 -0
- package/src/css/look-board-detail.css +24 -0
- package/src/css/look-board-index.css +21 -0
- package/src/css/look-board.css +31 -2
- package/src/css/table.css +13 -0
- package/src/utils/builtInFormatters.js +22 -0
- package/src/utils/fillTemplate.js +7 -1
- package/src/utils/myTeam.js +24 -4
- package/src/utils/resolveFilterTokens.js +5 -4
- package/src/utils/transitionInputs.js +42 -1
package/l10n/en.json
CHANGED
|
@@ -2378,7 +2378,10 @@
|
|
|
2378
2378
|
"Search a person or group": "Search a person or group",
|
|
2379
2379
|
"Now: by {order}.": "Now: by {order}.",
|
|
2380
2380
|
"You save the order along with a view.": "You save the order along with a view.",
|
|
2381
|
-
"me": "me"
|
|
2381
|
+
"me": "me",
|
|
2382
|
+
"Open profile": "Open profile",
|
|
2383
|
+
"Link a contact from your address book.": "Link a contact from your address book.",
|
|
2384
|
+
"The value could not be shown.": "The value could not be shown."
|
|
2382
2385
|
},
|
|
2383
2386
|
"plurals": {
|
|
2384
2387
|
"{count} member": [
|
package/l10n/nl.json
CHANGED
|
@@ -2628,7 +2628,13 @@
|
|
|
2628
2628
|
"Search a person or group": "Zoek een persoon of groep",
|
|
2629
2629
|
"Now: by {order}.": "Nu: op {order}.",
|
|
2630
2630
|
"You save the order along with a view.": "De sortering bewaart u mee in een weergave.",
|
|
2631
|
-
"me": "ik"
|
|
2631
|
+
"me": "ik",
|
|
2632
|
+
"Open profile": "Profiel openen",
|
|
2633
|
+
"Link a contact from your address book.": "Koppel een contact uit je adresboek.",
|
|
2634
|
+
"The value could not be shown.": "De waarde kon niet worden getoond.",
|
|
2635
|
+
"Save as draft": "Opslaan als concept",
|
|
2636
|
+
"Draft saved at {time}": "Concept opgeslagen om {time}",
|
|
2637
|
+
"The draft could not be saved": "Het concept kon niet worden opgeslagen"
|
|
2632
2638
|
},
|
|
2633
2639
|
"plurals": {
|
|
2634
2640
|
"{count} member": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@conduction/nextcloud-vue",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.140.0",
|
|
4
4
|
"description": "Shared Vue component library for Conduction Nextcloud apps — complements @nextcloud/vue with higher-level components, OpenRegister integration, and NL Design System support",
|
|
5
5
|
"homepage": "https://github.com/ConductionNL/nextcloud-vue#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -770,9 +770,10 @@ import { DEFAULT_FORGE, resolveForge } from '../../utils/forge.js'
|
|
|
770
770
|
import { BUILT_IN_KB_PROVIDERS } from '../../utils/kbSearchProviders.js'
|
|
771
771
|
import { createManifestTranslate } from '../../utils/manifestTranslate.js'
|
|
772
772
|
import { installModalStack, uninstallModalStack } from '../../utils/modalStack.js'
|
|
773
|
+
import { loadMyTeam } from '../../utils/myTeam.js'
|
|
773
774
|
import { OBJECTS_CHANGED_EVENT } from '../../utils/objectSignals.js'
|
|
774
775
|
import { resolveBrand } from '../../utils/resolveBrand.js'
|
|
775
|
-
import { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
776
|
+
import { dropOptionalUnresolved, MY_TEAM_TOKEN, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
776
777
|
import { pageOffersSavedViews } from '../../utils/savedViewHelpers.js'
|
|
777
778
|
import { normalizeSavedViewMenu, SAVED_VIEW_MENU_KEY, SAVED_VIEW_MENU_PREFIX, syncSavedViewMenu, toggleSavedViewMenuEntry, withSavedViewMenu } from '../../utils/savedViewMenu.js'
|
|
778
779
|
import { passesContextPredicates } from '../../utils/visibleIfContext.js'
|
|
@@ -4475,7 +4476,13 @@ export default {
|
|
|
4475
4476
|
async _fetchMenuItemCount(item) {
|
|
4476
4477
|
try {
|
|
4477
4478
|
const { register, schema, filter } = item.count
|
|
4478
|
-
const
|
|
4479
|
+
const raw = filter && typeof filter === 'object' ? filter : {}
|
|
4480
|
+
// `@myTeam` counts as the list filters: once the team is read,
|
|
4481
|
+
// every team the app answers, and without one no team at all.
|
|
4482
|
+
if (Object.values(raw).some((value) => value === MY_TEAM_TOKEN || (Array.isArray(value) && value.includes(MY_TEAM_TOKEN)))) {
|
|
4483
|
+
await loadMyTeam()
|
|
4484
|
+
}
|
|
4485
|
+
const resolved = dropOptionalUnresolved(resolveFilterTokens(raw, {}))
|
|
4479
4486
|
const url = generateUrl(`/apps/openregister/api/objects/${encodeURIComponent(register)}/${encodeURIComponent(schema)}`)
|
|
4480
4487
|
const { data } = await axios.get(url, { params: { _limit: 1, ...resolved } })
|
|
4481
4488
|
const total = data?.total
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
<!-- Built-in "label" widget: the value's label (an enum's translated
|
|
57
57
|
label) as plain text, where a list wants words and no pill. -->
|
|
58
58
|
<template v-else-if="widget === 'label'">
|
|
59
|
-
<span v-if="hasValue" :title="rawTitle">{{
|
|
59
|
+
<span v-if="hasValue" :title="rawTitle">{{ shownLabel }}</span>
|
|
60
60
|
<span v-else class="cn-cell-renderer__dash">—</span>
|
|
61
61
|
</template>
|
|
62
62
|
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
shapes it; the colour stays keyed on the raw value. -->
|
|
66
66
|
<template v-else-if="widget === 'badge'">
|
|
67
67
|
<CnStatusBadge v-if="hasValue"
|
|
68
|
-
:label="
|
|
68
|
+
:label="shownLabel"
|
|
69
69
|
:variant="badgeVariant"
|
|
70
70
|
:colorMap="badgeColorMap"
|
|
71
71
|
:colorKey="badgeColorKey"
|
|
@@ -438,7 +438,7 @@ import { ageInDays, boardDateLocale, formatAge, formatBoardDate, formatRelativeD
|
|
|
438
438
|
import { daysUntil, parseDateValue, resolveDateVariant } from '../../utils/dateVariant.js'
|
|
439
439
|
import { pillLook } from '../../utils/detailActionModel.js'
|
|
440
440
|
import { resolveDue } from '../../utils/dueText.js'
|
|
441
|
-
import { formatBoardDateTime } from '../../utils/fillTemplate.js'
|
|
441
|
+
import { fillTemplate, formatBoardDateTime } from '../../utils/fillTemplate.js'
|
|
442
442
|
import { formatMoney } from '../../utils/formatMetric.js'
|
|
443
443
|
import { groupDisplayName, loadGroupDisplayName } from '../../utils/groupAutocomplete.js'
|
|
444
444
|
import { objectFieldValue } from '../../utils/objectName.js'
|
|
@@ -1455,6 +1455,22 @@ export default {
|
|
|
1455
1455
|
* @return {string}
|
|
1456
1456
|
* @spec openspec/changes/cell-labels-and-draft-indicator/specs/cell-labels-and-draft-indicator/spec.md#requirement-a-built-in-cell-widget-shows-an-enum-value-by-its-label
|
|
1457
1457
|
*/
|
|
1458
|
+
/**
|
|
1459
|
+
* What a `label` or `badge` cell shows: its label, worded by
|
|
1460
|
+
* `widgetProps.textTemplate` when given (translated, filled from the
|
|
1461
|
+
* row, `{value}` the label; DqPostvak "Lijkt op 2026-0079").
|
|
1462
|
+
*
|
|
1463
|
+
* @return {string}
|
|
1464
|
+
* @spec openspec/changes/cell-text-template/specs/cell-text-template/spec.md
|
|
1465
|
+
*/
|
|
1466
|
+
shownLabel() {
|
|
1467
|
+
const template = this.widgetProps && this.widgetProps.textTemplate
|
|
1468
|
+
if (typeof template !== 'string' || template === '') {
|
|
1469
|
+
return this.labelledValue
|
|
1470
|
+
}
|
|
1471
|
+
return fillTemplate(this.cnTranslate(template), { ...(this.row || {}), value: this.labelledValue })
|
|
1472
|
+
},
|
|
1473
|
+
|
|
1458
1474
|
labelledValue() {
|
|
1459
1475
|
if (!this.hasFormatter && this.isEnum && this.hasValue && typeof this.value !== 'object') {
|
|
1460
1476
|
return this.enumLabel
|
|
@@ -109,6 +109,7 @@ Fallback slot — shown when ApexCharts is not available:
|
|
|
109
109
|
| `barMax` | Number | `null` | `bar-list`: the value a full track stands for (100 for percentages); null takes the largest value |
|
|
110
110
|
| `matrixLabelWidth` | Number | `null` | `matrix`: the row-label column's width in px; a long label wraps (DqProcesanalyse "Dagen per fase": 240). Null sizes the column to its longest label, at least 120px |
|
|
111
111
|
| `footnote` | String | `''` | A line under the chart, through the host's translate: under the rows of a `bar-list` or `matrix`, under the plot of a `line`, `area` or `bar` chart |
|
|
112
|
+
| `notice` | Object | `null` | A toned note card under the chart: `{ lead?, text?, values?, labels?, tone? }`. `{name}` in `lead` (bold) and `text` is filled from `values[name]`, a literal or a record token (`@object.<field>`); `labels[name]` (`"rows"`, `"columns"`, `"values"` or a map) turns a key into words. `tone`: info, success, warning, error. Hidden while a value is missing |
|
|
112
113
|
| `referenceLines` | Array | `[]` | Dashed horizontal reference lines on the value axis of a line, area or bar chart, such as a norm: `[{ value, label?, color? }]`. The label goes through the host's translate; the colour defaults to the theme's error colour (board DqDoorlooptijd). `valuePath` reads the value from the endpoint answer instead, `{value}` in the label writes it, and a line without a value is not drawn. |
|
|
113
114
|
| `categoryFormat` | String | `''` | How date categories read on the x-axis: `month` ("jan") or `month-year` ("jan 2026"), in the user's language, or `week` for ISO week keys ("2026-W40" as "w40"). Categories that are not dates are left as is; a bare year key stays a year. |
|
|
114
115
|
| `emptyCellLabel` | String | `''` | `matrix`: the text of a cell without a value ("geen"), through the host's translate. Empty leaves the cell blank |
|
|
@@ -148,12 +148,25 @@
|
|
|
148
148
|
data-testid="cn-chart-widget-footnote">
|
|
149
149
|
{{ hostTranslate(footnote) }}
|
|
150
150
|
</p>
|
|
151
|
+
<!-- `notice`: a toned note card under the chart whose words carry
|
|
152
|
+
values from the page record (DqHandhaving "Advies: ..."). -->
|
|
153
|
+
<NcNoteCard
|
|
154
|
+
v-if="noticeParts"
|
|
155
|
+
:type="noticeParts.type"
|
|
156
|
+
:data-tone="noticeParts.type"
|
|
157
|
+
class="cn-chart-widget__notice"
|
|
158
|
+
data-testid="cn-chart-widget-notice">
|
|
159
|
+
<p class="cn-chart-widget__notice-text">
|
|
160
|
+
<strong v-if="noticeParts.lead">{{ noticeParts.lead }}</strong>{{ noticeParts.lead && noticeParts.text ? ' ' : '' }}{{ noticeParts.text }}
|
|
161
|
+
</p>
|
|
162
|
+
</NcNoteCard>
|
|
151
163
|
</div>
|
|
152
164
|
</template>
|
|
153
165
|
|
|
154
166
|
<script>
|
|
155
167
|
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
|
|
156
168
|
import { getLanguage, translate as t } from '@nextcloud/l10n'
|
|
169
|
+
import { NcNoteCard } from '@nextcloud/vue'
|
|
157
170
|
import { inject, markRaw, ref } from 'vue'
|
|
158
171
|
import VueApexCharts from 'vue3-apexcharts'
|
|
159
172
|
import { useDataSource } from '../../composables/useDataSource.js'
|
|
@@ -405,7 +418,7 @@ function formatterFor(vf) {
|
|
|
405
418
|
export default {
|
|
406
419
|
name: 'CnChartWidget',
|
|
407
420
|
|
|
408
|
-
components: { CnEndpointNotice },
|
|
421
|
+
components: { CnEndpointNotice, NcNoteCard },
|
|
409
422
|
|
|
410
423
|
inject: {
|
|
411
424
|
/**
|
|
@@ -467,6 +480,24 @@ export default {
|
|
|
467
480
|
default: null,
|
|
468
481
|
},
|
|
469
482
|
|
|
483
|
+
/**
|
|
484
|
+
* A toned note card under the chart whose words carry values from the
|
|
485
|
+
* page record: `{ lead?, text?, values?, labels?, tone? }`. `lead`
|
|
486
|
+
* (bold) and `text` go through the host's translate, then each
|
|
487
|
+
* `{name}` is filled from `values[name]`, a literal or a token such as
|
|
488
|
+
* `@object.recommendedIntervention`. `labels[name]` turns a key into
|
|
489
|
+
* words: `"rows"`, `"columns"` or `"values"` read the matrix's
|
|
490
|
+
* cellSource label maps, an object is its own map. `tone` is info
|
|
491
|
+
* (default), success, warning or error. Hidden while a value is
|
|
492
|
+
* missing, so a half-filled sentence never shows.
|
|
493
|
+
*
|
|
494
|
+
* @spec openspec/changes/chart-notice/specs/chart-notice/spec.md#requirement-a-chart-can-carry-a-notice-with-record-values
|
|
495
|
+
*/
|
|
496
|
+
notice: {
|
|
497
|
+
type: Object,
|
|
498
|
+
default: null,
|
|
499
|
+
},
|
|
500
|
+
|
|
470
501
|
/**
|
|
471
502
|
* A line under the chart ("Klik een balk om de zaken te zien"),
|
|
472
503
|
* through the host's translate: under the rows of a `bar-list` or
|
|
@@ -1601,6 +1632,38 @@ export default {
|
|
|
1601
1632
|
}
|
|
1602
1633
|
},
|
|
1603
1634
|
|
|
1635
|
+
/**
|
|
1636
|
+
* The notice's bold lead, text and note-card type, values filled in;
|
|
1637
|
+
* null when there is none or a value is missing.
|
|
1638
|
+
*
|
|
1639
|
+
* @return {{lead: string, text: string, type: string}|null} The notice.
|
|
1640
|
+
* @spec openspec/changes/chart-notice/specs/chart-notice/spec.md#requirement-a-chart-can-carry-a-notice-with-record-values
|
|
1641
|
+
*/
|
|
1642
|
+
noticeParts() {
|
|
1643
|
+
const def = this.notice
|
|
1644
|
+
if (!def || typeof def !== 'object' || (!def.lead && !def.text)) {
|
|
1645
|
+
return null
|
|
1646
|
+
}
|
|
1647
|
+
const declared = def.values && typeof def.values === 'object' ? def.values : {}
|
|
1648
|
+
const resolved = resolveFilterTokens(declared, this.chartTokenCtx())
|
|
1649
|
+
const source = this.cellSource && typeof this.cellSource === 'object' ? this.cellSource : {}
|
|
1650
|
+
const maps = { rows: source.rowLabels, columns: source.columnLabels, values: source.valueLabels }
|
|
1651
|
+
const words = {}
|
|
1652
|
+
for (const name of Object.keys(declared)) {
|
|
1653
|
+
const raw = resolved[name]
|
|
1654
|
+
if (raw === null || raw === undefined || raw === '' || (typeof raw === 'string' && raw.charAt(0) === '@')) {
|
|
1655
|
+
return null
|
|
1656
|
+
}
|
|
1657
|
+
const ref = def.labels && def.labels[name]
|
|
1658
|
+
const map = typeof ref === 'string' ? maps[ref] : ref
|
|
1659
|
+
const label = map && typeof map === 'object' && typeof map[String(raw)] === 'string' ? map[String(raw)] : null
|
|
1660
|
+
words[name] = label ? this.hostTranslate(label) : String(raw)
|
|
1661
|
+
}
|
|
1662
|
+
const fill = (key) => (key ? this.hostTranslate(key).replace(/\{(\w+)\}/g, (match, name) => (name in words ? words[name] : match)) : '')
|
|
1663
|
+
const type = ['info', 'success', 'warning', 'error'].includes(def.tone) ? def.tone : 'info'
|
|
1664
|
+
return { lead: fill(def.lead), text: fill(def.text), type }
|
|
1665
|
+
},
|
|
1666
|
+
|
|
1604
1667
|
/**
|
|
1605
1668
|
* The matrix cell of the record (`highlight: { row, column }`, tokens
|
|
1606
1669
|
* such as `@object.behaviour` resolved), as row and column indexes. A
|
|
@@ -2948,6 +3011,14 @@ export default {
|
|
|
2948
3011
|
margin: 0;
|
|
2949
3012
|
}
|
|
2950
3013
|
|
|
3014
|
+
.cn-chart-widget__notice {
|
|
3015
|
+
margin: 12px 0 0;
|
|
3016
|
+
}
|
|
3017
|
+
|
|
3018
|
+
.cn-chart-widget__notice-text {
|
|
3019
|
+
margin: 0;
|
|
3020
|
+
}
|
|
3021
|
+
|
|
2951
3022
|
/* Under an ApexCharts plot the footnote is a sibling of the canvas, so it keeps
|
|
2952
3023
|
its own line height while the canvas takes the rest of a fitted column. */
|
|
2953
3024
|
.cn-chart-widget > .cn-chart-widget__footnote {
|
|
@@ -2964,10 +3035,14 @@ export default {
|
|
|
2964
3035
|
display: contents;
|
|
2965
3036
|
}
|
|
2966
3037
|
|
|
3038
|
+
/* A column takes its share of the width (minmax(0, 1fr)), so a long phase
|
|
3039
|
+
name breaks inside its own column instead of running into the next. */
|
|
2967
3040
|
.cn-chart-widget__matrix-head {
|
|
2968
3041
|
--nldesign-color-on-surface: var(--color-text-maxcontrast);
|
|
2969
3042
|
color: var(--color-text-maxcontrast);
|
|
2970
3043
|
padding: 6px;
|
|
3044
|
+
hyphens: auto;
|
|
3045
|
+
overflow-wrap: anywhere;
|
|
2971
3046
|
}
|
|
2972
3047
|
|
|
2973
3048
|
.cn-chart-widget__matrix-label {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
<template>
|
|
7
7
|
<div class="cn-case-widget-card">
|
|
8
8
|
<CnConversationThread
|
|
9
|
+
ref="thread"
|
|
9
10
|
:messages="messages"
|
|
10
11
|
:title="tr(content.title || '')"
|
|
11
12
|
:subtitle="tr(content.subtitle || '')"
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
data-testid="cn-conversation-templates"
|
|
35
36
|
@apply="applyTemplate($event, setDraft)" />
|
|
36
37
|
</template>
|
|
37
|
-
<template v-if="replyOptions.length > 0" #reply-actions>
|
|
38
|
+
<template v-if="replyOptions.length > 0 || draftUrl" #reply-actions="{ draft }">
|
|
38
39
|
<NcCheckboxRadioSwitch
|
|
39
40
|
v-for="option in replyOptions"
|
|
40
41
|
:key="option.key"
|
|
@@ -42,6 +43,19 @@
|
|
|
42
43
|
:data-testid="`cn-conversation-option-${option.key}`">
|
|
43
44
|
{{ tr(option.label) }}
|
|
44
45
|
</NcCheckboxRadioSwitch>
|
|
46
|
+
<span v-if="draftUrl && draftStatus"
|
|
47
|
+
class="cn-conversation-widget__draft-status"
|
|
48
|
+
role="status"
|
|
49
|
+
data-testid="cn-conversation-draft-status">
|
|
50
|
+
{{ draftStatus }}
|
|
51
|
+
</span>
|
|
52
|
+
<NcButton
|
|
53
|
+
v-if="draftUrl"
|
|
54
|
+
data-testid="cn-conversation-save-draft"
|
|
55
|
+
:disabled="savingDraft"
|
|
56
|
+
@click="saveDraft(draft)">
|
|
57
|
+
{{ t('nextcloud-vue', 'Save as draft') }}
|
|
58
|
+
</NcButton>
|
|
45
59
|
</template>
|
|
46
60
|
<template v-if="headerAction || statusBadgeText" #header-actions>
|
|
47
61
|
<span v-if="statusBadgeText"
|
|
@@ -71,11 +85,13 @@
|
|
|
71
85
|
|
|
72
86
|
<script>
|
|
73
87
|
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
|
|
88
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
74
89
|
import { NcButton, NcCheckboxRadioSwitch } from '@nextcloud/vue'
|
|
75
90
|
import CnIcon from '../CnIcon/CnIcon.vue'
|
|
76
91
|
import CnTemplateSelect from '../CnTemplateSelect/CnTemplateSelect.vue'
|
|
77
92
|
import CnConversationThread from './CnConversationThread.vue'
|
|
78
93
|
import { dispatchAction } from '../../utils/actionsDispatcher.js'
|
|
94
|
+
import { formatTimeOfDay } from '../../utils/boardDate.js'
|
|
79
95
|
import { fetchReferenceObjects } from '../../utils/boardReferenceColumns.js'
|
|
80
96
|
import { readPath } from '../../utils/readPath.js'
|
|
81
97
|
import { hasUnresolvedDeepTokens, resolveDeepTokens } from '../../utils/resolveFilterTokens.js'
|
|
@@ -178,6 +194,10 @@ export default {
|
|
|
178
194
|
acting: '',
|
|
179
195
|
/** The threads `statusBadge` reads. */
|
|
180
196
|
badgeRows: [],
|
|
197
|
+
/** What the last draft save said: when it was saved, or that it failed. */
|
|
198
|
+
draftStatus: '',
|
|
199
|
+
/** Whether a draft is being saved. */
|
|
200
|
+
savingDraft: false,
|
|
181
201
|
}
|
|
182
202
|
},
|
|
183
203
|
|
|
@@ -315,6 +335,21 @@ export default {
|
|
|
315
335
|
* @return {string} The URL, or '' when the content names none.
|
|
316
336
|
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-conversation-thread
|
|
317
337
|
*/
|
|
338
|
+
/**
|
|
339
|
+
* The reply draft's url (`content.draft.url`, `@objectId` filled in),
|
|
340
|
+
* or '' without one.
|
|
341
|
+
*
|
|
342
|
+
* @return {string}
|
|
343
|
+
* @spec openspec/changes/conversation-reply-draft/specs/conversation-reply-draft/spec.md#requirement-a-reply-can-be-saved-as-a-draft-and-comes-back
|
|
344
|
+
*/
|
|
345
|
+
draftUrl() {
|
|
346
|
+
const url = this.content.draft && this.content.draft.url
|
|
347
|
+
if (typeof url !== 'string' || url === '') {
|
|
348
|
+
return ''
|
|
349
|
+
}
|
|
350
|
+
return url.replace(/@objectId/g, encodeURIComponent(String(this.objectId ?? '')))
|
|
351
|
+
},
|
|
352
|
+
|
|
318
353
|
endpoint() {
|
|
319
354
|
const url = this.content.endpoint
|
|
320
355
|
if (typeof url !== 'string' || url === '') {
|
|
@@ -340,19 +375,101 @@ export default {
|
|
|
340
375
|
handler() {
|
|
341
376
|
this.loadSource()
|
|
342
377
|
this.loadBadge()
|
|
378
|
+
this.loadDraft()
|
|
343
379
|
},
|
|
344
380
|
},
|
|
345
381
|
},
|
|
346
382
|
|
|
347
383
|
mounted() {
|
|
348
384
|
subscribe('cn:page:refresh', this.onPageRefresh)
|
|
385
|
+
subscribe('cn:conversation:reply-sent', this.onReplySentElsewhere)
|
|
349
386
|
},
|
|
350
387
|
|
|
351
388
|
beforeUnmount() {
|
|
352
389
|
unsubscribe('cn:page:refresh', this.onPageRefresh)
|
|
390
|
+
unsubscribe('cn:conversation:reply-sent', this.onReplySentElsewhere)
|
|
353
391
|
},
|
|
354
392
|
|
|
355
393
|
methods: {
|
|
394
|
+
t,
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Read the saved reply draft into the reply box.
|
|
398
|
+
*
|
|
399
|
+
* @return {Promise<void>}
|
|
400
|
+
* @spec openspec/changes/conversation-reply-draft/specs/conversation-reply-draft/spec.md#requirement-a-reply-can-be-saved-as-a-draft-and-comes-back
|
|
401
|
+
*/
|
|
402
|
+
async loadDraft() {
|
|
403
|
+
if (!this.draftUrl) {
|
|
404
|
+
return
|
|
405
|
+
}
|
|
406
|
+
try {
|
|
407
|
+
const { default: axios } = await import('@nextcloud/axios')
|
|
408
|
+
const { generateUrl } = await import('@nextcloud/router')
|
|
409
|
+
const { data } = await axios.get(generateUrl(this.draftUrl))
|
|
410
|
+
const text = data && typeof data.text === 'string' ? data.text : ''
|
|
411
|
+
if (text !== '' && this.$refs.thread) {
|
|
412
|
+
this.$refs.thread.setDraft(text)
|
|
413
|
+
}
|
|
414
|
+
} catch {
|
|
415
|
+
// No saved draft to read leaves the box empty.
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Store the reply box as the draft and say when it was saved.
|
|
421
|
+
*
|
|
422
|
+
* @param {string} text The reply box's text.
|
|
423
|
+
* @return {Promise<void>}
|
|
424
|
+
*/
|
|
425
|
+
async saveDraft(text) {
|
|
426
|
+
this.savingDraft = true
|
|
427
|
+
try {
|
|
428
|
+
await this.storeDraft(String(text ?? ''))
|
|
429
|
+
this.draftStatus = t('nextcloud-vue', 'Draft saved at {time}', { time: formatTimeOfDay(new Date()) })
|
|
430
|
+
} catch {
|
|
431
|
+
this.draftStatus = t('nextcloud-vue', 'The draft could not be saved')
|
|
432
|
+
} finally {
|
|
433
|
+
this.savingDraft = false
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* A reply on this record went out through a dialog or another surface
|
|
439
|
+
* (`cn:conversation:reply-sent` with `{ objectId }`): the saved draft
|
|
440
|
+
* and the reply box are cleared.
|
|
441
|
+
*
|
|
442
|
+
* @param {{objectId?: string}} payload The record the reply was on.
|
|
443
|
+
* @return {Promise<void>}
|
|
444
|
+
* @spec openspec/changes/conversation-reply-draft/specs/conversation-reply-draft/spec.md#requirement-a-reply-can-be-saved-as-a-draft-and-comes-back
|
|
445
|
+
*/
|
|
446
|
+
async onReplySentElsewhere(payload) {
|
|
447
|
+
if (!this.draftUrl || String(payload?.objectId ?? '') !== String(this.objectId ?? '')) {
|
|
448
|
+
return
|
|
449
|
+
}
|
|
450
|
+
if (this.$refs.thread) {
|
|
451
|
+
this.$refs.thread.setDraft('')
|
|
452
|
+
}
|
|
453
|
+
this.draftStatus = ''
|
|
454
|
+
try {
|
|
455
|
+
await this.storeDraft('')
|
|
456
|
+
} catch {
|
|
457
|
+
// The next save overwrites a draft that could not be cleared.
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* PUT the draft text.
|
|
463
|
+
*
|
|
464
|
+
* @param {string} text The text; '' clears the draft.
|
|
465
|
+
* @return {Promise<void>}
|
|
466
|
+
*/
|
|
467
|
+
async storeDraft(text) {
|
|
468
|
+
const { default: axios } = await import('@nextcloud/axios')
|
|
469
|
+
const { generateUrl } = await import('@nextcloud/router')
|
|
470
|
+
await axios.put(generateUrl(this.draftUrl), { text })
|
|
471
|
+
},
|
|
472
|
+
|
|
356
473
|
/**
|
|
357
474
|
* Read the messages again when the page refreshes (a dialog sent one).
|
|
358
475
|
*
|
|
@@ -691,6 +808,9 @@ export default {
|
|
|
691
808
|
if (typeof text !== 'string' || text === '') {
|
|
692
809
|
return
|
|
693
810
|
}
|
|
811
|
+
if (this.draftUrl) {
|
|
812
|
+
this.storeDraft('')
|
|
813
|
+
}
|
|
694
814
|
this.sentHere.push({
|
|
695
815
|
id: `sent-${this.sentHere.length}`,
|
|
696
816
|
time: new Date().toISOString(),
|
|
@@ -722,4 +842,9 @@ export default {
|
|
|
722
842
|
border-radius: var(--border-radius-large);
|
|
723
843
|
padding: calc(5 * var(--default-grid-baseline));
|
|
724
844
|
}
|
|
845
|
+
|
|
846
|
+
.cn-conversation-widget__draft-status {
|
|
847
|
+
color: var(--color-text-maxcontrast);
|
|
848
|
+
font-size: 13px;
|
|
849
|
+
}
|
|
725
850
|
</style>
|
|
@@ -66,19 +66,25 @@
|
|
|
66
66
|
v-else-if="row.to"
|
|
67
67
|
:to="row.to"
|
|
68
68
|
class="cn-facts-widget__value-link">
|
|
69
|
-
{{ row
|
|
69
|
+
{{ shownValue(row) }}
|
|
70
70
|
</router-link>
|
|
71
|
-
<span v-else>{{ row
|
|
71
|
+
<span v-else>{{ shownValue(row) }}</span>
|
|
72
72
|
<button
|
|
73
73
|
v-if="row.reveal && !revealed[row.key]"
|
|
74
74
|
type="button"
|
|
75
75
|
class="cn-facts-widget__inline-button"
|
|
76
76
|
:aria-label="revealLabel(row)"
|
|
77
77
|
:data-testid="`cn-fact-reveal-${row.key}`"
|
|
78
|
+
:disabled="revealing === row.key"
|
|
78
79
|
@click="reveal(row)">
|
|
79
80
|
<EyeOutline :size="16" aria-hidden="true" />
|
|
80
81
|
{{ showLabel }}
|
|
81
82
|
</button>
|
|
83
|
+
<span
|
|
84
|
+
v-if="revealErrors[row.key]"
|
|
85
|
+
class="cn-facts-widget__reveal-error"
|
|
86
|
+
role="alert"
|
|
87
|
+
:data-testid="`cn-fact-reveal-error-${row.key}`">{{ revealErrors[row.key] }}</span>
|
|
82
88
|
<button
|
|
83
89
|
v-if="row.copy"
|
|
84
90
|
type="button"
|
|
@@ -420,7 +426,7 @@ export default {
|
|
|
420
426
|
* `as` is `row` (default when `label` or `icon` is set), `title`, `line`, `text` (a line in the normal ink), `lead` (an icon tile or initials beside `title` and `subtitle`), `pill` or `meter`.
|
|
421
427
|
* `countFrom` (`{ register, schema, filter? }`, `@objectId` / `@object.<field>` in the filter) shows how many records that list holds ("Lopende zaken 43"); the row is left out while the count cannot be read.
|
|
422
428
|
* A `meter` shows the amount at `path` against the limit at `limitPath` (or a constant `limit`) as "12 of 50" with a bar; past the limit the bar turns red and the text says "over the limit"; without a positive limit it shows the amount and no bar.
|
|
423
|
-
* A row's `format` may also be `email`, `phone` or `url` (a link) or `masked` (the last four characters; `reveal: true` adds Show). `copy: true` adds a copy button; `icon` replaces the label.
|
|
429
|
+
* A row's `format` may also be `email`, `phone` or `url` (a link) or `masked` (the last four characters; `reveal: true` adds Show, which unmasks the record's value; `reveal: { url, path? }` makes Show ask the server for the value at `url` (tokens resolved), read at `path` (else the entry's path), so a server that answers the mask can check and record the reveal). `copy: true` adds a copy button; `icon` replaces the label.
|
|
424
430
|
* A part may be `{ text }`, a constant; a `line` or `text` entry without `path` or `parts` shows its `text`.
|
|
425
431
|
* `resolve: { register, schema, labelField? }` on an entry or a part turns a value that is a reference (an id, or a list of ids) into the label of the referenced object (`labelField`, else `title`, then `name`): the case type or status of a task shows its name, not its uuid. The ids are fetched once per register and schema through the object store and shared by every facts widget on the page; the entry stays empty until its label is known, and when no label can be read. A resolved value works as a pill too: `map` is applied to the resolved label, and `variantMap` / `colorMap` look the tone up by the value of `resolve.toneField` on the referenced object when that is set (a status named by its `name`, coloured by its `state`), then by the resolved label, then by the stored value.
|
|
426
432
|
* `route` (`{ name, params?, query? }`, `@source.<path>` in a param) on a `title` or a `row` links its value to that route; a param that does not fill leaves it plain.
|
|
@@ -474,6 +480,9 @@ export default {
|
|
|
474
480
|
error: false,
|
|
475
481
|
/** Keys of the masked rows the reader chose to show. */
|
|
476
482
|
revealed: {},
|
|
483
|
+
revealedValues: {},
|
|
484
|
+
revealErrors: {},
|
|
485
|
+
revealing: '',
|
|
477
486
|
/** Key of the row whose value was just copied. */
|
|
478
487
|
copied: '',
|
|
479
488
|
/** Labels of the referenced objects read so far, by `refKey`. */
|
|
@@ -1321,7 +1330,12 @@ export default {
|
|
|
1321
1330
|
extras.external = true
|
|
1322
1331
|
} else if (field.format === 'masked') {
|
|
1323
1332
|
extras.masked = value.length > 4 ? '*'.repeat(Math.min(value.length - 4, 5)) + value.slice(-4) : value
|
|
1324
|
-
|
|
1333
|
+
const server = field.reveal && typeof field.reveal === 'object' && typeof field.reveal.url === 'string' && field.reveal.url !== ''
|
|
1334
|
+
extras.reveal = (field.reveal === true || server) && value.length > 4
|
|
1335
|
+
if (server) {
|
|
1336
|
+
extras.revealUrl = field.reveal.url
|
|
1337
|
+
extras.revealPath = typeof field.reveal.path === 'string' && field.reveal.path !== '' ? field.reveal.path : (field.path || '')
|
|
1338
|
+
}
|
|
1325
1339
|
}
|
|
1326
1340
|
return extras
|
|
1327
1341
|
},
|
|
@@ -1333,7 +1347,14 @@ export default {
|
|
|
1333
1347
|
* @param {object} row The row.
|
|
1334
1348
|
* @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-the-facts-card-draws-contact-details-identity-and-a-lead
|
|
1335
1349
|
*/
|
|
1336
|
-
reveal(row) {
|
|
1350
|
+
async reveal(row) {
|
|
1351
|
+
if (row.revealUrl) {
|
|
1352
|
+
const value = await this.fetchRevealed(row)
|
|
1353
|
+
if (value === null) {
|
|
1354
|
+
return
|
|
1355
|
+
}
|
|
1356
|
+
this.revealedValues = { ...this.revealedValues, [row.key]: value }
|
|
1357
|
+
}
|
|
1337
1358
|
this.revealed = { ...this.revealed, [row.key]: true }
|
|
1338
1359
|
const detail = { path: row.path }
|
|
1339
1360
|
/**
|
|
@@ -1344,6 +1365,47 @@ export default {
|
|
|
1344
1365
|
emit(FACTS_REVEAL_CHANNEL, { ...detail, objectId: this.tokenCtx.objectId, register: this.tokenCtx.register || null, schema: this.tokenCtx.schema || null })
|
|
1345
1366
|
},
|
|
1346
1367
|
|
|
1368
|
+
/**
|
|
1369
|
+
* The text a row shows: its mask until revealed, then the value the
|
|
1370
|
+
* server answered (a server reveal) or the value the record holds.
|
|
1371
|
+
*
|
|
1372
|
+
* @param {object} row The row.
|
|
1373
|
+
* @return {string}
|
|
1374
|
+
* @spec openspec/changes/facts-reveal-from-server/specs/facts-widget/spec.md#requirement-a-masked-fact-can-be-revealed-by-the-server
|
|
1375
|
+
*/
|
|
1376
|
+
shownValue(row) {
|
|
1377
|
+
if (!row.masked || !this.revealed[row.key]) {
|
|
1378
|
+
return row.masked || row.value
|
|
1379
|
+
}
|
|
1380
|
+
return Object.hasOwn(this.revealedValues, row.key) ? this.revealedValues[row.key] : row.value
|
|
1381
|
+
},
|
|
1382
|
+
|
|
1383
|
+
/**
|
|
1384
|
+
* Ask the server for a masked value (`reveal.url`, tokens resolved,
|
|
1385
|
+
* read at `reveal.path`), never from a cache: each show is a request
|
|
1386
|
+
* the server can check and record.
|
|
1387
|
+
*
|
|
1388
|
+
* @param {object} row The row.
|
|
1389
|
+
* @return {Promise<string|null>} The value, or null when it could not be read.
|
|
1390
|
+
* @spec openspec/changes/facts-reveal-from-server/specs/facts-widget/spec.md#requirement-a-masked-fact-can-be-revealed-by-the-server
|
|
1391
|
+
*/
|
|
1392
|
+
async fetchRevealed(row) {
|
|
1393
|
+
this.revealing = row.key
|
|
1394
|
+
this.revealErrors = { ...this.revealErrors, [row.key]: '' }
|
|
1395
|
+
try {
|
|
1396
|
+
const payload = await fetchEndpointSource({ url: row.revealUrl, responsePath: row.revealPath }, this.tokenCtx, { force: true })
|
|
1397
|
+
if (payload === null || payload === undefined || payload === '' || typeof payload === 'object') {
|
|
1398
|
+
throw new Error('no value')
|
|
1399
|
+
}
|
|
1400
|
+
return String(payload)
|
|
1401
|
+
} catch {
|
|
1402
|
+
this.revealErrors = { ...this.revealErrors, [row.key]: t('nextcloud-vue', 'The value could not be shown.') }
|
|
1403
|
+
return null
|
|
1404
|
+
} finally {
|
|
1405
|
+
this.revealing = ''
|
|
1406
|
+
}
|
|
1407
|
+
},
|
|
1408
|
+
|
|
1347
1409
|
/**
|
|
1348
1410
|
* Copy a row's value to the clipboard.
|
|
1349
1411
|
*
|
|
@@ -1845,11 +1907,13 @@ export default {
|
|
|
1845
1907
|
display: contents;
|
|
1846
1908
|
}
|
|
1847
1909
|
|
|
1848
|
-
/* Nextcloud core styles `dt` and `dd` (text-align end, 12px padding
|
|
1849
|
-
pushed the label to the right of its column
|
|
1850
|
-
|
|
1851
|
-
|
|
1910
|
+
/* Nextcloud core styles `dt` and `dd` (text-align end, 12px padding, a
|
|
1911
|
+
130px-wide `dt`), which pushed the label to the right of its column, added
|
|
1912
|
+
12px around both and widened the label column to 130px. The class is
|
|
1913
|
+
doubled so these resets outrank that rule, and a dossiq-style `:deep()`
|
|
1914
|
+
override in an app is not needed. */
|
|
1852
1915
|
.cn-facts-widget__label.cn-facts-widget__label {
|
|
1916
|
+
width: auto;
|
|
1853
1917
|
margin: 0;
|
|
1854
1918
|
padding: 0;
|
|
1855
1919
|
text-align: start;
|
|
@@ -1953,6 +2017,11 @@ export default {
|
|
|
1953
2017
|
font: inherit;
|
|
1954
2018
|
}
|
|
1955
2019
|
|
|
2020
|
+
.cn-facts-widget__reveal-error {
|
|
2021
|
+
font-size: 13px;
|
|
2022
|
+
color: var(--color-text-error, var(--color-error));
|
|
2023
|
+
}
|
|
2024
|
+
|
|
1956
2025
|
.cn-facts-widget__inline-button {
|
|
1957
2026
|
background: var(--color-main-background);
|
|
1958
2027
|
border: 1px solid var(--color-border-dark);
|