@dualbox/editor 1.0.71 → 1.0.73
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/html/editor.html +1 -1
- package/js/dist/GraphEditor.js +540 -440
- package/js/dist/GraphEditor.min.js +539 -439
- package/js/src/c/GraphController.js +66 -67
- package/js/src/m/GraphModel.js +34 -9
- package/js/src/m/History.js +23 -6
- package/js/src/v/AppManager.js +1 -0
- package/js/src/v/GraphView.js +4 -0
- package/js/src/v/templates/debugNodeInfos.vue +4 -4
- package/js/src/v/templates/editMainSettings.vue +1 -1
- package/js/src/v/templates/editNodeSettings.vue +4 -3
- package/js/src/v/templates/editValue.vue +123 -125
- package/js/src/v/templates/graphNode.vue +185 -200
- package/js/src/v/templates/main.vue +194 -175
- package/package.json +1 -1
|
@@ -1,55 +1,62 @@
|
|
|
1
1
|
<style scoped>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
.template-value-container,
|
|
3
|
+
.field-editor {
|
|
4
|
+
margin-bottom: 10px;
|
|
5
|
+
}
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
.template-value-message {
|
|
8
|
+
text-align: center;
|
|
9
|
+
margin-top: 30px;
|
|
10
|
+
margin-bottom: 30px;
|
|
11
|
+
width: 100%;
|
|
12
|
+
opacity: 0.5;
|
|
13
|
+
}
|
|
13
14
|
</style>
|
|
14
15
|
|
|
15
16
|
<template>
|
|
16
17
|
<div class="w-100">
|
|
17
18
|
<p style="display: none;">{{cIndex}}</p>
|
|
18
19
|
<template v-if="isBasicDataType()">
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
<template v-if="dataType === 'string'">
|
|
21
|
+
<input class="edit-value-input" type="text" @keyup.enter="setStringValue" @keyup.esc="setStringValue" :value="v" @focus="$event.target.select()"></input>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<template v-else-if="dataType === 'boolean'">
|
|
25
|
+
<select class="edit-value-input" @change="setBoolValue">
|
|
26
|
+
<option value="true" :selected="v == true">True</option>
|
|
27
|
+
<option value="false" :selected="v == false">False</option>
|
|
28
|
+
</select>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<template v-else-if="dataType === 'number'">
|
|
32
|
+
<input class="edit-value-input" type="number" @keyup.enter="setNumberValue" @blur="setNumberValue" :value="v" @focus="$event.target.select()" style="width: 40px;">
|
|
33
|
+
</input>
|
|
34
|
+
</template>
|
|
31
35
|
</template>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
<div
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
<div class="
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<
|
|
45
|
-
|
|
36
|
+
|
|
37
|
+
<template v-else>
|
|
38
|
+
<!-- we already are in a modal, don't instanciate another one -->
|
|
39
|
+
<template v-if="isTemplateType()">
|
|
40
|
+
<div class="container template-value-container" style="border: 1px solid rgba(0, 0, 0, 0.125);">
|
|
41
|
+
<div v-if="isArrayType()" class="row" style="background-color: rgba(0,0,0,.125);">Array Editor</div>
|
|
42
|
+
<div v-else-if="isMapType()" class="row" style="background-color: rgba(0,0,0,.125);">Map Editor</div>
|
|
43
|
+
<div class="row" style="1px solid rgba(0,0,0,.125);">
|
|
44
|
+
<div class="col list-group-flush" :class="{ 'col-1' : isArrayType(), 'col-2' : isMapType()}" style="padding: 0; background-color: white; overflow-y: auto; border-right: 1px solid rgba(0, 0, 0, 0.125);">
|
|
45
|
+
<button v-for="index in deserializedValue.keys()" :key="index" :data-key="index" type="button" class="list-group-item list-group-item-action" :class="{ active: isSelectedIndex(index) }" style="text-align: center;" @click="selectIndex(index)">{{index}}</button>
|
|
46
|
+
<button type="button" @click="addItem" class="list-group-item list-group-item-action add-item" style="text-align: center;"><i class="fas fa-plus-circle"></i></button>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="col" style="padding: 10px">
|
|
49
|
+
<edit-value v-if="selectedIndex !== undefined && selectedIndex !== null" :cIndex="selectedIndex" :type="embeddedType" :v="getElement(selectedIndex)" @edited="onSubValueEdited(selectedIndex, $event)"></edit-value>
|
|
50
|
+
<p v-else class="template-value-message">Select an element to edit.</p>
|
|
51
|
+
</div>
|
|
46
52
|
</div>
|
|
47
53
|
</div>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
</template>
|
|
55
|
+
|
|
56
|
+
<template v-else>
|
|
57
|
+
<div class="field-editor" :data-value="hash()">
|
|
58
|
+
</div>
|
|
59
|
+
</template>
|
|
53
60
|
</template>
|
|
54
61
|
|
|
55
62
|
<!-- set null and delete buttons -->
|
|
@@ -67,34 +74,35 @@ import swal from 'sweetalert2';
|
|
|
67
74
|
import JSONEditor from '@dualbox/dualbox-lib-jsoneditor';
|
|
68
75
|
|
|
69
76
|
String.prototype.hashCode = function() {
|
|
70
|
-
var hash = 0,
|
|
77
|
+
var hash = 0,
|
|
78
|
+
i, chr;
|
|
71
79
|
if (this.length === 0) return hash;
|
|
72
80
|
for (i = 0; i < this.length; i++) {
|
|
73
|
-
chr
|
|
74
|
-
hash
|
|
81
|
+
chr = this.charCodeAt(i);
|
|
82
|
+
hash = ((hash << 5) - hash) + chr;
|
|
75
83
|
hash |= 0; // Convert to 32bit integer
|
|
76
84
|
}
|
|
77
85
|
return hash;
|
|
78
86
|
};
|
|
79
87
|
|
|
80
88
|
export default {
|
|
81
|
-
name
|
|
89
|
+
name: "edit-value",
|
|
82
90
|
props: [
|
|
83
91
|
// required
|
|
84
|
-
"v",
|
|
85
|
-
"type",
|
|
92
|
+
"v", // the value
|
|
93
|
+
"type", // the dualbox type of the value
|
|
86
94
|
|
|
87
95
|
// specific properties (for recursivity)
|
|
88
|
-
"cIndex"
|
|
96
|
+
"cIndex" // index for reference when in an array
|
|
89
97
|
],
|
|
90
|
-
data: function
|
|
98
|
+
data: function() {
|
|
91
99
|
return {
|
|
92
|
-
"dataType": null,
|
|
100
|
+
"dataType": null, // the real javascript dataType of this.v
|
|
93
101
|
"deserializedValue": null,
|
|
94
102
|
|
|
95
103
|
// if type is a collection (dataType=="map" or dataType=="array")
|
|
96
|
-
"embeddedType": null,
|
|
97
|
-
"selectedIndex": null,
|
|
104
|
+
"embeddedType": null, // the embedded type of this templated collection
|
|
105
|
+
"selectedIndex": null, // the current index of the element we are editing
|
|
98
106
|
};
|
|
99
107
|
},
|
|
100
108
|
created: function() {
|
|
@@ -114,23 +122,21 @@ export default {
|
|
|
114
122
|
this.updateData();
|
|
115
123
|
},
|
|
116
124
|
updated: function() {
|
|
117
|
-
if(
|
|
125
|
+
if (this.autochange) {
|
|
118
126
|
// change triggered by ourself, skip
|
|
119
127
|
this.autochange = false;
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
128
|
+
} else {
|
|
122
129
|
this.updateEditor();
|
|
123
130
|
}
|
|
124
131
|
},
|
|
125
132
|
beforeDestroy: function() {
|
|
126
133
|
// make sure the value was set
|
|
127
|
-
if(
|
|
128
|
-
if(
|
|
134
|
+
if (!this.emitted) {
|
|
135
|
+
if (this.isBasicDataType()) {
|
|
129
136
|
var elt = $(this.$el).find('.edit-value-input');
|
|
130
137
|
var val = elt.val();
|
|
131
138
|
this.$emit("edited", val);
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
139
|
+
} else {
|
|
134
140
|
this.saveChanges();
|
|
135
141
|
}
|
|
136
142
|
}
|
|
@@ -138,28 +144,27 @@ export default {
|
|
|
138
144
|
methods: {
|
|
139
145
|
hash: function() {
|
|
140
146
|
var str = new String(this.v).toString();
|
|
141
|
-
return str.split('').reduce((prevHash, currVal) => (((prevHash << 5) - prevHash) + currVal.charCodeAt(0))|0, 0);
|
|
147
|
+
return str.split('').reduce((prevHash, currVal) => (((prevHash << 5) - prevHash) + currVal.charCodeAt(0)) | 0, 0);
|
|
142
148
|
},
|
|
143
149
|
|
|
144
150
|
updateEditor: function() {
|
|
145
|
-
if(
|
|
146
|
-
if(
|
|
151
|
+
if (this.dataType === "object") {
|
|
152
|
+
if (!this.editor && $(this.$el).find('.field-editor')[0] !== undefined) {
|
|
147
153
|
// bind the json editor
|
|
148
154
|
this.editor = new JSONEditor(
|
|
149
155
|
$(this.$el).find('.field-editor')[0], {
|
|
150
|
-
modes: ['tree', 'code', 'text'
|
|
156
|
+
modes: ['tree', 'code', 'text'],
|
|
151
157
|
onChange: () => {
|
|
152
158
|
try {
|
|
153
159
|
var json = this.editor.get();
|
|
154
160
|
this.autochange = true;
|
|
155
161
|
this.$emit('edited', json);
|
|
156
|
-
}
|
|
157
|
-
catch(e) {}
|
|
162
|
+
} catch (e) {}
|
|
158
163
|
}
|
|
159
164
|
});
|
|
160
165
|
}
|
|
161
166
|
|
|
162
|
-
if(
|
|
167
|
+
if (this.editor) {
|
|
163
168
|
this.editor.set(this.v || {});
|
|
164
169
|
}
|
|
165
170
|
}
|
|
@@ -169,42 +174,40 @@ export default {
|
|
|
169
174
|
var resolvedType = this.type ? this.type.toLowerCase() : typeof this.v;
|
|
170
175
|
|
|
171
176
|
// If "*", resolve the type by detecting it dynamically from value
|
|
172
|
-
if(
|
|
177
|
+
if (resolvedType.indexOf("*") !== -1) {
|
|
173
178
|
resolvedType = window.DualBox.Type.detectType(this.v).toLowerCase();
|
|
174
179
|
}
|
|
175
180
|
|
|
176
181
|
// Check if we have a template type. If so, determine current index and embedded type
|
|
177
182
|
var templateType = null;
|
|
178
|
-
if(
|
|
183
|
+
if (resolvedType.startsWith("array")) {
|
|
179
184
|
templateType = "array";
|
|
180
185
|
this.deserializedValue = this.v || [];
|
|
181
|
-
}
|
|
182
|
-
else if( resolvedType.startsWith("map") ) {
|
|
186
|
+
} else if (resolvedType.startsWith("map")) {
|
|
183
187
|
templateType = "map";
|
|
184
188
|
this.deserializedValue = window.DualBox.Type.deserialize(this.v || {
|
|
185
|
-
"metadata":{
|
|
186
|
-
"type":"Map<String,"+ this.firstLetterUppercase(
|
|
189
|
+
"metadata": {
|
|
190
|
+
"type": "Map<String," + this.firstLetterUppercase(this.getEmbeddedType(resolvedType)) + ">"
|
|
187
191
|
},
|
|
188
192
|
"data": {}
|
|
189
193
|
});
|
|
190
194
|
}
|
|
191
195
|
|
|
192
|
-
if(
|
|
196
|
+
if (templateType) {
|
|
193
197
|
this.dataType = templateType;
|
|
194
198
|
this.embeddedType = this.getEmbeddedType(resolvedType);
|
|
195
|
-
if(
|
|
199
|
+
if (this.selectedIndex == null) {
|
|
196
200
|
var firstKey = this.deserializedValue.keys().next();
|
|
197
|
-
if(
|
|
201
|
+
if (firstKey && firstKey.value) {
|
|
198
202
|
this.selectedIndex = firstKey.value;
|
|
199
203
|
}
|
|
200
204
|
}
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
205
|
+
} else {
|
|
203
206
|
this.dataType = resolvedType;
|
|
204
207
|
}
|
|
205
208
|
|
|
206
209
|
// Finally, transform to object if not a basic value
|
|
207
|
-
if(
|
|
210
|
+
if (["string", "number", "boolean", "array", "map"].indexOf(this.dataType) == -1) {
|
|
208
211
|
this.dataType = "object";
|
|
209
212
|
}
|
|
210
213
|
//console.log(`Type: ${this.type}, Datatype: ${this.dataType}, embeddedType: ${this.embeddedType}`);
|
|
@@ -220,22 +223,20 @@ export default {
|
|
|
220
223
|
this.resolveDatatype();
|
|
221
224
|
},
|
|
222
225
|
|
|
223
|
-
isBasicDataType()
|
|
226
|
+
isBasicDataType() {
|
|
224
227
|
return this.dataType == "string" ||
|
|
225
228
|
this.dataType == "number" ||
|
|
226
229
|
this.dataType == "boolean";
|
|
227
230
|
},
|
|
228
231
|
|
|
229
|
-
serialize(
|
|
230
|
-
if(
|
|
231
|
-
if(
|
|
232
|
+
serialize(value) {
|
|
233
|
+
if (this.isTemplateType()) {
|
|
234
|
+
if (this.isArrayType()) {
|
|
232
235
|
return value; // we store arrays as is
|
|
233
|
-
}
|
|
234
|
-
else if( this.isMapType() ) {
|
|
236
|
+
} else if (this.isMapType()) {
|
|
235
237
|
return window.DualBox.Type.serialize(value);
|
|
236
238
|
}
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
+
} else {
|
|
239
240
|
return value;
|
|
240
241
|
}
|
|
241
242
|
},
|
|
@@ -243,11 +244,10 @@ export default {
|
|
|
243
244
|
saveChanges: function() {
|
|
244
245
|
this.$forceUpdate();
|
|
245
246
|
|
|
246
|
-
if(
|
|
247
|
+
if (this.isTemplateType()) {
|
|
247
248
|
this.$emit("edited", this.serialize(this.deserializedValue));
|
|
248
249
|
this.emitted = true;
|
|
249
|
-
}
|
|
250
|
-
else if( this.isObjectType() ) {
|
|
250
|
+
} else if (this.isObjectType()) {
|
|
251
251
|
// emit the result to the parent Vue
|
|
252
252
|
this.$emit("edited", this.editor.get());
|
|
253
253
|
this.emitted = true;
|
|
@@ -268,8 +268,13 @@ export default {
|
|
|
268
268
|
|
|
269
269
|
setNumberValue: function(e) {
|
|
270
270
|
var val = parseFloat($(e.target).val());
|
|
271
|
-
|
|
272
|
-
|
|
271
|
+
if (isNaN(val)) {
|
|
272
|
+
this.deleteValue(e);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
this.$emit("edited", val);
|
|
276
|
+
this.emitted = true;
|
|
277
|
+
}
|
|
273
278
|
},
|
|
274
279
|
|
|
275
280
|
setNullValue: function() {
|
|
@@ -280,10 +285,9 @@ export default {
|
|
|
280
285
|
},
|
|
281
286
|
|
|
282
287
|
selectNextIndex: function() {
|
|
283
|
-
if(
|
|
288
|
+
if (this.isArrayType()) {
|
|
284
289
|
this.selectedIndex = this.deserializedValue.length > 0 ? 0 : null;
|
|
285
|
-
}
|
|
286
|
-
else if( this.isMapType() ) {
|
|
290
|
+
} else if (this.isMapType()) {
|
|
287
291
|
var keys = this.deserializedValue.keys();
|
|
288
292
|
var next = keys.next();
|
|
289
293
|
this.selectedIndex = next ? next.value : null;
|
|
@@ -316,22 +320,21 @@ export default {
|
|
|
316
320
|
|
|
317
321
|
getEmbeddedType: function(type) {
|
|
318
322
|
var start = type.indexOf('<') + 1;
|
|
319
|
-
var end
|
|
320
|
-
if(
|
|
323
|
+
var end = type.lastIndexOf('>');
|
|
324
|
+
if (type.toLowerCase().startsWith('array')) {
|
|
321
325
|
return type.substr(start, end - start).trim().toLowerCase();
|
|
322
|
-
}
|
|
323
|
-
else if( type.toLowerCase().startsWith('map') ) {
|
|
326
|
+
} else if (type.toLowerCase().startsWith('map')) {
|
|
324
327
|
var sub = type.substr(start, end - start);
|
|
325
328
|
return sub.substr(sub.indexOf(',') + 1).trim().toLowerCase();
|
|
326
329
|
}
|
|
327
330
|
},
|
|
328
331
|
|
|
329
|
-
firstLetterUppercase: function(s)
|
|
332
|
+
firstLetterUppercase: function(s) {
|
|
330
333
|
return s.charAt(0).toUpperCase() + s.slice(1)
|
|
331
334
|
},
|
|
332
335
|
|
|
333
336
|
selectIndex: function(index) {
|
|
334
|
-
if(
|
|
337
|
+
if (this.selectedIndex !== index) {
|
|
335
338
|
this.selectedIndex = index;
|
|
336
339
|
this.$forceUpdate();
|
|
337
340
|
}
|
|
@@ -342,30 +345,26 @@ export default {
|
|
|
342
345
|
},
|
|
343
346
|
|
|
344
347
|
getElement: function(i) {
|
|
345
|
-
if(
|
|
348
|
+
if (Array.isArray(this.v)) {
|
|
346
349
|
return this.deserializedValue[i];
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
350
|
+
} else {
|
|
349
351
|
return this.deserializedValue.get(i);
|
|
350
352
|
}
|
|
351
353
|
},
|
|
352
354
|
|
|
353
355
|
onSubValueEdited: function(index, newValue) {
|
|
354
356
|
console.log(`Edited at index ${index}: ${JSON.stringify(newValue)}`);
|
|
355
|
-
if(
|
|
356
|
-
if(
|
|
357
|
+
if (this.isArrayType()) {
|
|
358
|
+
if (newValue === undefined) {
|
|
357
359
|
// we just remove this value
|
|
358
360
|
this.deserializedValue.splice(index, 1);
|
|
359
|
-
}
|
|
360
|
-
else {
|
|
361
|
+
} else {
|
|
361
362
|
this.deserializedValue[index] = newValue;
|
|
362
363
|
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
if( newValue === undefined ) {
|
|
364
|
+
} else if (this.isMapType()) {
|
|
365
|
+
if (newValue === undefined) {
|
|
366
366
|
this.deserializedValue.delete(index);
|
|
367
|
-
}
|
|
368
|
-
else {
|
|
367
|
+
} else {
|
|
369
368
|
this.deserializedValue.set(index, newValue);
|
|
370
369
|
}
|
|
371
370
|
}
|
|
@@ -374,20 +373,19 @@ export default {
|
|
|
374
373
|
},
|
|
375
374
|
|
|
376
375
|
addItem: function() {
|
|
377
|
-
if(
|
|
378
|
-
this.deserializedValue[
|
|
376
|
+
if (this.isArrayType()) {
|
|
377
|
+
this.deserializedValue[this.deserializedValue.length] = null;
|
|
379
378
|
this.selectedIndex = this.deserializedValue.length - 1;
|
|
380
379
|
this.saveChanges();
|
|
381
380
|
this.$forceUpdate();
|
|
382
|
-
}
|
|
383
|
-
else if( this.isMapType() ) {
|
|
381
|
+
} else if (this.isMapType()) {
|
|
384
382
|
this.swalFixBootstrapModal();
|
|
385
383
|
swal({
|
|
386
384
|
input: 'text',
|
|
387
385
|
title: 'Enter map key',
|
|
388
386
|
}).then((result) => {
|
|
389
387
|
this.swalRestoreBootstrapModal();
|
|
390
|
-
if(
|
|
388
|
+
if (result && result.value) {
|
|
391
389
|
this.selectedIndex = result.value;
|
|
392
390
|
this.deserializedValue.set(result.value, null);
|
|
393
391
|
this.saveChanges();
|
|
@@ -399,18 +397,18 @@ export default {
|
|
|
399
397
|
|
|
400
398
|
// call this before showing SweetAlert:
|
|
401
399
|
swalFixBootstrapModal() {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
400
|
+
var modal = $("body").find('.modal[tabindex="-1"]');
|
|
401
|
+
if (!modal) return;
|
|
402
|
+
modal.removeAttr('tabindex');
|
|
403
|
+
modal.addClass('js-swal-fixed');
|
|
406
404
|
},
|
|
407
405
|
|
|
408
406
|
// call this before hiding SweetAlert (inside done callback):
|
|
409
407
|
swalRestoreBootstrapModal() {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
408
|
+
var modal = $("body").find('.modal.js-swal-fixed');
|
|
409
|
+
if (!modal) return;
|
|
410
|
+
modal.attr('tabindex', '-1');
|
|
411
|
+
modal.removeClass('js-swal-fixed');
|
|
414
412
|
}
|
|
415
413
|
}
|
|
416
414
|
}
|