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"
|
||||
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"
|
||||
:readOnly="readOnly"
|
||||
:spacerTop="componentsData[index].props.spacerTop"
|
||||
|
@ -101,6 +107,10 @@
|
|||
<div class="row item action add-continue-container" v-if="visibleAddButtons.continue">
|
||||
<AddTile icon="fas fa-rotate" title="Add Continue" @click="addContinue" />
|
||||
</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>
|
||||
|
@ -117,6 +127,7 @@ import ListItem from "./ListItem"
|
|||
import LoopBlock from "./LoopBlock"
|
||||
import Mixin from "./Mixin"
|
||||
import ReturnTile from "./ReturnTile"
|
||||
import SetVariablesTile from "./SetVariablesTile"
|
||||
import Utils from "@/Utils"
|
||||
|
||||
export default {
|
||||
|
@ -146,6 +157,7 @@ export default {
|
|||
ListItem,
|
||||
LoopBlock,
|
||||
ReturnTile,
|
||||
SetVariablesTile,
|
||||
},
|
||||
|
||||
props: {
|
||||
|
@ -280,6 +292,10 @@ export default {
|
|||
data.props.type = 'while'
|
||||
}
|
||||
|
||||
if (this.isSet(action)) {
|
||||
data.props.value = action.set
|
||||
}
|
||||
|
||||
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() {
|
||||
return this.newStringValue !== this.stringValue
|
||||
},
|
||||
|
@ -463,9 +489,10 @@ export default {
|
|||
this.fors[index] ||
|
||||
this.whiles[index] ||
|
||||
this.isAction(action) ||
|
||||
this.isReturn(action) ||
|
||||
this.isBreak(action) ||
|
||||
this.isContinue(action)
|
||||
this.isContinue(action) ||
|
||||
this.isReturn(action) ||
|
||||
this.isSet(action)
|
||||
) {
|
||||
acc[index] = action
|
||||
}
|
||||
|
@ -481,6 +508,7 @@ export default {
|
|||
condition: this.allowAddButtons,
|
||||
for: this.allowAddButtons,
|
||||
while: this.allowAddButtons,
|
||||
set: this.allowAddButtons,
|
||||
else: (
|
||||
this.allowAddButtons &&
|
||||
this.parent &&
|
||||
|
@ -562,7 +590,7 @@ export default {
|
|||
return
|
||||
}
|
||||
|
||||
event.stopPropagation()
|
||||
event.stopPropagation?.()
|
||||
this.$emit('dragenter', index)
|
||||
},
|
||||
|
||||
|
@ -571,7 +599,7 @@ export default {
|
|||
return
|
||||
}
|
||||
|
||||
event.stopPropagation()
|
||||
event.stopPropagation?.()
|
||||
this.$emit('dragleave', index)
|
||||
},
|
||||
|
||||
|
@ -584,7 +612,7 @@ export default {
|
|||
return
|
||||
}
|
||||
|
||||
event.stopPropagation()
|
||||
event.stopPropagation?.()
|
||||
let dropIndices = []
|
||||
|
||||
if (!event.detail?.length) {
|
||||
|
@ -673,7 +701,7 @@ export default {
|
|||
// 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
|
||||
// text fields in the action tiles.
|
||||
event.stopPropagation()
|
||||
event.stopPropagation?.()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -713,6 +741,11 @@ export default {
|
|||
this.newValue.push('continue')
|
||||
},
|
||||
|
||||
addSet() {
|
||||
this.newValue.push({ 'set': {} })
|
||||
this.selectLastExprEditor()
|
||||
},
|
||||
|
||||
addReturn() {
|
||||
this.newValue.push({ 'return': null })
|
||||
this.selectLastExprEditor()
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
<input class="checkbox"
|
||||
type="checkbox"
|
||||
name="async"
|
||||
ref="async"
|
||||
:checked="async"
|
||||
@input.stop="onInput('async', $event)" />
|
||||
Run in parallel
|
||||
|
@ -76,11 +77,12 @@ export default {
|
|||
onSubmit() {
|
||||
const iterator = this.$refs.iterator.value.trim()
|
||||
const iterable = this.$refs.iterable.value.trim()
|
||||
const async_ = this.$refs.async.checked
|
||||
if (!iterator.length || !iterable.length) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$emit('change', { iterator, iterable })
|
||||
this.$emit('change', { iterator, iterable, async: async_ })
|
||||
},
|
||||
|
||||
onInput(target, event) {
|
||||
|
|
|
@ -18,8 +18,7 @@
|
|||
<i class="fas fa-arrow-rotate-left" />
|
||||
</span>
|
||||
<span class="name" v-if="type === 'for'">
|
||||
<span class="keyword">for<span v-if="async">k</span></span>
|
||||
<span class="code" v-text="iterator" />
|
||||
<span class="keyword">for<span v-if="async">k</span></span> <span class="code" v-text="iterator" />
|
||||
<span class="keyword"> in </span> [
|
||||
<span class="code" v-text="iterable" /> ]
|
||||
</span>
|
||||
|
|
|
@ -67,6 +67,10 @@ export default {
|
|||
|
||||
return this.getKey(value) === 'return'
|
||||
},
|
||||
|
||||
isSet(value) {
|
||||
return this.getKey(value) === 'set'
|
||||
},
|
||||
},
|
||||
}
|
||||
</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