forked from platypush/platypush
[#341] Added support for setting variables in procedure editor.
This commit is contained in:
parent
dfbbea93fd
commit
e7e76087c0
5 changed files with 408 additions and 9 deletions
|
@ -31,6 +31,12 @@
|
||||||
:dragging="isDragging"
|
:dragging="isDragging"
|
||||||
v-else-if="loops[index]" />
|
v-else-if="loops[index]" />
|
||||||
|
|
||||||
|
<SetVariablesTile v-bind="componentsData[index].props"
|
||||||
|
v-on="componentsData[index].on"
|
||||||
|
:collapsed="collapsedBlocks[index]"
|
||||||
|
:dragging="isDragging"
|
||||||
|
v-else-if="sets[index]" />
|
||||||
|
|
||||||
<BreakTile :active="isDragging"
|
<BreakTile :active="isDragging"
|
||||||
:readOnly="readOnly"
|
:readOnly="readOnly"
|
||||||
:spacerTop="componentsData[index].props.spacerTop"
|
:spacerTop="componentsData[index].props.spacerTop"
|
||||||
|
@ -101,6 +107,10 @@
|
||||||
<div class="row item action add-continue-container" v-if="visibleAddButtons.continue">
|
<div class="row item action add-continue-container" v-if="visibleAddButtons.continue">
|
||||||
<AddTile icon="fas fa-rotate" title="Add Continue" @click="addContinue" />
|
<AddTile icon="fas fa-rotate" title="Add Continue" @click="addContinue" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row item action add-set-container" v-if="visibleAddButtons.set">
|
||||||
|
<AddTile icon="fas fa-square-root-variable" title="Set Variables" @click="addSet" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -117,6 +127,7 @@ import ListItem from "./ListItem"
|
||||||
import LoopBlock from "./LoopBlock"
|
import LoopBlock from "./LoopBlock"
|
||||||
import Mixin from "./Mixin"
|
import Mixin from "./Mixin"
|
||||||
import ReturnTile from "./ReturnTile"
|
import ReturnTile from "./ReturnTile"
|
||||||
|
import SetVariablesTile from "./SetVariablesTile"
|
||||||
import Utils from "@/Utils"
|
import Utils from "@/Utils"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -146,6 +157,7 @@ export default {
|
||||||
ListItem,
|
ListItem,
|
||||||
LoopBlock,
|
LoopBlock,
|
||||||
ReturnTile,
|
ReturnTile,
|
||||||
|
SetVariablesTile,
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
|
@ -280,6 +292,10 @@ export default {
|
||||||
data.props.type = 'while'
|
data.props.type = 'while'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isSet(action)) {
|
||||||
|
data.props.value = action.set
|
||||||
|
}
|
||||||
|
|
||||||
return data
|
return data
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -365,6 +381,16 @@ export default {
|
||||||
}, {})
|
}, {})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
sets() {
|
||||||
|
return this.newValue?.reduce?.((acc, action, index) => {
|
||||||
|
if (this.isSet(action)) {
|
||||||
|
acc[index] = action
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, {}) || {}
|
||||||
|
},
|
||||||
|
|
||||||
hasChanges() {
|
hasChanges() {
|
||||||
return this.newStringValue !== this.stringValue
|
return this.newStringValue !== this.stringValue
|
||||||
},
|
},
|
||||||
|
@ -463,9 +489,10 @@ export default {
|
||||||
this.fors[index] ||
|
this.fors[index] ||
|
||||||
this.whiles[index] ||
|
this.whiles[index] ||
|
||||||
this.isAction(action) ||
|
this.isAction(action) ||
|
||||||
this.isReturn(action) ||
|
|
||||||
this.isBreak(action) ||
|
this.isBreak(action) ||
|
||||||
this.isContinue(action)
|
this.isContinue(action) ||
|
||||||
|
this.isReturn(action) ||
|
||||||
|
this.isSet(action)
|
||||||
) {
|
) {
|
||||||
acc[index] = action
|
acc[index] = action
|
||||||
}
|
}
|
||||||
|
@ -481,6 +508,7 @@ export default {
|
||||||
condition: this.allowAddButtons,
|
condition: this.allowAddButtons,
|
||||||
for: this.allowAddButtons,
|
for: this.allowAddButtons,
|
||||||
while: this.allowAddButtons,
|
while: this.allowAddButtons,
|
||||||
|
set: this.allowAddButtons,
|
||||||
else: (
|
else: (
|
||||||
this.allowAddButtons &&
|
this.allowAddButtons &&
|
||||||
this.parent &&
|
this.parent &&
|
||||||
|
@ -562,7 +590,7 @@ export default {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
event.stopPropagation()
|
event.stopPropagation?.()
|
||||||
this.$emit('dragenter', index)
|
this.$emit('dragenter', index)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -571,7 +599,7 @@ export default {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
event.stopPropagation()
|
event.stopPropagation?.()
|
||||||
this.$emit('dragleave', index)
|
this.$emit('dragleave', index)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -584,7 +612,7 @@ export default {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
event.stopPropagation()
|
event.stopPropagation?.()
|
||||||
let dropIndices = []
|
let dropIndices = []
|
||||||
|
|
||||||
if (!event.detail?.length) {
|
if (!event.detail?.length) {
|
||||||
|
@ -673,7 +701,7 @@ export default {
|
||||||
// otherwise the event will be caught by the parent element. If the parent
|
// otherwise the event will be caught by the parent element. If the parent
|
||||||
// is a modal, then the modal will be closed, making it impossible to edit
|
// is a modal, then the modal will be closed, making it impossible to edit
|
||||||
// text fields in the action tiles.
|
// text fields in the action tiles.
|
||||||
event.stopPropagation()
|
event.stopPropagation?.()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,6 +741,11 @@ export default {
|
||||||
this.newValue.push('continue')
|
this.newValue.push('continue')
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addSet() {
|
||||||
|
this.newValue.push({ 'set': {} })
|
||||||
|
this.selectLastExprEditor()
|
||||||
|
},
|
||||||
|
|
||||||
addReturn() {
|
addReturn() {
|
||||||
this.newValue.push({ 'return': null })
|
this.newValue.push({ 'return': null })
|
||||||
this.selectLastExprEditor()
|
this.selectLastExprEditor()
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
<input class="checkbox"
|
<input class="checkbox"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="async"
|
name="async"
|
||||||
|
ref="async"
|
||||||
:checked="async"
|
:checked="async"
|
||||||
@input.stop="onInput('async', $event)" />
|
@input.stop="onInput('async', $event)" />
|
||||||
Run in parallel
|
Run in parallel
|
||||||
|
@ -76,11 +77,12 @@ export default {
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
const iterator = this.$refs.iterator.value.trim()
|
const iterator = this.$refs.iterator.value.trim()
|
||||||
const iterable = this.$refs.iterable.value.trim()
|
const iterable = this.$refs.iterable.value.trim()
|
||||||
|
const async_ = this.$refs.async.checked
|
||||||
if (!iterator.length || !iterable.length) {
|
if (!iterator.length || !iterable.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$emit('change', { iterator, iterable })
|
this.$emit('change', { iterator, iterable, async: async_ })
|
||||||
},
|
},
|
||||||
|
|
||||||
onInput(target, event) {
|
onInput(target, event) {
|
||||||
|
|
|
@ -18,8 +18,7 @@
|
||||||
<i class="fas fa-arrow-rotate-left" />
|
<i class="fas fa-arrow-rotate-left" />
|
||||||
</span>
|
</span>
|
||||||
<span class="name" v-if="type === 'for'">
|
<span class="name" v-if="type === 'for'">
|
||||||
<span class="keyword">for<span v-if="async">k</span></span>
|
<span class="keyword">for<span v-if="async">k</span></span> <span class="code" v-text="iterator" />
|
||||||
<span class="code" v-text="iterator" />
|
|
||||||
<span class="keyword"> in </span> [
|
<span class="keyword"> in </span> [
|
||||||
<span class="code" v-text="iterable" /> ]
|
<span class="code" v-text="iterable" /> ]
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -67,6 +67,10 @@ export default {
|
||||||
|
|
||||||
return this.getKey(value) === 'return'
|
return this.getKey(value) === 'return'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isSet(value) {
|
||||||
|
return this.getKey(value) === 'set'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,361 @@
|
||||||
|
<template>
|
||||||
|
<ListItem class="set-variables-tile"
|
||||||
|
:class="{active}"
|
||||||
|
:value="value"
|
||||||
|
:active="active"
|
||||||
|
:read-only="readOnly"
|
||||||
|
:spacer-bottom="spacerBottom"
|
||||||
|
:spacer-top="spacerTop"
|
||||||
|
v-on="dragListeners"
|
||||||
|
@input="$emit('input', $event)">
|
||||||
|
<Tile v-bind="tileConf.props"
|
||||||
|
v-on="tileConf.on"
|
||||||
|
:draggable="!readOnly"
|
||||||
|
@click.stop="showEditor = true">
|
||||||
|
<div class="tile-name">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-square-root-variable" />
|
||||||
|
</span>
|
||||||
|
<span class="name">
|
||||||
|
<div class="keyword">set</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="variables">
|
||||||
|
<div class="variable" v-for="(value, name) in value" :key="name">
|
||||||
|
<span class="code name" v-text="name" /> =
|
||||||
|
<span class="code value" v-text="value" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tile>
|
||||||
|
|
||||||
|
<div class="editor-container" v-if="showEditor && !readOnly">
|
||||||
|
<Modal title="Set Variables"
|
||||||
|
:visible="true"
|
||||||
|
@close="showEditor = false">
|
||||||
|
<form class="editor" @submit.prevent="onChange">
|
||||||
|
<div class="variable" v-for="(v, i) in newValue" :key="i">
|
||||||
|
<span class="name">
|
||||||
|
<input type="text"
|
||||||
|
placeholder="Variable Name"
|
||||||
|
@blur="onBlur(i)"
|
||||||
|
@input.prevent.stop
|
||||||
|
v-model="newValue[i][0]"> =
|
||||||
|
</span>
|
||||||
|
<span class="value">
|
||||||
|
<input type="text"
|
||||||
|
placeholder="Value"
|
||||||
|
@input.prevent.stop
|
||||||
|
v-model="newValue[i][1]">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="variable">
|
||||||
|
<span class="name">
|
||||||
|
<input type="text"
|
||||||
|
placeholder="Variable Name"
|
||||||
|
ref="newVarName"
|
||||||
|
@input.prevent.stop
|
||||||
|
v-model="newVariable.name"> =
|
||||||
|
</span>
|
||||||
|
<span class="value">
|
||||||
|
<input type="text"
|
||||||
|
placeholder="Value"
|
||||||
|
ref="newVarValue"
|
||||||
|
@blur="onBlur(null)"
|
||||||
|
@input.prevent.stop
|
||||||
|
v-model="newVariable.value">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
</ListItem>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ListItem from "./ListItem"
|
||||||
|
import Modal from "@/components/Modal"
|
||||||
|
import Tile from "@/components/elements/Tile"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
emits: [
|
||||||
|
'click',
|
||||||
|
'delete',
|
||||||
|
'drag',
|
||||||
|
'dragend',
|
||||||
|
'dragenter',
|
||||||
|
'dragleave',
|
||||||
|
'dragover',
|
||||||
|
'drop',
|
||||||
|
'input',
|
||||||
|
],
|
||||||
|
|
||||||
|
components: {
|
||||||
|
ListItem,
|
||||||
|
Modal,
|
||||||
|
Tile,
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
active: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
readOnly: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
spacerBottom: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
spacerTop: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
dragListeners() {
|
||||||
|
return this.readOnly ? {} : {
|
||||||
|
drag: this.onDragStart,
|
||||||
|
dragend: this.onDragEnd,
|
||||||
|
dragenter: (event) => this.$emit('dragenter', event),
|
||||||
|
dragleave: (event) => this.$emit('dragleave', event),
|
||||||
|
dragover: (event) => this.$emit('dragover', event),
|
||||||
|
drop: this.onDrop,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
tileConf() {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
value: this.value,
|
||||||
|
class: 'keyword',
|
||||||
|
readOnly: this.readOnly,
|
||||||
|
withDelete: !this.readOnly,
|
||||||
|
},
|
||||||
|
|
||||||
|
on: {
|
||||||
|
...this.dragListeners,
|
||||||
|
delete: () => this.$emit('delete'),
|
||||||
|
input: this.onInput,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dragging: false,
|
||||||
|
newValue: [],
|
||||||
|
newVariable: {
|
||||||
|
name: '',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
showEditor: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onChange() {
|
||||||
|
this.showEditor = false
|
||||||
|
if (this.readOnly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const variables = this.newValue
|
||||||
|
if (this.newVariable.name?.trim?.()?.length) {
|
||||||
|
variables.push([this.newVariable.name, this.newVariable.value])
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = variables.map(([name, value]) => {
|
||||||
|
name = this.sanitizeName(name)
|
||||||
|
try {
|
||||||
|
value = JSON.parse(value)
|
||||||
|
} catch (e) {
|
||||||
|
value = value?.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
return [name, value]
|
||||||
|
})
|
||||||
|
.reduce((acc, [name, value]) => {
|
||||||
|
if (!name?.length) {
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[name] = value
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
if (!Object.keys(args).length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onInput(args)
|
||||||
|
},
|
||||||
|
|
||||||
|
onInput(value) {
|
||||||
|
if (!value || this.readOnly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('input', {set: value})
|
||||||
|
},
|
||||||
|
|
||||||
|
onBlur(index) {
|
||||||
|
if (this.readOnly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index != null) {
|
||||||
|
const name = this.sanitizeName(this.newValue[index][0])
|
||||||
|
if (!name?.length) {
|
||||||
|
this.newValue.splice(index, 1)
|
||||||
|
} else {
|
||||||
|
this.newValue[index][0] = name
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const name = this.sanitizeName(this.newVariable.name)
|
||||||
|
const value = this.newVariable.value
|
||||||
|
|
||||||
|
if (name?.length) {
|
||||||
|
this.newValue.push([name, value])
|
||||||
|
this.newVariable = {
|
||||||
|
name: '',
|
||||||
|
value: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.newVarName?.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragStart(event) {
|
||||||
|
if (this.readOnly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dragging = true
|
||||||
|
this.$emit('drag', event)
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragEnd(event) {
|
||||||
|
this.dragging = false
|
||||||
|
this.$emit('dragend', event)
|
||||||
|
},
|
||||||
|
|
||||||
|
onDrop(event) {
|
||||||
|
this.dragging = false
|
||||||
|
if (this.readOnly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('drop', event)
|
||||||
|
},
|
||||||
|
|
||||||
|
sanitizeName(name) {
|
||||||
|
return name?.trim()?.replace(/[^\w_]/g, '_')
|
||||||
|
},
|
||||||
|
|
||||||
|
syncValue() {
|
||||||
|
this.newValue = Object.entries(this.value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
showEditor(value) {
|
||||||
|
if (!value) {
|
||||||
|
this.newVariable = {
|
||||||
|
name: '',
|
||||||
|
value: '',
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.newVarName?.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
value: {
|
||||||
|
immediate: true,
|
||||||
|
handler() {
|
||||||
|
this.syncValue()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.syncValue()
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.newVarName?.focus()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "common";
|
||||||
|
|
||||||
|
.set-variables-tile {
|
||||||
|
&.active {
|
||||||
|
.spacer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.variables {
|
||||||
|
margin-left: 2.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.variable {
|
||||||
|
.value {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-spacer {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
.variable {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.value {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in a new issue