@conduction/nextcloud-vue 2.56.0-beta.31 → 2.56.0-beta.32
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/CnBannerWidget/CnBannerWidget.vue2.js +55 -4
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +51 -15
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +13 -2
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +63 -52
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js +10 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js +252 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js +140 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/index.js +26 -0
- package/dist/esm/components/CnChipsFieldWidget/index.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js +10 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js +354 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js +172 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/index.js +27 -0
- package/dist/esm/components/CnChoicePickerWidget/index.js.map +1 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +25 -2
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +3 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -0
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +20 -3
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +7 -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/CnFieldListWidget/CnFieldListWidget.vue2.js +77 -3
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +282 -69
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +0 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +65 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js +10 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js +174 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js +75 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/index.js +28 -0
- package/dist/esm/components/CnRadioCardsWidget/index.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +18 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +34 -25
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js +10 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js +222 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js +64 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/index.js +30 -0
- package/dist/esm/components/CnSavedViewsWidget/index.js.map +1 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -1
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js +10 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js +347 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js +184 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/index.js +27 -0
- package/dist/esm/components/CnSettingsFormWidget/index.js.map +1 -0
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +87 -6
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js +10 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js +159 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js +60 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/index.js +26 -0
- package/dist/esm/components/CnSwitchRowsWidget/index.js.map +1 -0
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +47 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +16 -14
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +80 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +75 -32
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +10 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +269 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js +106 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/index.js +27 -0
- package/dist/esm/components/CnToggleTableWidget/index.js.map +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +7 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +4 -2
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +1 -0
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +1 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +843 -4
- package/dist/esm/utils/contentHeaderLink.js +51 -0
- package/dist/esm/utils/contentHeaderLink.js.map +1 -0
- package/dist/esm/utils/fetchFilterCounts.js +40 -5
- package/dist/esm/utils/fetchFilterCounts.js.map +1 -1
- package/dist/esm/utils/initials.js +32 -0
- package/dist/esm/utils/initials.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +7 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/objectWrite.js +59 -0
- package/dist/esm/utils/objectWrite.js.map +1 -0
- package/dist/esm/utils/widgetDispatch.js +1 -1
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +10510 -6830
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +843 -4
- package/l10n/en.json +1 -0
- package/l10n/nl.json +1 -0
- package/package.json +1 -1
- package/src/components/CnBannerWidget/CnBannerWidget.vue +93 -13
- package/src/components/CnCellRenderer/CnCellRenderer.md +2 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +43 -3
- package/src/components/CnChipsFieldWidget/CnChipsFieldWidget.vue +433 -0
- package/src/components/CnChipsFieldWidget/index.js +25 -0
- package/src/components/CnChoicePickerWidget/CnChoicePickerWidget.vue +556 -0
- package/src/components/CnChoicePickerWidget/index.js +26 -0
- package/src/components/CnDashboardPage/CnDashboardPage.vue +38 -2
- package/src/components/CnDataTable/CnDataTable.vue +2 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +45 -2
- package/src/components/CnFactsWidget/CnFactsWidget.vue +18 -3
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +265 -4
- package/src/components/CnIndexPage/CnIndexPage.vue +0 -1
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +69 -2
- package/src/components/CnRadioCardsWidget/CnRadioCardsWidget.vue +313 -0
- package/src/components/CnRadioCardsWidget/index.js +27 -0
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +43 -1
- package/src/components/CnSavedViewsWidget/CnSavedViewsWidget.vue +296 -0
- package/src/components/CnSavedViewsWidget/index.js +29 -0
- package/src/components/CnSegmentedControl/CnSegmentedControl.vue +13 -0
- package/src/components/CnSettingsFormWidget/CnSettingsFormWidget.vue +555 -0
- package/src/components/CnSettingsFormWidget/index.js +26 -0
- package/src/components/CnStatWidget/CnStatWidget.vue +87 -6
- package/src/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue +226 -0
- package/src/components/CnSwitchRowsWidget/index.js +25 -0
- package/src/components/CnTabsWidget/CnTabsWidget.vue +82 -3
- package/src/components/CnTasksWidget/CnTasksWidget.vue +175 -15
- package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +386 -0
- package/src/components/CnToggleTableWidget/index.js +26 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +7 -0
- package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +28 -1
- package/src/components/index.js +7 -0
- package/src/css/look-board-detail.css +36 -2
- package/src/css/look-board-index.css +23 -0
- package/src/css/look-board.css +72 -0
- package/src/index.js +7 -0
- package/src/utils/contentHeaderLink.js +48 -0
- package/src/utils/fetchFilterCounts.js +39 -4
- package/src/utils/initials.js +29 -0
- package/src/utils/libraryWidgetKeys.js +7 -0
- package/src/utils/objectWrite.js +55 -0
- package/src/utils/widgetDispatch.js +1 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { NcCheckboxRadioSwitch, NcNoteCard } from '@nextcloud/vue';
|
|
2
|
+
import { objectUrlOf, patchObject } from '../../utils/objectWrite.js';
|
|
3
|
+
import { readPath } from '../../utils/readPath.js';
|
|
4
|
+
import { serverErrorMessage } from '../../utils/serverErrorMessage.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* CnSwitchRowsWidget — the `switch-rows` widget: a card of switches, each with
|
|
8
|
+
* a label and a hint, bound to boolean fields of the CURRENT object. A toggle
|
|
9
|
+
* saves at once with ONE PATCH of that field; a refused PATCH puts the switch
|
|
10
|
+
* back and shows the server's words.
|
|
11
|
+
*
|
|
12
|
+
* @spec openspec/changes/abstract-switch-rows/specs/switch-rows/spec.md
|
|
13
|
+
*/
|
|
14
|
+
var script = {
|
|
15
|
+
name: 'CnSwitchRowsWidget',
|
|
16
|
+
|
|
17
|
+
components: { NcCheckboxRadioSwitch, NcNoteCard },
|
|
18
|
+
|
|
19
|
+
inject: {
|
|
20
|
+
cnTranslate: { default: () => (key) => key },
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
props: {
|
|
24
|
+
/**
|
|
25
|
+
* The widget configuration.
|
|
26
|
+
*
|
|
27
|
+
* - `rows` (required) — `[{ key, label, hint?, invert? }]`; `key` is a top-level boolean field. `invert` shows the switch on when the field is false (a stored `disabled` read as "enabled").
|
|
28
|
+
* - `readOnly`.
|
|
29
|
+
*
|
|
30
|
+
* @type {object}
|
|
31
|
+
*/
|
|
32
|
+
content: {
|
|
33
|
+
type: Object,
|
|
34
|
+
default: () => ({}),
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/** The current detail-page object. */
|
|
38
|
+
objectData: {
|
|
39
|
+
type: Object,
|
|
40
|
+
default: null,
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
/** The current object id. */
|
|
44
|
+
objectId: {
|
|
45
|
+
type: [String, Number],
|
|
46
|
+
default: '',
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
/** OpenRegister register slug of the surface. */
|
|
50
|
+
register: {
|
|
51
|
+
type: [String, Object],
|
|
52
|
+
default: '',
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/** OpenRegister schema slug of the surface. */
|
|
56
|
+
schema: {
|
|
57
|
+
type: [String, Object],
|
|
58
|
+
default: '',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
emits: ['saved'],
|
|
63
|
+
|
|
64
|
+
data() {
|
|
65
|
+
return {
|
|
66
|
+
/** The switch states as last saved (by key, as the switch shows them). */
|
|
67
|
+
saved: {},
|
|
68
|
+
values: {},
|
|
69
|
+
busy: '',
|
|
70
|
+
notice: '',
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
computed: {
|
|
75
|
+
rows() {
|
|
76
|
+
const list = Array.isArray(this.content.rows) ? this.content.rows : [];
|
|
77
|
+
return list
|
|
78
|
+
.filter((row) => row && row.key)
|
|
79
|
+
.map((row) => ({ ...row, label: this.tr(row.label || row.key), hint: this.tr(row.hint || '') }))
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
readOnly() {
|
|
83
|
+
return this.content.readOnly === true
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
objectUrl() {
|
|
87
|
+
return objectUrlOf(this.register, this.schema, this.objectId || (this.objectData && this.objectData.id))
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
watch: {
|
|
92
|
+
objectData() {
|
|
93
|
+
this.adopt();
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
created() {
|
|
98
|
+
this.adopt();
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
methods: {
|
|
102
|
+
tr(text) {
|
|
103
|
+
if (typeof text !== 'string' || text === '') {
|
|
104
|
+
return ''
|
|
105
|
+
}
|
|
106
|
+
return typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
/** Read every switch off the object. */
|
|
110
|
+
adopt() {
|
|
111
|
+
const values = {};
|
|
112
|
+
for (const row of this.rows) {
|
|
113
|
+
const stored = readPath(this.objectData || {}, row.key) === true;
|
|
114
|
+
values[row.key] = row.invert ? !stored : stored;
|
|
115
|
+
}
|
|
116
|
+
this.saved = values;
|
|
117
|
+
this.values = { ...values };
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Flip one switch and save that one field.
|
|
122
|
+
*
|
|
123
|
+
* @param {object} row The row.
|
|
124
|
+
* @param {boolean} on The new switch state.
|
|
125
|
+
* @return {Promise<void>}
|
|
126
|
+
* @spec openspec/changes/abstract-switch-rows/specs/switch-rows/spec.md#requirement-a-toggle-saves-that-one-field
|
|
127
|
+
*/
|
|
128
|
+
async toggle(row, on) {
|
|
129
|
+
if (!this.objectUrl) {
|
|
130
|
+
this.notice = this.tr('This switch cannot be saved here.');
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
this.notice = '';
|
|
134
|
+
this.busy = row.key;
|
|
135
|
+
this.values = { ...this.values, [row.key]: on };
|
|
136
|
+
const stored = row.invert ? !on : on;
|
|
137
|
+
try {
|
|
138
|
+
const data = await patchObject(this.objectUrl, { [row.key]: stored });
|
|
139
|
+
const written = data[row.key] !== undefined ? data[row.key] === true : stored;
|
|
140
|
+
const shown = row.invert ? !written : written;
|
|
141
|
+
this.saved = { ...this.saved, [row.key]: shown };
|
|
142
|
+
this.values = { ...this.values, [row.key]: shown };
|
|
143
|
+
/**
|
|
144
|
+
* @event saved A switch was written.
|
|
145
|
+
* @type {{field: string, value: boolean}}
|
|
146
|
+
*/
|
|
147
|
+
this.$emit('saved', { field: row.key, value: written });
|
|
148
|
+
} catch (e) {
|
|
149
|
+
this.values = { ...this.values, [row.key]: this.saved[row.key] };
|
|
150
|
+
this.notice = serverErrorMessage(e);
|
|
151
|
+
} finally {
|
|
152
|
+
this.busy = '';
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export { script as default };
|
|
159
|
+
//# sourceMappingURL=CnSwitchRowsWidget.vue2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnSwitchRowsWidget.vue2.js","sources":["../../../../src/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-switch-rows\" data-testid=\"cn-switch-rows\">\n\t\t<ul class=\"cn-switch-rows__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t:key=\"row.key\"\n\t\t\t\tclass=\"cn-switch-rows__row\"\n\t\t\t\t:data-testid=\"`cn-switch-rows-row-${row.key}`\">\n\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\t:modelValue=\"values[row.key] === true\"\n\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t:disabled=\"readOnly || busy === row.key\"\n\t\t\t\t\t@update:modelValue=\"toggle(row, $event)\">\n\t\t\t\t\t<span class=\"cn-switch-rows__label\">{{ row.label }}</span>\n\t\t\t\t\t<span v-if=\"row.hint\" class=\"cn-switch-rows__hint\">{{ row.hint }}</span>\n\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t</li>\n\t\t</ul>\n\t\t<NcNoteCard v-if=\"notice\" type=\"error\" data-testid=\"cn-switch-rows-notice\">\n\t\t\t{{ notice }}\n\t\t</NcNoteCard>\n\t</div>\n</template>\n\n<script>\nimport { NcCheckboxRadioSwitch, NcNoteCard } from '@nextcloud/vue'\nimport { objectUrlOf, patchObject } from '../../utils/objectWrite.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { serverErrorMessage } from '../../utils/serverErrorMessage.js'\n\n/**\n * CnSwitchRowsWidget — the `switch-rows` widget: a card of switches, each with\n * a label and a hint, bound to boolean fields of the CURRENT object. A toggle\n * saves at once with ONE PATCH of that field; a refused PATCH puts the switch\n * back and shows the server's words.\n *\n * @spec openspec/changes/abstract-switch-rows/specs/switch-rows/spec.md\n */\nexport default {\n\tname: 'CnSwitchRowsWidget',\n\n\tcomponents: { NcCheckboxRadioSwitch, NcNoteCard },\n\n\tinject: {\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget configuration.\n\t\t *\n\t\t * - `rows` (required) — `[{ key, label, hint?, invert? }]`; `key` is a top-level boolean field. `invert` shows the switch on when the field is false (a stored `disabled` read as \"enabled\").\n\t\t * - `readOnly`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The current detail-page object. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The current object id. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** OpenRegister register slug of the surface. */\n\t\tregister: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** OpenRegister schema slug of the surface. */\n\t\tschema: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['saved'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** The switch states as last saved (by key, as the switch shows them). */\n\t\t\tsaved: {},\n\t\t\tvalues: {},\n\t\t\tbusy: '',\n\t\t\tnotice: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\trows() {\n\t\t\tconst list = Array.isArray(this.content.rows) ? this.content.rows : []\n\t\t\treturn list\n\t\t\t\t.filter((row) => row && row.key)\n\t\t\t\t.map((row) => ({ ...row, label: this.tr(row.label || row.key), hint: this.tr(row.hint || '') }))\n\t\t},\n\n\t\treadOnly() {\n\t\t\treturn this.content.readOnly === true\n\t\t},\n\n\t\tobjectUrl() {\n\t\t\treturn objectUrlOf(this.register, this.schema, this.objectId || (this.objectData && this.objectData.id))\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectData() {\n\t\t\tthis.adopt()\n\t\t},\n\t},\n\n\tcreated() {\n\t\tthis.adopt()\n\t},\n\n\tmethods: {\n\t\ttr(text) {\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/** Read every switch off the object. */\n\t\tadopt() {\n\t\t\tconst values = {}\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst stored = readPath(this.objectData || {}, row.key) === true\n\t\t\t\tvalues[row.key] = row.invert ? !stored : stored\n\t\t\t}\n\t\t\tthis.saved = values\n\t\t\tthis.values = { ...values }\n\t\t},\n\n\t\t/**\n\t\t * Flip one switch and save that one field.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @param {boolean} on The new switch state.\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/abstract-switch-rows/specs/switch-rows/spec.md#requirement-a-toggle-saves-that-one-field\n\t\t */\n\t\tasync toggle(row, on) {\n\t\t\tif (!this.objectUrl) {\n\t\t\t\tthis.notice = this.tr('This switch cannot be saved here.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.notice = ''\n\t\t\tthis.busy = row.key\n\t\t\tthis.values = { ...this.values, [row.key]: on }\n\t\t\tconst stored = row.invert ? !on : on\n\t\t\ttry {\n\t\t\t\tconst data = await patchObject(this.objectUrl, { [row.key]: stored })\n\t\t\t\tconst written = data[row.key] !== undefined ? data[row.key] === true : stored\n\t\t\t\tconst shown = row.invert ? !written : written\n\t\t\t\tthis.saved = { ...this.saved, [row.key]: shown }\n\t\t\t\tthis.values = { ...this.values, [row.key]: shown }\n\t\t\t\t/**\n\t\t\t\t * @event saved A switch was written.\n\t\t\t\t * @type {{field: string, value: boolean}}\n\t\t\t\t */\n\t\t\t\tthis.$emit('saved', { field: row.key, value: written })\n\t\t\t} catch (e) {\n\t\t\t\tthis.values = { ...this.values, [row.key]: this.saved[row.key] }\n\t\t\t\tthis.notice = serverErrorMessage(e)\n\t\t\t} finally {\n\t\t\t\tthis.busy = ''\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-switch-rows {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tmin-width: 0;\n}\n\n.cn-switch-rows__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-switch-rows__row {\n\tpadding: 10px 0;\n\tborder-top: 1px solid var(--cn-board-hairline, var(--color-border));\n}\n\n.cn-switch-rows__row:first-child {\n\tborder-top: none;\n\tpadding-top: 0;\n}\n\n.cn-switch-rows__label {\n\tdisplay: block;\n\tfont-weight: 600;\n}\n\n.cn-switch-rows__hint {\n\tdisplay: block;\n\tfont-size: 12px;\n\tfont-weight: 400;\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { resolveComponent, openBlock, createElementBlock, createElementVNode, Fragment, renderList, createVNode, withCtx, toDisplayString, createCommentVNode, createBlock, createTextVNode } from 'vue';
|
|
2
|
+
|
|
3
|
+
const _hoisted_1 = {
|
|
4
|
+
class: "cn-switch-rows",
|
|
5
|
+
"data-testid": "cn-switch-rows"
|
|
6
|
+
};
|
|
7
|
+
const _hoisted_2 = { class: "cn-switch-rows__list" };
|
|
8
|
+
const _hoisted_3 = ["data-testid"];
|
|
9
|
+
const _hoisted_4 = { class: "cn-switch-rows__label" };
|
|
10
|
+
const _hoisted_5 = {
|
|
11
|
+
key: 0,
|
|
12
|
+
class: "cn-switch-rows__hint"
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
16
|
+
const _component_NcCheckboxRadioSwitch = resolveComponent("NcCheckboxRadioSwitch");
|
|
17
|
+
const _component_NcNoteCard = resolveComponent("NcNoteCard");
|
|
18
|
+
|
|
19
|
+
return (openBlock(), createElementBlock("div", _hoisted_1, [
|
|
20
|
+
createElementVNode("ul", _hoisted_2, [
|
|
21
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList($options.rows, (row) => {
|
|
22
|
+
return (openBlock(), createElementBlock("li", {
|
|
23
|
+
key: row.key,
|
|
24
|
+
class: "cn-switch-rows__row",
|
|
25
|
+
"data-testid": `cn-switch-rows-row-${row.key}`
|
|
26
|
+
}, [
|
|
27
|
+
createVNode(_component_NcCheckboxRadioSwitch, {
|
|
28
|
+
modelValue: $data.values[row.key] === true,
|
|
29
|
+
type: "switch",
|
|
30
|
+
disabled: $options.readOnly || $data.busy === row.key,
|
|
31
|
+
"onUpdate:modelValue": $event => ($options.toggle(row, $event))
|
|
32
|
+
}, {
|
|
33
|
+
default: withCtx(() => [
|
|
34
|
+
createElementVNode("span", _hoisted_4, toDisplayString(row.label), 1 /* TEXT */),
|
|
35
|
+
(row.hint)
|
|
36
|
+
? (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString(row.hint), 1 /* TEXT */))
|
|
37
|
+
: createCommentVNode("v-if", true)
|
|
38
|
+
]),
|
|
39
|
+
_: 2 /* DYNAMIC */
|
|
40
|
+
}, 1032 /* PROPS, DYNAMIC_SLOTS */, ["modelValue", "disabled", "onUpdate:modelValue"])
|
|
41
|
+
], 8 /* PROPS */, _hoisted_3))
|
|
42
|
+
}), 128 /* KEYED_FRAGMENT */))
|
|
43
|
+
]),
|
|
44
|
+
($data.notice)
|
|
45
|
+
? (openBlock(), createBlock(_component_NcNoteCard, {
|
|
46
|
+
key: 0,
|
|
47
|
+
type: "error",
|
|
48
|
+
"data-testid": "cn-switch-rows-notice"
|
|
49
|
+
}, {
|
|
50
|
+
default: withCtx(() => [
|
|
51
|
+
createTextVNode(toDisplayString($data.notice), 1 /* TEXT */)
|
|
52
|
+
]),
|
|
53
|
+
_: 1 /* STABLE */
|
|
54
|
+
}))
|
|
55
|
+
: createCommentVNode("v-if", true)
|
|
56
|
+
]))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { render };
|
|
60
|
+
//# sourceMappingURL=CnSwitchRowsWidget.vue3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnSwitchRowsWidget.vue3.js","sources":["../../../../src/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue?vue&type=template&id=9dd5b4e2&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-switch-rows\" data-testid=\"cn-switch-rows\">\n\t\t<ul class=\"cn-switch-rows__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t:key=\"row.key\"\n\t\t\t\tclass=\"cn-switch-rows__row\"\n\t\t\t\t:data-testid=\"`cn-switch-rows-row-${row.key}`\">\n\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\t:modelValue=\"values[row.key] === true\"\n\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t:disabled=\"readOnly || busy === row.key\"\n\t\t\t\t\t@update:modelValue=\"toggle(row, $event)\">\n\t\t\t\t\t<span class=\"cn-switch-rows__label\">{{ row.label }}</span>\n\t\t\t\t\t<span v-if=\"row.hint\" class=\"cn-switch-rows__hint\">{{ row.hint }}</span>\n\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t</li>\n\t\t</ul>\n\t\t<NcNoteCard v-if=\"notice\" type=\"error\" data-testid=\"cn-switch-rows-notice\">\n\t\t\t{{ notice }}\n\t\t</NcNoteCard>\n\t</div>\n</template>\n\n<script>\nimport { NcCheckboxRadioSwitch, NcNoteCard } from '@nextcloud/vue'\nimport { objectUrlOf, patchObject } from '../../utils/objectWrite.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { serverErrorMessage } from '../../utils/serverErrorMessage.js'\n\n/**\n * CnSwitchRowsWidget — the `switch-rows` widget: a card of switches, each with\n * a label and a hint, bound to boolean fields of the CURRENT object. A toggle\n * saves at once with ONE PATCH of that field; a refused PATCH puts the switch\n * back and shows the server's words.\n *\n * @spec openspec/changes/abstract-switch-rows/specs/switch-rows/spec.md\n */\nexport default {\n\tname: 'CnSwitchRowsWidget',\n\n\tcomponents: { NcCheckboxRadioSwitch, NcNoteCard },\n\n\tinject: {\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget configuration.\n\t\t *\n\t\t * - `rows` (required) — `[{ key, label, hint?, invert? }]`; `key` is a top-level boolean field. `invert` shows the switch on when the field is false (a stored `disabled` read as \"enabled\").\n\t\t * - `readOnly`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The current detail-page object. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The current object id. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** OpenRegister register slug of the surface. */\n\t\tregister: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** OpenRegister schema slug of the surface. */\n\t\tschema: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['saved'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** The switch states as last saved (by key, as the switch shows them). */\n\t\t\tsaved: {},\n\t\t\tvalues: {},\n\t\t\tbusy: '',\n\t\t\tnotice: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\trows() {\n\t\t\tconst list = Array.isArray(this.content.rows) ? this.content.rows : []\n\t\t\treturn list\n\t\t\t\t.filter((row) => row && row.key)\n\t\t\t\t.map((row) => ({ ...row, label: this.tr(row.label || row.key), hint: this.tr(row.hint || '') }))\n\t\t},\n\n\t\treadOnly() {\n\t\t\treturn this.content.readOnly === true\n\t\t},\n\n\t\tobjectUrl() {\n\t\t\treturn objectUrlOf(this.register, this.schema, this.objectId || (this.objectData && this.objectData.id))\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectData() {\n\t\t\tthis.adopt()\n\t\t},\n\t},\n\n\tcreated() {\n\t\tthis.adopt()\n\t},\n\n\tmethods: {\n\t\ttr(text) {\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/** Read every switch off the object. */\n\t\tadopt() {\n\t\t\tconst values = {}\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst stored = readPath(this.objectData || {}, row.key) === true\n\t\t\t\tvalues[row.key] = row.invert ? !stored : stored\n\t\t\t}\n\t\t\tthis.saved = values\n\t\t\tthis.values = { ...values }\n\t\t},\n\n\t\t/**\n\t\t * Flip one switch and save that one field.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @param {boolean} on The new switch state.\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/abstract-switch-rows/specs/switch-rows/spec.md#requirement-a-toggle-saves-that-one-field\n\t\t */\n\t\tasync toggle(row, on) {\n\t\t\tif (!this.objectUrl) {\n\t\t\t\tthis.notice = this.tr('This switch cannot be saved here.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.notice = ''\n\t\t\tthis.busy = row.key\n\t\t\tthis.values = { ...this.values, [row.key]: on }\n\t\t\tconst stored = row.invert ? !on : on\n\t\t\ttry {\n\t\t\t\tconst data = await patchObject(this.objectUrl, { [row.key]: stored })\n\t\t\t\tconst written = data[row.key] !== undefined ? data[row.key] === true : stored\n\t\t\t\tconst shown = row.invert ? !written : written\n\t\t\t\tthis.saved = { ...this.saved, [row.key]: shown }\n\t\t\t\tthis.values = { ...this.values, [row.key]: shown }\n\t\t\t\t/**\n\t\t\t\t * @event saved A switch was written.\n\t\t\t\t * @type {{field: string, value: boolean}}\n\t\t\t\t */\n\t\t\t\tthis.$emit('saved', { field: row.key, value: written })\n\t\t\t} catch (e) {\n\t\t\t\tthis.values = { ...this.values, [row.key]: this.saved[row.key] }\n\t\t\t\tthis.notice = serverErrorMessage(e)\n\t\t\t} finally {\n\t\t\t\tthis.busy = ''\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-switch-rows {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tmin-width: 0;\n}\n\n.cn-switch-rows__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-switch-rows__row {\n\tpadding: 10px 0;\n\tborder-top: 1px solid var(--cn-board-hairline, var(--color-border));\n}\n\n.cn-switch-rows__row:first-child {\n\tborder-top: none;\n\tpadding-top: 0;\n}\n\n.cn-switch-rows__label {\n\tdisplay: block;\n\tfont-weight: 600;\n}\n\n.cn-switch-rows__hint {\n\tdisplay: block;\n\tfont-size: 12px;\n\tfont-weight: 400;\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["_createElementBlock","_Fragment","_renderList","_createElementVNode","_toDisplayString"],"mappings":";;;AAWM,EAAA,KAAK,EAAC,gBAAgB;AAAC,EAAA,aAAC,EAAA,gBAAA;;AACxB,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,sBAAsB,GAAA;;AAWvB,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,uBAAwB,GAAA;;;AAC3B,EAAA,KAAA,EAAA,sBAAA;;;;;;;;;wBAXVA,kBAaE,CAAAC,QAAA,EAAA,IAAA,EAAAC,UAAA,CAZY,QAAI,CAAA,IAAA,EAAA,CAAX,GAAG,KAAA;6BADVF,kBAaE,CAAA,IAAA,EAAA;UAXD,GAAG,EAAE,GAAG,CAAC,GAAG;AACZ,UAAA,KAAI,EAAC,qBAAqB;UAC1B,aAAW,EAAA,CAAA,mBAAA,EAAwB,GAAG,CAAC,GAAC,CAAA,CAAA;;qBAQ/B,CAAA,gCAAA,EAAA;AANR,YAAA,UAAU,EAAE,KAAA,CAAA,MAAM,CAAC,GAAG,CAAC,GAAG,CAAA,KAAA,IAAA;AAC3B,YAAA,IAAI,EAAC,QAAQ;AACZ,YAAA,QAAC,EAAA,QAAA,CAAA,QAAA,IAAA,UAAA,KAAA,GAAA,CAAA,GAAA;AACD,YAAA,qBAAA,EAAA,MAAA,KAAA,QAAA,CAAA,MAAA,CAAA,GAAA,EAAA,MAAA,CAAA,CAAA;;6BACD,MAA0D;AAA1D,cAAAG,kBAAA,CAA0D,MAA1D,EAAA,UAA0D,EAAnBC,eAAA,CAAA,GAAG,CAAC,KAAK,CAAA,EAAA,CAAA,YAAA;AACvC,cAAA,CAAA,GAAA,CAAA,IAAA;mDAAA,kBAAA,EAAAA,eAAA,CAAA,GAAA,CAAA,IAAA,CAAA,EAAA,CAAA,YAAA;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import __sfc_main from './CnSwitchRowsWidget.vue.js';
|
|
2
|
+
import { registerDashboardWidget } from '../CnWidgetGrid/dashboardWidgetRegistry.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
6
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
7
|
+
*
|
|
8
|
+
* CnSwitchRowsWidget — self-registers the `switch-rows` widget type (a card of
|
|
9
|
+
* switches, each with a label and a hint, bound to boolean fields of the
|
|
10
|
+
* current object and saved on toggle) into the shared dashboardWidgetRegistry.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
registerDashboardWidget('switch-rows', {
|
|
15
|
+
renderer: __sfc_main,
|
|
16
|
+
form: null,
|
|
17
|
+
defaultContent: {
|
|
18
|
+
rows: [],
|
|
19
|
+
},
|
|
20
|
+
displayName: 'Switch rows',
|
|
21
|
+
icon: 'ToggleSwitch',
|
|
22
|
+
surfaces: ['detail-page'],
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export { __sfc_main as CnSwitchRowsWidget, __sfc_main as default };
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/components/CnSwitchRowsWidget/index.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * CnSwitchRowsWidget — self-registers the `switch-rows` widget type (a card of\n * switches, each with a label and a hint, bound to boolean fields of the\n * current object and saved on toggle) into the shared dashboardWidgetRegistry.\n */\n\nimport CnSwitchRowsWidget from './CnSwitchRowsWidget.vue'\nimport { registerDashboardWidget } from '../CnWidgetGrid/dashboardWidgetRegistry.js'\n\nregisterDashboardWidget('switch-rows', {\n\trenderer: CnSwitchRowsWidget,\n\tform: null,\n\tdefaultContent: {\n\t\trows: [],\n\t},\n\tdisplayName: 'Switch rows',\n\ticon: 'ToggleSwitch',\n\tsurfaces: ['detail-page'],\n})\n\nexport { CnSwitchRowsWidget }\nexport default CnSwitchRowsWidget\n"],"names":["CnSwitchRowsWidget"],"mappings":";;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA,uBAAuB,CAAC,aAAa,EAAE;AACvC,CAAC,QAAQ,EAAEA,UAAkB;AAC7B,CAAC,IAAI,EAAE,IAAI;AACX,CAAC,cAAc,EAAE;AACjB,EAAE,IAAI,EAAE,EAAE;AACV,EAAE;AACF,CAAC,WAAW,EAAE,aAAa;AAC3B,CAAC,IAAI,EAAE,cAAc;AACrB,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAC;AAC1B,CAAC;;;;"}
|
|
@@ -170,12 +170,17 @@ var script = {
|
|
|
170
170
|
* names the list; `count` and `countField` win over it. `overflow: true`
|
|
171
171
|
* lists the tab under "More".
|
|
172
172
|
*
|
|
173
|
+
* A tab with `widgetIds` can lay them out in a grid instead of one under
|
|
174
|
+
* the other: `columns: 4` gives each a quarter of the row, and
|
|
175
|
+
* `layout: [{ widgetId, gridWidth }]` sets a width (of 12) per widget, as
|
|
176
|
+
* the page grid does. Widgets wrap onto the next row.
|
|
177
|
+
*
|
|
173
178
|
* `maxVisibleTabs` caps the strip: later tabs go under "More".
|
|
174
179
|
* `hideEmpty: true` moves a tab whose count is 0 there too.
|
|
175
180
|
* `moreLabel` names that menu. `variant: "segmented"` draws the strip
|
|
176
181
|
* as a pill switch (CnTabs' segmented variant); `line` is the default.
|
|
177
182
|
*
|
|
178
|
-
* @type {{ tabs?: Array<{widgetId?: string, widgetIds?: string[], label?: string, icon?: string, count?: number, countField?: string, countFrom?: ('widget'|{register: string, schema: string, filter?: object}), overflow?: boolean}>, ariaLabel?: string, maxVisibleTabs?: number, hideEmpty?: boolean, moreLabel?: string, variant?: ('line'|'segmented') }}
|
|
183
|
+
* @type {{ tabs?: Array<{widgetId?: string, widgetIds?: string[], label?: string, icon?: string, count?: number, countField?: string, countFrom?: ('widget'|{register: string, schema: string, filter?: object}), overflow?: boolean, columns?: number, layout?: Array<{widgetId: string, gridWidth: number}>}>, ariaLabel?: string, maxVisibleTabs?: number, hideEmpty?: boolean, moreLabel?: string, variant?: ('line'|'segmented') }}
|
|
179
184
|
*/
|
|
180
185
|
content: {
|
|
181
186
|
type: Object,
|
|
@@ -392,6 +397,7 @@ var script = {
|
|
|
392
397
|
icon: (tab && tab.icon) || widget?.icon || '',
|
|
393
398
|
widget,
|
|
394
399
|
stack: widgets.slice(1),
|
|
400
|
+
grid: this.stackGrid(tab, widgets),
|
|
395
401
|
count,
|
|
396
402
|
overflow,
|
|
397
403
|
pending: visibility === 'pending',
|
|
@@ -591,6 +597,46 @@ var script = {
|
|
|
591
597
|
},
|
|
592
598
|
|
|
593
599
|
methods: {
|
|
600
|
+
/**
|
|
601
|
+
* The grid a stack lays its widgets out in, as one column span (of 12)
|
|
602
|
+
* per widget in order, or null for the plain vertical stack. A tab says
|
|
603
|
+
* `columns: 4` (every widget spans a quarter of the row) and/or
|
|
604
|
+
* `layout: [{ widgetId, gridWidth }]`, the page grid's width key per
|
|
605
|
+
* widget (of 12; a widget it does not name takes the `columns` share, or
|
|
606
|
+
* the whole row). Widgets wrap onto the next row. Heights follow the
|
|
607
|
+
* content.
|
|
608
|
+
*
|
|
609
|
+
* @param {object|string} tab The `content.tabs[]` entry.
|
|
610
|
+
* @param {Array<object>} widgets The widgets of the stack, in order.
|
|
611
|
+
* @return {number[]|null} The column span of each widget, or null.
|
|
612
|
+
*/
|
|
613
|
+
stackGrid(tab, widgets) {
|
|
614
|
+
if (!tab || typeof tab !== 'object' || widgets.length < 2) {
|
|
615
|
+
return null
|
|
616
|
+
}
|
|
617
|
+
const clamp = (value) => Math.min(12, Math.max(1, Math.floor(Number(value))));
|
|
618
|
+
const columns = Number(tab.columns);
|
|
619
|
+
const share = Number.isFinite(columns) && columns >= 1 ? clamp(12 / Math.min(12, Math.floor(columns))) : 12;
|
|
620
|
+
const placed = new Map((Array.isArray(tab.layout) ? tab.layout : [])
|
|
621
|
+
.filter((entry) => entry && typeof entry.widgetId === 'string' && Number.isFinite(Number(entry.gridWidth)))
|
|
622
|
+
.map((entry) => [entry.widgetId, clamp(entry.gridWidth)]));
|
|
623
|
+
if (placed.size === 0 && share === 12) {
|
|
624
|
+
return null
|
|
625
|
+
}
|
|
626
|
+
return widgets.map((widget) => placed.get(widget.id) ?? share)
|
|
627
|
+
},
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* The inline column span of one widget of a gridded stack.
|
|
631
|
+
*
|
|
632
|
+
* @param {number[]|null} grid The spans of the stack, or null.
|
|
633
|
+
* @param {number} index The widget's position in the stack.
|
|
634
|
+
* @return {object|undefined} The style, or undefined without a grid.
|
|
635
|
+
*/
|
|
636
|
+
spanStyle(grid, index) {
|
|
637
|
+
return grid ? { gridColumn: `span ${grid[index] || 12}` } : undefined
|
|
638
|
+
},
|
|
639
|
+
|
|
594
640
|
/**
|
|
595
641
|
* The widget ids a tab names: `widgetIds` (a stack) when it holds at
|
|
596
642
|
* least one id, else `widgetId`, or the bare string.
|