@conduction/nextcloud-vue 2.56.0-beta.48 → 2.56.0-beta.49
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 +46 -28
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +17 -10
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBrandBar/CnBrandBar.vue2.js.map +1 -1
- package/dist/esm/components/CnBrandBar/CnBrandBar.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +56 -16
- 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/CnDeviceFramesWidget/CnDeviceFramesWidget.vue.js +9 -0
- package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue.js.map +1 -0
- package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue2.js +240 -0
- package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue3.js +65 -0
- package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnDeviceFramesWidget/index.js +30 -0
- package/dist/esm/components/CnDeviceFramesWidget/index.js.map +1 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +17 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +30 -31
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue.js +6 -0
- package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue.js.map +1 -0
- package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue2.js +90 -0
- package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue2.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +48 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +20 -2
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue.js +1 -1
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js +230 -25
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js.map +1 -1
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js +447 -221
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -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 +18 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +18 -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 +356 -114
- package/dist/esm/utils/libraryWidgetKeys.js +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +6964 -5943
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +356 -114
- package/l10n/en.json +18 -1
- package/l10n/nl.json +18 -1
- package/package.json +1 -1
- package/src/components/CnAppRoot/CnAppRoot.vue +20 -0
- package/src/components/CnBrandBar/CnBrandBar.vue +5 -4
- package/src/components/CnDetailPage/CnDetailPage.vue +55 -15
- package/src/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue +360 -0
- package/src/components/CnDeviceFramesWidget/index.js +29 -0
- package/src/components/CnFactsWidget/CnFactsWidget.vue +46 -2
- package/src/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue +107 -0
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +81 -1
- package/src/components/CnRegisterMapping/CnRegisterMapping.md +10 -0
- package/src/components/CnRegisterMapping/CnRegisterMapping.vue +511 -28
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/src/components/index.js +1 -0
- package/src/index.js +1 -0
- package/src/schemas/app-manifest-v2.schema.json +6 -1
- package/src/utils/libraryWidgetKeys.js +1 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
<template v-for="block in blocks" :key="block.key">
|
|
19
19
|
<dl
|
|
20
20
|
v-if="block.kind === 'rows'"
|
|
21
|
-
class="cn-facts-widget__rows"
|
|
21
|
+
class="cn-facts-widget__rows"
|
|
22
|
+
:class="{ 'cn-facts-widget__rows--columns': twoColumns }">
|
|
22
23
|
<div
|
|
23
24
|
v-for="row in block.rows"
|
|
24
25
|
:key="row.key"
|
|
@@ -268,6 +269,7 @@ export default {
|
|
|
268
269
|
* - `source` — `{ register, schema, id }` or `{ url }`; omitted = the current object. An entry with `from: "object"` reads the current object even then (a client card that also shows the ticket's channel).
|
|
269
270
|
* - `link` — `{ label, route? | href? }` at the foot.
|
|
270
271
|
* - `hideEmpty` — drop an entry whose value is empty (default true).
|
|
272
|
+
* - `columns` — `2` lays the rows out in two columns with the label above the value (board DqContact); a row with `parts` joins them like a line (an address on one row).
|
|
271
273
|
* - `emptyText` — shown when no entry has a value.
|
|
272
274
|
*
|
|
273
275
|
* @type {{fields?: Array<object>, source?: object, link?: object, hideEmpty?: boolean, emptyText?: string}}
|
|
@@ -310,6 +312,16 @@ export default {
|
|
|
310
312
|
},
|
|
311
313
|
|
|
312
314
|
computed: {
|
|
315
|
+
/**
|
|
316
|
+
* Whether the rows sit in two columns, label above value.
|
|
317
|
+
*
|
|
318
|
+
* @spec openspec/changes/facts-columns/specs/facts-widget/spec.md#requirement-facts-rows-can-sit-in-two-columns
|
|
319
|
+
* @return {boolean}
|
|
320
|
+
*/
|
|
321
|
+
twoColumns() {
|
|
322
|
+
return Number(this.content?.columns) === 2
|
|
323
|
+
},
|
|
324
|
+
|
|
313
325
|
/**
|
|
314
326
|
* The token context: the detail object (prop, else the injected context)
|
|
315
327
|
* and the page workspace.
|
|
@@ -546,7 +558,7 @@ export default {
|
|
|
546
558
|
if (kind === 'lead') {
|
|
547
559
|
return this.partsValue(record, field.title)
|
|
548
560
|
}
|
|
549
|
-
if ((kind === 'line' || kind === 'text') && Array.isArray(field.parts)) {
|
|
561
|
+
if ((kind === 'line' || kind === 'text' || kind === 'row') && Array.isArray(field.parts)) {
|
|
550
562
|
return this.lineValue(record, field)
|
|
551
563
|
}
|
|
552
564
|
if ((kind === 'line' || kind === 'text') && !field.path && typeof field.text === 'string') {
|
|
@@ -924,6 +936,11 @@ export default {
|
|
|
924
936
|
if (!spec.path && typeof spec.text === 'string') {
|
|
925
937
|
return this.tr(spec.text)
|
|
926
938
|
}
|
|
939
|
+
// A group of parts with its own separator ("Parkstraat 24" inside
|
|
940
|
+
// "Parkstraat 24, 3421 AB Zuiddrecht").
|
|
941
|
+
if (!spec.path && Array.isArray(spec.parts)) {
|
|
942
|
+
return this.lineValue(record, spec)
|
|
943
|
+
}
|
|
927
944
|
const value = this.format(readPath(record, spec.path), spec, record)
|
|
928
945
|
if (value === '') {
|
|
929
946
|
return ''
|
|
@@ -1240,4 +1257,31 @@ export default {
|
|
|
1240
1257
|
color: var(--color-primary-element);
|
|
1241
1258
|
text-decoration: underline;
|
|
1242
1259
|
}
|
|
1260
|
+
|
|
1261
|
+
/* `columns: 2` (board DqContact): two columns, the label above the value, a
|
|
1262
|
+
hairline under each fact; one column on a phone. */
|
|
1263
|
+
.cn-facts-widget__rows--columns {
|
|
1264
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1265
|
+
gap: 14px 24px;
|
|
1266
|
+
font-size: 15px;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
.cn-facts-widget__rows--columns .cn-facts-widget__row {
|
|
1270
|
+
display: flex;
|
|
1271
|
+
flex-direction: column;
|
|
1272
|
+
gap: 3px;
|
|
1273
|
+
min-width: 0;
|
|
1274
|
+
padding-bottom: 10px;
|
|
1275
|
+
border-bottom: 1px solid var(--color-border);
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
.cn-facts-widget__rows--columns .cn-facts-widget__label.cn-facts-widget__label {
|
|
1279
|
+
font-size: 13px;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
@media (max-width: 640px) {
|
|
1283
|
+
.cn-facts-widget__rows--columns {
|
|
1284
|
+
grid-template-columns: minmax(0, 1fr);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1243
1287
|
</style>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
CnHeaderSearchLabel — the app's own words on the search field in Nextcloud's
|
|
3
|
+
header (screens-board-top-bar-search). Renders nothing of its own.
|
|
4
|
+
|
|
5
|
+
Under the board look the screens give the top bar's search field a line that
|
|
6
|
+
names what the app holds ("Zoek zaken, documenten of contacten"), where
|
|
7
|
+
Nextcloud shows its global "Search apps, files, tags, messages …". Nextcloud
|
|
8
|
+
offers no way for an app to set that line, so this component replaces the
|
|
9
|
+
TEXT of the field's label and nothing else:
|
|
10
|
+
- it changes only the text of `.unified-search-input__label` (which is also
|
|
11
|
+
the search button's accessible name); it never adds, moves or removes a node;
|
|
12
|
+
- it writes the text again when Nextcloud re-renders the field, and only
|
|
13
|
+
when the text differs, so its own write never loops;
|
|
14
|
+
- on unmount it puts Nextcloud's text back;
|
|
15
|
+
- with no header or no such label it does nothing.
|
|
16
|
+
|
|
17
|
+
The search Nextcloud opens is unchanged: this is the resting field's line.
|
|
18
|
+
-->
|
|
19
|
+
<script>
|
|
20
|
+
const LABEL_SELECTOR = '.unified-search-input__label'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Put the app's own line on the search field in Nextcloud's header.
|
|
24
|
+
*
|
|
25
|
+
* @spec openspec/changes/screens-board-top-bar-search/specs/layout-components/spec.md#requirement-the-board-look-names-what-the-header-search-finds
|
|
26
|
+
*/
|
|
27
|
+
export default {
|
|
28
|
+
name: 'CnHeaderSearchLabel',
|
|
29
|
+
|
|
30
|
+
props: {
|
|
31
|
+
/**
|
|
32
|
+
* The line to show, already translated.
|
|
33
|
+
*
|
|
34
|
+
* @type {string}
|
|
35
|
+
*/
|
|
36
|
+
label: {
|
|
37
|
+
type: String,
|
|
38
|
+
required: true,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
data() {
|
|
43
|
+
return { observer: null, original: null, labelNode: null }
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
watch: {
|
|
47
|
+
label() {
|
|
48
|
+
this.apply()
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
mounted() {
|
|
53
|
+
const header = document.getElementById('header')
|
|
54
|
+
if (!header) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
this.apply()
|
|
58
|
+
if (typeof MutationObserver === 'function') {
|
|
59
|
+
this.observer = new MutationObserver(() => this.apply())
|
|
60
|
+
this.observer.observe(header, { childList: true, subtree: true, characterData: true })
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
beforeUnmount() {
|
|
65
|
+
this.observer?.disconnect()
|
|
66
|
+
this.observer = null
|
|
67
|
+
if (this.labelNode && this.original !== null && this.labelNode.isConnected) {
|
|
68
|
+
this.labelNode.textContent = this.original
|
|
69
|
+
}
|
|
70
|
+
this.labelNode = null
|
|
71
|
+
this.original = null
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
methods: {
|
|
75
|
+
/**
|
|
76
|
+
* Write the line into the label when it shows something else.
|
|
77
|
+
* Remembers the first text Nextcloud drew in each label node, to put back.
|
|
78
|
+
*
|
|
79
|
+
* @return {void}
|
|
80
|
+
*/
|
|
81
|
+
apply() {
|
|
82
|
+
const node = document.querySelector('#header ' + LABEL_SELECTOR)
|
|
83
|
+
if (!node || !this.label) {
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
if (node !== this.labelNode) {
|
|
87
|
+
this.labelNode = node
|
|
88
|
+
this.original = node.textContent
|
|
89
|
+
}
|
|
90
|
+
if (node.textContent.trim() === this.label) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
// Keep Nextcloud's own text node where there is one, so its renderer still holds the live node.
|
|
94
|
+
const text = node.childNodes.length === 1 && node.firstChild.nodeType === 3 ? node.firstChild : null
|
|
95
|
+
if (text) {
|
|
96
|
+
text.data = this.label
|
|
97
|
+
} else {
|
|
98
|
+
node.textContent = this.label
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
render() {
|
|
104
|
+
return null
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
</script>
|
|
@@ -169,6 +169,20 @@
|
|
|
169
169
|
{{ item.title }}
|
|
170
170
|
</component>
|
|
171
171
|
</div>
|
|
172
|
+
<!-- `bar`: a thin track under the row, filled to the row's value
|
|
173
|
+
against its own maximum or against the largest row. -->
|
|
174
|
+
<span
|
|
175
|
+
v-if="presentation === 'rows' && item.bar"
|
|
176
|
+
class="cn-record-list__bar"
|
|
177
|
+
:class="{ 'cn-record-list__bar--over': item.bar.over }"
|
|
178
|
+
role="meter"
|
|
179
|
+
:aria-label="item.title || null"
|
|
180
|
+
:aria-valuenow="item.bar.value"
|
|
181
|
+
aria-valuemin="0"
|
|
182
|
+
:aria-valuemax="item.bar.max"
|
|
183
|
+
data-testid="cn-record-list-bar">
|
|
184
|
+
<span class="cn-record-list__bar-fill" :style="{ width: `${item.bar.share}%` }" />
|
|
185
|
+
</span>
|
|
172
186
|
</li>
|
|
173
187
|
</ul>
|
|
174
188
|
</div>
|
|
@@ -254,7 +268,11 @@ export default {
|
|
|
254
268
|
* The list config: `{ register, schema, filter?, sort?, limit?, extend?,
|
|
255
269
|
* presentation?, title?, meta?, text?, label?, pill?, trailing?, icon?,
|
|
256
270
|
* rowRoute?, rowIdField?, headerLink?, headerNote?, emptyText?, errorText?,
|
|
257
|
-
* valueLabels?, endpointSource? }`. `
|
|
271
|
+
* valueLabels?, endpointSource?, bar? }`. `bar` (`{ valueField, maxField?,
|
|
272
|
+
* overVariant? }`) draws a thin track under each row: filled to
|
|
273
|
+
* `valueField` against `maxField` (drawn in the error colour past it
|
|
274
|
+
* with `overVariant: "error"`), or without `maxField` against the
|
|
275
|
+
* largest row. `endpointSource` (`{ url, method?,
|
|
258
276
|
* params?, responsePath? }`) reads the rows from an app endpoint instead
|
|
259
277
|
* of `register` / `schema`. `valueLabels` maps a field's stored values to the labels
|
|
260
278
|
* the templates show.
|
|
@@ -402,6 +420,7 @@ export default {
|
|
|
402
420
|
/** @return {Array<object>} The rows ready to draw. */
|
|
403
421
|
items() {
|
|
404
422
|
const c = this.content || {}
|
|
423
|
+
const barScale = this.barScale
|
|
405
424
|
return this.rows.slice(0, this.limit).map((row, index) => {
|
|
406
425
|
const link = this.rowLink(row)
|
|
407
426
|
return {
|
|
@@ -420,10 +439,26 @@ export default {
|
|
|
420
439
|
badges: this.badgesOf(row),
|
|
421
440
|
due: this.dueOf(row),
|
|
422
441
|
icon: this.iconOf(row),
|
|
442
|
+
bar: this.barOf(row, barScale),
|
|
423
443
|
}
|
|
424
444
|
})
|
|
425
445
|
},
|
|
426
446
|
|
|
447
|
+
/**
|
|
448
|
+
* The largest `bar.valueField` over the rows shown, the scale of a
|
|
449
|
+
* `bar` without `maxField`. 0 without a `bar`.
|
|
450
|
+
*
|
|
451
|
+
* @return {number} The largest value.
|
|
452
|
+
* @spec openspec/changes/record-list-row-bar/specs/record-list-row-bar/spec.md#requirement-a-record-list-row-can-carry-a-bar
|
|
453
|
+
*/
|
|
454
|
+
barScale() {
|
|
455
|
+
const field = this.content?.bar?.valueField
|
|
456
|
+
if (!field) {
|
|
457
|
+
return 0
|
|
458
|
+
}
|
|
459
|
+
return Math.max(0, ...this.rows.slice(0, this.limit).map((row) => Number(readPath(row, field)) || 0))
|
|
460
|
+
},
|
|
461
|
+
|
|
427
462
|
/** @return {object|null} The header link with `{total}` filled and tokens resolved. */
|
|
428
463
|
headerLink() {
|
|
429
464
|
const link = this.content?.headerLink
|
|
@@ -518,6 +553,32 @@ export default {
|
|
|
518
553
|
return typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text
|
|
519
554
|
},
|
|
520
555
|
|
|
556
|
+
/**
|
|
557
|
+
* A row's `bar`: its value, the maximum it is measured against (the
|
|
558
|
+
* row's `maxField`, else the largest row), the filled share and
|
|
559
|
+
* whether it is over that maximum. Null without `bar` or a value.
|
|
560
|
+
*
|
|
561
|
+
* @param {object} row The row.
|
|
562
|
+
* @param {number} scale The largest value over the rows shown.
|
|
563
|
+
* @return {{value: number, max: number, share: number, over: boolean}|null} The bar.
|
|
564
|
+
* @spec openspec/changes/record-list-row-bar/specs/record-list-row-bar/spec.md#requirement-a-record-list-row-can-carry-a-bar
|
|
565
|
+
*/
|
|
566
|
+
barOf(row, scale) {
|
|
567
|
+
const bar = this.content?.bar
|
|
568
|
+
if (!bar || typeof bar !== 'object' || !bar.valueField) {
|
|
569
|
+
return null
|
|
570
|
+
}
|
|
571
|
+
const value = Number(readPath(row, bar.valueField))
|
|
572
|
+
if (!Number.isFinite(value)) {
|
|
573
|
+
return null
|
|
574
|
+
}
|
|
575
|
+
const ownMax = bar.maxField ? Number(readPath(row, bar.maxField)) : NaN
|
|
576
|
+
const max = bar.maxField ? (Number.isFinite(ownMax) && ownMax > 0 ? ownMax : 0) : scale
|
|
577
|
+
const share = max > 0 ? Math.min(100, Math.max(0, (value / max) * 100)) : 0
|
|
578
|
+
const over = Boolean(bar.maxField) && bar.overVariant === 'error' && max > 0 && value > max
|
|
579
|
+
return { value, max, share, over }
|
|
580
|
+
},
|
|
581
|
+
|
|
521
582
|
/**
|
|
522
583
|
* The initials of the row's avatar (`avatar`: a name template such as
|
|
523
584
|
* `"{client.name}"`, or `{ text }`), or '' without one.
|
|
@@ -1075,6 +1136,25 @@ export default {
|
|
|
1075
1136
|
border-top: 1px solid var(--color-border);
|
|
1076
1137
|
}
|
|
1077
1138
|
|
|
1139
|
+
.cn-record-list__bar {
|
|
1140
|
+
background: var(--color-border);
|
|
1141
|
+
border-radius: 3px;
|
|
1142
|
+
display: block;
|
|
1143
|
+
height: 6px;
|
|
1144
|
+
margin: -6px 0 12px;
|
|
1145
|
+
overflow: hidden;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
.cn-record-list__bar-fill {
|
|
1149
|
+
background: var(--color-primary-element);
|
|
1150
|
+
display: block;
|
|
1151
|
+
height: 100%;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
.cn-record-list__bar--over .cn-record-list__bar-fill {
|
|
1155
|
+
background: var(--color-error);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1078
1158
|
.cn-record-list__row {
|
|
1079
1159
|
align-items: center;
|
|
1080
1160
|
color: var(--color-main-text);
|
|
@@ -115,6 +115,14 @@ export default {
|
|
|
115
115
|
|
|
116
116
|
With more than one group each group starts closed: its heading is a button that shows the group name and how many of its types are configured ("2/8 configured"), and opens the register select and type list on a click or Enter. A page with several registers is a short card until the administrator opens the group they need.
|
|
117
117
|
|
|
118
|
+
## Board look
|
|
119
|
+
|
|
120
|
+
In the board look (`look="board"`, or the `cnLook` that `CnAppRoot` or `CnAdminSettingsShell` provides) one group is open: its name, a status chip, the register select and a type/schema table with a check icon on each configured type. It lists `boardRowLimit` types and counts the rest in a line with a button that lists them all. The other groups are rows with their type count and a status chip; activating a row opens that group. The save button closes the section instead of sitting in its header.
|
|
121
|
+
|
|
122
|
+
## Loading
|
|
123
|
+
|
|
124
|
+
The register select lists every register without its schemas. Only the registers the configuration maps have their schemas fetched (`registers/{id}/schemas`), and a register chosen in the select is fetched when chosen.
|
|
125
|
+
|
|
118
126
|
## Additional props
|
|
119
127
|
|
|
120
128
|
| Prop | Type | Default | Description |
|
|
@@ -127,5 +135,7 @@ With more than one group each group starts closed: its heading is a button that
|
|
|
127
135
|
| `saveButtonText` | String | `'Save configuration'` | Label for the save button |
|
|
128
136
|
| `reimportButtonText` | String | `'Re-import configuration'` | Label for the reimport button |
|
|
129
137
|
| `collapseGroups` | Boolean | `null` | Draw each group as a heading button that opens and closes it, with its configured count shown while closed; the groups start closed. `null` does this when there is more than one group, `true` or `false` forces it |
|
|
138
|
+
| `look` | String | `''` | `board` or `nextcloud`; empty follows the provided `cnLook` |
|
|
139
|
+
| `boardRowLimit` | Number | `4` | Board look: how many types the open group lists before the "show all" line; `0` lists every type |
|
|
130
140
|
| `autoMatch` | Boolean | `true` | Automatically match schema titles to type slugs when a register is selected |
|
|
131
141
|
| `labels` | Object | *(see below)* | UI label overrides for all user-visible strings (register, schema, notConfigured, noSchemas, selectRegister, selectSchema, allConfigured, partiallyConfigured) |
|