@conduction/nextcloud-vue 2.119.0 → 2.121.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/CnAiFeaturesWidget/CnAiFeaturesWidget.vue.js +9 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue2.js +226 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue3.js +211 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue.js +9 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue2.js +64 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue2.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue3.js +89 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue3.js.map +1 -0
- package/dist/esm/components/CnIcon/semanticIcons.js +2 -2
- package/dist/esm/components/CnIcon/semanticIcons.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +53 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +546 -523
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useNamedSource.js +9 -0
- package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue.js +9 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue.js.map +1 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue2.js +257 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue2.js.map +1 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue3.js +266 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue3.js.map +1 -0
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js +14 -7
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue.js +9 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue2.js +278 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue3.js +128 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue.js +9 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue.js.map +1 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue2.js +214 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue2.js.map +1 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue3.js +125 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue3.js.map +1 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue.js +9 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue2.js +163 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue3.js +131 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSchemaRulesWidget/schemaRules.js +127 -0
- package/dist/esm/components/CnSchemaRulesWidget/schemaRules.js.map +1 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue.js +9 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue.js.map +1 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue2.js +82 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue2.js.map +1 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue3.js +61 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue3.js.map +1 -0
- package/dist/esm/components/CnSearchIndexStatus/searchIndexStatus.js +60 -0
- package/dist/esm/components/CnSearchIndexStatus/searchIndexStatus.js.map +1 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue.js +10 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue.js.map +1 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue2.js +226 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue2.js.map +1 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue3.js +126 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue.js +10 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue2.js +944 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue2.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue3.js +276 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue.js +9 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue2.js +354 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue2.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue3.js +392 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue.js +9 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue2.js +180 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue2.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue3.js +208 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue.js +9 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue2.js +244 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue2.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue3.js +76 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/stateMachineFields.js +290 -0
- package/dist/esm/components/CnStateMachineEditor/stateMachineFields.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js +9 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js +448 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js +303 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +19 -2
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +73 -62
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +6 -0
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +42 -32
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/composables/indexSources.js +116 -16
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/index.js +9 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +207 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +207 -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 +951 -16
- package/dist/esm/utils/aiFeatures.js +245 -0
- package/dist/esm/utils/aiFeatures.js.map +1 -0
- package/dist/esm/utils/aiSuggestionTracker.js +182 -0
- package/dist/esm/utils/aiSuggestionTracker.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +2 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/objectPermissions.js +552 -0
- package/dist/esm/utils/objectPermissions.js.map +1 -0
- package/dist/esm/utils/objectTranslations.js +180 -0
- package/dist/esm/utils/objectTranslations.js.map +1 -0
- package/dist/esm/utils/stateMachineValidation.js +254 -0
- package/dist/esm/utils/stateMachineValidation.js.map +1 -0
- package/dist/esm/utils/widgetDispatch.js +3 -1
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +23038 -14567
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +951 -16
- package/l10n/en.json +207 -1
- package/l10n/nl.json +207 -1
- package/package.json +1 -1
- package/src/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue +416 -0
- package/src/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue +136 -0
- package/src/components/CnAiFeaturesWidget/__tests__/CnAiFeaturesWidget.spec.js +213 -0
- package/src/components/CnAiFeaturesWidget/index.js +14 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +74 -3
- package/src/components/CnIndexPage/useNamedSource.js +9 -0
- package/src/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue +499 -0
- package/src/components/CnObjectPermissionsTab/__tests__/CnObjectPermissionsTab.spec.js +186 -0
- package/src/components/CnObjectPermissionsTab/index.js +13 -0
- package/src/components/CnObjectSidebar/CnObjectSidebar.vue +8 -1
- package/src/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue +424 -0
- package/src/components/CnObjectTranslationsWidget/__tests__/CnObjectTranslationsWidget.spec.js +189 -0
- package/src/components/CnObjectTranslationsWidget/index.js +13 -0
- package/src/components/CnReadinessChecklist/CnReadinessChecklist.vue +335 -0
- package/src/components/CnReadinessChecklist/index.js +9 -0
- package/src/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue +300 -0
- package/src/components/CnSchemaRulesWidget/index.js +10 -0
- package/src/components/CnSchemaRulesWidget/schemaRules.js +123 -0
- package/src/components/CnSearchIndexStatus/CnSearchIndexStatus.vue +160 -0
- package/src/components/CnSearchIndexStatus/index.js +10 -0
- package/src/components/CnSearchIndexStatus/searchIndexStatus.js +56 -0
- package/src/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue +328 -0
- package/src/components/CnShippedConfigurationSection/index.js +9 -0
- package/src/components/CnStateMachineEditor/CnStateMachineEditor.vue +1332 -0
- package/src/components/CnStateMachineEditor/CnStateMachineFields.vue +791 -0
- package/src/components/CnStateMachineEditor/CnStateMachineNode.vue +442 -0
- package/src/components/CnStateMachineEditor/CnStateMachinePanel.vue +326 -0
- package/src/components/CnStateMachineEditor/index.js +8 -0
- package/src/components/CnStateMachineEditor/stateMachineFields.js +287 -0
- package/src/components/CnTimelineWidget/CnTimelineManage.vue +827 -0
- package/src/components/CnTimelineWidget/CnTimelineWidget.vue +29 -3
- package/src/components/CnWidgetGrid/builtInWidgets.js +6 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +10 -0
- package/src/components/index.js +8 -0
- package/src/composables/indexSources.js +116 -16
- package/src/css/index-page.css +7 -0
- package/src/index.js +9 -0
- package/src/utils/aiFeatures.js +241 -0
- package/src/utils/aiSuggestionTracker.js +178 -0
- package/src/utils/libraryWidgetKeys.js +2 -0
- package/src/utils/objectPermissions.js +548 -0
- package/src/utils/objectTranslations.js +176 -0
- package/src/utils/stateMachineValidation.js +250 -0
- package/src/utils/widgetDispatch.js +3 -1
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { NcButton } from '@nextcloud/vue';
|
|
2
|
+
import Close from 'vue-material-design-icons/Close.vue';
|
|
3
|
+
import DeleteOutline from 'vue-material-design-icons/DeleteOutline.vue';
|
|
4
|
+
import __sfc_main from './CnStateMachineFields.vue.js';
|
|
5
|
+
import { readRecord, moveItem, buildRecord } from './stateMachineFields.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The property panel of one state, step or transition.
|
|
9
|
+
*
|
|
10
|
+
* Edits a copy of `record` described by `fields` and emits the whole record on
|
|
11
|
+
* every change; the host stores it and passes it back down. A record that comes
|
|
12
|
+
* back equal to the last one emitted does not reset the copy, so typing never
|
|
13
|
+
* loses the caret.
|
|
14
|
+
*/
|
|
15
|
+
var script = {
|
|
16
|
+
name: 'CnStateMachinePanel',
|
|
17
|
+
|
|
18
|
+
components: { NcButton, Close, DeleteOutline, CnStateMachineFields: __sfc_main },
|
|
19
|
+
|
|
20
|
+
provide() {
|
|
21
|
+
// The panel itself: its `uid`, `locked`, `tr`, `optionsOf`, `isEnabled`,
|
|
22
|
+
// `setEnabled`, `set`, `push`, `splice` and `move` are what the fields use.
|
|
23
|
+
return { cnStateMachinePanel: this }
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
props: {
|
|
27
|
+
/** The record being edited. */
|
|
28
|
+
record: {
|
|
29
|
+
type: Object,
|
|
30
|
+
required: true,
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
/** Field descriptors, see CnStateMachineEditor's `config`. */
|
|
34
|
+
fields: {
|
|
35
|
+
type: Array,
|
|
36
|
+
default: () => [],
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
/** The heading, already translated. */
|
|
40
|
+
title: {
|
|
41
|
+
type: String,
|
|
42
|
+
required: true,
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/** A line under the heading. */
|
|
46
|
+
subtitle: {
|
|
47
|
+
type: String,
|
|
48
|
+
default: '',
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
/** The close button's accessible name. */
|
|
52
|
+
closeLabel: {
|
|
53
|
+
type: String,
|
|
54
|
+
required: true,
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/** The delete button's text. */
|
|
58
|
+
deleteLabel: {
|
|
59
|
+
type: String,
|
|
60
|
+
default: '',
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
/** Whether to offer the delete button. */
|
|
64
|
+
canDelete: {
|
|
65
|
+
type: Boolean,
|
|
66
|
+
default: false,
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
/** Disable every field. */
|
|
70
|
+
readOnly: {
|
|
71
|
+
type: Boolean,
|
|
72
|
+
default: false,
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
/** Disable the fields marked `lockable`. */
|
|
76
|
+
locked: {
|
|
77
|
+
type: Boolean,
|
|
78
|
+
default: false,
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/** Named option lists a field can point at with `optionSet`. */
|
|
82
|
+
optionSets: {
|
|
83
|
+
type: Object,
|
|
84
|
+
default: () => ({}),
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/** Translates a configured text. */
|
|
88
|
+
translate: {
|
|
89
|
+
type: Function,
|
|
90
|
+
default: (text) => text,
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
/** Prefix for the DOM ids inside the panel. */
|
|
94
|
+
uid: {
|
|
95
|
+
type: String,
|
|
96
|
+
required: true,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
emits: ['update', 'delete', 'close'],
|
|
101
|
+
|
|
102
|
+
data() {
|
|
103
|
+
const { model, enabled } = readRecord(this.record, this.fields);
|
|
104
|
+
return {
|
|
105
|
+
model,
|
|
106
|
+
enabled,
|
|
107
|
+
lastEmitted: null,
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
watch: {
|
|
112
|
+
record: {
|
|
113
|
+
deep: true,
|
|
114
|
+
handler(next, previous) {
|
|
115
|
+
const sameRecord = next && previous && next.id === previous.id;
|
|
116
|
+
if (sameRecord && this.lastEmitted !== null && JSON.stringify(next) === this.lastEmitted) {
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
this.reset();
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
fields() {
|
|
124
|
+
this.reset();
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
methods: {
|
|
129
|
+
/**
|
|
130
|
+
* Rebuild the copy from `record`.
|
|
131
|
+
*
|
|
132
|
+
* @return {void}
|
|
133
|
+
*/
|
|
134
|
+
reset() {
|
|
135
|
+
const { model, enabled } = readRecord(this.record, this.fields);
|
|
136
|
+
this.model = model;
|
|
137
|
+
this.enabled = enabled;
|
|
138
|
+
this.lastEmitted = null;
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The options of a select or checkboxes field.
|
|
143
|
+
*
|
|
144
|
+
* @param {object} field The descriptor.
|
|
145
|
+
* @return {Array<{value: unknown, label: string}>} The options, labels translated when they are the config's own.
|
|
146
|
+
*/
|
|
147
|
+
optionsOf(field) {
|
|
148
|
+
if (Array.isArray(field.options)) {
|
|
149
|
+
return field.options.map((option) => ({ value: option.value, label: this.translate(option.label) }))
|
|
150
|
+
}
|
|
151
|
+
const set = field.optionSet ? this.optionSets[field.optionSet] : null;
|
|
152
|
+
return Array.isArray(set) ? set : []
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @param {string} text A configured text.
|
|
157
|
+
* @return {string} It translated.
|
|
158
|
+
*/
|
|
159
|
+
tr(text) {
|
|
160
|
+
return this.translate(text)
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @param {string} path An optional group's path.
|
|
165
|
+
* @return {boolean} Whether it is switched on.
|
|
166
|
+
*/
|
|
167
|
+
isEnabled(path) {
|
|
168
|
+
return this.enabled[path] === true
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @param {string} path An optional group's path.
|
|
173
|
+
* @param {boolean} value Whether to switch it on.
|
|
174
|
+
* @return {void}
|
|
175
|
+
*/
|
|
176
|
+
setEnabled(path, value) {
|
|
177
|
+
this.enabled[path] = value === true;
|
|
178
|
+
this.emitUpdate();
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Write one value into the copy.
|
|
183
|
+
*
|
|
184
|
+
* @param {object|Array} target An object or list inside the copy.
|
|
185
|
+
* @param {string|number} key The key or index.
|
|
186
|
+
* @param {unknown} value The value.
|
|
187
|
+
* @return {void}
|
|
188
|
+
*/
|
|
189
|
+
set(target, key, value) {
|
|
190
|
+
target[key] = value;
|
|
191
|
+
this.emitUpdate();
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @param {Array} list A list inside the copy.
|
|
196
|
+
* @param {unknown} item The entry to append.
|
|
197
|
+
* @return {void}
|
|
198
|
+
*/
|
|
199
|
+
push(list, item) {
|
|
200
|
+
list.push(item);
|
|
201
|
+
this.emitUpdate();
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* @param {Array} list A list inside the copy.
|
|
206
|
+
* @param {number} index The entry to remove.
|
|
207
|
+
* @return {void}
|
|
208
|
+
*/
|
|
209
|
+
splice(list, index) {
|
|
210
|
+
list.splice(index, 1);
|
|
211
|
+
this.emitUpdate();
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @param {Array} list A list inside the copy.
|
|
216
|
+
* @param {number} from The entry's index.
|
|
217
|
+
* @param {number} to Its new index.
|
|
218
|
+
* @return {void}
|
|
219
|
+
*/
|
|
220
|
+
move(list, from, to) {
|
|
221
|
+
if (moveItem(list, from, to)) {
|
|
222
|
+
this.emitUpdate();
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Emit the record built from the copy.
|
|
228
|
+
*
|
|
229
|
+
* @return {void}
|
|
230
|
+
*/
|
|
231
|
+
emitUpdate() {
|
|
232
|
+
const record = buildRecord(this.model, this.fields, this.enabled);
|
|
233
|
+
this.lastEmitted = JSON.stringify(record);
|
|
234
|
+
/**
|
|
235
|
+
* @event update The edited record, complete. Keys the fields do not
|
|
236
|
+
* describe are carried through unchanged.
|
|
237
|
+
*/
|
|
238
|
+
this.$emit('update', record);
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export { script as default };
|
|
244
|
+
//# sourceMappingURL=CnStateMachinePanel.vue2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnStateMachinePanel.vue2.js","sources":["../../../../src/components/CnStateMachineEditor/CnStateMachinePanel.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>\n - SPDX-License-Identifier: EUPL-1.2\n -->\n<template>\n\t<section class=\"cn-sm-panel\" :aria-labelledby=\"`${uid}-title`\">\n\t\t<header class=\"cn-sm-panel__header\">\n\t\t\t<div class=\"cn-sm-panel__titles\">\n\t\t\t\t<h3 :id=\"`${uid}-title`\" class=\"cn-sm-panel__title\">\n\t\t\t\t\t{{ title }}\n\t\t\t\t</h3>\n\t\t\t\t<span v-if=\"subtitle\" class=\"cn-sm-panel__subtitle\">{{ subtitle }}</span>\n\t\t\t</div>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t:aria-label=\"closeLabel\"\n\t\t\t\t@click=\"$emit('close')\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Close :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t</NcButton>\n\t\t</header>\n\n\t\t<CnStateMachineFields\n\t\t\tv-if=\"fields.length > 0\"\n\t\t\t:fields=\"fields\"\n\t\t\t:target=\"model\"\n\t\t\t:disabled=\"readOnly\" />\n\n\t\t<!-- @slot default Extra content below the fields. -->\n\t\t<slot />\n\n\t\t<div v-if=\"canDelete && !readOnly\" class=\"cn-sm-panel__danger\">\n\t\t\t<NcButton variant=\"error\" @click=\"$emit('delete')\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<DeleteOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ deleteLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t</section>\n</template>\n\n<script>\nimport { NcButton } from '@nextcloud/vue'\nimport Close from 'vue-material-design-icons/Close.vue'\nimport DeleteOutline from 'vue-material-design-icons/DeleteOutline.vue'\nimport CnStateMachineFields from './CnStateMachineFields.vue'\nimport { buildRecord, moveItem, readRecord } from './stateMachineFields.js'\n\n/**\n * The property panel of one state, step or transition.\n *\n * Edits a copy of `record` described by `fields` and emits the whole record on\n * every change; the host stores it and passes it back down. A record that comes\n * back equal to the last one emitted does not reset the copy, so typing never\n * loses the caret.\n */\nexport default {\n\tname: 'CnStateMachinePanel',\n\n\tcomponents: { NcButton, Close, DeleteOutline, CnStateMachineFields },\n\n\tprovide() {\n\t\t// The panel itself: its `uid`, `locked`, `tr`, `optionsOf`, `isEnabled`,\n\t\t// `setEnabled`, `set`, `push`, `splice` and `move` are what the fields use.\n\t\treturn { cnStateMachinePanel: this }\n\t},\n\n\tprops: {\n\t\t/** The record being edited. */\n\t\trecord: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Field descriptors, see CnStateMachineEditor's `config`. */\n\t\tfields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The heading, already translated. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** A line under the heading. */\n\t\tsubtitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The close button's accessible name. */\n\t\tcloseLabel: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** The delete button's text. */\n\t\tdeleteLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Whether to offer the delete button. */\n\t\tcanDelete: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Disable every field. */\n\t\treadOnly: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Disable the fields marked `lockable`. */\n\t\tlocked: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Named option lists a field can point at with `optionSet`. */\n\t\toptionSets: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Translates a configured text. */\n\t\ttranslate: {\n\t\t\ttype: Function,\n\t\t\tdefault: (text) => text,\n\t\t},\n\n\t\t/** Prefix for the DOM ids inside the panel. */\n\t\tuid: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\t},\n\n\temits: ['update', 'delete', 'close'],\n\n\tdata() {\n\t\tconst { model, enabled } = readRecord(this.record, this.fields)\n\t\treturn {\n\t\t\tmodel,\n\t\t\tenabled,\n\t\t\tlastEmitted: null,\n\t\t}\n\t},\n\n\twatch: {\n\t\trecord: {\n\t\t\tdeep: true,\n\t\t\thandler(next, previous) {\n\t\t\t\tconst sameRecord = next && previous && next.id === previous.id\n\t\t\t\tif (sameRecord && this.lastEmitted !== null && JSON.stringify(next) === this.lastEmitted) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.reset()\n\t\t\t},\n\t\t},\n\n\t\tfields() {\n\t\t\tthis.reset()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Rebuild the copy from `record`.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\treset() {\n\t\t\tconst { model, enabled } = readRecord(this.record, this.fields)\n\t\t\tthis.model = model\n\t\t\tthis.enabled = enabled\n\t\t\tthis.lastEmitted = null\n\t\t},\n\n\t\t/**\n\t\t * The options of a select or checkboxes field.\n\t\t *\n\t\t * @param {object} field The descriptor.\n\t\t * @return {Array<{value: unknown, label: string}>} The options, labels translated when they are the config's own.\n\t\t */\n\t\toptionsOf(field) {\n\t\t\tif (Array.isArray(field.options)) {\n\t\t\t\treturn field.options.map((option) => ({ value: option.value, label: this.translate(option.label) }))\n\t\t\t}\n\t\t\tconst set = field.optionSet ? this.optionSets[field.optionSet] : null\n\t\t\treturn Array.isArray(set) ? set : []\n\t\t},\n\n\t\t/**\n\t\t * @param {string} text A configured text.\n\t\t * @return {string} It translated.\n\t\t */\n\t\ttr(text) {\n\t\t\treturn this.translate(text)\n\t\t},\n\n\t\t/**\n\t\t * @param {string} path An optional group's path.\n\t\t * @return {boolean} Whether it is switched on.\n\t\t */\n\t\tisEnabled(path) {\n\t\t\treturn this.enabled[path] === true\n\t\t},\n\n\t\t/**\n\t\t * @param {string} path An optional group's path.\n\t\t * @param {boolean} value Whether to switch it on.\n\t\t * @return {void}\n\t\t */\n\t\tsetEnabled(path, value) {\n\t\t\tthis.enabled[path] = value === true\n\t\t\tthis.emitUpdate()\n\t\t},\n\n\t\t/**\n\t\t * Write one value into the copy.\n\t\t *\n\t\t * @param {object|Array} target An object or list inside the copy.\n\t\t * @param {string|number} key The key or index.\n\t\t * @param {unknown} value The value.\n\t\t * @return {void}\n\t\t */\n\t\tset(target, key, value) {\n\t\t\ttarget[key] = value\n\t\t\tthis.emitUpdate()\n\t\t},\n\n\t\t/**\n\t\t * @param {Array} list A list inside the copy.\n\t\t * @param {unknown} item The entry to append.\n\t\t * @return {void}\n\t\t */\n\t\tpush(list, item) {\n\t\t\tlist.push(item)\n\t\t\tthis.emitUpdate()\n\t\t},\n\n\t\t/**\n\t\t * @param {Array} list A list inside the copy.\n\t\t * @param {number} index The entry to remove.\n\t\t * @return {void}\n\t\t */\n\t\tsplice(list, index) {\n\t\t\tlist.splice(index, 1)\n\t\t\tthis.emitUpdate()\n\t\t},\n\n\t\t/**\n\t\t * @param {Array} list A list inside the copy.\n\t\t * @param {number} from The entry's index.\n\t\t * @param {number} to Its new index.\n\t\t * @return {void}\n\t\t */\n\t\tmove(list, from, to) {\n\t\t\tif (moveItem(list, from, to)) {\n\t\t\t\tthis.emitUpdate()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Emit the record built from the copy.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\temitUpdate() {\n\t\t\tconst record = buildRecord(this.model, this.fields, this.enabled)\n\t\t\tthis.lastEmitted = JSON.stringify(record)\n\t\t\t/**\n\t\t\t * @event update The edited record, complete. Keys the fields do not\n\t\t\t * describe are carried through unchanged.\n\t\t\t */\n\t\t\tthis.$emit('update', record)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-sm-panel {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 14px;\n\tmin-width: 0;\n}\n\n.cn-sm-panel__header {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 8px;\n}\n\n.cn-sm-panel__titles {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tmin-width: 0;\n}\n\n.cn-sm-panel__title {\n\tmargin: 0;\n\tfont-size: 17px;\n\tfont-weight: 700;\n\toverflow-wrap: anywhere;\n}\n\n.cn-sm-panel__subtitle {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast, #6b6b6b);\n}\n\n.cn-sm-panel__danger {\n\tpadding-top: 12px;\n\tborder-top: 1px solid var(--color-border, #d0d0d0);\n}\n</style>\n"],"names":["CnStateMachineFields"],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { resolveComponent, openBlock, createElementBlock, createElementVNode, toDisplayString, createCommentVNode, createVNode, withCtx, createBlock, renderSlot, createTextVNode } from 'vue';
|
|
2
|
+
|
|
3
|
+
const _hoisted_1 = ["aria-labelledby"];
|
|
4
|
+
const _hoisted_2 = { class: "cn-sm-panel__header" };
|
|
5
|
+
const _hoisted_3 = { class: "cn-sm-panel__titles" };
|
|
6
|
+
const _hoisted_4 = ["id"];
|
|
7
|
+
const _hoisted_5 = {
|
|
8
|
+
key: 0,
|
|
9
|
+
class: "cn-sm-panel__subtitle"
|
|
10
|
+
};
|
|
11
|
+
const _hoisted_6 = {
|
|
12
|
+
key: 1,
|
|
13
|
+
class: "cn-sm-panel__danger"
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
17
|
+
const _component_Close = resolveComponent("Close");
|
|
18
|
+
const _component_NcButton = resolveComponent("NcButton");
|
|
19
|
+
const _component_CnStateMachineFields = resolveComponent("CnStateMachineFields");
|
|
20
|
+
const _component_DeleteOutline = resolveComponent("DeleteOutline");
|
|
21
|
+
|
|
22
|
+
return (openBlock(), createElementBlock("section", {
|
|
23
|
+
class: "cn-sm-panel",
|
|
24
|
+
"aria-labelledby": `${$props.uid}-title`
|
|
25
|
+
}, [
|
|
26
|
+
createElementVNode("header", _hoisted_2, [
|
|
27
|
+
createElementVNode("div", _hoisted_3, [
|
|
28
|
+
createElementVNode("h3", {
|
|
29
|
+
id: `${$props.uid}-title`,
|
|
30
|
+
class: "cn-sm-panel__title"
|
|
31
|
+
}, toDisplayString($props.title), 9 /* TEXT, PROPS */, _hoisted_4),
|
|
32
|
+
($props.subtitle)
|
|
33
|
+
? (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString($props.subtitle), 1 /* TEXT */))
|
|
34
|
+
: createCommentVNode("v-if", true)
|
|
35
|
+
]),
|
|
36
|
+
createVNode(_component_NcButton, {
|
|
37
|
+
variant: "tertiary",
|
|
38
|
+
"aria-label": $props.closeLabel,
|
|
39
|
+
onClick: _cache[0] || (_cache[0] = $event => (_ctx.$emit('close')))
|
|
40
|
+
}, {
|
|
41
|
+
icon: withCtx(() => [
|
|
42
|
+
createVNode(_component_Close, { size: 20 })
|
|
43
|
+
]),
|
|
44
|
+
_: 1 /* STABLE */
|
|
45
|
+
}, 8 /* PROPS */, ["aria-label"])
|
|
46
|
+
]),
|
|
47
|
+
($props.fields.length > 0)
|
|
48
|
+
? (openBlock(), createBlock(_component_CnStateMachineFields, {
|
|
49
|
+
key: 0,
|
|
50
|
+
fields: $props.fields,
|
|
51
|
+
target: $data.model,
|
|
52
|
+
disabled: $props.readOnly
|
|
53
|
+
}, null, 8 /* PROPS */, ["fields", "target", "disabled"]))
|
|
54
|
+
: createCommentVNode("v-if", true),
|
|
55
|
+
renderSlot(_ctx.$slots, "default"),
|
|
56
|
+
($props.canDelete && !$props.readOnly)
|
|
57
|
+
? (openBlock(), createElementBlock("div", _hoisted_6, [
|
|
58
|
+
createVNode(_component_NcButton, {
|
|
59
|
+
variant: "error",
|
|
60
|
+
onClick: _cache[1] || (_cache[1] = $event => (_ctx.$emit('delete')))
|
|
61
|
+
}, {
|
|
62
|
+
icon: withCtx(() => [
|
|
63
|
+
createVNode(_component_DeleteOutline, { size: 20 })
|
|
64
|
+
]),
|
|
65
|
+
default: withCtx(() => [
|
|
66
|
+
createTextVNode(" " + toDisplayString($props.deleteLabel), 1 /* TEXT */)
|
|
67
|
+
]),
|
|
68
|
+
_: 1 /* STABLE */
|
|
69
|
+
})
|
|
70
|
+
]))
|
|
71
|
+
: createCommentVNode("v-if", true)
|
|
72
|
+
], 8 /* PROPS */, _hoisted_1))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { render };
|
|
76
|
+
//# sourceMappingURL=CnStateMachinePanel.vue3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnStateMachinePanel.vue3.js","sources":["../../../../src/components/CnStateMachineEditor/CnStateMachinePanel.vue?vue&type=template&id=dd581f76&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>\n - SPDX-License-Identifier: EUPL-1.2\n -->\n<template>\n\t<section class=\"cn-sm-panel\" :aria-labelledby=\"`${uid}-title`\">\n\t\t<header class=\"cn-sm-panel__header\">\n\t\t\t<div class=\"cn-sm-panel__titles\">\n\t\t\t\t<h3 :id=\"`${uid}-title`\" class=\"cn-sm-panel__title\">\n\t\t\t\t\t{{ title }}\n\t\t\t\t</h3>\n\t\t\t\t<span v-if=\"subtitle\" class=\"cn-sm-panel__subtitle\">{{ subtitle }}</span>\n\t\t\t</div>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t:aria-label=\"closeLabel\"\n\t\t\t\t@click=\"$emit('close')\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Close :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t</NcButton>\n\t\t</header>\n\n\t\t<CnStateMachineFields\n\t\t\tv-if=\"fields.length > 0\"\n\t\t\t:fields=\"fields\"\n\t\t\t:target=\"model\"\n\t\t\t:disabled=\"readOnly\" />\n\n\t\t<!-- @slot default Extra content below the fields. -->\n\t\t<slot />\n\n\t\t<div v-if=\"canDelete && !readOnly\" class=\"cn-sm-panel__danger\">\n\t\t\t<NcButton variant=\"error\" @click=\"$emit('delete')\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<DeleteOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ deleteLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t</section>\n</template>\n\n<script>\nimport { NcButton } from '@nextcloud/vue'\nimport Close from 'vue-material-design-icons/Close.vue'\nimport DeleteOutline from 'vue-material-design-icons/DeleteOutline.vue'\nimport CnStateMachineFields from './CnStateMachineFields.vue'\nimport { buildRecord, moveItem, readRecord } from './stateMachineFields.js'\n\n/**\n * The property panel of one state, step or transition.\n *\n * Edits a copy of `record` described by `fields` and emits the whole record on\n * every change; the host stores it and passes it back down. A record that comes\n * back equal to the last one emitted does not reset the copy, so typing never\n * loses the caret.\n */\nexport default {\n\tname: 'CnStateMachinePanel',\n\n\tcomponents: { NcButton, Close, DeleteOutline, CnStateMachineFields },\n\n\tprovide() {\n\t\t// The panel itself: its `uid`, `locked`, `tr`, `optionsOf`, `isEnabled`,\n\t\t// `setEnabled`, `set`, `push`, `splice` and `move` are what the fields use.\n\t\treturn { cnStateMachinePanel: this }\n\t},\n\n\tprops: {\n\t\t/** The record being edited. */\n\t\trecord: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Field descriptors, see CnStateMachineEditor's `config`. */\n\t\tfields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The heading, already translated. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** A line under the heading. */\n\t\tsubtitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The close button's accessible name. */\n\t\tcloseLabel: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** The delete button's text. */\n\t\tdeleteLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Whether to offer the delete button. */\n\t\tcanDelete: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Disable every field. */\n\t\treadOnly: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Disable the fields marked `lockable`. */\n\t\tlocked: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Named option lists a field can point at with `optionSet`. */\n\t\toptionSets: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Translates a configured text. */\n\t\ttranslate: {\n\t\t\ttype: Function,\n\t\t\tdefault: (text) => text,\n\t\t},\n\n\t\t/** Prefix for the DOM ids inside the panel. */\n\t\tuid: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\t},\n\n\temits: ['update', 'delete', 'close'],\n\n\tdata() {\n\t\tconst { model, enabled } = readRecord(this.record, this.fields)\n\t\treturn {\n\t\t\tmodel,\n\t\t\tenabled,\n\t\t\tlastEmitted: null,\n\t\t}\n\t},\n\n\twatch: {\n\t\trecord: {\n\t\t\tdeep: true,\n\t\t\thandler(next, previous) {\n\t\t\t\tconst sameRecord = next && previous && next.id === previous.id\n\t\t\t\tif (sameRecord && this.lastEmitted !== null && JSON.stringify(next) === this.lastEmitted) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.reset()\n\t\t\t},\n\t\t},\n\n\t\tfields() {\n\t\t\tthis.reset()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Rebuild the copy from `record`.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\treset() {\n\t\t\tconst { model, enabled } = readRecord(this.record, this.fields)\n\t\t\tthis.model = model\n\t\t\tthis.enabled = enabled\n\t\t\tthis.lastEmitted = null\n\t\t},\n\n\t\t/**\n\t\t * The options of a select or checkboxes field.\n\t\t *\n\t\t * @param {object} field The descriptor.\n\t\t * @return {Array<{value: unknown, label: string}>} The options, labels translated when they are the config's own.\n\t\t */\n\t\toptionsOf(field) {\n\t\t\tif (Array.isArray(field.options)) {\n\t\t\t\treturn field.options.map((option) => ({ value: option.value, label: this.translate(option.label) }))\n\t\t\t}\n\t\t\tconst set = field.optionSet ? this.optionSets[field.optionSet] : null\n\t\t\treturn Array.isArray(set) ? set : []\n\t\t},\n\n\t\t/**\n\t\t * @param {string} text A configured text.\n\t\t * @return {string} It translated.\n\t\t */\n\t\ttr(text) {\n\t\t\treturn this.translate(text)\n\t\t},\n\n\t\t/**\n\t\t * @param {string} path An optional group's path.\n\t\t * @return {boolean} Whether it is switched on.\n\t\t */\n\t\tisEnabled(path) {\n\t\t\treturn this.enabled[path] === true\n\t\t},\n\n\t\t/**\n\t\t * @param {string} path An optional group's path.\n\t\t * @param {boolean} value Whether to switch it on.\n\t\t * @return {void}\n\t\t */\n\t\tsetEnabled(path, value) {\n\t\t\tthis.enabled[path] = value === true\n\t\t\tthis.emitUpdate()\n\t\t},\n\n\t\t/**\n\t\t * Write one value into the copy.\n\t\t *\n\t\t * @param {object|Array} target An object or list inside the copy.\n\t\t * @param {string|number} key The key or index.\n\t\t * @param {unknown} value The value.\n\t\t * @return {void}\n\t\t */\n\t\tset(target, key, value) {\n\t\t\ttarget[key] = value\n\t\t\tthis.emitUpdate()\n\t\t},\n\n\t\t/**\n\t\t * @param {Array} list A list inside the copy.\n\t\t * @param {unknown} item The entry to append.\n\t\t * @return {void}\n\t\t */\n\t\tpush(list, item) {\n\t\t\tlist.push(item)\n\t\t\tthis.emitUpdate()\n\t\t},\n\n\t\t/**\n\t\t * @param {Array} list A list inside the copy.\n\t\t * @param {number} index The entry to remove.\n\t\t * @return {void}\n\t\t */\n\t\tsplice(list, index) {\n\t\t\tlist.splice(index, 1)\n\t\t\tthis.emitUpdate()\n\t\t},\n\n\t\t/**\n\t\t * @param {Array} list A list inside the copy.\n\t\t * @param {number} from The entry's index.\n\t\t * @param {number} to Its new index.\n\t\t * @return {void}\n\t\t */\n\t\tmove(list, from, to) {\n\t\t\tif (moveItem(list, from, to)) {\n\t\t\t\tthis.emitUpdate()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Emit the record built from the copy.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\temitUpdate() {\n\t\t\tconst record = buildRecord(this.model, this.fields, this.enabled)\n\t\t\tthis.lastEmitted = JSON.stringify(record)\n\t\t\t/**\n\t\t\t * @event update The edited record, complete. Keys the fields do not\n\t\t\t * describe are carried through unchanged.\n\t\t\t */\n\t\t\tthis.$emit('update', record)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-sm-panel {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 14px;\n\tmin-width: 0;\n}\n\n.cn-sm-panel__header {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 8px;\n}\n\n.cn-sm-panel__titles {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tmin-width: 0;\n}\n\n.cn-sm-panel__title {\n\tmargin: 0;\n\tfont-size: 17px;\n\tfont-weight: 700;\n\toverflow-wrap: anywhere;\n}\n\n.cn-sm-panel__subtitle {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast, #6b6b6b);\n}\n\n.cn-sm-panel__danger {\n\tpadding-top: 12px;\n\tborder-top: 1px solid var(--color-border, #d0d0d0);\n}\n</style>\n"],"names":["_createElementBlock","_createElementVNode","_toDisplayString","_createVNode","_createBlock","_renderSlot","_createTextVNode"],"mappings":";;;AAUQ,MAAA,UAAA,GAAA,EAAA,KAAA,EAAA,qBAAA,GAAA;AACA,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,qBAAqB,GAAA;;;;AAIT,EAAA,KAAC,EAAA,uBAAA;;;;AAqBb,EAAA,KAAA,EAAA,qBAAA;;;;;;;;;uBA3BTA;AAAK,IAAA,KAAK,EAAA,aAAA;AAAA,IAAA,iBAAA,KAAA,MAAA,CAAA,GAAA,CAAA,MAAA,CAAA;;uBAgBJ,oBAAA,EAAA;AAdP,MAAAC,kBAAA,CAKK,OALL,UAKK,EAAA;QAJLA,kBAEK,CAAA,IAAA,EAAA;AAFD,UAAA,EAAA,KAAA,MAAA,CAAA,GAAA,CAAA,MAAA,CAAA;AAAA,UAAA,KAAA,EAAA,oBAAA;2BACA,MAAG,CAAA,KAAA,CAAA,EAAA,CAAA,oBAAA,UAAA,CAAA;SAEK,MAAQ,CAAA,QAAA;0BAApBD,kBAAuB,CAAA,MAAA,EAAvB,UAAuB,EAAAE,eAAA,CAAA,MAAA,CAAA,QAAA,CAAA,EAAA,CAAA,YAAA;;;MAEvBC,WAOU,CAAA,mBAAA,EAAA;AANT,QAAA,SAAO;AACP,QAAA,YAAS,EAAA,MAAA,CAAA,UAAA;AACT,QAAA,OAAK,uCAAE,IAAC,CAAA,KAAA,CAAA,OAAA,CAAA,CAAA,CAAA;;AACH,QAAA,IAAA;;;;;;;sBAMPC;;AAEC,UAAA,MAAM,EAAG,MAAK,CAAA,MAAA;AACd,UAAA,MAAK,EAAA,KAAA,CAAA,KAAA;;;;IAILC,UAAM,CAAA,IAAA,CAAA,MAAA,EAAA,SAAA,CAAA;AAEG,IAAA,CAAA,MAAA,CAAA,SAAC,KAAA,MAAA,CAAA,QAAA;;UACVF;AAAS,YAAA,OAAO,EAAA,OAAA;AAAA,YAAA,OAAA,uCAAA,IAAA,CAAA,KAAA,CAAA,QAAA,CAAA,CAAA,CAAA;;AACP,YAAA,IAAA,UACR,MAAE;cAAFA,WAAE,CAAA,wBAAA,EAAA,EAAA,IAAA,EAAA,EAAA,EAAA,CAAA;;6BACG;AAAA,cAAAG,eAAA,CAAA;;;;;;;;;;;"}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>
|
|
3
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
*
|
|
5
|
+
* The record model behind CnStateMachineEditor's property panels: turning a
|
|
6
|
+
* stored record into an editable copy described by field descriptors, and the
|
|
7
|
+
* copy back into the record the host stores.
|
|
8
|
+
*
|
|
9
|
+
* A field descriptor is `{ key, type, label, ... }` with `type` one of
|
|
10
|
+
* `text`, `textarea`, `number`, `checkbox`, `select`, `checkboxes`, `list`,
|
|
11
|
+
* `typed-list`, `group` or `row`. A `group` with a `key` edits a nested object;
|
|
12
|
+
* without one, like a `row`, it only arranges its `fields`.
|
|
13
|
+
*
|
|
14
|
+
* @module components/CnStateMachineEditor/stateMachineFields
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const LIST_TYPES = new Set(['list', 'typed-list', 'checkboxes']);
|
|
18
|
+
|
|
19
|
+
let generatedIds = 0;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parse a value a host may have stored as a JSON string.
|
|
23
|
+
*
|
|
24
|
+
* @param {unknown} raw The stored value.
|
|
25
|
+
* @return {unknown} The parsed value, or the value itself when it is not JSON text.
|
|
26
|
+
*/
|
|
27
|
+
function parseStored(raw) {
|
|
28
|
+
if (typeof raw !== 'string') {
|
|
29
|
+
return raw
|
|
30
|
+
}
|
|
31
|
+
const text = raw.trim();
|
|
32
|
+
if (text.charAt(0) !== '[' && text.charAt(0) !== '{') {
|
|
33
|
+
return raw
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(text)
|
|
37
|
+
} catch {
|
|
38
|
+
return raw
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Deep copy of plain data.
|
|
44
|
+
*
|
|
45
|
+
* @param {unknown} value The value.
|
|
46
|
+
* @return {unknown} The copy.
|
|
47
|
+
*/
|
|
48
|
+
function cloneData(value) {
|
|
49
|
+
if (value === undefined) {
|
|
50
|
+
return undefined
|
|
51
|
+
}
|
|
52
|
+
return JSON.parse(JSON.stringify(value))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Whether a value counts as not filled in.
|
|
57
|
+
*
|
|
58
|
+
* @param {unknown} value The value.
|
|
59
|
+
* @return {boolean} True for null, undefined, '', NaN and blank strings.
|
|
60
|
+
*/
|
|
61
|
+
function isBlank(value) {
|
|
62
|
+
if (value === null || value === undefined) {
|
|
63
|
+
return true
|
|
64
|
+
}
|
|
65
|
+
if (typeof value === 'number') {
|
|
66
|
+
return Number.isNaN(value)
|
|
67
|
+
}
|
|
68
|
+
return typeof value === 'string' && value.trim() === ''
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Whether a value is a plain object.
|
|
73
|
+
*
|
|
74
|
+
* @param {unknown} value The value.
|
|
75
|
+
* @return {boolean} True for `{}`-like values.
|
|
76
|
+
*/
|
|
77
|
+
function isPlainObject(value) {
|
|
78
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* A fresh id for a new list item.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} prefix The id prefix.
|
|
85
|
+
* @return {string} The id.
|
|
86
|
+
*/
|
|
87
|
+
function newItemId(prefix = 'item-') {
|
|
88
|
+
generatedIds += 1;
|
|
89
|
+
return `${prefix}${Date.now().toString(36)}-${generatedIds}`
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The path that identifies an optional group in the enabled map.
|
|
94
|
+
*
|
|
95
|
+
* @param {string} base The parent path.
|
|
96
|
+
* @param {string} key The group key.
|
|
97
|
+
* @return {string} The path.
|
|
98
|
+
*/
|
|
99
|
+
function pathOf(base, key) {
|
|
100
|
+
return base ? `${base}.${key}` : key
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Normalise `target` in place so every described field holds an editable value,
|
|
105
|
+
* and record which optional groups are switched on.
|
|
106
|
+
*
|
|
107
|
+
* Text fields that are absent stay absent: a panel opened and closed must not
|
|
108
|
+
* add empty keys to a record.
|
|
109
|
+
*
|
|
110
|
+
* @param {object} target The copy being edited.
|
|
111
|
+
* @param {Array<object>} fields The descriptors.
|
|
112
|
+
* @param {object} enabled Optional-group state by path, filled in here.
|
|
113
|
+
* @param {string} [base] The path of `target`.
|
|
114
|
+
* @return {object} `target`.
|
|
115
|
+
*/
|
|
116
|
+
function normaliseFields(target, fields, enabled, base = '') {
|
|
117
|
+
for (const field of fields || []) {
|
|
118
|
+
if ((field.type === 'group' && !field.key) || field.type === 'row') {
|
|
119
|
+
normaliseFields(target, field.fields, enabled, base);
|
|
120
|
+
continue
|
|
121
|
+
}
|
|
122
|
+
if (!field.key) {
|
|
123
|
+
continue
|
|
124
|
+
}
|
|
125
|
+
const stored = parseStored(target[field.key]);
|
|
126
|
+
if (field.type === 'group') {
|
|
127
|
+
const present = isPlainObject(stored);
|
|
128
|
+
if (field.optional) {
|
|
129
|
+
enabled[pathOf(base, field.key)] = present;
|
|
130
|
+
}
|
|
131
|
+
const value = { ...cloneData(field.defaults || {}), ...(present ? stored : {}) };
|
|
132
|
+
normaliseFields(value, field.fields, enabled, pathOf(base, field.key));
|
|
133
|
+
target[field.key] = value;
|
|
134
|
+
} else if (LIST_TYPES.has(field.type)) {
|
|
135
|
+
target[field.key] = Array.isArray(stored) ? stored : [];
|
|
136
|
+
} else if (field.type === 'checkbox') {
|
|
137
|
+
if (stored !== undefined || field.default !== undefined) {
|
|
138
|
+
target[field.key] = stored === undefined ? !!field.default : !!stored;
|
|
139
|
+
}
|
|
140
|
+
} else if (stored === undefined && field.default !== undefined) {
|
|
141
|
+
target[field.key] = cloneData(field.default);
|
|
142
|
+
} else if (stored !== undefined) {
|
|
143
|
+
target[field.key] = stored;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return target
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Turn a stored record into the copy a panel edits.
|
|
151
|
+
*
|
|
152
|
+
* @param {object} record The stored record.
|
|
153
|
+
* @param {Array<object>} fields The descriptors.
|
|
154
|
+
* @return {{ model: object, enabled: object }} The copy, and which optional groups are on.
|
|
155
|
+
*/
|
|
156
|
+
function readRecord(record, fields) {
|
|
157
|
+
const enabled = {};
|
|
158
|
+
const model = normaliseFields(cloneData(record || {}), fields, enabled);
|
|
159
|
+
return { model, enabled }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Every leaf key a group describes, through rows and key-less groups.
|
|
164
|
+
*
|
|
165
|
+
* @param {Array<object>} fields The descriptors.
|
|
166
|
+
* @return {Array<object>} The leaf descriptors.
|
|
167
|
+
*/
|
|
168
|
+
function leafFields(fields) {
|
|
169
|
+
const leaves = [];
|
|
170
|
+
for (const field of fields || []) {
|
|
171
|
+
if ((field.type === 'group' && !field.key) || field.type === 'row') {
|
|
172
|
+
leaves.push(...leafFields(field.fields));
|
|
173
|
+
} else if (field.key) {
|
|
174
|
+
leaves.push(field);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return leaves
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Drop what is not filled in from one pruned object.
|
|
182
|
+
*
|
|
183
|
+
* Lists of strings lose their blank entries; then any key holding a blank
|
|
184
|
+
* value or an empty list goes. `false` and `0` are values and stay.
|
|
185
|
+
*
|
|
186
|
+
* @param {object} value The object.
|
|
187
|
+
* @param {Array<object>} fields Its descriptors.
|
|
188
|
+
* @return {object} The pruned object.
|
|
189
|
+
*/
|
|
190
|
+
function prune(value, fields) {
|
|
191
|
+
const byKey = Object.fromEntries(leafFields(fields).map((field) => [field.key, field]));
|
|
192
|
+
for (const key of Object.keys(value)) {
|
|
193
|
+
let entry = value[key];
|
|
194
|
+
const field = byKey[key];
|
|
195
|
+
if (Array.isArray(entry) && field && field.type === 'list' && field.itemType !== 'object') {
|
|
196
|
+
entry = entry.filter((item) => !isBlank(item));
|
|
197
|
+
value[key] = entry;
|
|
198
|
+
}
|
|
199
|
+
if (isBlank(entry) || (Array.isArray(entry) && entry.length === 0)) {
|
|
200
|
+
delete value[key];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return value
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Turn an edited copy back into the record to store.
|
|
208
|
+
*
|
|
209
|
+
* Per group: an optional group that is off is left out; a `requireAll` group is
|
|
210
|
+
* left out unless every leaf is filled in; a `prune` group drops its empty
|
|
211
|
+
* entries and, with `omitWhenEmpty`, is left out once nothing remains.
|
|
212
|
+
*
|
|
213
|
+
* @param {object} model The edited copy.
|
|
214
|
+
* @param {Array<object>} fields The descriptors.
|
|
215
|
+
* @param {object} enabled Optional-group state by path.
|
|
216
|
+
* @param {string} [base] The path of `model`.
|
|
217
|
+
* @return {object} The record.
|
|
218
|
+
*/
|
|
219
|
+
function buildRecord(model, fields, enabled, base = '') {
|
|
220
|
+
const out = cloneData(model || {});
|
|
221
|
+
const visit = (target, list, path) => {
|
|
222
|
+
for (const field of list || []) {
|
|
223
|
+
if ((field.type === 'group' && !field.key) || field.type === 'row') {
|
|
224
|
+
visit(target, field.fields, path);
|
|
225
|
+
continue
|
|
226
|
+
}
|
|
227
|
+
if (field.type !== 'group' || !field.key) {
|
|
228
|
+
continue
|
|
229
|
+
}
|
|
230
|
+
const groupPath = pathOf(path, field.key);
|
|
231
|
+
if (field.optional && !enabled[groupPath]) {
|
|
232
|
+
delete target[field.key];
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
const inner = isPlainObject(target[field.key]) ? target[field.key] : {};
|
|
236
|
+
visit(inner, field.fields, groupPath);
|
|
237
|
+
if (field.requireAll && leafFields(field.fields).some((leaf) => isBlank(inner[leaf.key]))) {
|
|
238
|
+
delete target[field.key];
|
|
239
|
+
continue
|
|
240
|
+
}
|
|
241
|
+
if (field.prune) {
|
|
242
|
+
prune(inner, field.fields);
|
|
243
|
+
}
|
|
244
|
+
if (field.omitWhenEmpty && Object.keys(inner).length === 0) {
|
|
245
|
+
delete target[field.key];
|
|
246
|
+
continue
|
|
247
|
+
}
|
|
248
|
+
target[field.key] = inner;
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
visit(out, fields, base);
|
|
252
|
+
return out
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* A new item for a list field.
|
|
257
|
+
*
|
|
258
|
+
* @param {object} field The list descriptor.
|
|
259
|
+
* @return {unknown} The item: '' for a list of strings, else a copy of `newItem` with a fresh id when `idKey` is set.
|
|
260
|
+
*/
|
|
261
|
+
function newListItem(field) {
|
|
262
|
+
if (field.type === 'list' && field.itemType !== 'object') {
|
|
263
|
+
return ''
|
|
264
|
+
}
|
|
265
|
+
const item = cloneData(field.newItem || {});
|
|
266
|
+
if (field.idKey) {
|
|
267
|
+
item[field.idKey] = newItemId(field.idPrefix);
|
|
268
|
+
}
|
|
269
|
+
return item
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Move one entry of a list.
|
|
274
|
+
*
|
|
275
|
+
* @param {Array} list The list, changed in place.
|
|
276
|
+
* @param {number} from The entry's index.
|
|
277
|
+
* @param {number} to Its new index.
|
|
278
|
+
* @return {boolean} Whether anything moved.
|
|
279
|
+
*/
|
|
280
|
+
function moveItem(list, from, to) {
|
|
281
|
+
if (from === to || from < 0 || to < 0 || from >= list.length || to >= list.length) {
|
|
282
|
+
return false
|
|
283
|
+
}
|
|
284
|
+
const [item] = list.splice(from, 1);
|
|
285
|
+
list.splice(to, 0, item);
|
|
286
|
+
return true
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export { buildRecord, cloneData, isBlank, moveItem, newItemId, newListItem, normaliseFields, parseStored, readRecord };
|
|
290
|
+
//# sourceMappingURL=stateMachineFields.js.map
|